	 ______   ___    ___
	/\  _  \ /\_ \  /\_ \
	\ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
	 \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
	  \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
	   \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
	    \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
					   /\____/
					   \_/__/     Version 2.2


	       A game programming library for djgpp

		   By Shawn Hargreaves, 1994/97



#include <std_disclaimer.h>

   "I do not accept responsibility for any effects, adverse or otherwise, 
    that this code may have on you, your computer, your sanity, your dog, 
    and anything else that you can think of. Use it at your own risk."



=======================================
============ Using Allegro ============
=======================================

See readme.txt for a general introduction, copyright details, and 
information about how to compile Allegro.

All the Allegro functions, variables, and data structures are defined in 
allegro.h. You should include this in your programs, and link with 
liballeg.a. To do this you should:

   - Put the following line at the beginning of all C or C++ files that use 
     Allegro:

	 #include <allegro.h>

   - If you compile from the command line or with a makefile, add '-lalleg' 
     to the end of the gcc command, eg:

	 gcc foo.c -o foo.exe -lalleg

   - If you are using Rhide, go to the Options/Libraries menu, type 'alleg' 
     into the first empty space, and make sure the box next to it is checked.

extern char allegro_id[];
   Text string containing a date and version number for the library, in case 
   you want to display these somewhere.

extern char allegro_error[];
   Text string used by set_gfx_mode() and install_sound() to report error 
   messages. If they fail and you want to tell the user why, this is the 
   place to look for a description of the problem.

extern int windows_version, windows_sub_version;
   These are set by allegro_init(), using int 0x2F, ax=0x1600 to detect the 
   presence of Microsoft Windows. Under windows 3.1, windows_version will be 
   set to 3, under win95 it will be 4, under NT it will be 0x100, and if 
   windows isn't present it will be zero.

int allegro_init();
   Initialises the Allegro library. It doesn't actually do very much except 
   setting up some global variables, locking some memory, and installing 
   allegro_exit() as an atexit() routine, but you must call it before doing 
   anything else. Returns zero for success (at the moment it can't fail, so 
   there is not much point checking the return value).

void allegro_exit();
   Closes down the Allegro system. This includes returning the system to 
   text mode and removing whatever mouse, keyboard, and timer routines have 
   been installed. You don't normally need to bother making an explicit call 
   to this function, because allegro_init() installs it as an atexit() 
   routine so it will be called automatically when your program exits.



========================================
============ Mouse routines ============
========================================

The Allegro mouse handler runs on top of the DOS int33 mouse driver, so it 
will only work when the DOS driver (usually mouse.com or mouse.exe) is 
active. It is useful as a simple wrapper for the int33 functions, and also 
because it can display mouse pointers in SVGA modes, which most 
implementations of the standard driver cannot.

int install_mouse();
   Installs the Allegro mouse handler. You must do this before using any 
   other mouse functions. Returns -1 on failure (ie. if the int33 driver is 
   not loaded), otherwise the number of buttons on the mouse.

void remove_mouse();
   Removes the mouse handler. You don't normally need to bother calling 
   this, because allegro_exit() will do it for you.

extern volatile int mouse_x;
extern volatile int mouse_y;
extern volatile int mouse_b;
   Global variables containing the current mouse position and button state. 
   The mouse_x and mouse_y positions are integers ranging from zero to the 
   bottom right corner of the screen. The mouse_b variable is a bitfield 
   indicating the state of each button: bit 0 is the left button, bit 1 the 
   right, and bit 2 the middle button. For example:

      if (mouse_b & 1)
	 printf("Left button is pressed\n");

      if (!(mouse_b & 2))
	 printf("Right button is not pressed\n");

void show_mouse(BITMAP *bmp);
   Tells Allegro to display a mouse pointer on the screen. This will only 
   work if the timer module has been installed. The mouse pointer will be 
   drawn onto the specified bitmap, which should normally be 'screen' (see 
   later for information about bitmaps). To hide the mouse pointer, call 
   show_mouse(NULL). Warning: if you draw anything onto the screen while the 
   pointer is visible, a mouse movement interrupt could occur in the middle 
   of your drawing operation. If this happens the mouse buffering and SVGA 
   bank switching code will get confused and will leave 'mouse droppings' 
   all over the screen. To prevent this, you must make sure you turn off the 
   mouse pointer whenever you draw onto the screen.

extern int freeze_mouse_flag;
   If this flag is set, the mouse pointer won't be redrawn when the mouse 
   moves. This can avoid the need to hide the pointer every time you draw to 
   the screen, as long as you make sure your drawing doesn't overlap with 
   the current pointer position.

void position_mouse(int x, int y);
   Moves the mouse to the specified screen position. It is safe to call even 
   when a mouse pointer is being displayed.

void set_mouse_range(int x1, int y1, int x2, int y2);
   Sets the area of the screen within which the mouse can move. Pass the top 
   left corner and the bottom right corner (inclusive). If you don't call 
   this function the range defaults to (0, 0, SCREEN_W-1, SCREEN_H-1).

void set_mouse_speed(int xspeed, int yspeed);
   Sets the mouse speed. Larger values of xspeed and yspeed represent slower 
   mouse movement: the default for both is 2.

void set_mouse_sprite(BITMAP *sprite);
   You don't like my mouse pointer? No problem. Use this function to supply 
   an alternative of your own. If you change the pointer and then want to 
   get my lovely arrow back again, call set_mouse_sprite(NULL).

void set_mouse_sprite_focus(int x, int y);
   The mouse focus is the bit of the pointer that represents the actual 
   mouse position, ie. the (mouse_x, mouse_y) position. By default this is 
   the top left corner of the arrow, but if you are using a different mouse 
   pointer you might need to alter it.



========================================
============ Timer routines ============
========================================

The standard PC clock only ticks 18.2 times a second, which is not much good 
for fast action games. Allegro can replace the system timer routine with a 
custom one, which reprograms the clock for higher tick rates while still 
calling the BIOS handler at the old speed. You can set up several virtual 
timers of your own, all going at different speeds, and Allegro will 
constantly reprogram the clock to make sure they are all called at the 
correct times.

int install_timer();
   Installs the Allegro timer interrupt handler. You must do this before 
   installing any user timer routines, and also before displaying a mouse 
   pointer, playing FLI animations or MIDI music, and using any of the GUI 
   routines.

void remove_timer();
   Removes the Allegro timer handler and passes control of the clock back to 
   the BIOS. You don't normally need to bother calling this, because 
   allegro_exit() will do it for you.

int install_int(void (*proc)(), int speed);
   Installs a user timer handler, with the speed given as the number of 
   milliseconds between ticks. This is the same thing as 
   install_int_ex(proc, MSEC_TO_TIMER(speed)).

int install_int_ex(void (*proc)(), int speed);
   Adds a function to the list of user timer handlers, or if it is already 
   installed, adjusts its speed. The speed is given in hardware clock ticks, 
   of which there are 1193181 a second. You can convert from other time 
   formats to hardware clock ticks with the macros:

      SECS_TO_TIMER(secs)  - give the number of seconds between each tick
      MSEC_TO_TIMER(msec)  - give the number of milliseconds between ticks
      BPS_TO_TIMER(bps)    - give the number of ticks each second
      BPM_TO_TIMER(bpm)    - give the number of ticks per minute

   If there is no room to add a new user timer, install_int_ex() will return 
   a negative number, otherwise it returns zero. There can only be eight 
   timers in use at a time, and some other parts of Allegro (the GUI code, 
   the mouse pointer display routines, rest(), the FLI player, and the MIDI 
   player) need to install handlers of their own, so you should avoid using 
   more than two or three at a time.

   Your function will be called by the Allegro interrupt handler and not 
   directly by the processor, so it can be a normal C function and does not 
   need a special wrapper. You should be aware, however, that it will be 
   called in an interrupt context, which imposes a lot of restrictions on 
   what you can do in it. It should not use large amounts of stack, it must 
   not make any calls to DOS or use C library functions which may in turn 
   call DOS routines, and it must execute very quickly. Don't try to do lots 
   of complicated code in a timer handler: as a general rule you should just 
   set some flags and respond to these later in your main control loop.

   If you are programming in C++, you will notice that gcc complains when 
   you pass your handler function to install_int() or install_int_ex(). To 
   avoid this you should declare it as taking a variable number of 
   arguments, eg. void my_timer_handler(...);

   In a protected mode environment like djgpp, memory is virtualised and can 
   be swapped to disk. Due to the non-reentrancy of DOS, if a disk swap 
   occurs inside an interrupt handler the system will die a painful death, 
   so you need to make sure you lock all the memory (both code and data) 
   that is touched inside timer routines. Allegro will lock everything it 
   uses, but you are responsible for locking your handler functions. The 
   macros LOCK_VARIABLE(variable), END_OF_FUNCTION(function_name), and 
   LOCK_FUNCTION(function_name) can be used to simplify this task. For 
   example, if you want an interrupt handler that increments a counter 
   variable, you should write:

      volatile int counter;

      void my_timer_handler()
      {
	 counter++;
      }

      END_OF_FUNCTION(my_timer_handler);

   and in your initialisation code you should lock the memory:

      LOCK_VARIABLE(counter);
      LOCK_FUNCTION(my_timer_handler);

   Obviously this can get awkward if you use complicated data structures and 
   call other functions from within your handler, so you should try to keep 
   your interrupt routines as simple as possible.

void remove_int(void (*proc)());
   Removes a function from the list of user interrupt routines. At program 
   termination, allegro_exit() does this automatically.

extern int i_love_bill;
   If set to TRUE, enables the special 'windows friendly' timer mode, which 
   locks the hardware timer interrupt to a speed of 200 ticks per second 
   rather than dynamically reprogramming it. This mode reduces the accuracy 
   of the timer (for instance, rest() will round the delay time to the 
   nearest 5 milliseconds), and prevents the vertical retrace simulator from 
   working (calls to timer_simulate_retrace() are ignored while in this 
   mode). On the plus side, though, it makes Allegro programs work under 
   windows 3.1, and stops win95 from complaining that it should be run in 
   DOS mode. This flag should be set before you install the timer module, 
   and you should not change it while the timer is active. By default, 
   allegro_init() will enable this mode if it detects the presence of 
   Windows.

The Allegro timer handler can also be used to simulate vertical retrace 
interrupts. Retrace interrupts are often extremely useful for implementing 
smooth animation, and are essential for triple buffering algorithms, but 
unfortunately the VGA doesn't support them. The EGA did, and some SVGA 
chipsets do, but not enough, and not in a sufficiently standardised way, for 
it to be useful. Allegro works around this by programming the timer to 
generate an interrupt when it thinks a retrace is next likely to occur, and 
polling the VGA inside the interrupt handler to make sure it stays in sync 
with the monitor refresh. This works quite well in some situations, but 
there are a lot of caveats:

   - Don't ever use the retrace simulator in SVGA modes. It will work with 
     some chipsets, but not others, and it conflicts with most VESA 
     implementations. Retrace simulation is only reliable in VGA mode 13h 
     and mode-X.

   - Retrace simulation doesn't work under win95, because win95 returns 
     garbage when I try to read the elapsed time from the PIT. If anyone 
     knows how I can make this work, please tell me!

   - Retrace simulation involves a lot of waiting around in the timer 
     handler with interrupts disabled. This will significantly slow down 
     your entire system, and may also cause static when playing samples on 
     SB 1.0 cards (because they don't support auto-initialised DMA: SB 2.0 
     and above will be fine).

Bearing all those problems in mind, I'd strongly advise against relying on 
the retrace simulator. If you are coding in mode-X, and don't care about 
your program working under win95, it is great, but it would be a good idea 
to give the user an option to disable it.

extern volatile int retrace_count;
   If the retrace simulator is installed, this is incremented on each 
   vertical retrace, otherwise it is incremented 70 times a second (ignoring 
   retraces). This provides a useful way of controlling the speed of your 
   program without the hassle of installing user timer functions.

   The speed of retraces varies depending on the graphics mode. In mode 13h 
   and 200/400 line mode-X resolutions there are 70 retraces a second, and 
   in 240/480 line modes there are 60. It can be as low as 50 (in 376x282 
   mode) or as high as 92 (in 400x300 mode).

extern void (*retrace_proc)();
   If the retrace simulator is installed, this function is called during 
   every vertical retrace, otherwise it is called 70 times a second 
   (ignoring retraces). Set it to NULL to disable the callback. The function 
   must obey the same rules as regular timer handlers (ie. it must be 
   locked, and mustn't call DOS or libc functions) but even more so: it must 
   execute _very_ quickly, or it will mess up the timer synchronisation. The 
   only use I can see for this function is for doing palette manipulations, 
   since triple buffering can be done with the request_modex_scroll() 
   function, and the retrace_count variable can be used for timing your 
   code. If you want to alter the palette in the retrace_proc you should use 
   the inline _set_color() function rather than the regular set_color() or 
   set_palette(), and you shouldn't try to alter more than two or three 
   palette entries in a single retrace.

void timer_simulate_retrace(int enable);
   Turns the vertical retrace simulator on or off (the default is off). When 
   retrace interrupt simulation is enabled, the retrace_count variable and 
   retrace_proc callback will operate in exact synchronisation with the 
   monitor refresh, rather than just assuming 70 ticks a second.

   Retrace simulation must be enabled before you use the triple buffering 
   request_modex_scroll() function. It can also be useful for simple retrace 
   detection, because the polling vsync() function can occassionally miss 
   retraces if a soundcard or timer interrupt occurs at exactly the same 
   time as the retrace. When retrace interrupt simulation is enabled, 
   vsync() will check the retrace_count variable rather than polling the 
   VGA, so it won't miss retraces even if they are masked by other 
   interrupts.

   Don't ever enable retrace simulation in SVGA modes, or under win95! It 
   won't work (see above).

void rest(long time);
   Once Allegro has taken over the timer the standard delay() function will 
   no longer work, so you should use this routine instead. The time is given 
   in milliseconds.



===========================================
============ Keyboard routines ============
===========================================

If you want to detect multiple keypresses at the same time, the BIOS 
keyboard routines are useless. Allegro can install a replacement keyboard 
handler, which provides both buffered input and a set of flags storing the 
state of each key. Note that it is not possible to correctly detect every 
combination of keys, due to the design of the PC keyboard. Up to two or 
three keys at a time will work fine, but if you press more than that the 
extras are likely to be ignored (exactly which combinations are possible 
seems to vary from one keyboard to another).

int install_keyboard();
   Installs the Allegro keyboard interrupt handler. You must call this 
   before using any of the keyboard input routines. Once you have set up the 
   Allegro handler, you can no longer use DOS/BIOS calls or C library 
   functions to access the keyboard.

void remove_keyboard();
   Removes the keyboard handler, returning control to the BIOS. You don't 
   normally need to bother calling this, because allegro_exit() will do it 
   for you.

void install_keyboard_hooks(int (*keypressed)(), int (*readkey)());
   You should only use this function if you *aren't* using the rest of the 
   keyboard handler. It should be called in the place of install_keyboard(), 
   and lets you provide callback routines to detect and read keypresses, 
   which will be used by the main keypressed() and readkey() functions. This 
   can be useful if you want to use Allegro's GUI code with a custom 
   keyboard handler, as it provides a way for the GUI to access keyboard 
   input from your own code. If you want to use the BIOS keyboard routines, 
   the libc function _bios_keybrd(_KEYBRD_READ) returns keypresses in the 
   correct format.

extern volatile char key[128];
   Array of flags indicating the state of each key, ordered by scancode. The 
   scancodes are defined in allegro.h as a series of KEY_* constants. For 
   example, you could write:

      if (key[KEY_SPACE])
	 printf("Space is pressed\n");

extern volatile int key_shifts;
   Bitmask containing the current shift/ctrl/alt state. This can contain any 
   of the flags:

      KB_SHIFT_FLAG
      KB_CTRL_FLAG
      KB_ALT_FLAG
      KB_SCROLOCK_FLAG
      KB_NUMLOCK_FLAG
      KB_CAPSLOCK_FLAG
      KB_INALTSEQ_FLAG

int keypressed();
   Returns TRUE if there are keypresses waiting in the input buffer. This is 
   equivalent to the libc kbhit() function.

int readkey();
   Returns the next character from the keyboard buffer. If the buffer is 
   empty, it waits until a key is pressed. The low byte of the return value 
   contains the ASCII code of the key, and the high byte the scancode. The 
   scancode remains the same whatever the state of the shift, ctrl and alt 
   keys. The ASCII code is affected by shift and ctrl in the normal way 
   (shift changes case, ctrl+letter gives the position of that letter in the 
   alphabet, eg. ctrl+A = 1, ctrl+B = 2, etc). Pressing alt+key returns only 
   the scancode, with a zero ASCII code in the low byte. For example:

      if ((readkey() & 0xff) == 'd')         // by ASCII code
	 printf("You pressed 'd'\n");

      if ((readkey() >> 8) == KEY_SPACE)     // by scancode
	 printf("You pressed Space\n");

      if ((readkey() & 0xff) == 3)           // ctrl+letter
	 printf("You pressed Control+C\n");

      if (readkey() == (KEY_X << 8))         // alt+letter
	 printf("You pressed Alt+X\n");

void simulate_keypress(int key);
   Stuffs a key into the keyboard buffer, just as if the user had pressed 
   it. The parameter is in the same format returned by readkey().

void clear_keybuf();
   Empties the keyboard buffer.

extern int three_finger_flag;
   The Allegro keyboard handler provides an 'emergency exit' sequence which 
   you can use to kill off your program. If you are running under DOS this 
   is the three finger salute, ctrl+alt+del. Most multitasking OS's will 
   trap this combination before it reaches the Allegro handler, in which 
   case you can use the alternative ctrl+alt+end. If you want to disable 
   this behaviour in release versions of your program, set this flag to 
   FALSE.

extern int key_led_flag;
   By default, the capslock, numlock, and scroll-lock keys toggle the 
   keyboard LED indicators when they are pressed. If you are using these 
   keys for input in your game (eg. capslock to fire) this may not be 
   desirable, so you can clear this flag to prevent the LED's being updated.



===========================================
============ Joystick routines ============
===========================================

Getting input from the PC joystick is not a pleasant task. Not because it is 
particularly complicated, but because it depends on a high-precision timing 
loop, and the exact results of this can vary depending on the type of 
joystick, the speed of your computer, the temperature of the room, and the 
phases of the moon. If you want to get meaningful input you have to 
calibrate the joystick before using it, which is a royal pain in the arse.

extern int joy_type;
   Global variable telling the joystick code what sort of joystick is 
   plugged in. Possible values are:

   JOY_TYPE_STANDARD
      A normal two button stick. This is the default, and should work with 
      any joystick.

   JOY_TYPE_FSPRO
      If you have a CH Flightstick Pro, and want to use the cool features it 
      provides (four buttons, an analog throttle, and a 4-direction coolie 
      hat), you should set this value before calling any other joystick 
      functions.

   JOY_TYPE_4BUTTON
      Set this value to enable the extra buttons on a 4-button joystick (eg. 
      the Gravis Gamepad).

int initialise_joystick();
   Initialises the joystick, and calibrates the centre position value. You 
   must call this before using any other joystick functions, and you should 
   make sure the user has centered the joystick when you do. Returns 
   non-zero if there is no joystick present (in which case you can still 
   call the other joystick routines, but they will return zero positions).

void poll_joystick();
   Unlike the mouse and keyboard, the joystick is not interrupt driven, so 
   you need to call this function every now and again to update the global 
   position values.

extern int joy_left, joy_right, joy_up, joy_down;
   These variables simulate a digital joystick. They are updated by 
   poll_joystick(), and will contain non-zero if the joystick is moved in 
   the relevant direction. You don't need to fully calibrate the joystick in 
   order to use these: just initialise_joystick() is enough (this is how the 
   demo game accesses the joystick).

extern int joy_b1, joy_b2;
   Joystick button states. Call poll_joystick() to update them. Note that 
   these values tend to bounce between on and off a few times when the 
   button is pressed or released, so in some situations you may need to add 
   a small delay after the buttons change state (see test.c for an example).

extern int joy_b3, joy_b4;
   Like the two standard buttons, these are updated by poll_joystick(). They 
   will only contain valid data if joy_type is set to JOY_TYPE_FSPRO or 
   JOY_TYPE_4BUTTON. You can also access the Flightstick Pro buttons with 
   the aliases (from allegro.h):

      #define joy_FSPRO_trigger     joy_b1
      #define joy_FSPRO_butleft     joy_b2
      #define joy_FSPRO_butright    joy_b3
      #define joy_FSPRO_butmiddle   joy_b4

extern int joy_hat;
   Updated by poll_joystick(). This will only contain valid data if joy_type 
   equals JOY_TYPE_FSPRO. It can have one of the following values (these are 
   mutually exclusive):

      JOY_HAT_CENTRE
      JOY_HAT_UP
      JOY_HAT_DOWN
      JOY_HAT_LEFT
      JOY_HAT_RIGHT

int calibrate_joystick_tl();
int calibrate_joystick_br();
   If you want to get analogue stick position information, rather than just 
   using the joy_left, joy_right, joy_up, and joy_down variables, you need 
   to individually calibrate the top left, bottom right, and centre joystick 
   positions. Calling initialise_joystick() will calibrate the centre, and 
   must be done first. After this you can tell the user to move the joystick 
   to the top left and bottom right extremes, and call the appropriate 
   calibration routine for each corner. See test.c for an example. After 
   doing this, you can use the position variables:

extern int joy_x, joy_y;
   Analogue axis positions, ranging from -128 to 128. You must fully 
   calibrate the joystick before using these variables: see above. Call 
   poll_joystick() to update them.

int calibrate_joystick_throttle_min();
int calibrate_joystick_throttle_max();
   There is seemingly no end to the things that need calibrating :-) To use 
   the Flightstick Pro's analogue throttle, call these functions with the 
   throttle in the minimum and maximum positions respectively. After doing 
   this you can use the variable:

extern int joy_throttle;
   Throttle position, ranging from 0 to 255 (whether 0 is fully forward or 
   fully backward depends on how the user calibrated the throttle: some 
   people prefer to use the throttle backwards to the way most people use 
   it). This is updated by poll_joystick(), and only contains valid data 
   when joy_type = JOY_TYPE_FSPRO and you have calibrated the throttle.

int save_joystick_data(char *filename);
   After all the headache of calibrating the joystick, you may not want to 
   make your poor users repeat the process every time they run your program. 
   Call this function to save the joystick calibration data into the 
   specified file, from which it can later be read by load_joystick_data(). 
   Returns zero on success.

int load_joystick_data(char *filename);
   Restores calibration data previously stored by save_joystick_data(). This 
   sets up all aspects of the joystick code: you don't even need to call 
   initialise_joystick() if you are using this function. Returns zero on 
   success: if it fails the joystick state is undefined and you must 
   reinitialise and calibrate it.



====================================================
============ Color and palette routines ============
====================================================

All the Allegro drawing functions use integer parameters to represent 
colors. The format of these integers depends on the current video mode (at 
the moment Allegro only supports 8 bit, 256 color modes, but I am planning 
to add 15, 16, and 32 bit modes in the next release, so I've added some 
functions for handling different color formats). In 256 color modes, color 
values are treated as indexes into the current palette, which specifies the 
red, green and blue intensities for each of the 256 possible colors. In 
truecolor modes the red, green, and blue components are stored directly in 
the color value, so there is no need for a palette.

Palette entries are stored in an RGB structure, which contains red, green 
and blue intensities in the VGA hardware format, ranging from 0-63, and is 
defined as:

typedef struct RGB
{
   unsigned char r, g, b;
} RGB;

For example:

   RGB black = { 0,  0,  0  };
   RGB white = { 63, 63, 63 };
   RGB green = { 0,  63, 0  };
   RGB grey  = { 32, 32, 32 };

The type PALETTE is defined to be an array of 256 RGB structures.

void vsync();
   Waits for a vertical retrace to begin. The retrace happens when the 
   electron beam in your monitor has reached the bottom of the screen and is 
   moving back to the top ready for another scan. During this short period 
   the graphics card isn't sending any data to the monitor, so you can do 
   things to it that aren't possible at other times, such as altering the 
   palette without causing flickering (snow). Allegro will automatically 
   wait for a retrace before altering the palette or doing any hardware 
   scrolling, though, so you don't normally need to bother with this 
   function.

void set_color(int index, RGB *p);
   Sets the specified palette entry to the specified RGB triplet. Unlike the 
   other palette functions this doesn't do any retrace synchronisation, so 
   you should call vsync() before it to prevent snow problems.

void _set_color(int index, RGB *p);
   This is an inline version of set_color(), intended for use in the 
   vertical retrace simulator callback function. It should only be used in 
   VGA mode 13h and mode-X, because some of the more recent SVGA chipsets 
   aren't VGA compatible (set_color() and set_palette() will use VESA calls 
   on these cards, but _set_color() doesn't know about that).

void set_palette(PALETTE p);
   Sets the entire palette of 256 colors. You should provide an array of 256 
   RGB structures. Unlike set_color(), there is no need to call vsync() 
   before this function.

void set_palette_range(PALETTE p, int from, int to, int vsync);
   Sets the palette entries between from and to (inclusive: pass 0 and 255 
   to set the entire palette). If vsync is set it waits for the vertical 
   retrace, otherwise it sets the colors immediately.

void get_color(int index, RGB *p);
   Retrieves the specified palette entry.

void get_palette(PALETTE p);
   Retrieves the entire palette of 256 colors. You should provide an array 
   of 256 RGB structures to store it in.

void get_palette_range(PALETTE p, int from, int to);
   Retrieves the palette entries between from and to (inclusive: pass 0 and 
   255 to get the entire palette).

void fade_interpolate(PALETTE source, dest, output, int pos, int from, to);
   Calculates a temporary palette part way between source and dest, 
   returning it in the output parameter. The position between the two 
   extremes is specified by the pos value: 0 returns an exact copy of 
   source, 64 returns dest, 32 returns a palette half way between the two, 
   etc. This routine only affects colors between from and to (inclusive: 
   pass 0 and 255 to interpolate the entire palette).

void fade_from_range(PALETTE source, dest, int speed, int from, to);
   Gradually fades a part of the palette from the source palette to the dest 
   palette. The speed is from 1 (the slowest) up to 64 (instantaneous). This 
   routine only affects colors between from and to (inclusive: pass 0 and 
   255 to fade the entire palette).

void fade_in_range(PALETTE p, int speed, int from, to);
   Gradually fades a part of the palette from a black screen to the 
   specified palette. The speed is from 1 (the slowest) up to 64 
   (instantaneous). This routine only affects colors between from and to 
   (inclusive: pass 0 and 255 to fade the entire palette).

void fade_out_range(int speed, int from, to);
   Gradually fades a part of the palette from the current palette to a black 
   screen. The speed is from 1 (the slowest) up to 64 (instantaneous). This 
   routine only affects colors between from and to (inclusive: pass 0 and 
   255 to fade the entire palette).

void fade_from(PALETTE source, PALETTE dest, int speed);
   Fades gradually from the source palette to the dest palette. The speed is 
   from 1 (the slowest) up to 64 (instantaneous).

void fade_in(PALETTE p, int speed);
   Fades gradually from a black screen to the specified palette. The speed 
   is from 1 (the slowest) up to 64 (instantaneous).

void fade_out(int speed);
   Fades gradually from the current palette to a black screen. The speed is 
   from 1 (the slowest) up to 64 (instantaneous).

extern PALETTE black_palette;
   A palette containing solid black colors, used by the fade routines.

extern PALETTE desktop_palette;
   The palette used by the Atari ST low resolution desktop. I'm not quite 
   sure why this is still here, except that the grabber and test programs 
   use it. It is probably the only Atari legacy code left in Allegro, and it 
   would be a shame to remove it :-)

void hsv_to_rgb(float h, float s, float v, RGB *rgb);
void rgb_to_hsv(RGB *rgb, float *h, float *s, float *v);
   Convert color values between the HSV and RGB colorspaces.

int makecol8(int r, int g, int b);
int makecol15(int r, int g, int b);
int makecol16(int r, int g, int b);
int makecol32(int r, int g, int b);
   These functions convert colors from a hardware independent form (red, 
   green, and blue values ranging 0-255) into various display dependent 
   pixel formats (until I finish implementing truecolor modes, makecol8 is 
   the only one of these that is really any use). Converting to 15, 16, or 
   32 bit formats only takes a few shifts, so it is fairly efficient. 
   Converting to an 8 bit color involves searching the palette to find the 
   closest match, which is very slow unless you have set up an RGB mapping 
   table (see below).

int makecol(int r, int g, int b);
   Converts colors from a hardware independent format (red, green, and blue 
   values ranging 0-255) to the pixel format required by the current video 
   mode, calling the preceding 8, 15, 16, or 32 bit makecol functions as 
   appropriate.

int getr8(int c);
int getg8(int c);
int getb8(int c);
int getr15(int c);
int getg15(int c);
int getb15(int c);
int getr16(int c);
int getg16(int c);
int getb16(int c);
int getr32(int c);
int getg32(int c);
int getb32(int c);
   Given a color in a display dependent format, these functions extract one 
   of the red, green, or blue components (ranging 0-255).

int getr(int c);
int getg(int c);
int getb(int c);
   Given a color in the format being used by the current video mode, these 
   functions extract one of the red, green, or blue components (ranging 
   0-255), calling the preceding 8, 15, 16, or 32 bit get functions as 
   appropriate.

To speed up converting RGB values to 8 bit paletted colors, Allegro uses a 
32k lookup table (5 bits for each color component). You must set up one of 
these tables before using the gouraud shading routines, and if present the 
table will also vastly accelerate the makecol8() function. RGB tables can be 
precalculated with the rgbmap utility, or generated at runtime. They are 
stored in the structure:

typedef struct {
   unsigned char data[32][32][32];
} RGB_MAP;

extern RGB_MAP *rgb_map;
   Global pointer to the current RGB mapping table (defaults to NULL).

void create_rgb_table(RGB_MAP *table, PALETTE pal, void (*callback)(int pos));
   Fills the specified RGB mapping table with lookup data for the specified 
   palette. If the callback function is not NULL, it will be called 256 
   times during the calculation, allowing you to display a progress 
   indicator.

Translucency and lighting are implemented with 64k lookup tables, which 
contain the result of combining any two colors c1 and c2. You must set up 
one of these tables before you use any of the lighting or translucency 
routines. Depending on how you construct the table, a range of different 
effects are possible. For example, translucency can be implemented by using 
a color halfway between c1 and c2 as the result of the combination. Lighting 
is achieved by treating one of the colors as a light level (0-255) rather 
than a color, and setting up the table appropriately. A range of specialised 
effects are possible, for instance replacing any color with any other color 
and making individual source or destination colors completely solid or 
invisible.

Color mapping tables can be precalculated with the colormap utility, or 
generated at runtime. They are stored in the structure:

typedef struct {
   unsigned char data[PAL_SIZE][PAL_SIZE];
} COLOR_MAP;

extern COLOR_MAP *color_map;
   Global pointer to the current color mapping table (defaults to NULL).

void create_light_table(COLOR_MAP *table, PALETTE pal,
                        int r, g, b, void (*callback)(int pos));
   Fills the specified color mapping table with lookup data for doing 
   lighting effects with the specified palette. When combining the colors c1 
   and c2 with this table, c1 is treated as a light level from 0-255. At 
   light level 255 the table will output color c2 unchanged, at light level 
   0 it will output the r, g, b value you specify to this function, and at 
   intermediate light levels it will output a color somewhere between the 
   two extremes. If the callback function is not NULL, it will be called 256 
   times during the calculation, allowing you to display a progress 
   indicator.

void create_trans_table(COLOR_MAP *table, PALETTE pal,
                        int r, g, b, void (*callback)(int pos));
   Fills the specified color mapping table with lookup data for doing 
   translucency effects with the specified palette. When combining the 
   colors c1 and c2 with this table, the result will be a color somewhere 
   between the two. The r, g, and b parameters specifiy the solidity of each 
   color component, ranging from 0 (totally transparent) to 255 (totally 
   solid). For 50% solidity, pass 128. This function treats source color #0 
   as a special case, leaving the destination unchanged whenever a zero 
   source pixel is encountered, so that masked sprites will draw correctly. 
   If the callback function is not NULL, it will be called 256 times during 
   the calculation, allowing you to display a progress indicator.

void create_color_table(COLOR_MAP *table, PALETTE pal,
                        RGB (*blend)(PALETTE pal, int x, int y),
                        void (*callback)(int pos));
   Fills the specified color mapping table with lookup data for doing 
   customised effects with the specified palette, calling the blend function 
   to determine the results of each color combination. Your blend routine 
   will be passed a pointer to the palette and the two colors which are to 
   be combined, and should return the desired result in RGB (0-63) format. 
   If the callback function is not NULL, it will be called 256 times during 
   the calculation, allowing you to display a progress indicator.

Note: you will notice that a lot of the code in Allegro spells 'palette' as 
'pallete'. This is because the headers from my old Mark Williams compiler on 
the Atari spelt it with two l's, and that is what I'm used to. Allegro will 
happily accept either spelling, due to some #defines in allegro.h.



====================================================
============ Screen and bitmap routines ============
====================================================

void set_color_depth(int depth);
   Not yet implemented! Eventually, this function will be used to select 
   15/16/32 bit pixel formats.

int set_gfx_mode(int card, int w, int h, int v_w, int v_h);
   Switches into graphics mode. The card parameter should be one of the 
   values:

      GFX_TEXT          - return to text mode
      GFX_AUTODETECT    - let Allegro pick an appropriate graphics driver
      GFX_VGA           - select VGA mode 13h (320x200)
      GFX_MODEX         - select a planar, tweaked VGA mode
      GFX_VESA1         - use the VESA 1.x driver
      GFX_VESA2B        - use the VBE 2.0 banked mode driver
      GFX_VESA2L        - use the VBE 2.0 linear framebuffer driver
      GFX_VBEAF         - use the VBE/AF accelerator API (unfinished)
      GFX_XTENDED       - use the unchained mode 640x400 driver
      GFX_ATI           - use the ATI 18800/28800 driver (unfinished)
      GFX_MACH64        - use the ATI mach64 driver (unfinished)
      GFX_CIRRUS64      - use the Cirrus 64xx driver
      GFX_CIRRUS54      - use the Cirrus 54xx driver
      GFX_PARADISE      - use the Paradise driver
      GFX_S3            - use the S3 driver
      GFX_TRIDENT       - use the Trident driver
      GFX_ET3000        - use the Tseng ET3000 driver
      GFX_ET4000        - use the Tseng ET4000 driver
      GFX_VIDEO7        - use the Video-7 driver

   The w and h parameters specify what screen resolution you want. Possible 
   modes are:

	VGA mode 13h, sized 320x200, using the GFX_VGA driver. This will 
	work on any VGA, but doesn't support large virtual screens and 
	hardware scrolling.

	Mode-X, using the GFX_MODEX driver. This will work on any VGA card, 
	and provides a range of different tweaked resolutions.

	Stable mode-X resolutions:

	   Square aspect ratio: 320x240

	   Skewed aspect ratio: 256x224, 256x240, 320x200, 320x400,
				320x480, 320x600, 360x200, 360x240,
				360x360, 360x400, 360x480, 360x600

	   These have worked on every card/monitor that I've tested.

	Unstable mode-X resolutions:

	   Square aspect ratio: 360x270, 376x282, 400x300

	   Skewed aspect ratio: 256x200, 256x256, 376x308, 376x564,
				400x150, 400x600

	   These only work on some monitors. They were fine on my old 
	   machine, but don't get on very well with my new monitor. If you 
	   are worried about the possibility of damaging your monitor by 
	   using these modes, don't be. Of course I'm not providing any 
	   warranty with any of this, and if your hardware does blow up that 
	   is tough, but I don't think this sort of tweaking can do any 
	   damage. From the documentation of Robert Schmidt's TWEAK program:

	      "Some time ago, putting illegal or unsupported values or 
	      combinations of such into the video card registers might prove 
	      hazardous to both your monitor and your health. I have *never* 
	      claimed that bad things can't happen if you use TWEAK, 
	      although I'm pretty sure it never will. I've never heard of 
	      any damage arising from trying out TWEAK, or from general VGA 
	      tweaking in any case."

	Most of the mode-X drawing functions are slower than in mode 13h, 
	due to the complexity of the planar bitmap organisation, but solid 
	area fills and plane-aligned blits from one part of video memory to 
	another can be significantly faster, particularly on older hardware. 
	Mode-X can address the full 256k of VGA RAM, so hardware scrolling 
	and page flipping are possible, and it is possible to split the 
	screen in order to scroll the top part of the display but have a 
	static status indicator at the bottom.

	The standard SVGA modes, 640x480, 800x600, and 1024x768. These ought 
	to work with any SVGA card. If they don't, get a copy of UniVBE and 
	see if that fixes it.

	Many cards also support 640x400, 1280x1024, and 1600x1200, but these 
	aren't available on everything, for example the S3 chipset has no 
	640x400 mode. Other weird resolution may be possible, eg. some Tseng 
	boards can do 640x350, and the Avance Logic has a 512x512 mode.

	Xtended mode, as described by Mark Feldman in the PCGPE, sized 
	640x400 and using the GFX_XTENDED driver. This uses VESA to select 
	an SVGA mode (so it will only work on cards supporting the VESA 
	640x400 resolution), and then unchains the VGA hardware as for 
	mode-X. This allows the entire screen to be addressed without the 
	need for bank switching, but hardware scrolling and page flipping 
	are not possible. This driver will never be autodetected (the normal 
	VESA 640x400 mode will be chosen instead), so if you want to use it 
	you will have to explicitly pass GFX_XTENDED to set_gfx_mode().

	UniVBE provides several scrollable low resolution modes (320x200, 
	320x240, 320x400, 320x480, 360x200, 360x240, 360x400, and 360x480 
	all work on my ET4000). These are lovely, allowing scrolling and 
	page flipping without the complexity of the mode-X planar setup. 
	Unfortunately they aren't standard, so you'll need UniVBE in order 
	to use them.

	VBE/AF is a superset of the VBE 2.0 standard, which provides an API 
	for accessing hardware accelerator features. VBE/AF is currently 
	only supported by UniVBE, and SciTech have only implemented it 
	properly on the Mach 64 chipset. Allegro supports as much of the 
	standard as I could test on my ET4000, ie. the non-accelerated 
	drawing modes. I would really like to get this driver to support 
	accelerator functions, but that will have to wait until SciTech 
	implement it on the Matrox Mystique, or somebody buys me a Mach 64 
	(hint hint :-)

   The v_w and v_h parameters specify the minumum virtual screen size, in 
   case you need a large virtual screen for hardware scrolling or page 
   flipping. You should set them to zero if you don't care about the virtual 
   screen size. Virtual screens can cause a lot of confusion, but they are 
   really quite simple. Warning: patronising explanation coming up, so you 
   may wish to skip the rest of this paragraph :-) Think of video memory as 
   a rectangular piece of paper which is being viewed through a small hole 
   (your monitor) in a bit of cardboard. Since the paper is bigger than the 
   hole you can only see part of it at any one time, but by sliding the 
   cardboard around you can alter which portion of the image is visible. You 
   could just leave the hole in one position and ignore the parts of video 
   memory that aren't visible, but you can get all sorts of useful effects 
   by sliding the screen window around, or by drawing images in a hidden 
   part of video memory and then flipping across to display them.

   For example, you could select a 640x480 mode in which the monitor acts as 
   a window onto a 1024x1024 virtual screen, and then move the visible 
   screen around in this larger area. Initially, with the visible screen 
   positioned at the top left corner of video memory, this setup would look 
   like:

      (0,0)------------(640,0)----(1024,0)
	|                  |           |
	|  visible screen  |           |
	|                  |           |
      (0,480)----------(640,480)       |
	|                              |
	|   the rest of video memory   |
	|                              |
      (0,1024)--------------------(1024,1024)

   What's that? You are viewing this with a proportional font? Hehehe.

   When you call set_gfx_mode(), the v_w and v_h parameters represent the 
   minimum size of virtual screen that is acceptable for your program. The 
   range of possible sizes is usually very restricted, and Allegro is likely 
   to end up creating a virtual screen much larger than the one you request. 
   On an SVGA card with one megabyte of vram you can count on getting a 
   1024x1024 virtual screen, and with 512k vram you can get 1024x512. Other 
   sizes may or may not be possible: don't assume that they will work. In 
   mode-X the virtual width can be any multiple of eight greater than or 
   equal to the physical screen width, and the virtual height will be set 
   accordingly (the VGA has 256k of vram, so the virtual height will be 
   256*1024/virtual_width).

   After you select a graphics mode, the physical and virtual screen sizes 
   can be checked with the macros SCREEN_W(), SCREEN_H(), VIRTUAL_W(), and 
   VIRTUAL_H().

   If Allegro is unable to select an appropriate mode, set_gfx_mode() 
   returns a negative number and stores a description of the problem in 
   allegro_error. Otherwise it returns zero.

int scroll_screen(int x, int y);
   Attempts to scroll the hardware screen to display a different part of the 
   virtual screen (initially it will be positioned at 0, 0, which is the top 
   left corner). Returns zero on success: it may fail if the graphics driver 
   can't handle hardware scrolling or the virtual screen isn't large enough. 
   This function will wait for a vertical retrace if the graphics card 
   requires it, so you don't need to call vsync() yourself. You can use this 
   to move the screen display around in a large virtual screen space, or to 
   page flip back and forth between two non-overlapping areas of the virtual 
   screen. Note that to draw outside the original position in the screen 
   bitmap you will have to alter the clipping rectangle: see below.

   Mode-X scrolling is reliable and will work on any card. Unfortunately a 
   lot of VESA implementations (including UniVBE!) can only handle 
   horizontal scrolling in four pixel increments, so smooth horizontal 
   panning is impossible. This is a shame, but I can't see any way round it.

   Allegro will handle any necessary vertical retrace synchronisation when 
   scrolling the screen, so you don't need to call vsync() before it. This 
   means that scroll_screen() has the same time delay effects as vsync().

void request_modex_scroll(int x, int y);
   This function can only be used in mode-X, and if the vertical retrace 
   interrupt simulator is active. It requests a hardware scroll to the 
   specified position, but returns immediately rather than waiting for a 
   retrace. The scroll will take place during the next vertical retrace. 
   This function is useful for implementing triple buffering (see 
   examples/ex20.c).

int poll_modex_scroll();
   This function can only be used in mode-X, and if the vertical retrace 
   interrupt simulator is active. It returns non-zero if the hardware scroll 
   previously set by request_modex_scroll() is still waiting to take place, 
   and zero if it has already happened.

void split_modex_screen(int line);
   This function is only available in mode-X. It splits the VGA display into 
   two parts at the specified line. The top half of the screen can be 
   scrolled to any part of video memory with the scroll_screen() function, 
   but the part below the specified line number will remain fixed and will 
   display from position (0, 0) of the screen bitmap. After splitting the 
   screen you will generally want to scroll so that the top part of the 
   display starts lower down in video memory, and then create two 
   sub-bitmaps to access the two sections (see examples/ex19.c for a 
   demonstration of how this could be done). To disable the split, call 
   split_modex_screen(0).

Once you have selected a graphics mode, you can draw things onto the screen 
via the 'screen' bitmap. All the Allegro graphics routines draw onto BITMAP 
structures, which are areas of memory containing rectangular images, stored 
as packed byte arrays (eight bits per pixel). You can create and manipulate 
bitmaps in system RAM, or you can write to the special 'screen' bitmap which 
represents the video memory in your graphics card.

For example, to draw a pixel onto the screen you would write:

   putpixel(screen, x, y, color);

Or to implement a double-buffered system:

   BITMAP *bmp = create_bitmap(320, 200);    // make a bitmap in system RAM
   clear(bmp);                               // zero the memory bitmap
   putpixel(bmp, x, y, color);               // draw onto the memory bitmap
   blit(bmp, screen, 0, 0, 0, 0, 320, 200);  // copy it to the screen

See below for information on how to get direct access to the image memory in 
a bitmap.

Allegro supports several different types of bitmaps:

   - The screen bitmap, which represents the hardware video memory. 
     Ultimately you have to draw onto this in order for your image to be 
     visible.

   - Memory bitmaps, which are located in system RAM and can be used to 
     store graphics or as temporary drawing spaces for double buffered 
     systems. These can be obtained by calling create_bitmap(), load_pcx(), 
     or by loading a grabber datafile.

   - Sub-bitmaps. These share image memory with a parent bitmap (which 
     can be the screen, a memory bitmap, or another sub-bitmap), so drawing 
     onto them will also change their parent. They can be of any size and 
     located anywhere within the parent bitmap, and can have their own 
     clipping rectangles, so they are a useful way of dividing a bitmap into 
     several smaller units, eg. splitting a large virtual screen into two 
     sections for page flipping (see examples/ex9.c).

extern BITMAP *screen;
   Global pointer to a bitmap, sized VIRTUAL_W x VIRTUAL_H. This is created 
   by set_gfx_mode(), and represents the hardware video memory. Only a part 
   of this bitmap will actually be visible, sized SCREEN_W x SCREEN_H. 
   Normally this is the top left corner of the larger virtual screen, so you 
   can ignore the extra invisible virtual size of the bitmap if you aren't 
   interested in hardware scrolling or page flipping. To move the visible 
   window to other parts of the screen bitmap, call scroll_screen(). 
   Initially the clipping rectangle will be limited to the physical screen 
   size, so if you want to draw onto a larger virtual screen space outside 
   this rectangle, you will need to adjust the clipping.

BITMAP *create_bitmap(int width, int height);
   Creates a memory bitmap sized width by height, and returns a pointer to 
   it. The bitmap will have clipping turned on, and the clipping rectangle 
   set to the full size of the bitmap. The image memory will not be cleared, 
   so it will probably contain garbage: you should clear the bitmap before 
   using it.

BITMAP *create_sub_bitmap(BITMAP *parent, int x, y, width, height);
   Creates a sub-bitmap, ie. a bitmap sharing drawing memory with a 
   pre-existing bitmap, but possibly with different size and clipping 
   settings. When creating a sub-bitmap of the mode-X screen, the x position 
   must be a multiple of four.

void destroy_bitmap(BITMAP *bitmap);
   Destroys a memory bitmap or sub-bitmap when you are finished with it.

int is_same_bitmap(BITMAP *bmp1, BITMAP *bmp2);
   Returns TRUE if the two bitmaps describe the same drawing surface, ie. 
   the pointers are equal, one is a sub-bitmap of the other, or they are 
   both sub-bitmaps of a common parent.

int is_linear_bitmap(BITMAP *bmp);
   Returns TRUE if bmp is a linear bitmap, ie. a memory bitmap, mode 13h 
   screen, or SVGA screen. Linear bitmaps can be used with the _putpixel(), 
   _getpixel(), bmp_write_line(), and bmp_read_line() functions.

int is_planar_bitmap(BITMAP *bmp);
   Returns TRUE if bmp is a planar (mode-X or Xtended mode) screen bitmap.

int is_memory_bitmap(BITMAP *bmp);
   Returns TRUE if bmp is a memory bitmap, ie. it was created by calling 
   create_bitmap() or loaded from a grabber datafile or PCX file. Memory 
   bitmaps can be accessed directly via the line pointers in the bitmap 
   structure, eg. bmp->line[y][x] = color.

int is_screen_bitmap(BITMAP *bmp);
   Returns TRUE if bmp is the screen bitmap, or a sub-bitmap of it.

int is_sub_bitmap(BITMAP *bmp);
   Returns TRUE if bmp is a sub-bitmap.



===========================================
============ Graphics routines ============
===========================================

void set_clip(BITMAP *bitmap, int x1, int y1, int x2, int y2);
   Each bitmap has an associated clipping rectangle, which is the area of 
   the image that it is ok to draw on. Nothing will be drawn to positions 
   outside this space. Pass the two opposite corners of the clipping 
   rectangle: these are inclusive, eg. set_clip(bitmap, 16, 16, 32, 32) will 
   allow drawing to (16, 16) and (32, 32), but not to (15, 15) and (33, 33). 
   If x1, y1, x2, and y2 are all zero, clipping will be turned off, which 
   may slightly speed up some drawing operations (usually a negligible 
   difference, although every little helps) but will result in your program 
   dying a horrible death if you try to draw beyond the edges of the bitmap.

void drawing_mode(int mode, BITMAP *pattern, int x_anchor, int y_anchor);
   Sets the graphics drawing mode. This only affects the geometric routines 
   like putpixel, lines, rectangles, circles, polygons, floodfill, etc, not 
   the text output, blitting, or sprite drawing functions. The mode should 
   be one of the values:

      DRAW_MODE_SOLID               - the default, solid color drawing
      DRAW_MODE_XOR                 - exclusive-or drawing
      DRAW_MODE_COPY_PATTERN        - multicolored pattern fill
      DRAW_MODE_SOLID_PATTERN       - single color pattern fill
      DRAW_MODE_MASKED_PATTERN      - transparent pattern fill
      DRAW_MODE_TRANS               - translucent drawing

   In DRAW_MODE_XOR, pixels are written to the bitmap with an exclusive-or 
   operation rather than a simple copy, so drawing the same shape twice will 
   erase it. Because it involves reading as well as writing the bitmap 
   memory, xor drawing is a lot slower than the normal replace mode.

   With the patterned modes, you provide a pattern bitmap which is tiled 
   across the surface of the shape. Allegro stores a pointer to this bitmap 
   rather than copying it, so you must not destroy the bitmap while it is 
   still selected as the pattern. The width and height of the pattern must 
   be powers of two, but they can be different, eg. a 64x16 pattern is fine, 
   but a 17x3 one is not. The pattern is tiled in a grid starting at point 
   (x_anchor, y_anchor). Normally you should just pass zero for these 
   values, which lets you draw several adjacent shapes and have the patterns 
   meet up exactly along the shared edges. Zero alignment may look peculiar 
   if you are moving a patterned shape around the screen, however, because 
   the shape will move but the pattern alignment will not, so in some 
   situations you may wish to alter the anchor position.

   When you select DRAW_MODE_COPY_PATTERN, pixels are simply copied from the 
   pattern bitmap onto the destination bitmap. This allows the use of 
   multicolored patterns, and means that the color you pass to the drawing 
   routine is ignored. This is the fastest of the patterned modes.

   In DRAW_MODE_SOLID_PATTERN, each pixel in the pattern bitmap is compared 
   with zero. If it is non-zero, a pixel of the color you passed to the 
   drawing routine is written to the destination bitmap, otherwise a zero is 
   written. The pattern is thus treated as a monochrome bitmask, which lets 
   you use the same pattern to draw different shapes in different colors, 
   but prevents the use of multicolored patterns.

   DRAW_MODE_MASKED_PATTERN is almost the same as DRAW_MODE_SOLID_PATTERN, 
   but the zero pixels are skipped rather than being written as zeros, so 
   the background shows through the gaps in the pattern.

   In DRAW_MODE_TRANS, the global color_map table is used to blend pixels 
   with the existing image. This must only be used after you have set up the 
   color mapping table! Because it involves reading as well as writing the 
   bitmap memory, translucent drawing is very slow if you draw directly to 
   video RAM, so wherever possible you should use a memory bitmap instead.

void xor_mode(int xor);
   This is a shortcut for toggling xor drawing mode on and off. Calling 
   xor_mode(TRUE) is equivalent to drawing_mode(DRAW_MODE_XOR, NULL, 0, 0);

void solid_mode();
   This is a shortcut for selecting solid drawing mode. It is equivalent to 
   calling drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);

void putpixel(BITMAP *bmp, int x, int y, int color);
   Writes a pixel to the specified position in the bitmap, using the current 
   drawing mode and the bitmap's clipping rectangle.

void _putpixel(BITMAP *bmp, int x, int y, unsigned char color);
   Like the regular putpixel(), but much faster because it is implemented as 
   an inline assembler function. This won't work in mode-X, doesn't perform 
   any clipping (it will crash if you try to draw outside the bitmap!), and 
   ignores the drawing mode. It clobbers the %fs register, so you shouldn't 
   mix it with code that uses the _farsetsel() and _farns*() functions.

int getpixel(BITMAP *bmp, int x, int y);
   Reads a pixel from point x, y in the bitmap. Returns -1 if the point lies 
   outside the bitmap.

unsigned char _getpixel(BITMAP *bmp, int x, int y);
   Faster inline version of getpixel(). This won't work in mode-X, and it 
   doesn't do any clipping, so you must make sure the point lies inside the 
   bitmap.

void vline(BITMAP *bmp, int x, int y1, int y2, int color);
   Draws a vertical line onto the bitmap, from point (x, y1) to (x, y2).

void hline(BITMAP *bmp, int x1, int y, int x2, int color);
   Draws a horizontal line onto the bitmap, from point (x1, y) to (x2, y).

void do_line(BITMAP *bmp, int x1, y1, x2, y2, int d, void (*proc)());
   Calculates all the points along a line from point (x1, y1) to (x2, y2), 
   calling the supplied function for each one. This will be passed a copy of 
   the bmp parameter, the x and y position, and a copy of the d parameter, 
   so it is suitable for use with putpixel().

void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
   Draws a line onto the bitmap, from point (x1, y1) to (x2, y2).

void triangle(BITMAP *bmp, int x1, y1, x2, y2, x3, y3, int color);
   Draws a filled triangle between the three points.

void polygon(BITMAP *bmp, int vertices, int *points, int color);
   Draws a filled polygon with an arbitrary number of corners. Pass the 
   number of vertices and an array containing a series of x, y points (a 
   total of vertices*2 values).

void rect(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
   Draws an outline rectangle with the two points as its opposite corners.

void rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
   Draws a solid, filled rectangle with the two points as its opposite 
   corners.

void do_circle(BITMAP *bmp, int x, int y, int radius, int d, void (*proc)());
   Calculates all the points in a circle around point (x, y) with radius r, 
   calling the supplied function for each one. This will be passed a copy of 
   the bmp parameter, the x and y position, and a copy of the d parameter, 
   so it is suitable for use with putpixel().

void circle(BITMAP *bmp, int x, int y, int radius, int color);
   Draws a circle with the specified centre and radius.

void circlefill(BITMAP *bmp, int x, int y, int radius, int color);
   Draws a filled circle with the specified centre and radius.

void calc_spline(int points[8], int npts, int *x, int *y);
   Calculates a series of npts values along a bezier spline, storing them in 
   the output x and y arrays. The bezier curve is specified by the four 
   x/y control points in the points array: points[0] and points[1] contain 
   the coordinates of the first control point, points[2] and points[3] are 
   the second point, etc. Control points 0 and 3 are the ends of the spline, 
   and points 1 and 2 are guides. The curve probably won't pass through 
   points 1 and 2, but they affect the shape of the curve between points 0 
   and 3 (the lines p0-p1 and p2-p3 are tangents to the spline). The easiest 
   way to think of it is that the curve starts at p0, heading in the 
   direction of p1, but curves round so that it arrives at p3 from the 
   direction of p2. In addition to their role as graphics primitives, spline 
   curves can be useful for constructing smooth paths around a series of 
   control points, as in ex23.c.

void spline(BITMAP *bmp, int points[8], int color);
   Draws a bezier spline using the four control points specified in the 
   points array.

void floodfill(BITMAP *bmp, int x, int y, int color);
   Floodfills an enclosed area, starting at point (x, y), with the specified 
   color.

void draw_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y);
   Draws a copy of the sprite bitmap onto the destination bitmap at the 
   specified position. This is almost the same as blit(sprite, bmp, 0, 0, x, 
   y, sprite->w, sprite->h), but it uses a masked drawing mode where 
   zero pixels are skipped, so the background image will show through the 
   transparent parts of the sprite. The sprite must be a memory bitmap, not 
   the screen or a sub-bitmap. The destination can be any bitmap.

void draw_sprite_v_flip(BITMAP *bmp, BITMAP *sprite, int x, int y);
void draw_sprite_h_flip(BITMAP *bmp, BITMAP *sprite, int x, int y);
void draw_sprite_vh_flip(BITMAP *bmp, BITMAP *sprite, int x, int y);
   These are like draw_sprite(), but they flip the image about the vertical, 
   horizontal, or diagonal, axis. This produces exact mirror images, which 
   is not the same as rotating the sprite (and it is a lot faster than the 
   rotation routine). The sprite must be a memory bitmap.

void draw_trans_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y);
   Uses the global color_map table to blend the sprite with the existing 
   image. This must only be used after you have set up the color mapping 
   table! In the interests of speed, this function doesn't bother checking 
   for zero pixels, so to prevent the masked areas of the sprite being 
   drawn, you need to construct the color mapping table in such a way that 
   zero source values leave the destination unchanged (this is done by 
   default when you call create_trans_table()). Because it involves reading 
   as well as writing the bitmap memory, translucent drawing is very slow if 
   you draw directly to video RAM, so wherever possible you should use a 
   memory bitmap instead.

void draw_lit_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int color);
   Tints the sprite image to the specified color or light level, using the 
   global color_map table, and draws the resulting image to the destination 
   bitmap. This must only be used after you have set up the color mapping 
   table!

void rotate_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, fixed angle);
   Draws the sprite image onto the bitmap at the specified position, 
   rotating it by the specified angle. The angle is a fixed point 16.16 
   number in the same format used by the fixed point trig routines, with 256 
   equal to a full circle, 64 a right angle, etc. The sprite must be a 
   memory bitmap.

void stretch_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int w, int h);
   Draws the sprite image onto the bitmap at the specified position, 
   stretching it to the specified width and height. The difference between 
   stretch_sprite() and stretch_blit() is that stretch_sprite() masks out 
   zero pixels.

void blit(BITMAP *source, BITMAP *dest, int source_x, int source_y,
          int dest_x, int dest_y, int width, int height);
   Copies a rectangular area of the source bitmap to the destination bitmap. 
   The source_x and source_y parameters are the top left corner of the area 
   to copy from the source bitmap, and dest_x and dest_y are the 
   corresponding position in the destination bitmap. This routine respects 
   the destination clipping rectangle, and it will also clip if you try to 
   blit from areas outside the source bitmap.

   You can blit between any parts of any two bitmaps, even if the two memory 
   areas overlap (ie. source and dest are the same, or one is sub-bitmap of 
   the other). You should be aware, however, that a lot of SVGA cards don't 
   provide separate read and write banks, which means that blitting from one 
   part of the screen to another requires the use of a temporary bitmap in 
   memory, and is therefore extremely slow. As a general rule you should 
   avoid blitting from the screen onto itself in SVGA modes.

   In mode-X, on the other hand, blitting from one part of the screen to 
   another can be significantly faster than blitting from memory onto the 
   screen, as long as the source and destination are correctly aligned with 
   each other. Copying between overlapping screen rectangles is slow, but if 
   the areas don't overlap, and if they have the same plane alignment (ie. 
   (source_x%4) == (dest_x%4)), the VGA latch registers can be used for a 
   very fast data transfer. To take advantage of this, in mode-X it is often 
   worth storing tile graphics in a hidden area of video memory (using a 
   large virtual screen), and blitting them from there onto the visible part 
   of the screen.

void stretch_blit(BITMAP *source, BITMAP *dest,
                  int source_x, source_y, source_width, source_height,
                  int dest_x, dest_y, dest_width, dest_height);
   Like blit(), except it can scale images so the source and destination 
   rectangles don't need to be the same size. This routine doesn't do as 
   much safety checking as the regular blit: in particular you must take 
   care not to copy from areas outside the source bitmap, and you cannot 
   blit between overlapping regions, ie. you must use different bitmaps for 
   the source and the destination. Also, the source must be a memory bitmap 
   or sub-bitmap, not the hardware screen.

void clear_to_color(BITMAP *bitmap, int color);
   Clears the bitmap to the specified color.

void clear(bitmap);
   Clears the bitmap to color 0.

Because bitmaps can be used in so many different ways, the bitmap structure 
is quite complicated, and it contains a lot of data. In many situations, 
though, you will find yourself storing images that are only ever copied to 
the screen, rather than being drawn onto or used as filling patterns, etc. 
If this is the case you may be better off storing your images in RLE_SPRITE 
or COMPILED_SPRITE structures rather than bitmaps.

RLE sprites store the image in a simple run-length encoded format, where 
repeated zero pixels are replaced by a single length count, and strings of 
non-zero pixels are preceded by a counter giving the length of the solid 
run. RLE sprites are usually much smaller then normal bitmaps, both because 
of the run length compression, and because they avoid most of the overhead 
of the bitmap structure. They are often also faster than normal bitmaps, 
because rather than having to compare every single pixel with zero to 
determine whether it should be drawn, it is possible to skip over a whole 
run of zeros with a single add, or to copy a long run of non-zero pixels 
with fast string instructions.

Every silver lining has a cloud, though, and in the case of RLE sprites it 
is a lack of flexibility. You can't draw onto them, and you can't flip them, 
rotate them, or stretch them. In fact the only thing you can do with them is 
to blast them onto a bitmap with the draw_rle_sprite() function, which is 
equivalent to using draw_sprite() with a regular bitmap. You can convert 
bitmaps into RLE sprites at runtime, or you can create RLE sprite structures 
in grabber datafiles by making a bitmap object and checking the 'RLE' button.

Compiled sprites are stored as actual machine code instructions that draw a 
specific image onto a bitmap, using mov instructions with immediate data 
values. This is the fastest way to draw a masked image: on my machine 
drawing compiled sprites is about five times as fast as using draw_sprite() 
with a regular bitmap. Compiled sprites are big, so if memory is tight you 
should use RLE sprites instead, and what you can do with them is even more 
restricted than with RLE sprites, because they don't support clipping. If 
you try to draw one off the edge of a bitmap, you will corrupt memory and 
probably crash the system. You can convert bitmaps into compiled sprites at 
runtime, or you can create compiled sprite structures in grabber datafiles 
by making a bitmap object and checking the 'Linear Compiled' or 'Mode-X 
Compiled' buttons. 

RLE_SPRITE *get_rle_sprite(BITMAP *bitmap);
   Creates an RLE sprite based on the specified bitmap (which must be a 
   memory bitmap).

void destroy_rle_sprite(RLE_SPRITE *sprite);
   Destroys an RLE sprite structure previously returned by get_rle_sprite().

void draw_rle_sprite(BITMAP *bmp, RLE_SPRITE *sprite, int x, int y);
   Draws an RLE sprite onto a bitmap at the specified position.

void draw_trans_rle_sprite(BITMAP *bmp, RLE_SPRITE *sprite, int x, int y);
   Translucent version of draw_rle_sprite(). See the description of 
   draw_trans_sprite(). This must only be used after you have set up the 
   color mapping table!

void draw_lit_rle_sprite(BITMAP *bmp, RLE_SPRITE *sprite, int x, y, color);
   Tinted version of draw_rle_sprite(). See the description of
   draw_lit_sprite(). This must only be used after you have set up the color 
   mapping table!

COMPILED_SPRITE *get_compiled_sprite(BITMAP *bitmap, int planar);
   Creates a compiled sprite based on the specified bitmap (which must be a 
   memory bitmap). Compiled sprites are device-dependent, so you have to 
   specify whether to compile it into a linear or planar format. Pass FALSE 
   as the second parameter if you are going to be drawing it onto memory 
   bitmaps or mode 13h and SVGA screen bitmaps, and pass TRUE if you are 
   going to draw it onto mode-X or Xtended mode screen bitmaps.

void destroy_compiled_sprite(COMPILED_SPRITE *sprite);
   Destroys a compiled sprite structure previously returned by 
   get_compiled_sprite().

void draw_compiled_sprite(BITMAP *bmp, COMPILED_SPRITE *sprite, int x, int y);
   Draws a compiled sprite onto a bitmap at the specified position. The 
   sprite must have been compiled for the correct type of bitmap (linear or 
   planar). This function does not support clipping.

   Hint: if not being able to clip compiled sprites is a problem, a neat 
   trick is to set up a work surface (memory bitmap, mode-X virtual screen, 
   or whatever) a bit bigger than you really need, and use the middle of it 
   as your screen. That way you can draw slightly off the edge without any 
   trouble... 

Allegro provides text output routines that work with both proportional 
(individual characters can be any size) and fixed size 8x8 or 8x16 fonts. A 
font contains the ASCII characters 32 to 255: all other characters will be 
drawn as spaces. The grabber program can create fonts from sets of 
characters drawn on a PCX file (see grabber.txt for more information), and 
can also import GRX or BIOS format font files.

extern FONT *font;
   A simple 8x8 fixed size font (the mode 13h BIOS default). If you want to 
   alter the font used by the GUI routines, change this to point to one of 
   your own fonts.

void text_mode(int mode);
   Sets the mode in which text will be drawn. If mode is zero or positive, 
   text output will be opaque and the background of the characters will be 
   set to color #mode. If mode is negative, text will be drawn transparently 
   (ie. the background of the characters will not be altered). The default 
   is a mode of zero.

void textout(BITMAP *bmp, FONT *f, unsigned char *s, int x, y, int color);
   Writes the string s onto the bitmap at position x, y, using the current 
   text mode and the specified font and foreground color. If the color is -1 
   and a proportional font is in use, it will be drawn using the colors from 
   the original font bitmap (the one you imported into the grabber program), 
   which allows multicolored text output.

void textout_centre(BITMAP *bmp, FONT *f, unsigned char *s, int x, y, color);
   Like textout(), but interprets the x coordinate as the centre rather than 
   the left edge of the string.

int text_length(FONT *f, unsigned char *str);
   Returns the length (in pixels) of a string in the specified font.

int text_height(FONT *f)
   Returns the height (in pixels) of the specified font.

void destroy_font(FONT *f);
   Frees the memory being used by a font structure.

void polygon3d(BITMAP *bmp, int type, BITMAP *texture, int vc, V3D *vtx[]);
void polygon3d_f(BITMAP *bmp, int type, BITMAP *texture, int vc, V3D_f *vtx[]);
   Draw 3d polygons onto the specified bitmap, using the specified rendering 
   mode. Unlike the regular polygon() function, these routines don't support 
   concave or self-intersecting shapes, and they can't draw onto mode-X 
   screen bitmaps (if you want to write 3d code in mode-X, draw onto a 
   memory bitmap and then blit to the screen). The width and height of the 
   texture bitmap must be powers of two, but can be different, eg. a 64x16 
   texture is fine, but a 17x3 one is not. The vertex count parameter (vc) 
   should be followed by an array containing the appropriate number of 
   pointers to vertex structures: polygon3d() uses the fixed point V3D 
   structure, while polygon3d_f() uses the floating point V3D_f structure. 
   These are defined as:

   typedef struct V3D
   {
      fixed x, y, z;       - position
      fixed u, v;          - texture map coordinates
      int c;               - color
   } V3D;

   typedef struct V3D_f
   {
      float x, y, z;       - position
      float u, v;          - texture map coordinates
      int c;               - color
   } V3D_f;

   How the vertex data is used depends on the rendering mode:

   The x and y values specify the position of the vertex in 2d screen 
   coordinates.

   The z value is only required when doing perspective correct texture 
   mapping, and specifies the depth of the point in 3d world coordinates.

   The u and v coordinates are only required when doing texture mapping, and 
   specify the position of the point in the texture bitmap, for example 0, 0 
   maps the vertex onto the top left corner of the texture, and if the 
   texture is sized 32x32, setting u=32 and v=16 maps the vertex to the 
   point half way down the right edge of the texture. The u/v coordinates 
   wrap at the edge of the texture, so with a 32x32 texture, u=v=32 is the 
   same as u=v=0. This can be used to tile textures several times across a 
   polygon.

   The c value specifies the vertex color, and is interpreted differently by 
   various rendering modes.

   The type parameter specifies the polygon rendering mode, and can be any 
   of the values:

   POLYTYPE_FLAT:
      A simple flat shaded polygon, taking the color from the c value of the 
      first vertex.

   POLYTYPE_GCOL:
      A single-color gouraud shaded polygon. The colors for each vertex are 
      taken from the c value, and interpolated across the polygon. This is 
      very fast, but will only work if your palette contains a smooth 
      gradient between the colors.

   POLYTYPE_GRGB:
      A gouraud shaded polygon which interpolates RGB triplets rather than a 
      single color, and uses the global rgb_map table to convert the result 
      to an 8 bit paletted color. This must only be used after you have set 
      up the RGB mapping table! This mode is significantly slower than 
      POLYTYPE_GCOL, but doesn't require the palette to contain all the 
      color gradients you are going to use. The colors for each vertex are 
      taken from the c value, which is interpreted as a 24 bit RGB triplet 
      (0xFF000 is red, 0x00FF00 is green, and 0x0000FF is blue).

   POLYTYPE_ATEX:
      An affine texture mapped polygon. This stretches the texture across 
      the polygon with a simple 2d linear interpolation, which is fast but 
      not mathematically correct. It can look ok if the polygon is fairly 
      small or flat-on to the camera, but because it doesn't deal with 
      perspective foreshortening, it can produce strange warping artifacts. 
      To see what I mean, run test.exe and see what happens to the 
      polygon3d() test when you zoom in very close to the cube.

   POLYTYPE_PTEX:
      A perspective-correct texture mapped polygon. This uses the z value 
      from the vertex structure as well as the u/v coordinates, so textures 
      are displayed correctly regardless of the angle they are viewed from. 
      Because it involves division calculations in the inner texture mapping 
      loop, this mode is a lot slower than POLYTYPE_ATEX, and it uses 
      floating point so it will be very slow on anything less than a Pentium 
      (even with an FPU, a 486 can't overlap floating point division with 
      other integer operations like the Pentium can).

   POLYTYPE_ATEX_MASK:
   POLYTYPE_PTEX_MASK:
      Like POLYTYPE_ATEX and POLYTYPE_PTEX, but zero texture map pixels are 
      skipped, allowing parts of the texture map to be transparent.

   POLYTYPE_ATEX_LIT:
   POLYTYPE_PTEX_LIT:
      Like POLYTYPE_ATEX and POLYTYPE_PTEX, but the global color_map table 
      is used to blend the texture with a light level taken from the c value 
      in the vertex structure. This must only be used after you have set up 
      the color mapping table! These modes cannot be used with texture maps 
      larger than 256x256.

void triangle3d(BITMAP *bmp, int type, BITMAP *tex, V3D *v1, *v2, *v3);
void triangle3d_f(BITMAP *bmp, int type, BITMAP *tex, V3D_f *v1, *v2, *v3);
   Draw 3d triangles, using either fixed or floating point vertex 
   structures. These are equivalent to calling
      polygon3d(bmp, type, tex, 3, v1, v2, v3);
   or
      polygon3d_f(bmp, type, tex, 3, v1, v2, v3);

void quad3d(BITMAP *bmp, int type, BITMAP *tex, V3D *v1, *v2, *v3, *v4);
void quad3d_f(BITMAP *bmp, int type, BITMAP *tex, V3D_f *v1, *v2, *v3, *v4);
   Draw 3d quads, using either fixed or floating point vertex structures. 
   These are equivalent to calling
      polygon3d(bmp, type, tex, 4, v1, v2, v3, v4);
   or
      polygon3d_f(bmp, type, tex, 4, v1, v2, v3, v4);



=============================================
============ Video memory access ============
=============================================

The bitmap structure looks like:

typedef struct BITMAP
{
   int w, h;                  - size of the bitmap in pixels
   int clip;                  - non-zero if clipping is turned on
   int cl, cr, ct, cb;        - clip rectangle left, right, top, and bottom
   int seg;                   - segment for use with the line pointers
   unsigned char *line[];     - pointers to the start of each line
} BITMAP;

There is some other stuff in the structure as well, but it is liable to 
change and you shouldn't use anything except the above. The clipping 
rectangle is inclusive on the left and top (0 allows drawing to position 0) 
but exclusive on the right and bottom (10 allows drawing to position 9, but 
not to 10). Note this is not the same format as you pass to set_clip(), 
which takes inclusive coordinates for all four corners.

There are several ways to get direct access to the image memory of a bitmap, 
varying in complexity depending on what sort of bitmap you are using.

The simplest approach will only work with memory bitmaps (obtained from 
create_bitmap(), grabber datafiles, and PCX files) and sub-bitmaps of memory 
bitmaps. This uses a table of char pointers, called 'line', which is a part 
of the bitmap structure and contains pointers to the start of each line of 
the image. For example, a simple memory bitmap putpixel function is:

   void memory_putpixel(BITMAP *bmp, int x, int y, int color)
   {
      bmp->line[y][x] = color;
   }

If you want to write to the screen as well as to memory bitmaps, you need to 
use far pointers. Rewriting the above function to use the routines in 
sys/farptr.h will allow it to be used with screen bitmaps, as long as they 
do not require bank switching (this means mode 13h screens and VBE 2.0 
linear framebuffer screens). Using far pointers, the putpixel is:

   #include <sys/farptr.h>

   void farptr_putpixel(BITMAP *bmp, int x, int y, int color)
   {
      _farpokeb(bmp->seg, (unsigned long)bmp->line[y]+x, color);
   }

This still won't work in banked SVGA modes, however. For more flexible 
access to bitmap memory, you need to call the bank switching functions:

unsigned long bmp_write_line(BITMAP *bmp, int line);
   Selects the line of a bitmap that you are going to draw onto.

unsigned long bmp_read_line(BITMAP *bmp, int line);
   Selects the line of a bitmap that you are going to read from.

These are implemented as inline assembler routines, so they are not as 
inefficient as they might seem. If the bitmap doesn't require bank switching 
(ie. it is a memory bitmap, mode 13h screen, etc), these functions just 
return bmp->line[line].

Although SVGA bitmaps are banked, Allegro provides linear access to the 
memory within each scanline, so you only need to pass a y coordinate to 
these functions. Various x positions can be obtained by simply adding the x 
coordinate to the returned address. The return value is an unsigned long 
rather than a char pointer because the bitmap memory may not be in your data 
segment, and you need to access it with far pointers. For example, a 
putpixel using the bank switching functions is:

   #include <sys/farptr.h>

   void banked_putpixel(BITMAP *b, int x, int y, int color)
   {
      unsigned long address = bmp_write_line(bmp, y);
      _farpokeb(bmp->seg, address+x, color);
   }

You will notice that Allegro provides separate functions for setting the 
read and write banks. It is important that you distinguish between these, 
because on some graphics cards the banks can be set individually, and on 
others the video memory is read and written at different addresses. Life is 
never quite as simple as we might wish it to be, though (this is true even 
when we _aren't_ talking about graphics coding :-) and so of course some 
cards only provide a single bank. On these the read and write bank functions 
will behave identically, so you shouldn't assume that you can read from one 
part of video memory and write to another at the same time. You can call 
bmp_read_line(), and read whatever you like from that line, and then call 
bmp_write_line() with the same or a different line number, and write 
whatever you like to this second line, but you mustn't call bmp_read_line() 
and bmp_write_line() together and expect to be able to read one line and 
write the other simultaneously. It would be nice if this was possible, but 
if you do it, your code won't work on single banked SVGA cards.

And then there's mode-X. If you've never done any mode-X graphics coding, 
you probably won't understand this, but for those of you who want to know 
how Allegro sets up the mode-X screen bitmaps, here goes...

The line pointers are still present, and they contain planar addresses, ie. 
the actual location at which you access the first pixel in the line. These 
addresses are guaranteed to be quad aligned, so you can just set the write 
plane, divide your x coordinate by four, and add it to the line pointer. For 
example, a mode-X putpixel is:

   #include <pc.h>
   #include <sys/farptr.h>

   void modex_putpixel(BITMAP *b, int x, int y, int color)
   {
      outportw(0x3C4, (0x100<<(x&3))|2);
      _farpokeb(bmp->seg, (unsigned long)bmp->line[y]+(x>>2), color);
   }



=======================================
============ FLIC routines ============
=======================================

There are two high level functions for playing FLI/FLC animations:

int play_fli(char *filename, BITMAP *bmp, int loop, int (*callback)());
   Plays an AutoDesk Animator FLI or FLC animation file, reading the data 
   from disk as it is required.

int play_memory_fli(void *fli_data, BITMAP *bmp, int loop, int (*callback)());
   Plays an Autodesk Animator FLI or FLC animation, reading the data from a 
   copy of the file which is held in memory. You can obtain the fli_data 
   pointer by mallocing a block of memory and reading an FLI file into it, 
   or by importing an FLI into a grabber datafile. Playing animations from 
   memory is obviously faster than cueing them directly from disk, and is 
   particularly useful with short, looped FLI's. Animations can easily get 
   very large, though, so in most cases you will probably be better just 
   using play_fli().

Apart from the different sources of the data, play_fli() and 
play_memory_fli() behave identically. They draw the animation onto the 
specified bitmap, which should normally be the screen. Frames will be 
aligned with the top left corner of the bitmap: if you want to position them 
somewhere else you will need to create a sub-bitmap for the FLI player to 
draw onto. If loop is set the player will cycle when it reaches the end of 
the file, otherwise it will play through the animation once and then return. 
If the callback function is not NULL it will be called once for each frame, 
allowing you to perform background tasks of your own. This callback should 
normally return zero: if it returns non-zero the player will terminate (this 
is the only way to stop an animation that is playing in looped mode). The 
FLI player returns FLI_OK if it reached the end of the file, FLI_ERROR if 
something went wrong, and the value returned by the callback function if 
that was what stopped it. If you need to distinguish between different 
return values, your callback should return positive integers, since FLI_OK 
is zero and FLI_ERROR is negative. Note that the FLI player will only work 
when the timer module is installed, and that it will alter the palette 
according to whatever palette data is present in the animation file.

Occassionally you may need more detailed control over how an FLI is played, 
for example if you want to superimpose a text scroller on top of the 
animation, or to play it back at a different speed. You could do both of 
these with the lower level functions:

int open_fli(char *filename);
int open_memory_fli(void *fli_data);
   Open FLI files ready for playing, reading the data from disk or memory 
   respectively. Return FLI_OK on success. Information about the current FLI 
   is held in global variables, so you can only have one animation open at a 
   time.

void close_fli();
   Closes an FLI file when you have finished reading from it.

int next_fli_frame(int loop);
   Reads the next frame of the current animation file. If loop is set the 
   player will cycle when it reaches the end of the file, otherwise it will 
   return FLI_EOF. Returns FLI_OK on success, FLI_ERROR or FLI_NOT_OPEN on 
   error, and FLI_EOF on reaching the end of the file. The frame is read 
   into the global variables:

extern BITMAP *fli_bitmap;
   Contains the current frame of the FLI/FLC animation.

extern PALETTE fli_palette;
   Contains the current FLI palette.

extern int fli_bmp_dirty_from;
extern int fli_bmp_dirty_to;
   These variables are set by next_fli_frame() to indicate which part of the 
   fli_bitmap has changed since the last call to reset_fli_variables(). If 
   fli_bmp_dirty_from is greater than fli_bmp_dirty_to, the bitmap has not 
   changed, otherwise lines fli_bmp_dirty_from to fli_bmp_dirty_to 
   (inclusive) have altered. You can use these when copying the fli_bitmap 
   onto the screen, to avoid moving data unnecessarily.

extern int fli_pal_dirty_from;
extern int fli_pal_dirty_to;
   These variables are set by next_fli_frame() to indicate which part of the 
   fli_palette has changed since the last call to reset_fli_variables(). If 
   fli_pal_dirty_from is greater than fli_pal_dirty_to, the palette has not 
   changed, otherwise colors fli_pal_dirty_from to fli_pal_dirty_to 
   (inclusive) have altered. You can use these when updating the hardware 
   palette, to avoid unnecessary calls to set_palette().

void reset_fli_variables();
   Once you have done whatever you are going to do with the fli_bitmap and 
   fli_palette, call this function to reset the fli_bmp_dirty_* and 
   fli_pal_dirty_* variables.

extern int fli_frame;
   Global variable containing the current frame number in the FLI file. This 
   is useful for synchronising other events with the animation, for instance 
   you could check it in a play_fli() callback function and use it to 
   trigger a sample at a particular point.

extern volatile int fli_timer;
   Global variable for timing FLI playback. When you open an FLI file, a 
   timer interrupt is installed which increments this variable every time a 
   new frame should be displayed. Calling next_fli_frame() decrements it, so 
   you can test it and know that it is time to display a new frame if it is 
   greater than zero.



========================================
============ Sound routines ============
========================================

int install_sound(int digi_card, int midi_card, char *cfg_path); 
   Initialises the sound module, returning zero on success. The digi_card 
   parameter should be one of the values:

      DIGI_AUTODETECT      - let Allegro pick a digital sound driver
      DIGI_NONE            - no digital sound
      DIGI_SB              - Sound Blaster (autodetect type)
      DIGI_SB10            - SB 1.0 (8 bit mono single shot dma)
      DIGI_SB15            - SB 1.5 (8 bit mono single shot dma)
      DIGI_SB20            - SB 2.0 (8 bit mono auto-initialised dma)
      DIGI_SBPRO           - SB Pro (8 bit stereo)
      DIGI_SB16            - SB16 (16 bit stereo)
      DIGI_GUS             - Gravis Ultrasound (not written yet)

   The midi_card should be one of the values:

      MIDI_AUTODETECT      - let Allegro pick a MIDI sound driver
      MIDI_NONE            - no MIDI sound
      MIDI_ADLIB           - Adlib or SB FM synth (autodetect type)
      MIDI_OPL2            - OPL2 synth (mono, used in Adlib and SB)
      MIDI_2XOPL2          - dual OPL2 synths (stereo, used in SB Pro-I)
      MIDI_OPL3            - OPL3 synth (stereo, used in SB Pro-II and above)
      MIDI_SB_OUT          - SB MIDI interface
      MIDI_MPU             - MPU-401 MIDI interface
      MIDI_GUS             - Gravis Ultrasound (not written yet)
      MIDI_DIGMID          - sample-based player (not written yet)

   For more detailed control over the sound setup you can make a sound.cfg 
   file: see readme.txt for details. You should pass the directory 
   containing your sound.cfg as the third parameter to install_sound(). To 
   look in the same directory as your executable you can pass a copy of 
   argv[0], otherwise you can just pass NULL to use the current directory.

   Returns zero if the sound is successfully installed, and -1 on failure. 
   If it fails it will store a description of the problem in allegro_error.

void remove_sound();
   Cleans up after you are finished with the sound routines. You don't 
   normally need to call this, because allegro_exit() will do it for you.

void set_volume(int digi_volume, int midi_volume);
   Alters the global sound output volume. Specify volumes for both digital 
   samples and MIDI playback, as integers from 0 to 255. If possible this 
   routine will use a hardware mixer to control the volume, otherwise it 
   will tell the sample and MIDI players to simulate a mixer in software.

SAMPLE *load_sample(char *filename);
   Loads a sample from a file, returning a pointer to it, or NULL on error. 
   At present this function only supports mono WAV files.

SAMPLE *load_wav(char *filename);
   Loads a sample from a RIFF WAV file.

void destroy_sample(SAMPLE *spl);
   Destroys a sample structure when you are done with it. It is safe to call 
   this even when the sample might be playing, because it checks and will 
   kill it off if it is active.

void play_sample(SAMPLE *spl, int vol, int pan, int freq, int loop);
   Triggers a sample at the specified volume, pan position, and frequency. 
   The volume and pan range from 0 (min/left) to 255 (max/right). Frequency 
   is relative rather than absolute: 1000 represents the frequency that the 
   sample was recorded at, 2000 is twice this, etc. If the loop flag is set, 
   the sample will repeat until you call stop_sample(), and can be 
   manipulated while it is playing by calling adjust_sample().

void adjust_sample(SAMPLE *spl, int vol, int pan, int freq, int loop);
   Alters the parameters of a sample while it is playing (useful for 
   manipulating looped sounds). You can alter the volume, pan, and 
   frequency, and can also clear the loop flag, which will stop the sample 
   when it next reaches the end of its loop. If there are several copies of 
   the same sample playing, this will adjust the first one it comes across. 
   If the sample is not playing it has no effect.

void stop_sample(SAMPLE *spl);
   Kills off a sample, which is required if you have set a sample going in 
   looped mode. If there are several copies of the sample playing, it will 
   stop them all.

MIDI *load_midi(char *filename);
   Loads a MIDI file (handles both format 0 and format 1), returning a 
   pointer to a MIDI structure, or NULL on error.

void destroy_midi(MIDI *midi);
   Destroys a MIDI structure when you are done with it. It is safe to call 
   this even when the MIDI file might be playing, because it checks and will 
   kill it off if it is active.

int play_midi(MIDI *midi, int loop);
   Starts playing the specified MIDI file, first stopping whatever music was 
   previously playing. If the loop flag is set, the data will be repeated 
   until replaced with something else, otherwise it will stop at the end of 
   the file. Passing a NULL pointer will stop whatever music is currently 
   playing. Returns non-zero if an error occurs (this may happen if a 
   patch-caching wavetable driver is unable to load the required samples, or 
   at least it might in the future when somebody writes some patch-caching 
   wavetable drivers :-)

void stop_midi();
   Stops whatever music is currently playing. This is the same thing as 
   calling play_midi(NULL, FALSE).

extern volatile long midi_pos;
   Stores the current position (beat number) in the MIDI file, or contains 
   -1 if no music is currently playing. Useful for synchronising animations 
   with the music, and for checking whether a MIDI file has finished playing.



=======================================================
============ File and compression routines ============
=======================================================

char *get_filename(char *path);
   When passed a completely specified file path, this returns a pointer to 
   the filename portion. Both '\' and '/' are recognized as directory 
   separators.

char *get_extension(char *filename);
   When passed a complete filename (with or without path information) this 
   returns a pointer to the file extension.

void put_backslash(char *filename);
   If the last character of the filename is not a '\' or '/', this routine 
   will concatenate a '\' on to it.

int file_exists(char *filename, int attrib, int *aret);
   Checks whether a file matching the given name and attributes exists, 
   returning non-zero if it does. The file attribute may contain any of the 
   FA_* constants from dir.h. If aret is not NULL, it will be set to the 
   attributes of the matching file. If an error occurs the system error code 
   will be stored in errno.

int exists(char *filename);
   Shortcut version of file_exists(), which checks for normal files, which 
   may have the archive or read-only bits set, but are not hidden, 
   directories, system files, etc.

long file_size(char *filename);
   Returns the size of a file, in bytes. If the file does not exist or an 
   error occurs, it will return zero and store the system error code in 
   errno.

long file_time(char *filename);
   Returns the modification time of a file.

int delete_file(char *filename);
   Removes a file from the disk.

int for_each_file(char *name, int attrib, void (*callback)(), int param);
   Finds all the files on the disk which match the given wildcard 
   specification and file attributes, and executes callback() once for each. 
   callback() will be passed three arguments, the first a string which 
   contains the completed filename, the second being the attributes of the 
   file, and the third an int which is simply a copy of param (you can use 
   this for whatever you like). If an error occurs an error code will be 
   stored in errno, and callback() can cause for_each_file() to abort by 
   setting errno itself. Returns the number of successful calls made to 
   callback(). The file attribute may contain any of the FA_* flags from 
   dir.h.

The following routines implement a fast buffered file I/O system, which 
supports the reading and writing of compressed files using a ring buffer 
algorithm based on the LZSS compressor by Haruhiko Okumura. This does not 
achieve quite such good compression as programs like zip and lha, but 
unpacking is very fast and it does not require much memory. Packed files 
always begin with the 32 bit value F_PACK_MAGIC, and autodetect files with 
the value F_NOPACK_MAGIC.

PACKFILE *pack_fopen(char *filename, char *mode);
   Opens a file according to mode, which may contain any of the flags:
      'r' - open file for reading.
      'w' - open file for writing, overwriting any existing data.
      'p' - open file in packed mode. Data will be compressed as it is 
	    written to the file, and automatically uncompressed during read 
	    operations. Files created in this mode will produce garbage if 
	    they are read without this flag being set. 
      '!' - open file for writing in normal, unpacked mode, but add the 
	    value F_NOPACK_MAGIC to the start of the file, so that it can 
	    later be opened in packed mode and Allegro will automatically 
	    detect that the data does not need to be decompressed. 

   Instead of these flags, one of the constants F_READ, F_WRITE, 
   F_READ_PACKED, F_WRITE_PACKED or F_WRITE_NOPACK may be used as the mode 
   parameter. On success, pack_fopen() returns a pointer to a file 
   structure, and on error it returns NULL and stores an error code in 
   errno. An attempt to read a normal file in packed mode will cause errno 
   to be set to EDOM.

int  pack_fclose(PACKFILE *f);
int  pack_fseek(PACKFILE *f, int offset);
int  pack_feof(PACKFILE *f);
int  pack_ferror(PACKFILE *f);
int  pack_getc(PACKFILE *f);
int  pack_putc(int c, PACKFILE *f);
int  pack_igetw(PACKFILE *f);
long pack_igetl(PACKFILE *f);
int  pack_iputw(int w, PACKFILE *f);
long pack_iputl(long l, PACKFILE *f);
int  pack_mgetw(PACKFILE *f);
long pack_mgetl(PACKFILE *f);
int  pack_mputw(int w, PACKFILE *f);
long pack_mputl(long l, PACKFILE *f);
long pack_fread(void *p, long n, PACKFILE *f);
long pack_fwrite(void *p, long n, PACKFILE *f);
char *pack_fgets(char *p, int max, PACKFILE *f);
int  pack_fputs(char *p, PACKFILE *f);

   These all work like the equivalent stdio functions, except that 
   pack_fread() and pack_fwrite() take a single size parameter instead of 
   that silly size and num_elements system, and seeking only supports 
   forward movement relative to the current position. The pack_i* and 
   pack_m* routines read and write 16 and 32 bit values using the Intel and 
   Motorola byte ordering systems (endianness) respectively. Note that 
   seeking is very slow when reading compressed files, and so should be 
   avoided unless you are sure that the file is not compressed.

PACKFILE *pack_fopen_chunk(PACKFILE *f, int pack);
PACKFILE *pack_fclose_chunk(PACKFILE *f);

   Open and close sub-chunks of a file. Chunks are primarily intended for 
   use by the datafile code, but they may also be useful for your own file 
   routines. A chunk provides a logical view of part of a file, which can be 
   compressed as an individual entity and will automatically insert and 
   check length counts to prevent reading past the end of the chunk. To 
   write a chunk to the file f, use the code:

      /* assumes f is a PACKFILE * which has been opened in write mode */
      f = pack_fopen_chunk(f, pack);
      write some data to f
      f = pack_fclose_chunk(f);

   The data written to the chunk will be prefixed with two length counts (32 
   bit, big-endian). For uncompressed chunks these will both be set to the 
   size of the data in the chunk. For compressed chunks (created by setting 
   the pack flag), the first length will be the raw size of the chunk, and 
   the second will be the negative size of the uncompressed data.

   To read the chunk, use the code:

      /* assumes f is a PACKFILE * which has been opened in read mode */
      f = pack_fopen_chunk(f, FALSE);
      read data from f
      f = pack_fclose_chunk(f);

   This sequence will read the length counts created when the chunk was 
   written, and automatically decompress the contents of the chunk if it 
   was compressed. The length will also be used to prevent reading past the 
   end of the chunk (Allegro will return EOF if you attempt this), and to 
   automatically skip past any unread chunk data when you call 
   pack_fclose_chunk().

   Chunks can be nested inside each other by making repeated calls to 
   pack_fopen_chunk(). When writing a file, the compression status is 
   inherited from the parent file, so you only need to set the pack flag if 
   the parent is not compressed but you want to pack the chunk data. If the 
   parent file is already open in packed mode, setting the pack flag will 
   result in data being compressed twice: once as it is written to the 
   chunk, and again as the chunk passes it on to the parent file.



===========================================
============ Datafile routines ============
===========================================

Datafiles are created by the grabber utility, and have a .dat extension. 
They can contain bitmaps, palettes, fonts, samples, MIDI music, FLI/FLC 
animations, and any other binary data that you import. To load one of these 
files into memory from within your program, call the routine:

DATAFILE *load_datafile(char *filename);
   Loads a datafile into memory, and returns a pointer to it, or NULL on 
   error. See grabber.txt for more information.

void unload_datafile(DATAFILE *dat);
   Frees all the objects in a datafile.

DATAFILE *load_datafile_object(char *filename, char *objectname);
   Loads a specific object from a datafile. This won't work if you strip the 
   object names from the file, and it will be very slow if you save the file 
   with global compression. See grabber.txt for more information.

void unload_datafile_object(DATAFILE *dat);
   Frees an object previously loaded by load_datafile_object().

char *get_datafile_property(DATAFILE *dat, int type);
   Returns the specified property string for the object, or an empty string 
   if the property isn't present. See grabber.txt for more information.

void register_datafile_object(int id, void *(*load)(PACKFILE *f, long size),
                                      void (*destroy)(void *data));
   Used to add custom object types, specifying functions to load and destroy 
   objects of this type. See grabber.txt for more information.

When you load a datafile, you will obtain a pointer to an array of DATAFILE 
structures:

typedef struct DATAFILE
{
   void *dat;     - pointer to the actual data
   int type;      - type of the data
   long size;     - size of the data in bytes
   void *prop;    - list of object properties
} DATAFILE;

The type field will be one of the values:
   DAT_FILE       - dat points to a nested datafile
   DAT_DATA       - dat points to a block of binary data
   DAT_FONT       - dat points to a font object
   DAT_SAMPLE     - dat points to a sample structure
   DAT_MIDI       - dat points to a MIDI file
   DAT_FLI        - dat points to an FLI/FLC animation
   DAT_BITMAP     - dat points to a BITMAP structure
   DAT_RLE_SPRITE - dat points to a RLE_SPRITE structure
   DAT_C_SPRITE   - dat points to a linear compiled sprite
   DAT_XC_SPRITE  - dat points to a mode-X compiled sprite
   DAT_PALETTE    - dat points to an array of 256 RGB structures
   DAT_END        - special flag to mark the end of the data list

The grabber program can also produce a header file defining the index of 
each object within the file as a series of #defined constants, using the 
names you gave the objects in the grabber. So, for example, if you have made 
a datafile called foo.dat which contains a bitmap called THE_IMAGE, you 
could display it with the code fragment:

   #include "foo.h"

   DATAFILE *data = load_datafile("foo.dat");
   draw_sprite(screen, data[THE_IMAGE].dat, x, y);

If you are programming in C++ you will get an error because the dat field is 
a void pointer and draw_sprite() expects a BITMAP pointer. You can get 
around this with a cast, eg:

   draw_sprite(screen, (BITMAP *)data[THE_IMAGE].dat, x, y);

BITMAP *load_bitmap(char *filename, RGB *pal);
   Loads a bitmap from a file, returning a pointer to a bitmap and storing 
   the palette data in the specified location, which should be an array of 
   256 RGB structures. You are responsible for destroying the bitmap when 
   you are finished with it. Returns NULL on error. At present this function 
   supports BMP, LBM, PCX, and TGA files, determining the type from the file 
   extension.

BITMAP *load_bmp(char *filename, RGB *pal);
   Loads a Windows BMP file.

BITMAP *load_lbm(char *filename, RGB *pal);
   Loads an IFF ILBM/PBM file.

BITMAP *load_pcx(char *filename, RGB *pal);
   Loads a 256 color PCX file.

BITMAP *load_tga(char *filename, RGB *pal);
   Loads a 256 color or 24 bit uncompressed TGA file.

int save_bitmap(char *filename, BITMAP *bmp, RGB *pal);
   Writes a bitmap into a file, using the specified palette, which should be 
   an array of 256 RGB structures. Returns non-zero on error. The output 
   format is determined from the filename extension: at present this 
   function supports PCX and TGA formats. One thing to watch out for: if you 
   use this to dump the screen into a file you may end up with an image much 
   larger than you were expecting, because Allegro often creates virtual 
   screens larger than the visible screen. You can get around this by using 
   a sub-bitmap to specify which part of the screen to save, eg:

      BITMAP *bmp;
      PALETTE pal;

      get_palette(pal);
      bmp = create_sub_bitmap(screen, 0, 0, SCREEN_W, SCREEN_H);
      save_bitmap("dump.pcx", bmp, pal);
      destroy_bitmap(bmp);

int save_pcx(char *filename, BITMAP *bmp, RGB *pal);
   Writes a bitmap into a PCX file.

int save_tga(char *filename, BITMAP *bmp, RGB *pal);
   Writes a bitmap into a TGA file.


=======================================
============ Math routines ============
=======================================

Allegro provides some routines for working with fixed point numbers, and 
defines the type 'fixed' to be a signed 32 bit integer. The high word is 
used for the integer part and the low word for the fraction, giving a range 
of -32768 to 32767 and an accuracy of about four or five decimal places. 
Fixed point numbers can be assigned, compared, added, subtracted, negated 
and shifted (for multiplying or dividing by powers of two) using the normal 
integer operators, but you should take care to use the appropriate 
conversion routines when mixing fixed point with integer or floating point 
values. Writing 'fixed_point_1 + fixed_point_2' is ok, but 'fixed_point + 
integer' is not.

To convert fixed point values to and from integer and floating point 
formats, use the inline functions:

fixed itofix(int x);
   Converts an integer to fixed point.

int fixtoi(fixed x);
   Converts fixed point to integer.

fixed ftofix(float x);
   Converts a floating point value to fixed point.

float fixtof(fixed x);
   Converts fixed point to floating point.

A fixed point value can be multiplied or divided by an integer with the 
normal '*' and '/' operators. To multiply or divide two fixed point values, 
you must use the functions:

fixed fmul(fixed x, fixed y);
fixed fdiv(fixed x, fixed y);

If an overflow or division by zero occurs, errno will be set and the maximum 
possible value will be returned, but errno is not cleared if the operation 
is successful. This means that if you are going to test for overflow you 
should set errno=0 before calling fmul() or fdiv().

Although fixed point numbers can be added and subtracted with the normal '+' 
and '-' integer operators, these don't provide any protection against 
overflow. If overflow is a problem, you should use the functions:

fixed fadd(fixed x, fixed y);
fixed fsub(fixed x, fixed y);

These are slower than using integer operators, but in the case of overflow 
they clamp the result, rather than just letting it wrap, and set errno.

The fixed point square root, sin, cos, tan, inverse sin, and inverse cos 
functions are implemented using lookup tables, which are very fast but not 
particularly accurate. At the moment the inverse tan uses an iterative 
search on the tan table, so it is a lot slower than the others.

Angles are represented in a binary format with 256 equal to a full circle, 
64 being a right angle and so on. This has the advantage that a simple 
bitwise 'and' can be used to keep the angle within the range zero to a full 
circle, eliminating all those tiresome 'if (angle >= 360)' checks.

fixed fsin(fixed x);
   Lookup table sine.

fixed fcos(fixed x);
   Lookup table cosine.

fixed ftan(fixed x);
   Lookup table tangent.

fixed fasin(fixed x);
   Lookup table inverse sine.

fixed facos(fixed x);
   Lookup table inverse cosine.

fixed fatan(fixed x);
   Fixed point inverse tangent.

fixed fatan2(fixed y, fixed x);
   Fixed point version of the libc atan2() routine.

fixed fsqrt(fixed x);
   Fixed point square root.


If you are programming in C++ you can ignore all the above and use the fix 
class instead, which overloads a lot of operators to provide automatic 
conversion to and from integer and floating point values, and calls the 
above routines as they are required. You should not mix the fix class with 
the fixed typedef though, because the compiler will mistake the fixed values 
for regular integers and insert unneccessary conversions. For example, if x 
is an object of class fix, calling fsqrt(x) will return the wrong result. 
You should use the overloaded sqrt(x) or x.sqrt() instead.

Allegro also contains some 3d helper functions for manipulating vectors, 
constructing and using transformation matrices, and doing perspective 
projections from 3d space onto the screen. It is not, and never will be, a 
fully fledged 3d library (my goal is to supply generic support routines, not 
shrink-wrapped graphics code :-) but these functions may be useful for 
developing your own 3d code.

All the 3d math functions are available in two versions: one which uses 
fixed point arithmetic, and another which uses floating point. The syntax 
for these is identical, but the floating point functions and structures are 
postfixed with '_f', eg. the fixed point function cross_product() has a 
floating point equivalent cross_product_f(). If you are programming in C++, 
Allegro also overloads these functions for use with the 'fix' class.

3d transformations are accomplished by the use of a modelling matrix. This 
is a 4x4 array of numbers that can be multipled with a 3d point to produce a 
different 3d point. By putting the right values into the matrix, it can be 
made to do various operations like translation, rotation, and scaling. The 
clever bit is that you can multiply two matrices together to produce a third 
matrix, and this will have the same effect on points as applying the 
original two matrices one after the other. For example, if you have one 
matrix that rotates a point and another that shifts it sideways, you can 
combine them to produce a matrix that will do the rotation and the shift in 
a single step. You can build up extremely complex transformations in this 
way, while only ever having to multiply each point by a single matrix.

Allegro actually cheats in the way it implements the matrix structure. 
Rotation and scaling of a 3d point can be done with a simple 3x3 matrix, but 
in order to translate it and project it onto the screen, the matrix must be 
extended to 4x4, and the point extended into 4d space by the addition of an 
extra coordinate, w=1. This is a bad thing in terms of efficiency, but 
fortunately an optimisation is possible. Given the 4x4 matrix:

   ( a, b, c, d )
   ( e, f, g, h )
   ( i, j, k, l )
   ( m, n, o, p )

a pattern can be observed in which parts of it do what. The top left 3x3 
grid implements rotation and scaling. The three values in the top right 
column (d, h, and l) implement translation, and as long as the matrix is 
only used for affine transformations, m, n and o will always be zero and p 
will always be 1. If you don't know what affine means, read Foley & Van 
Damme: basically it covers scaling, translation, and rotation, but not 
projection. Since Allegro uses a separate function for projection, the 
matrix functions only need to support affine transformations, which means 
that there is no need to store the bottom row of the matrix. Allegro 
implicitly assumes that it contains (0,0,0,1), and optimises the matrix 
manipulation functions accordingly.

Matrices are stored in the structures:

typedef struct MATRIX            - fixed point matrix structure
{
   fixed v[3][3];                - 3x3 scaling and rotation component
   fixed t[3];                   - x/y/z translation component
} MATRIX;

typedef struct MATRIX_f          - floating point matrix structure
{
   float v[3][3];                - 3x3 scaling and rotation component
   float t[3];                   - x/y/z translation component
} MATRIX_f

extern MATRIX identity_matrix;
extern MATRIX_f identity_matrix_f;
   Global variables containing the 'do nothing' identity matrix. Multiplying 
   by the identity matrix has no effect.

void get_translation_matrix(MATRIX *m, fixed x, fixed y, fixed z);
void get_translation_matrix_f(MATRIX_f *m, float x, float y, float z);
   Constructs a translation matrix, storing it in m. When applied to the 
   point (px, py, pz), this matrix will produce the point (px+x, py+y, 
   pz+z). In other words, it moves things sideways.

void get_scaling_matrix(MATRIX *m, fixed x, fixed y, fixed z);
void get_scaling_matrix_f(MATRIX_f *m, float x, float y, float z);
   Constructs a scaling matrix, storing it in m. When applied to the point 
   (px, py, pz), this matrix will produce the point (px*x, py*y, pz*z). In 
   other words, it stretches or shrinks things.

void get_x_rotate_matrix(MATRIX *m, fixed r);
void get_y_rotate_matrix(MATRIX *m, fixed r);
void get_z_rotate_matrix(MATRIX *m, fixed r);
void get_x_rotate_matrix_f(MATRIX_f *m, float r);
void get_y_rotate_matrix_f(MATRIX_f *m, float r);
void get_z_rotate_matrix_f(MATRIX_f *m, float r);
   Construct rotation matrices for each axis, storing them in m. When 
   applied to a point, these matrices will rotate it about their respective 
   axis by the specified angle (given in binary, 256 degrees to a circle 
   format).

void get_rotation_matrix(MATRIX *m, fixed x, fixed y, fixed z);
void get_rotation_matrix_f(MATRIX_f *m, float x, float y, float z);
   Constructs a transformation matrix which will rotate points around all 
   three axis by the specified amounts (given in binary, 256 degrees to a 
   circle format).

void get_vector_rotation_matrix(MATRIX *m, fixed x, y, z, fixed a);
void get_vector_rotation_matrix_f(MATRIX_f *m, float x, y, z, float a);
   Constructs a transformation matrix which will rotate points around the 
   specified x,y,z vector by the specified angle (given in binary, 256 
   degrees to a circle format).

void get_transformation_matrix(MATRIX *m, fixed scale,
                               fixed xrot, yrot, zrot, x, y, z);
   Constructs a transformation matrix which will rotate points around all 
   three axis by the specified amounts (given in binary, 256 degrees to a 
   circle format), scale the result by the specified amount (pass 1 for no 
   change of scale), and then translate to the requested x, y, z position.

void get_transformation_matrix_f(MATRIX_f *m, float scale,
                                 float xrot, yrot, zrot, x, y, z);
   Floating point version of get_transformation_matrix().

void get_camera_matrix(MATRIX *m, fixed x, y, z, xfront, yfront, zfront,
                       fixed xup, yup, zup, fov, aspect);
   Constructs a camera matrix for translating world-space objects into a 
   normalised view space, ready for the perspective projection. The x, y, 
   and z parameters specify the camera position, xfront, yfront, and zfront 
   are the 'in front' vector specifying which way the camera is facing (this 
   can be any length: normalisation is not required), and xup, yup, and zup 
   are the 'up' direction vector. The fov parameter specifies the field of 
   view (ie. width of the camera focus) in binary, 256 degrees to the circle 
   format. For typical projections, a field of view in the region 32-48 will 
   work well. Finally, the aspect ratio is used to scale the Y dimensions of 
   the image relative to the X axis, so you can use it to adjust the 
   proportions of the output image (set it to 1 for no scaling).

void get_camera_matrix_f(MATRIX_f *m, float x, y, z, xfront, yfront, zfront,
                         float xup, yup, zup, fov, aspect);
   Floating point version of get_camera_matrix().

void matrix_mul(MATRIX *m1, MATRIX *m2, MATRIX *out);
void matrix_mul_f(MATRIX_f *m1, MATRIX_f *m2, MATRIX_f *out);
   Multiplies two matrices, storing the result in out (this must be 
   different from the two input matrices). The resulting matrix will have 
   the same effect as the combination of m1 and m2, ie. when applied to a 
   point p, (p * out) = ((p * m1) * m2). Any number of transformations can 
   be concatenated in this way. Note that matrix multiplication is not 
   commutative, ie. matrix_mul(m1, m2) != matrix_mul(m2, m1).

fixed vector_length(fixed x, fixed y, fixed z);
float vector_length_f(float x, float y, float z);
   Calculates the length of the vector (x, y, z), using that good 'ole 
   Pythagoras theorem.

void normalize_vector(fixed *x, fixed *y, fixed *z);
void normalize_vector_f(float *x, float *y, float *z);
   Converts the vector (*x, *y, *z) to a unit vector. This points in the 
   same direction as the original vector, but has a length of one.

fixed dot_product(fixed x1, y1, z1, x2, y2, z2);
float dot_product_f(float x1, y1, z1, x2, y2, z2);
   Calculates the dot product (x1, y1, z1) . (x2, y2, z2), returning the 
   result.

void cross_product(fixed x1, y1, z1, x2, y2, z2, *xout, *yout, *zout);
void cross_product_f(float x1, y1, z1, x2, y2, z2, *xout, *yout, *zout);
   Calculates the cross product (x1, y1, z1) x (x2, y2, z2), storing the 
   result in (*xout, *yout, *zout). The cross product is perpendicular to 
   both of the input vectors, so it can be used to generate polygon normals.

void apply_matrix(MATRIX *m, fixed x, y, z, *xout, *yout, *zout);
void apply_matrix_f(MATRIX_f *m, float x, y, z, *xout, *yout, *zout);
   Multiplies the point (x, y, z) by the transformation matrix m, storing 
   the result in (*xout, *yout, *zout).

void set_projection_viewport(int x, int y, int w, int h);
   Sets the viewport used to scale the output of the persp_project() 
   function. Pass the dimensions of the screen area you want to draw onto, 
   which will typically be 0, 0, SCREEN_W, and SCREEN_H.

void persp_project(fixed x, y, z, *xout, *yout);
void persp_project_f(float x, y, z, *xout, *yout);
   Projects the 3d point (x, y, z) into 2d screen space, storing the result 
   in (*xout, *yout) and using the scaling parameters previously set by 
   calling set_projection_viewport(). This function projects from the 
   normalized viewing pyramid, which has a camera at the origin and facing 
   along the positive z axis. The x axis runs left/right, y runs up/down, 
   and z increases with depth into the screen. The camera has a 90 degree 
   field of view, ie. points on the planes x=z and -x=z will map onto the 
   left and right edges of the screen, and the planes y=z and -y=z map to 
   the top and bottom of the screen. If you want a different field of view 
   or camara location, you should transform all your objects with an 
   appropriate viewing matrix, eg. to get the effect of panning the camera 
   10 degrees to the left, rotate all your objects 10 degrees to the right.



======================================
============ GUI routines ============
======================================

Allegro contains an object-oriented dialog manager, which was originally 
based on the Atari GEM system (form_do(), objc_draw(), etc: old ST 
programmers will know what I'm talking about :-) You can use the GUI as-is 
to knock out simple interfaces for things like the test program and grabber 
utility, or you can use it as a basis for more complicated systems of your 
own. Allegro lets you define your own object types by writing new dialog 
procedures, so you can take complete control over the visual aspects of the 
interface while still using Allegro to handle input from the mouse, 
keyboard, joystick, etc.

A GUI dialog is stored as an array of DIALOG objects, each one containing 
the fields:

typedef struct DIALOG
{
   int (*proc)(int, DIALOG *, int);    - dialog procedure (message handler)
   int x, y, w, h;                     - position and size of the object
   int fg, bg;                         - foreground and background colors
   int key;                            - ASCII keyboard shortcut
   int flags;                          - flags about the status of the object
   int d1, d2;                         - whatever you want to use them for
   void *dp;                           - pointer to more object-specific data
} DIALOG;

The array should end with an object which has the proc pointer set to NULL.

The flags field may contain any combination of the bit flags:
   D_EXIT          - this object should close the dialog when it is clicked
   D_SELECTED      - this object is selected
   D_GOTFOCUS      - this object has got the input focus
   D_GOTMOUSE      - the mouse is currently on top of this object
   D_HIDDEN        - this object is hidden and inactive
   D_DISABLED      - this object is greyed-out and inactive

Each object is controlled by a dialog procedure, which is stored in the proc 
pointer. This will be called by the dialog manager whenever any action 
concerning the object is required, or you can call it directly with the 
SEND_MESSAGE macro. The dialog procedure should follow the form:

   int foo(int msg, DIALOG *d, int c);

It will be passed a flag (msg) indicating what action it should perform, a 
pointer to the object concerned (d), and if msg is MSG_CHAR or MSG_XCHAR, 
the key that was pressed (c). Note that d is a pointer to a specific object, 
and not to the entire dialog.

The dialog procedure should return one of the values:
   D_O_K          - normal return status
   D_CLOSE        - tells the dialog manager to close the dialog
   D_REDRAW       - tells the dialog manager to redraw the entire dialog
   D_WANTFOCUS    - requests that the input focus be given to this object
   D_USED_CHAR    - MSG_CHAR and MSG_XCHAR return this if they used the key

Dialog procedures may be called with any of the messages:

MSG_START:
   Tells the object to initialise itself. The dialog manager sends this to 
   all the objects in a dialog just before it displays the dialog.

MSG_END:
   Sent to all objects when closing a dialog, allowing them to perform 
   whatever cleanup operations they require.

MSG_DRAW:
   Tells the object to draw itself onto the screen. The mouse pointer will 
   be turned off when this message is sent, so the drawing code does not 
   need to worry about it.

MSG_CLICK:
   Informs the object that a mouse button has been clicked while the mouse 
   was on top of the object. Typically an object will perform its own mouse 
   tracking as long as the button is held down, and only return from this 
   message handler when it is released.

MSG_DCLICK:
   Sent when the user double-clicks on an object. A MSG_CLICK will be sent 
   when the button is first pressed, then MSG_DCLICK if it is released and 
   pressed again within a short space of time.

MSG_KEY:
   Sent when the keyboard shortcut for the object is pressed, or if enter, 
   space, or a joystick button is pressed while it has the input focus.

MSG_CHAR:
   When a key is pressed, this message is sent to the object that has the 
   input focus. If the object deals with the keypress it should return 
   D_USED_CHAR, otherwise it should return D_O_K to allow the default 
   keyboard interface to operate.

MSG_XCHAR:
   When a key is pressed, Allegro will send a MSG_CHAR to the object with 
   the input focus. If this object doesn't process the key (ie. it returns 
   D_O_K rather than D_USED_CHAR), the dialog manager will look for an 
   object with a matching keyboard shortcut in the key field, and send it a 
   MSG_KEY. If this fails, it broadcasts a MSG_XCHAR to all objects in the 
   dialog, allowing them to respond to special keypresses even when they 
   don't have the input focus. Normally you should ignore this message 
   (return D_O_K rather than D_USED_CHAR), in which case Allegro will 
   perform default actions such as moving the focus in response to the arrow 
   keys and closing the dialog if ESC is pressed.

MSG_WANTFOCUS:
   Queries whether an object is willing to accept the input focus. It should 
   return D_WANTFOCUS if it does, or D_O_K if it isn't interested in getting 
   user input.

MSG_GOTFOCUS:
MSG_LOSTFOCUS:
   Sent whenever an object gains or loses the input focus. These messages 
   will always be followed by a MSG_DRAW, to let objects display themselves 
   differently when they have the input focus.

MSG_GOTMOUSE:
MSG_LOSTMOUSE:
   Sent when the mouse moves on top of or away from an object. Unlike the 
   focus messages, these are not followed by a MSG_DRAW, so if the object is 
   displayed differently when the mouse is on top of it, it is responsible 
   for redrawing itself in response to these messages.

MSG_IDLE:
   Sent whenever the dialog manager has nothing better to do.

MSG_RADIO:
   Sent by radio button objects to deselect other buttons in the same group 
   when they are clicked. The group number is passed in the c message 
   parameter.

Allegro provides several standard dialog procedures. You can use these as 
they are to provide simple user interface objects, or you can call them from 
within your own dialog procedures, resulting in a kind of OOP inheritance. 
For instance, you could make an object which calls d_button_proc to draw 
itself, but handles the click message in a different way, or an object which 
calls d_button_proc for everything except drawing itself, so it would behave 
like a normal button but could look completely different.

int d_clear_proc(int msg, DIALOG *d, int c);
   This just clears the screen when it is drawn. Useful as the first object 
   in a dialog.

int d_box_proc(int msg, DIALOG *d, int c);
int d_shadow_box_proc(int msg, DIALOG *d, int c);
   These draw boxes onto the screen, with or without a shadow.

int d_bitmap_proc(int msg, DIALOG *d, int c);
   This draws a bitmap onto the screen, which should be pointed to by the 
   dp field.

int d_text_proc(int msg, DIALOG *d, int c);
int d_ctext_proc(int msg, DIALOG *d, int c);
   These draw text onto the screen. The dp field should point to the string 
   to display. d_ctext_proc() centres the string around the x coordinate. 
   Any '&' characters in the string will be replaced with lines underneath 
   the following character, for displaying keyboard shortcuts (as in MS 
   Windows). To display a single ampersand, put "&&".

int d_button_proc(int msg, DIALOG *d, int c);
   A button object (the dp field points to the text string). This object can 
   be selected by clicking on it with the mouse or by pressing its keyboard 
   shortcut. If the D_EXIT flag is set, selecting it will close the dialog, 
   otherwise it will toggle on and off. Like d_text_proc(), ampersands can 
   be used to display the keyboard shortcut of the button.

int d_check_proc(int msg, DIALOG *d, int c);
   This is an example of how you can derive objects from other objects. Most 
   of the functionality comes from d_button_proc(), but it displays itself 
   as a check box.

int d_radio_proc(int msg, DIALOG *d, int c);
   A radio button object. A dialog can contain any number of radio button 
   groups: selecting a radio button causes other buttons within the same 
   group to be deselected. The dp field points to the text string, d1 
   specifies the group number, and d2 is the button style (0=circle, 
   1=square).

int d_keyboard_proc(int msg, DIALOG *d, int c);
   This is an invisible object for implementing keyboard shortcuts. You can 
   put an ASCII code in the key field of the dialog object (a character such 
   as 'a' to respond to a simple keypress, or a number 1-26 to respond to a 
   control key a-z), or you can put a keyboard scancode in the d1 and/or d2 
   fields. When one of these keys is pressed, the object will call the 
   function pointed to by dp. This should return an int, which will be 
   passed back to the dialog manager, so it can return D_O_K, D_REDRAW, 
   D_CLOSE, etc.

int d_edit_proc(int msg, DIALOG *d, int c);
   An editable text object (the dp field points to the string). When it has 
   the input focus (obtained by clicking on it with the mouse), text can be 
   typed into this object. The d1 field specifies the maximum number of 
   characters that it will accept, and d2 is the text cursor position within 
   the string.

int d_list_proc(int msg, DIALOG *d, int c);
   A list box object. This will allow the user to scroll through a list of 
   items and to select one by clicking or with the arrow keys. If the D_EXIT 
   flag is set, double clicking on a list item will close the dialog. The 
   index of the selected item is held in the d1 field, and d2 is used to 
   store how far it has scrolled through the list. The dp field points to a 
   function which will be called to obtain information about the contents of 
   the list. This should follow the form:

      char *foobar(int index, int *list_size);

   If index is zero or positive, the function should return a pointer to the 
   string which is to be displayed at position index in the list. If index 
   is negative, it should return NULL and list_size should be set to the 
   number of items in the list. 

int d_menu_proc(int msg, DIALOG *d, int c);
   This object is a menu bar which will drop down child menus when it is 
   clicked or if an alt+key corresponding to one of the shortcuts in the 
   menu is pressed. It ignores a lot of the fields in the dialog structure, 
   in particular the color is taken from the gui_*_color variables, and the 
   width and height are calculated automatically. The dp field points to an 
   array of menu structures: see do_menu() for more information. The top 
   level menu will be displayed as a horizontal bar, but when child menus 
   drop down from it they will be in the normal vertical format used by 
   do_menu(). When a menu item is selected, the return value from the menu 
   callback function is passed back to the dialog manager, so your callbacks 
   should return D_O_K, D_REDRAW, or D_CLOSE.

The behaviour of the dialog manager can be controlled by the variables:

extern int gui_mouse_focus;
   If set, the input focus follows the mouse pointer around the dialog, 
   otherwise a click is required to move it.

extern int gui_fg_color, gui_bg_color;
   The foreground and background colors for the standard dialogs (alerts, 
   menus, and the file selector). They default to 255 and 0.

extern int gui_mg_color;
   The color used for displaying greyed-out dialog objects (with the 
   D_DISABLED flag set). Defaults to 8.

extern int gui_font_baseline;
   If set to a non-zero value, adjusts the keyboard shortcut underscores to 
   account for the height of the descenders in your font.

You can also change the global 'font' pointer to make the GUI objects use 
something other than the standard 8x8 font. The standard dialog procedures, 
menus, and alert boxes, will work with fonts of any size, but the 
file_select() and gfx_mode_select() dialogs will look wrong with anything 
other than 8x8 fonts.

void centre_dialog(DIALOG *dialog);
   Moves an array of dialog objects so that it is centered in the screen.

void set_dialog_color(DIALOG *dialog, int fg, int bg);
   Sets the foreground and background colors of an array of dialog objects.

int find_dialog_focus(DIALOG *dialog);
   Searches the dialog for the object which has the input focus, returning 
   an index or -1 if the focus is not set. This is useful if you are calling 
   do_dialog() several times in a row and want to leave the focus in the 
   same place it was when the dialog was last displayed, as you can call 
   do_dialog(dlg, find_dialog_focus(dlg));

int dialog_message(DIALOG *dialog, int msg, int c, int *obj);
   Sends a message to all the objects in an array. If any of the dialog 
   procedures return values other than D_O_K, it returns the value and sets 
   obj to the index of the object which produced it.

int broadcast_dialog_message(int msg, int c);
   Broadcasts a message to all the objects in the active dialog. If any of 
   the dialog procedures return values other than D_O_K, it returns that 
   value.

int do_dialog(DIALOG *dialog, int focus_obj);
   The basic dialog manager function. This displays a dialog (an array of 
   dialog objects, terminated by one with a NULL dialog procedure), and sets 
   the input focus to the focus_obj (-1 if you don't want anything to have 
   the focus). It interprets user input and dispatches messages as they are 
   required, until one of the dialog procedures tells it to close the 
   dialog, at which point it returns the index of the object that caused it 
   to exit.

int popup_dialog(DIALOG *dialog, int focus_obj);
   Like do_dialog(), but it stores the data on the screen before drawing the 
   dialog and restores it when the dialog is closed. The screen area to be 
   stored is calculated from the dimensions of the first object in the 
   dialog, so all the other objects should lie within this one.

Popup or pulldown menus are created as an array of the structures:

typedef struct MENU
{
   char *text;                   - the text to display for the menu item
   int (*proc)();                - called when the menu item is clicked
   struct MENU *child;           - nested child menu
} MENU;

Each menu item contains a text string. This can use the '&' character to 
indicate keyboard shortcuts, or can be an zero-length string to display the 
item as a non-selectable splitter bar. The proc pointer is a function which 
will be called when the menu item is selected, and child points to another 
menu, allowing you to create nested menus. Both proc and child may be NULL. 
The proc function returns an integer which is ignored if the menu was 
brought up by calling do_menu(), but which is passed back to the dialog 
manager if it was created by a d_menu_proc() object. The array of menu items 
is terminated by an entry with a NULL text pointer.

int do_menu(MENU *menu, int x, int y)
   Displays and animates a popup menu at the specified screen coordinates 
   (these will be adjusted if the menu does not entirely fit on the screen). 
   Returns the index of the menu item that was selected, or -1 if the menu 
   was cancelled. Note that the return value cannot indicate selection from 
   child menus, so you will have to use the callback functions if you want 
   multi-level menus.

int alert(char *s1, *s2, *s3, char *b1, *b2, int c1, c2);
   Displays a popup alert box, containing three lines of text (s1-s3), and 
   with either one or two buttons. The text for these buttons is passed in 
   b1 and b2 (b2 may be NULL), and the keyboard shortcuts in c1 and c2. 
   Returns 1 or 2 depending on which button was clicked. If the alert is 
   dismissed by pressing ESC when ESC is not one of the keyboard shortcuts, 
   it treats it as a click on the second button (this is consistent with the 
   common "Ok", "Cancel" alert).

int alert3(char *s1, *s2, *s3, char *b1, *b2, *b3, int c1, c2, c3);
   Like alert(), but with three buttons. Returns 1, 2, or 3.

int file_select(char *message, char *path, char *ext);
   Displays the Allegro file selector, with the message as caption. The path 
   parameter contains the initial filename to display (this can be used to 
   set the starting directory, or to provide a default filename for a 
   save-as operation). The user selection is returned by altering path, so 
   it should have room for at least 80 characters. The list of files is 
   filtered according to the file extensions in ext. Passing NULL includes 
   all files, "PCX;BMP" includes only files with .PCX or .BMP extensions. 
   Returns zero if it was closed with the Cancel button, non-zero if it was 
   OK'd.

int gfx_mode_select(int *card, int *w, int *h);
   Displays the Allegro graphics mode selection dialog, which allows the 
   user to select a screen mode and graphics card. Stores the selection in 
   the three variables, and returns zero if it was closed with the Cancel 
   button or non-zero if it was OK'd.



=======================================
============ In Conclusion ============
=======================================

All good things must come to an end. Writing documentation is not a good 
thing, though, and that means it goes on for ever. There is always something 
I've forgotten to explain, or some essential detail I've left out, but for 
now you will have to make do with this. Feel free to ask if you can't figure 
something out.

Enjoy. If you find any of this stuff useful, write and tell me about it.
I want to hear from you!


By Shawn Hargreaves,
1 Salisbury Road,
Market Drayton,
Shropshire,
England, TF9 1AJ.

shawn@talula.demon.co.uk
http://www.talula.demon.co.uk/
