River Frenzy Daniel T. Restelli
Jeffrey Brokish
Scott Schwemin
An ECE 291 Final Project

A screen-shot of River Frenzy

Project Introduction

River Frenzy is a reincarnation/revitalization of the classic Atari 2600 game River Raid.

In River Frenzy, you, the player, are flying a new U.S. taxpayer funded military aircraft over the deadly river waters of the kingdom of Wishyfoo, your mortal enemy. The Wishyfoo military has responded and is out in force to stop you. Your super high-tech aircraft is equipped with a pea-shooter style weapon from which you can hurl one deadly projectile at a time towards oncoming enemies. Enemies will take the form of enemy battleships on the water, helicopters flying mindlessly back and forth, and high-speed, extremely dangerous 747 passenger airliners. Your aircraft, in keeping with the best traditions of U.S. technology, has really lousy gas mileage. As such, you will need to constantly refuel at the refueling stations placed strategically on the water. Refuel by flying over them. Be careful! A stray shot from your pea shooter can destroy these refueling stations!

You accumulate points by destroying enemies, not getting killed (a good practice in all areas of life), and advancing to later levels. To advance from one level to the next you must destroy the bridge separating the two sections of river with your pea shooter. Be careful, ramming bridges is not a viable option when it comes to advancing to future levels.

Should you collide with the shore, an enemy aircraft or ship, or the bridges separating levels (see above note on ramming), that loud sound you hear will be the sound of your highly expensive aircraft exploding into millions of highly expensive fragments.

Operation

The game controls are simple and intuitive. The game is started from the intro screen by pressing '1' for a one player game. Since River Frenzy is a single player game, there is no other choice. Once the game has begun, the controls are simple:

Game Action Control Key
Fly faster Up arrow key
Fly slower Down arrow key
Move left Left arrow key
Move right right arrow key
Fire Pea Shooter Space Bar
Quit game ESC key

Movements are sustained by holding the appropriate key down. In other words, to continue moving left you must hold down the left arrow key.

Detailed Project Implementation

The following section is a detailed project write-up, detailing the breakdown of the project into its components. Documentation on each subroutine/component of the program is included below. The information that follows is of interest only to ECE291 graders and students. In other words, if you stumbled here by accident, Turn Back Now before it's too late!

This project required around 120 programmer hours (Each of us put in around 40 hours on the project). This is fairly consistant with what we had predicted that this project was required. The only un-anticipated development time was due to the C++ code having to be re-written in assembler when it proved nearly impossible to link the C++ routines with the rest of the code.

Major goals for River Frenzy

- indicates major goal accomplished.

The River Frenzy project has been broken down into three large subsystems. All routines in each subsystem were coded by the team member noted in the table. The subsections are detailed as follows:

Part 1
Gameplay
coded by: Jeffrey Brokish
Our Hero Animation
Enemy Intelligence
Enemy Animation
Part 2
Background Graphics
coded by: Dan Restelli
Graphics development
Map generation
Background scrolling
Status display
Main game control loop
Part 3
Sound System
coded by: Scott Schwemin
Sound Effects
Background Music

This project will be primarily in assembly language, with one small C component used for MIDI control.

Main Loop

The game is governed by a main loop. This main loop performs the following actions

Since game control is interrupt-based, it need not enter into the main loop in any way. Since all the objects are truly object based, they need only be sent the Update signal by the main loop and they update their own positions accordingly. The result is an extremely simple main game loop.

Game Distribution

The source disk for river frenzy contains the following files. We have provided a brief description of each file required to compile and run the game.

FileName Description
*.INJ Raw sound files for sound effects
*.XMI MIDI files for background music
*.PCX Game graphics
A.BAT Makefile for River Frenzy
DIGPLAY.H Sound support header files.
DIGPLAY.OBJ Sound support object file.
DOSCALLS.ASM Source file for DOS BIOS calls in C.
DOSCALLS.H Header file for DOS BIOS calls in C.
LIB291.LIB Standard ECE291 library routines
LOADER.C MIDI file loader source.
LOADER.H MIDI file loader header file.
MIDPAK.ASM MIDI routines source.
MIDPAK.COM MIDI device driver.
MIDPAK.H MIDI routines header file.
RIVER.ASM River Frenzy main source code - the stuff we wrote
RIVER.DEF Level file. Stores how the river will look
RIVER.EXE Main executable. MIDPAK.COM must be run first.
SEGUE*.* More MIDI support.

Run the game by running "GO.BAT" from the game directory.

At this time, auto-detection of sound blaster hardware was not available. To function correctly, the game must be run on the Windows 95 pentiums in the ECE291 lab with sound blasters set to the following settings:

IRQ 10
DMA 3
I/O Port 220

River Frenzy sound can be set up to run on any SoundBlaster configuration, however small changes to hard-coded constants must be made, following a recompile of the program.

Part 1: Gameplay

The gameplay for River Frenzy is governed by a pseudo-object system. Each "object" is a record in a list that contains an ID, position, graphic pointer, and graphic characteristics (width, height). Our hero, every enemy, and every bullet exist as objects in memory. Each object knows reponds to signals based on its defined behavior. For example, a ship object moves side to side and scrolls with the water while an airplane object does not scroll downward with the water. A bridge object does not move at all.

Objects accept several commands from the main game loop. Each time the background is scrolled, the scrolllist function is called to update their Y coordinates accordingly (so that a ship appears to be stationary in the water). Some objects, such as the hero airplane, do not respond to scroll events. Objects can also be told to advance, at which point they will move left or right according to their own defined behavior. When all objects position have been updated, collision detection is performed and the objects are plotted into the Video Buffer.

Object Record

Lookup Tables

CreateObject
Purpose: Create an object of the specified type at the specified location.
Inputs: (char type, int xpos,int ypos) xpos and ypos are the coordinates of the object, and type is the object type to create.
  Object types are:
Value Meaning
0 Hero Plane
1 Helicopter
2 Boat
3 Enemy Plane
4 Fuel Tank
5 Bullet
6 Explosion
7 Bridge
Outputs: None.

Description:
Creates an object of the specified type at the specified location. Coordinates can be off the screen. The object is drawn into the video buffer on the next update. Object #0 is reserved for a bullet (and can be at no other location), and the last entry is reserved for the hero plane (and also can be at no other location). All other objects are given slots on a first-come, first-served basis.

_CheckCollide
Purpose: Check the location of an object to be plotted for a collision with another object, a bullet, or the shoreline.
Inputs: ES points to the segment address of video buffer. check the graphic. SI holds the offset of the object graphic to display. The variables _XPOS and _YPOS hold the coordinates, and _OBJWIDTH and _OBJHEIGHT hold the dimensions of the object. If the object is off the screen (even partially), CheckCollide will adjust the coordinates and dimensions appropriately.
Outputs: The collision checker returns a result in _retcond. All registers are preserved. The lowest four bits are used to represent collisions with different object types. If a collision occurs, the appropriate bit is set. The meaning of the bits:
 
Pallette Index Collision With
1 Shore
2 Fuel Tank
3 Enemy
5 Bridge
6 Bullet

After computing the edge of the screen, _CheckCollide will set _ActWidth, _ActHeight, and _ActLocation such that the object will draw correctly on a screen edge.

Description:
The routine "pretends" to draw the object, determining what if would overwrite if it were to actually plot. It returns a byte that holds which object types the object to be plotted "collides with". If the object is situated partially off the screen, the dimensions and/or location are adjusted. PlotObject will use those adjusted values to draw the object appropriately.

_PlotObject
Purpose: Draw an image into the video buffer.
Inputs: ES holds the segment of the video buffer, _actlocation holds the locationn to draw the graphic (based on CheckCollide's results) and _objtoplot holds the offset of the image to draw. _OBJWIDTH and _OBJHEIGHT hold the dimensions.
Outputs: The object is drawn into the video buffer, and will appear clipped at screen edges.
Notes: PlotObject should NEVER be called before CheckCollide. CheckCollide performs bounds checking and will prevent objects from overwriting the screen boundaries.

Description:
After checking the validity of the location with CheckCollide, PlotObject can be called to actually draw the object into the video buffer. It ignores any transparency pixels in the object graphic, allowing for irregularly shaped objects.

PlotList
Purpose: The object interface routine to draw all currently active objects.
Inputs: None.
Outputs: All currently active objects are drawn to the Video Buffer

Description:
PlotList will scan through the object list, looking for active objects. For each active object, it will call _CheckCollide, and pass this response to the appropriate collision response routine. Afterwards, it will call _PlotObject to draw the object.

AdvanceList
Purpose: The object interface routine to move all currently active objects.
Inputs: None.
Outputs: All currently active objects are moved to their next position. This routine does not draw any objects, it just computes their next location.

Description:
PlotList will scan through the object list, looking for active objects. For each active object, it will call their advance routine, updating their _xloc and _yloc values after each call.

ScrollList
Purpose: The object interface routine to scroll all currently active objects.
Inputs: None.
Outputs: All currently active objects besides the hero plane and bullets are scrolled. Nothing is drawn.

Description:
ScrollList will scan through the object list, looking for active objects. For each active object with object id <2 (not a hero plane or bullet), it will increment their y coordinate, deleting the object if it scrolls off the bottom of the screen.

LookupGraphic
Purpose: Given a graphic entry number, look up the graphic's width, height, and offset pointer.
Inputs: The variable graphictofind holds the graphic to look up.
Outputs: _ObjWidth, _ObjHeight, and _ObjOffset hold the respective values of the graphic desired.

Description:
LookupGraphic is useful for animations, since only the frame number is required and the graphic can then be looked up. This reduces the size of what each object is required to store. It is also used by CreateObject to determine the default graphic to be used for each object when it is created.

 

Part 2: Background Graphics

All graphics routines are written in pure Assembly to acheive maximum possible arcade potential.

Some explanation of our graphics methodology will be required. The graphics for this game are produced by utilizing several different 64k segments in addition to the code segments and stack segments required by the program code. ScratchSeg is, as its name implies, a segment used for any scratch work required. This segment is primarily used by the PCXLoad subroutine to load the raw PCX file into before processing. TileSeg is the segment to which all tile-based game graphics are decompressed. Tiles are copied from this segment to the area of memory where they are needed. BackSeg is where the background, consisting of the river and shrubbery, is constructed. The variable _MAP determines the form this background will take. Each row of _MAP contains 10 bytes which determine which 10 tiles will be copied from TileSeg to BackSeg to make up the terrain. The tiles are numbered sequentially, so an entry of '0' in the _MAP array will corrospond to the upper left-most tile in the TileSeg. Once the background is constructed in BackSeg, it is scrolled as necessary. Once scrolled, the background is tranferred from the background segment to VidBufSeg. This is the video buffer segment. Only a portion of the background is actually copied to the video buffer. This allows an "invisible" area in the BackSeg where new tiles can be constructed as old tiles scroll off the bottom of the segment. In VidBufSeg, this "invisible" area is occupied by the status bar, which is copied directly to VidBufSeg. It is as this time when the enemies and protagonist are painted on top of the scenery, and shore collisions are detected. Now the frame is fully constructed, and is swapped into active video memory by BufferSwap.

The graphics for the game were constructed from scratch using Photoshop and LViewPRO. Several special considerations had to be taken into account when designing these graphics. We used color indices for collision detection. This was accomplished by outlining each sprite with a particular color index. The collision routine then determines what has collided with what by detecting the overwrite of one of these indices. This was a simple and not too inefficient means of collision detection. The drawback is that each sprite has to be outlined with a row of pixels. The shorlines also had a particular color index in them which denoted a "shore collision."

_Enter13h
Purpose: Switch the video card into graphics mode 13h
Inputs: None.
Outputs: Video switched to mode 13h
Notes: All registers are preserved by this subroutine

Description:
The graphics for this game are done entirely in 320x200 VGA color graphics. This allows for a pallette of 256 colors out of 64 thousand possibilities. This subroutine will switch the video card into mode 13h using VBIOS call AX=0013h.

_Enter02h
Purpose: Switch the video card into text mode 02h
Inputs: None.
Outputs: Video switched to text mode 02h
Notes: All registers are preserved by this subroutine

Description:
Before exiting the program, this subroutine is called to return the VGA card to 80x25 color text mode

WaitSync
Purpose: Delay until electron gun of monitor starts a vertical retrace
Inputs: None.
Outputs: None.
Notes: All registers are preserved by this subroutine

Description:
Before dumping the video buffer contents to main video memory, it is sometimes desirable to wait for the electron gun of the monitor to begin a vertical retrace. This prevents "snow" and glitches that can occur if the video subsystem is readin video memory at the same time it is being written to from the video buffer. Use of WaitSync signifigantly improves the smoothness of scrolled graphics and sprites, but also signifigantly slows down the frame rate.

BufferDump
Purpose: Dump contents of video buffer segment to main video memory.
Inputs: None.
Outputs: None.
Notes: All registers are preserved by this subroutine
Uses: VidBufSeg, VidGrSeg, Waitsync

Description:
This subroutine uses fast string operations to copy the contents of the video buffer to main video memory.

DISBufferDump
Purpose: Dump contents of video buffer segment to main video memory using cool "dissolve" effect.
Inputs: None.
Outputs: Main video memory modified.
Notes: All registers are preserved by this subroutine
Uses: VidBufSeg, VidGrSeg, Waitsync

Description:
This subroutine copies the contents of the video buffer using a dissolve effect.

This code was modified from code written by Albery Veli and distributed freely on his internet homepage.

BufferClear
Purpose: Clear the video buffer.
Inputs: None.
Outputs: Video buffer modified.
Notes: All registers are preserved by this subroutine
Uses: VidBufSeg

Description:
This subroutine uses fast string operations to erase the video buffer segment. The erase is accomplished by overwriting all pixels with color 0 (defined to be black in the game's pallette).

BB2VBDumpScenery
Purpose: Copy the background to the video buffer segment.
Inputs: None.
Outputs: Video buffer modified
Notes: All registers are preserved by this subroutine
Uses: VidBufSeg, BackSeg

Description:
This subroutine uses fast string operations to copy the scenery to the video buffer. This subroutine copies only the bottom 260 rows of pixels to the buffer. This allows for the "invisible" space in the background buffer in which new tiles can be constructed with no visible effects. This also leaves room for the status display to be copied into the video buffer. The status display is exactly 40 pixels high to fill in the space left by the background.

PCXLoad
Purpose: Load a PCX file into the designated segment.
Inputs: DX - has the offset of the PCX filename to load
AX - has the segment address to load VGA pixel data to
Outputs: Memory modified.
PAL - contains pallette of current image
Notes: All registers are preserved by this subroutine
Uses: ScratchSeg

Description:
The main graphics and title screens for River Frenzy are stored in .PCX file format. This subroutine loads the raw file data into the Scratch Segment using a DOS call, and then decompresses that data into raw VGA pixel data into the segment specified by AX. After loading the pixel data, this subroutine then writes palette information to the video card. Pallette information is stores as part of the .PCX file formart.

The code in this subroutine was modified from the .PCX display code presented in our lab manual, and has been modified to allow the loading of VGA pixel data into any segment specified. This subroutine was also modified to make a copy of the pallette as the PCX file was loading. This copy of the pallette was then used for the fade-in and fade-out effects.

TileCopy
Purpose: Copy a specified tile from TileSeg to the specified segment.
Inputs: BX - height of block to copy
CX - width of block to copy
SI - offset into tile segment to start getting pixels
ES - destination segment
DI - offset into the destination segment to start placing pixels
Outputs: Memory modified.
Notes: All registers are preserved by this subroutine
Uses: TileSeg

Description:
This subroutine uses fast string operations to copy the specified contents of the TileSeg into the desired segment. This routine copies rectangles only. This routine is used to copy tiles to construct the background scenery.

TileCopyTrans
Purpose: Copy a specified tile from TileSeg to the specified segment using transparency
Inputs: BX - height of block to copy
CX - width of block to copy
SI - offset into tile segment to start getting pixels
ES - destination segment
DI - offset into the destination segment to start placing pixels
Outputs: Memory modified.
Notes: All registers are preserved by this subroutine
Uses: TileSeg, TRANSPARENCY

Description:
This subroutine uses not-so-fast bit by bit operations to copy a tile from TileSeg into the appropriate segment. This routine will ingore all pixels of color TRANSPARENCY to allow the background to show through.

RowMap
Purpose: Translates a row of map data into a row of graphic tiles.
Inputs: _CurrentRow - word variable pointing to current row in _MAP data
SI - offset into background segment to draw the new row of tiles
Outputs: Memory modified.
Notes: All registers are preserved by this subroutine
Uses: TileSeg, BackSeg, TileCopy

Description:
This subroutine is used to map _MAP data bytes into actual graphic tiles from the TileSeg. It takes a _MAP element byte and, using a table lookup, translates that byte into the offset into TileSeg of the appropriate tile's upper left hand corner. For example, a _MAP byte of 0 would translate to offset 0. A _MAP byte of 1 would translate into 32 (the tiles are 32x32 pixels, remember). Once the offset is obtained, the tile is copied via TileCopy into the background segment. A loop then performs this action 10 times (320/32=10 tiles across) to copy all 10 tiles in the current _MAP row to the background segment. The result is the graphical representation of the byte string in the current row of _MAP.

This subroutine will also create objects, such as ships or helicopters, as necessary in the currently drawing row. Not implemented yet.

DrawFirstGRScreen
Purpose: Draw a complete 6-row graphic screen in BackBuf
Inputs: _CurrentRow - word variable pointing to first row to be drawn in _MAP
Outputs: _CurrentRow - points to next row to be drawn in _MAP after these 6 rows
Notes: All registers are preserved by this subroutine
Uses: RowMap, BackSeg, BB2VBDumpScenery

Description:
This subroutine is used to initiate game action and draw out the very first visible frame. This would be used when the game begins, or after the player dies and must return to a previous point in the game.

This subroutine simply starts at _CurrentRow, calls TileCopy to creat the current row at the bottom of BackBuf, increments _CurrentRow, moves its offset into BackBuf up by 32 pixels, and draws the next row. It does this a total of six times to fill BackBuf.

MACRO ScrollBuf
Purpose: Scrolls the specified buffer down by 1 pixel
Inputs: ES - points to the buffer to be scrolled
Outputs: Memory modified
Notes: All registers are preserved by this subroutine
Uses:  

Description:
This MACRO uses fast string copy operations to simply scroll the specified buffer down by one pixel.

UpdateBackBuf
Purpose: Scrolls the background buffer.
Every 32 scrolls, draws a new row of _MAP data into the background buffer.
Copies the contents of the background buffer into the video buffer
Inputs: _CurrentPixel - number of pixels into the current row of _MAP data
_CurrentRow - next row of _MAP data to be drawn
BL - 1 to scroll the background, 0 to not scroll the background
Outputs: _CurrentPixel - number of pixels into the current row of _MAP data
_CurrentRow - next row of _MAP data to be drawn
Notes: All registers are preserved by this subroutine
Uses: BackSeg, VidBufSeg, BB2VBDumpScenery, RowMap

Description:
This subroutine first scrolls the background if necessary as indicated by BL. It will sometimes be desirable not to scroll the background for when the ship is moving slowly and we want to overwrite the objects without scrolling them. This decision is made by the mast game loop. Scrolling causes _CurrentPixel to be incremented.

Every 32 scrolls (the screen has moved down 32 pixels) the suubroutine must add another row of graphics to BackSeg. The number of pixels already scrolled is kept track of by _CurrentPixel. When this value hits 32, it is reset to zero and RowMap is called to translate another row of _MAP data into background graphics. _CurrentRow is then incremented. In this way the program scrolls continuously through the scenery.

After these steps have taken place, this subroutine calls BB2VBDumpScenery to dump the current scenery to the video buffer.

UpdateStatusDisplay
Purpose: Update the status display at the bottom of the screen with current score and fuel level.
Inputs: _FuelLevel - byte, current fuel level between 0 and 94
_Score - word, current score
Outputs: Memory modified.
Notes: All registers are preserved by this subroutine
Uses: VidBufSeg, TileCopy, BINASC,

Description:
The status display at the bottom of the players screen gives a up-to-date readout of current fuel level and score. This routine first copies the blank status readout as a tile from TileSeg to the VidBufSeg. The "blank" readout is missing the score and the colored bar which indicates fuel level.

The fuel gague is filled in by simple performing a TileCopy of the colored bar from the TileSeg. The width of the bar is goverend by _Score. If _Score is 35, only the portion of the colored bar 35 pixels wide is copied into the status display.

To generate a graphical readout of the score, _Score is first translated into a string using BINASC. This routine that takes each ASCII character in the string and performs a table lookup to determind the offset into TileSeg where that graphical element exists. The element (a '1' or '2', etc...) is then copied into the status display by TileCopy.

This routine also copies extra ships to the status bar to indicate how many lives the player has left to live.

INT9Install
Purpose: Installs a keyboard interrupt handler on interrupt 9
Inputs: None.
Outputs: Updates interrupt vector table.
_OldINT9V - double word variable contains old interrupt 9 vector, to be restored in INT9DeInstall
Notes: All registers are preserved by this subroutine
Uses: BIOS, DOS

Description:
Control for River Frenzy is accomplished through a keyboard ISR, installed here. This routine first reads the current interrupt 9 vector and stores it in _OldINT9V for restoration at the end of the program. The install routine then replaces that vector with the offset to KeyboardISR, the keyboard interrupt service routine for this game.

This subroutine also sets the keyboard repeat rate to its lowest value, and keyboard delay to its highest value. These values are modified using calls to ROM BIOS.

INT9DeInstall
Purpose: Restores old keyboard interrupt vector to the interrupt table.
Inputs: OldINT9V
Outputs: Interrupt vector table modified.
Notes: All registers are preserved by this subroutine
Uses: BIOS, DOS

Description:
This subroutine re-installs the old interrupt vectore we displaced with INT9Install, restoring the old Interrupt 9 ISR to the state it was in before the game was run. Keyboard repeat rate and delay time are also reset to normal values.

KeyboardISR
Purpose: Modifies control variable to indicate which keys are being held down
Inputs: None.
Outputs: _MoveLeft
_MoveRight
_MoveUp
_MoveDown
_Abort
_Fire
KeyPress - the scan code of the key that was hit
Notes: All registers are preserved by this subroutine

Description:
This ISR is triggered when a scan code is generated by the keyboard. This routine reads that scan code and updates the variables noted above according to which key has been pressed. For example, if the space bar has been pressed, _Fire is set to 1. When the spacebar is released, _Fire is set to 0. Any key events not involved in game control are simply thrown away. This routine then clears the keyboard interrupt so additional key events can be processed.

WaitKeyPress
Purpose: Uses the keyboard ISR to delay until a key has been pressed.
Inputs: None.
Outputs: None.
Uses: KeyPress

Description:
The keyboard ISR automatically updates the global variable KeyPress with the last scan code generated. This routine simply sets KeyPress to zero and loops until it changes again. Since there is no scan code 0, this has the effect of waiting for a key to be pressed.

PalletteBlack
Purpose: Sets all pallette entries in the current vga pallette to 0 (black, in this game)
Inputs: None.
Outputs: None.
Uses:  

Description:
Simply loops through each of the 256 VGA pallette registers and sets the R, G, and B componenets to zero.

FadeFromBlack
Purpose: Fades the pallette from black to the correct pallette for the PCX file loaded
Inputs: PAL, PALScratch
Outputs: None.
Uses:  

Description:
When the current PCX file was loaded, a copy of it's pallette information was made and stored in the array PAL. This routine first sets all values in PALScratch to zero. Each time through the loop, it compares the values in PALScratch to the values in PAL. If the PALScratch value is lowed than the corrosponding value in PAL (the correct, desired value) it then increases the value in PALScratch and writes the new value to the VGA pallette registers. The effect is a gradual fade-in from blackout to the correcly colored image.

FadeToBlack
Purpose: Fades the currently loaded screen to blackout.
Inputs: PAL
Outputs: None.
Uses: KeyPress

Description:
This routine looks at each value in PAL, and decrements each value, writes the value to the VGA registers, and continues the loop. If a value in PAL is already 0, it is not decremented again. The effect is a gradual fade-out from the image to blackness.

Flamer
Purpose: The cool flame effect in our intro screen is done by this subroutine.
Inputs: None.
Outputs: None.
Uses: KeyPress
Notes: This code was modified from code by Nathan Egge (a.k.a. The Unlord / Xylem) coded for the #coders 256 byte fire compo on 5/20/96. e-mail: regge@erols.com. His code was adapted to service ours. Modifications include: Turning TASM-isms into MASM-isms, adapting the code to use our Video buffer; adapting the code to use our Keyboard ISR and to return the key that was hit. This code was programmed for a contest in which the goal was to get the coolest flame effect in under 128bytes. As such, the code is almost completely unitelligible. I leave the original author's comments intact.

Description:
This code performs several functions. This subroutine loops through the flame effect until the played has hit an allowable key, either P, I, or ESC. For each frame during looping, the routine does the following: It updates the flame picture it is generating in real time in the BackSeg. It then transfers this image to the Video Buffer. The text for the River Frenzy title is then copied with transparency on top of the flame effect. The wholly constructed frame is then copied to active video memory via BufferDump. If an appropriate key press is detected during this loop, the flame effect does a cool little finish off and the routine passes the key pressed back to main via a variable.

Part 3: Sound System

A few assembly procedures together will produce the sound effects. External ASM routines set up the interrupt table for the sound card interrupt to be solely used by this program. An external routine also sets up the DMA controller for buffer access. The procedures that need to be created will select a sound based on numerical input, load it into memory, and set up the soundblaster to play that sound.

Sound Setup Code

Certain variables must be set for the DMA transfer to function correctly. This information about the soundblaster needs to be put in variable format so it can be changed easily. The BaseAddr will probably be 220h. The variables IRQ and DMAchannel will have to be put in manually, as an autodetect function was not operational at the time of printing. Here are the values of the PAGE_CHN, BASE_CHN, and COUNT_CHN which depend on DMA.

DMA PAGE BASE COUNT

0 87 0 1
1 83 2 3
2 81 4 5
3 82 6 7

 

LoadSound
Purpose: Loads the various sound effects into memory, storing their locations.
Inputs: BL is the number of the sound effect to play. .
Outputs: variable soundfilesize with the size of the sound file,
Notes: All registers are preserved by this subroutine
Labels made to signify start and end of each sample. Make your own!

Description:
Description: This procedure loads the needed sound effect into memory. Using an array of the file sizes, given by
picard 17508
testing 13651
boom 12696
bing 7490
shoot 1890
Depending on the BL input number, one of the files is loaded into memory using the INT 21h call, its size placed in SoundFileSize. This sound should be loaded at the beginning of the SoundSeg.

SetPageOffset
Purpose: From a value of a sound file to play, calculate its memory page and offset.
Inputs: SampleBuffer, SampleBufferLength
BL is the number of the sound effect to play.
Outputs: SI is the offset of the sample to play
BL is the page
Notes: destroys AX, BH

Description:
This routine retrieves the page and offset of the sample to be played. From storage made previously, it locates the page and offset. The outputs of offset and page are used to set up the DMA buffer playback.

PlaySound
Purpose: Plays one of N sounds in the background, using a DMA channel and buffer.
Inputs: SampleBuffer, SampleBufferLength chosen for particular sound
BL is the number of the sound effect to play.
Outputs: Sound.
Notes: Destroys BL.

Description:
This routine brings together all of the sound effect playing. It calls the external routine RestartSound, LoadSound, SetPageOffset, the external routine SetDMA to initialize the DMA controller, and then sets up the sound blaster to play the sound, returning with the sound playing in the background.

To write to a register on the sound blaster:
Write the register to the port given by BASEADDR + 0Ch.
Wait, by calling the external command WAITWRITE.
Write the data to the port, lower part first if 16 bit.
Wait, call WAITWRITE.

To use the sound blaster:
Set register 40 to the time control of the sample: 256 - 1,000,000 / Hz where Hz is the speed of the sample.
Set register 48 to the DMA buffer size.
Set register 90 to the higher 8 bits of the sample rate, for high speed 8-bit DMA auto initialized sound.

Once completed setting up, restore all system registers and exit back to the main program, sound in background.

Music Routines

The following procedures are given courtesy of John Ratcliff. They interface with his MIDPAK driver, which must be run to load into memory before the program starts.
LoadMidPak: Assures that the MIDPAK driver was loaded correctly.
InitMidPak: Enables the MIDI.
RegisterXMidi: Sets a song in memory to be ready for playing.
SetRelativeVolume: Sets MIDI volume.
PlaySequence: Sets a registered song playing in the background.

PlayMusic:
Purpose: Initialize midi, play a song from memory
Inputs: None.
Outputs: Music in the background.

Description:
Description: This C routine combines all of the above procedures to play a MIDI song in the background. Using allocation techniques, a song "COUNTDN3.XMI" is loaded into memory, and played from the initialized MIDPAK driver. Helpful libraries include MALLOC.H Called from the third screen of the intro, this sets the music into motion.

EndMusic
Purpose: Stop MIDI playback, deinitialize the MIDI driver.
Inputs: None.
Outputs: Halts music.

Description:
This short routine ends the music sequence. It calls the external routines DeInitMidpak and UnLoadMidpak and exits.

Last Updated: December 13, 1996

Page created and maintained by Dan Restelli - restelli@uiuc.edu