Zelda 291 runs under Mode X video and implements page-flipping instead of double-buffering for fast drawing. Sprites and tiles for the game are be loaded from PCX files on disk and placed in arrays.
There are a number of things that proved difficult to achieve
concerning the graphics of the game. First of all, the fact that
Mode X is unchained and planar (read further down under Implementation
for details on this) means that memory cannot be written quickly
and linearly to the video buffer; in order to make pixel plotting
fast enough, sprites were drawn column-by-column in order to
minimize the number of slow calls to GFXSelectPlane.
Since we have two video memory pages to keep track of, sprite
background scanning was that much more difficult -- two
background buffers were required for each object.
Mode X is a "tweaked" video mode
obtained by setting the video mode to the standard 13h VGA mode
and then writing certain values to the CRT controller port and
the sequencer port to unchain the memory mode and increase the
vertical resolution to 240 pixels. This allows our game to have
square pixels, since the aspect ratio of the monitor is matched.
To illustrate the layout of the unchained video memory used,
examine the following image taken from Robert Schmidt's excellent
Mode X tutorial:
When programming in mode 13h, double buffering is the only way to achieve fast flickerless animation. However, in Mode X, this is unreasonable due to the fact that a 320 x 240 double buffer will not fit into a single 64k segment. Fortunately, since we've unchained our video memory, we can implement page flipping!
We have defined in video memory two "pages," each containing 320 x 240 bytes. One page is be active at any given time; this is the page to which we draw all images. The other page is be visible, which means that it is the page that is being displayed on the screen at the moment. When a scene has been rendered on the active page, by swapping the offsets of the two pages from the start of video memory, we make the active page visible, and the visible page active. This is much faster than double-buffering!
In order for the game objects in the world to be able to interact with each other, we must perform what is known as collision detection. When Link collides with an enemy, for instance, we'll want a number of things to occur -- Link should take damage and recoil, and the corresponding sounds and graphics should be played and displayed. In order to implement collision detection, each sprite will have what's called a bounding box. This box defines the area of the sprite which is considered to be "substantial"; if the bounding box of one sprite overlaps that of another, then a collision has occurred.
SpriteSprite structure represents the image
drawn on the screen to represent a GameObject.
Its fields are as follows:spr_w db ? - The width, in pixels,
of the sprite. spr_h db ? - The height, in pixels,
of the sprite. spr_threshold_walk db ?,
spr_threshold_death db ? - Thresholds used
for walking and death animations. When a counter's
value reaches that of a threshold,
that indicates that a change in the object's
animation should be made (for instance, a walking
character should put his other foot forward). spr_frames dd MAX_NUM_FRAMES dup(?)
- The sprite's array of animation frame images. spr_num_frames db ? - The number of
frames of animation this sprite has. spr_bound_x1 db ?, spr_bound_y1 db ?,
spr_bound_x2 db ?, spr_bound_y2 db ? - The
coordinates of the sprite's bounding box, such
that (x1, y1) represents the
upper-left corner of the box and (x2, y2)
the lower-right corner. These coordinates are
relative to the upper-left corner of the sprite's
frame image itself.
RGBRGB structure represents one of the VGA
card's color registers. Its fields are as follows:red db ?, green db ?, blue db ? -
The register's red, green, and blue color
information.
RGBPaletteRGBPalette structure is an array of RGB
values defining a palette. Its fields are as follows:colors RGB 256 dup({}) - The array
of RGB values.
PCXPCX structure is used to manipulate PCX
files and images. Its fields are as follows:pcx_buffer dd ? - A far pointer to
the image's 64,000 byte image buffer, which is
dynamically allocated in order to conserve space.
pcx_pal RGBPalette {} - The PCX
file's palette information.
GFXSetVideoModemode - the video mode to
set, either 13h or 03h.
GFXSetModeX
GFXInit
GFXRefreshglobal_ptr - A far pointer
to the global array containing Link and
all other active projectiles. grm_ptr - A far pointer to
the current GameRoom structure.
GFXGetSpritePtrsprite_type - The
GameObject's gobj_type value.DX:AX - The address of the
desired Sprite structure.
GFXFlipPages actStart, visStart - The
offsets of the beginning of the active
and visible memory pages. actPage, visPage - The
indices of the active and visible pages.
GFXDrawSprites GameObject's
background buffer to the active video page;
scanning the background under the object's new
position into its background buffer; and finally
drawing the sprite to the page. global_ptr - A far pointer
to the global array. grm_ptr - A far pointer to
the GameRoom structure
representing the current room. This is
required so that we can draw all the
sprites in the room.
GFXScrollRoom global_ptr - A far pointer
to the global array. grm_ptr - A far pointer to
the current room.
GFXDrawRoom tilemap_ptr - A far pointer
to the TileMap structure for
the current room.
GFXScreenTransition effect - An integer
indicating the desired transition.
Possible values include FADE_TO_BLACK
and FADE_TO_RED.
GFXDoAnimation gobj_ptr - A far pointer to
the object to be animated.
GFXLoadTiles pcx_ptr - A far pointer to
the PCX object structure holding the
information for the tiles.tileArray - The array of
tiles.
GFXTileBlt tile - The index of the tile
to draw. x_pos, y_pos - The desired
position of the tile.
GFXFillScreen color - The color desired.
GFXSetActivePage pg - The index of the page
to set active.actStart - The offset of the
new active memory page.
GFXSetVisiblePage pg - The index of the page
to set visible.visPage - The offset of the
new visible memory page.
GFXSelectPlane plane - The index of the
plane to set active.
GFXPutPixel x, y - The coordinates of
the pixel. color - The color of the
pixel.
GFXGetPixel x, y - The coordinates of
the pixel. AL - The color of the pixel.
GFXDelay ticks - The number of clock
ticks to wait. One tick = 1/18th sec.
GFXWaitForVerticalRetrace
GFXDrawStatus global_ptr - A far pointer
to the global array containing Link.
GFXPutGlobalPixel x, y - The coordinates of
the pixel. color - The color of the
pixel.
GFXPrintChar x, y - The desired
coordinates of the character. color - The color of the
character. char - The ASCII value of
the character.
RGBWriteColorReg RGB
structure to one of the VGA's color registers. index - The index of the
color register. color - The RGB
structure.
RGBReadColorReg RGB
structure. index - The index of the
color register. color - The RGB
structure.
RGBWritePalette RGBPalette
structure to the VGA card. pal - A far pointer to the RGBPalette
structure.
RGBReadPalette RGBPalette
structure. pal - A far pointer to the RGBPalette
structure.
PCXInit PCX structure
by allocating the memory for its image buffer. pcx_image - A far pointer to
the PCX structure.
PCXDelete PCX structure by
deallocating the memory for its image buffer. pcx_image - A far pointer to
the PCX structure.
PCXLoad PCX
structure. file_name - A far pointer to
the PCX file's null-terminated filename
string. pcx_image - A far pointer to
the PCX structure. pcx_image
SPRInit Sprite
structure by setting its data fields and
allocating the memory for its image buffers. sprite_ptr - A far pointer
to the Sprite structure. w, h - The dimensions of the
sprite, in pixels. twalk, tdeath - The
thresholds for the sprite's walking and
death animations. num_frames - The number of
frames in the sprite's animation array. bound_x1, bound_y1, bound_x2,
bound_y2 - The coordinates of the
upper-left and lower-right corners of the
sprite's bounding box, relative to the
upper-left corner of the sprite. sprite_ptr
SPRDelete Sprite structure
by deallocating the memory for its image buffers.
sprite_ptr - A far pointer
to the Sprite structure. sprite_ptr
SPRLoadFrames sprite_ptr - A far pointer
to the Sprite structure. pcx_image - A far pointer to
the PCX structure containing
the image for the sprite's frames. sprite_ptr
SPRDraw gobj_ptr - A far pointer to
the GameObject structure;
from this, the Sprite
structure can be extracted and drawn. transparent - A flag
indicating whether or not pixels of the
special "transparent" color
should be drawn. A value of 1 indicates
that they should not. active_page - The index of
the currently active page.
SPRScanBackground gobj_ptr - A far pointer to
the GameObject structure. active_page - The index of
the currently active page. gobj_ptr
SPRErase gobj_ptr - A far pointer to
the GameObject structure. active_page - The index of
the currently active page.
IGLInitSingleObject GameObject by
allocating memory for its background buffers and
setting its Sprite pointer. gobj_ptr - A near pointer to
the GameObject structure. gobj_ptr
IGLDeleteSingleObject GameObject by
deallocating memory for its background buffers
and setting its gobj_type field to GT_NOOBJ.
gobj_ptr - A near pointer to
the GameObject structure. gobj_ptr
GOBJInit GameObjects
in the global array and each room's enemy data
array through calls to IGLInitSingleObject.
IGLDetectCollisions x, y - The projected
screen-map coordinates of the object's
desired new position. gobj_ptr - A near pointer to
the GameObject we're
checking for collisions against. grm_ptr - A near pointer to
the current GameRoom. CollRetCode - Returns a code
indicating the type of collision that has
occurred. Must be one of the following: COLL_NONE - No
collision occurred. COLL_ENEMY - A
collision with an enemy occurred.
COLL_PROJECTILE - A
collision with a weapon occurred.
COLL_INERT - A
collision with an inactive object
such as a wall or block occurred.
COLL_DOOR - A
collision with a door occurred. CollObstaclePtr - A near
pointer to the GameObject
this object collided with, if applicable;
if the object collided with a door, this
points to the GameRoom
structure for the next room. GameObject::gobj_collision_link
- This pointer in the GameObject the
object we're checking collided with is
set to the current GameObject.
For assistance in memory management, this module will
make use of Randall Hyde's UCR Standard Library,
which can be obtained at http://webster.ucr.edu/Page_asm/RHUCRLib.html
if you can manage to connect to their server. In
particular, liberal use will be made of the Malloc
and Free commands provided by the library.