Laboratory‎ > ‎

Assembly

ECE390 Computer Engineering II taken in Spring 2004 at University of Illinois. The following projects involved extensive assembly programming in X86 processor architecture platform

Final Project: Castles & Cannons: 10 seconds 

Variation of the classic game Rampart. Cannonball shooting game in 2-dimensional space with rebuild phase in which tetris-like block aligning for defense is involved.

Introduction

C & C: 10 seconds is a variation of a classic game "Rampart." The object of the game is to conquer all the castles in the given region before the other player does.

The game starts off with each player controlling one castle in his/her region. The game is separated into three phases. In the battle phase, the players attack each other with the cannons deployed in their castles. Each battle phase is followed by rebuild phase in which the players are given a limited time to rebuild and repair damage. At the end of the rebuild phase, the players are given additional cannons based on the number of castles conquered, which can be deployed during the following deploy phase. The cycle continues until one of the players conquer all the castles in his/her region or the other player loses all of his/her castles twice in a row.

Problem Description

  • Dividing and integrating functions
  • Determining different territories bounded by wall pieces laid out by the players
  • Data structure for wall pieces and how to deal with rotation and placement
  • Keeping track of real-time status of each object and each grid of the game map
  • Drawing pre-rendered images on correct wall pieces and implementing visual enhancements such as zooming and animation
  • Implementing music and sound effects
  • Keyboard inputs
  • Possible implementation of mouse inputs
  • Possible implementation of network play if time permits

Team members

  • Ki Chung: Overall design and flow of the game (main loop, data structures, etc.)
    • Graphics implementation
    • Game play implementation
    • Prepared all the images and graphics used in game (all original images with an exception of explosion and instruction images)
  • Hyun Jeong: Sound implementation (all the sound functions)
    • Input handling
    • Intro design (instruction images)
    • Contributed to the design of the game
  • Seunghoon Kim: Graphics
    • Graphics implementation
    • Game play implementation
    • Testing and debugging
    • Contributed to the design of the game
  • Gihyun Ko: Game mechanics and algorithm
    • Graphics implementation
    • Gamplay implementation
    • Testing and debugging
    • Contributed to the design of the game

Screen Shots



Main Pseudocode

Author: Ki Chung Description: Many of the game specifics needed to be determined before main loop could be written. Naturally, most of the function and data structures were designed while writing the main loop

_Game
   if [_Flags] == exit
      exit _Game
 
   if phase == break
   {
      if phase == deploy break
      {
         _ScrollBanner(_BannerX, _BannerY)
         load [_ScreenOff] from backup
         _DrawImage([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, [_DeployBannerOff], DEPLOY_BANNER_WIDTH, DEPLOY_BANNER_HEIGHT, [_BannerX], [_BannerY], 1, 1)
         if break time limit reached
         {
            [_Phase] = deploy
            reset [_BannerX] and [_BannerY]
         }
      }
      else if phase == battle break
      {
         _ScrollBanner(_BannerX, _BannerY)
         load [_ScreenOff] from backup
         _DrawImage([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, [_BattleBannerOff], BATTLE_BANNER_WIDTH, BATTLE_BANNER_HEIGHT, [_BannerX], [_BannerY], 1, 1)
         if break time limit reached
         {
            [_Phase] = battle
            reset [_BannerX] and [_BannerY]
         }
      }
      else if phase == rebuild break
      {
         _ScrollBanner(_BannerX, _BannerY)
         load [_ScreenOff] from backup
         _DrawImage([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, [_RebuildBannerOff], REBUILD_BANNER_WIDTH, REBUILD_BANNER_HEIGHT, [_BannerX], [_BannerY], 1, 1)
         if break time limit reached
         {
            [_Phase] = rebuild
            reset [_BannerX] and [_BannerY]
         }
      }
   }
   else
   {
      _DrawStatusBar([_ScreenOff], STATUSBAR_X, STATUSBAR_Y)
      _DrawMap([_MapScreenOff], [_GameMapOff])
 
      if phase == initialize
      {
         if P1_init == true
         {
            _UpdateInitCursor([_P1CastleArray], _P1X, _P1Y, [_P1_InputFlags])
            _DrawImage([_MapScreenOff], MAP_PIXEL_WIDTH, MAP_PIXEL_HEIGHT, [_P1InitCursorOff], INIT_CURSOR_WIDTH, INIT_CURSOR_HEIGHT, [_P1X] * 8 + 4, [_P1Y] * 8 + 4, NUM_FRAMES_INIT_CURSOR, 1)
            _BuildCastle([_GameMapOff], [_P1X], [_P1Y], _NumP1Territory, [_P1_InputFlags], P1_INIT_PHASE)
         }
         if P2_init == true
         {
            _UpdateInitCursor([_P2CastleArray], _P2X, _P2Y, [_P2_InputFlags])
            _DrawImage([_MapScreenOff], MAP_PIXEL_WIDTH, MAP_PIXEL_HEIGHT, [_P2InitCursorOff], INIT_CURSOR_WIDTH, INIT_CURSOR_HEIGHT, [_P2X] * 8 + 4, [_P2Y] * 8 + 4, NUM_FRAMES_INIT_CURSOR, 1)
            _BuildCastle([_GameMapOff], [_P2X], [_P2Y], _NumP2Territory, [_P2_InputFlags], P2_INIT_PHASE)
         }
         if all players are initialized == true
         {
            [_Phase] = break before deploy
            _DimBuffer([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, DIM_VAL)
            make a copy of the screen
         }
      }
      else if phase == deploy
      {
         if P1_deploy == true
         {
            _UpdateCursor(_P1X, _P1Y, 0, 0, GAME_MAP_WIDTH - 1, GAME_MAP_HEIGHT - 1, [_P1_InputFlags])
            _DrawDeployCursor([_MapScreenOff], [_P1X] * 8, [_P1Y] * 8, [_P1DeployCursorOff])
            _BuildCannon([_GameMapOff], [_P1CannonArray], _NumP1DeployCannon, _NumP1Cannon, [_P1X], [_P1Y], [_P1_InputFlags], P1_REGION, P1_DEPLOY_PHASE)
         }
         if P2_deploy == true
         {
            _UpdateCursor(_P2X, _P2Y, 0, 0, GAME_MAP_WIDTH - 1, GAME_MAP_HEIGHT - 1, [_P2_InputFlags])
            _DrawDeployCursor([_MapScreenOff], [_P2X] * 8, [_P2Y] * 8, [_P2DeployCursorOff])
            _BuildCannon([_GameMapOff], [_P2CannonArray], _NumP2DeployCannon, _NumP2Cannon, [_P2X], [_P2Y], [_P2_InputFlags], P2_REGION, P2_DEPLOY_PHASE)
         }
 
         if deploy time limit reached || all players are done deploying == true
         {
            reset [_TimeTick]
            [_Phase] = break before battle
            _DimBuffer([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, DIM_VAL)
            make a copy of the screen
         }
      }
      else if phase == battle
      {
         _DrawExplosion([_MapScreenOff], [_GameMapOff], [_ExplosionArray], [_ExplosionOff])
         _UpdateCannonBall([_GameMapOff], [_CBallArray])
         _DrawCannonBall([_MapScreenOff], [_CBallArray], [_CannonBallOff])
 
         _UpdateCursor(_P1X, _P1Y, 0, 0, MAP_PIXEL_WIDTH - 1, MAP_PIXEL_HEIGHT - 1, [_P1_InputFlags])
         _DrawImage([_MapScreenOff], MAP_PIXEL_WIDTH, MAP_PIXEL_HEIGHT, [_P1BattleCursorOff], BATTLE_CURSOR_WIDTH, BATTLE_CURSOR_HEIGHT, [_P1X], [_P1Y], NUM_FRAMES_BATTLE_CURSOR, 1)
         _FireCannon([_GameMapOff], [_P1CannonArray], [_CBallArray], [_P1X], [_P1Y], [_P1_InputFlags])
 
         _UpdateCursor(_P2X, _P2Y, 0, 0, MAP_PIXEL_WIDTH - 1, MAP_PIXEL_HEIGHT - 1, [_P2_InputFlags])
         _DrawImage([_MapScreenOff], MAP_PIXEL_WIDTH, MAP_PIXEL_HEIGHT, [_P2BattleCursorOff], BATTLE_CURSOR_WIDTH, BATTLE_CURSOR_HEIGHT, [_P2X], [_P2Y], NUM_FRAMES_BATTLE_CURSOR, 1)
         _FireCannon([_GameMapOff], [_P2CannonArray], [_CBallArray], [_P2X], [_P2Y], [_P2_InputFlags])
 
         if battle time limit reached
         {
            reset [_TimeTick]
            [_Phase] = break before rebuild
            _DimBuffer([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, DIM_VAL)
            make a copy of the screen
            _UpdateEnclosedRegion([_GameMapOff])
            _GenerateRandomBlock(_P1CurrentBlock)
            [_P2CurrentBlock] = [_P1CurrentBlock]
         }
      }
      else if phase == rebuild
      {
         _UpdateCursor(_P1X, _P1Y, 0, 0, GAME_MAP_WIDTH - 1, GAME_MAP_HEIGHT - 1, [_P1_InputFlags])
         _DrawBlock([_MapScreenOff], [_P1X] * 8, [_P1Y] * 8, [_P1CurrentBlock], [_P1BlockOff])
         _RotateBlock(_P1CurrentBlock, [_P1_InputFlags])
         _BuildWall([_GameMapOff], [_P1X], [_P1Y], _P1CurrentBlock, [_P1_InputFlags])
 
         _UpdateCursor(_P2X, _P2Y, 0, 0, GAME_MAP_WIDTH - 1, GAME_MAP_HEIGHT - 1, [_P2_InputFlags])
         _DrawBlock([_MapScreenOff], [_P2X] * 8, [_P2Y] * 8, [_P2CurrentBlock], [_P2BlockOff])
         _RotateBlock(_P2CurrentBlock, [_P2_InputFlags])
         _BuildWall([_GameMapOff], [_P2X], [_P2Y], _P2CurrentBlock, [_P2_InputFlags])
 
         if rebuild time limit reached
         {
            reset [_TimeTick]
 
            if P1 and P2 has at least one castle
            {
               [_Phase] = break before deploy
               update [_NumP1DeployCannon]
               update [_NumP2DeployCannon]
               dim screen
               make a copy of the screen
            }
            if P1 has no castle
            {
               decrement [_P1Life]
               increment [_P2Life]
               [_Phase] = P1 initialize
               update [_NumP1DeployCannon]
            }
            if P2 has no castle
            {
               decrement [_P2Life]
               increment [_P1Life]
               [_Phase] = P2 initialize
               update [_NumP2DeployCannon]
            }
            adjust [_P1Life] and [_P2Life] to max
            if P1 has no life && P2 has life
            {
               _DimBuffer([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, DIM_VAL)
               _DrawImage([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, [_P2WinBannerOff], WIN_BANNER_WIDTH, WIN_BANNER_HEIGHT, WIN_BANNER_X, WIN_BANNER_Y, 1, 0)
               loop until exit key pressed
               exit _Game
            }
            else if P2 has no life && P1 has life
            {
               _DimBuffer([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, DIM_VAL)
               _DrawImage([_ScreenOff], SCREEN_WIDTH, SCREEN_HEIGHT, [_P1WinBannerOff], WIN_BANNER_WIDTH, WIN_BANNER_HEIGHT, WIN_BANNER_X, WIN_BANNER_Y, 1, 0)
               loop until exit key pressed
               exit _Game
            }
         }
      } ;end of else if phase == rebuild
 
      _CopyBuffer([_MapScreenBuffer], MAP_PIXEL_WIDTH, MAP_PIXEL_HEIGHT, [_ScreenBuffer], SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0)
   } ;end of else
 
   display screen buffer on screen
   update [_AnimateTick] and [_AnimateCount]
   update [_TimeTick] and [_Time]
   update [_CBallTick]
   loop to _Game
The full source code link available at the bottom of this page.

Functions Explained

_Game function was debugged by everyone as more functions were added.

Although each person listed under authors section wrote all of the code, many of them were later tested and debugged by other group members. As it was hard to keep track of who fixed what where, only the major authors of the functions are listed. Everyone in the group contributed to testing and debugging.

Some of the functions which did not work correctly were rewritten by other group members.

Final code includes all the rewritten and debugged versions.

void _Menu( )

  • Author: Ki Chung
  • Inputs: -
  • Outputs: -
  • Returns: -
  • Calls: _Game, _Instruction, _Credits, _CopyBuffer, _ComposeBuffers, _CopyToScreen
  • The Menu from which the player can choose 'Play Game', 'Instruction', 'Credits', or 'Exit.' Initializes various necessary variables for _Game function when 'Play Game' is selected.

void _Instruction( )

  • Author: Hyun Jeong, Gihyun Ko
  • Inputs: -
  • Outputs: -
  • Returns: -
  • Calls: _CopyBuffer, _CopyToScreen
  • Displays instructions on how to play.

void _Game( )

  • Author: Ki Chung
  • Inputs: -
  • Outputs: -
  • Returns: -
  • Calls: _UpdateInitCursor, _UpdateCursor, _RotateBlock, _UpdateEnclosedRegion, _BuildCastle, _BuildCannon, _BuildWall, _GenerateRandomBlock, _UpdateCannonBall, _FireCannon, _ScrollBanner, _DrawStatusBar, _DrawMap, _DrawImage, _DrawDeployCursor, _DrawBlock, _DrawCannonBall, _DimBuffer, _ClearBuffer, _CopyBuffer, _ComposeBuffer, _CopyToScreen
  • This is the main loop of the game. _Game is called by _Menu when the player chooses the option 'Play Game' _Game then takes care of the entire flow of the game. For the details on how it functions, look at main pseudocode

void _UpdateInitCursor(dword *CastleArray, dword *X, dword *Y, word InputFlags)

  • Author: Ki Chung
  • Inputs:
    • CastleArray - array of castles
    • X - offset of x coordinate
    • Y - offset of y coordinate
    • InputFlags - input flags
  • Outputs:
    • [X] - X updated with new x coordinate
    • [Y] - Y updated with new y coordinate
  • Returns: -
  • Calls: -
  • Updates initialize phase cursor; Since initialize phase cursor needs to move between castles, it looks up the castle array for location info and updates the cursor. (eg. If left key is pressed, the function finds the castle with the closest x distance to the left of current and sets the location of this castle as the current location)

void _UpdateCursor(dword *X, dword *Y, word MinX, word MinY, word MaxX, word MaxY, word InputFlags)

  • Authors: initially written by Gihyun Ko, rewritten by Ki Chung
  • Inputs:
    • X - offset of x coordinate
    • Y - offset of y coordinate
    • MinX - minimum x of the boundary
    • MinY - minimum y of the boundary
    • MaxX - maximum x of the boundary
    • MaxY - maximum y of the boundary
    • InputFlags - input flags
  • Outputs:
    • [X] - X updated with new x coordinate
    • [Y] - Y updated with new y coordinate
  • Returns: -
  • Calls: -
  • Updates cursor depending on which key is pressed; if cursor is out of boundary, wraps around the location.

void _RotateBlock(dword *Block, word InputFlags)

  • Authors: Ki Chung, Gihyun Ko
  • Inputs:
    • Block - offset of block to rotate
    • InputFlags - input flags
  • Outputs: Block rotated 90 degrees counter-clockwise
  • Returns: -
  • Calls: -
  • Rotates 3 x 3 Block 90 degrees counter-clockwise if secondary key is pressed; Shifts the block word as described the map description section

dword _BoundaryInRegion(dword *MapOff, word X, word Y, word Width, word Height, word RegionVal)

  • Authors: Gihyun Ko, Seunghoon Kim, Ki Chung
  • Inputs:
    • MapOff - offset of map buffer
    • X - current x coordinate
    • Y - current y coordinate
    • Width - width of the region
    • Height - height of the region
    • RegionVal - map value of particular region
  • Outputs: -
  • Returns: 1 if boundary is filled with RegionVal; 0 otherwise
  • Calls: -
  • Compares the values held by the grids specified and the region value passed by the user. For instance, if the cannon image with x and y coordinates is passed in, then the function will compare all four grids with respect to the region value. If any of them does not satisfy the region value, the function will return 0, which means invalid for placement.

dword _BlockInRegion(dword *MapOff, word Block, word X, word Y, word RegionVal)

  • Author: Gihyun Ko, Seunghoon Kim
  • Inputs:
    • MapOff - offset of map buffer
    • Block - block to check if it is in region
    • X - current x coordinate
    • Y - current y coordinate
    • RegionVal - map value of particular region
  • Outputs: -
  • Returns: 1 if Block is filled with RegionVal; 0 otherwise
  • Calls: -
  • Similar to BoundaryInRegion. The difference is that there are various blocks that have different shapes, therefore occupying only part of the 3x3 grid. The function consists of 9 different comparisons for each grid, and returns 0 or 1, respect to the satisfaction to the region value.

void _UpdateEnclosedRegion(dword *MapOff)

  • Author: Seunghoon Kim
  • Inputs:
    • MapOff - offset of map buffer
  • Outputs:
    • [_NumP1Territory] - updates number of conquered grid by P1
    • [_NumP2Territory] - updates number of conquered grid by P2
  • Returns: -
  • Calls: -
  • Uses a floodfill algorithm presented in MP4. Due to the limited number of recursive calls that can be made in assembly programming, queue implementation was used instead. The function starts off at the center of the map. It enqueues the x and y coordinates for the center of the map, and executes a loop, where the first element in the queue is dequeued, and goes through number of comparisons. If the grid specified is not a wall nor marked, and inside the map region, then its flag will be marked, and its 8 neighbors will be inserted in the queue. It repeats until queue is empty.

void _BuildCastle(dword *MapOff, word X, word Y, word WallVal, dword *NumTerritory, word InputFlags, word CurrentPhase)

  • Author: Ki Chung
  • Inputs:
    • MapOff - offset of map buffer
    • X - x coordinate of location around which to build castle walls
    • Y - y coordinate of location around which to build castle walls
    • WallVal - map value for wall
    • NumTerritory - offset of variable holding number of territory
    • InputFlags - input flags
    • CurrentPhase - current phase
  • Outputs:
    • MapOff - updates map with correct wall and region values
    • [_Phase] - removes CurrentPhase from [_Phase] if walls are built
    • NumTerritory - updates amount of territory with NUM_INIT_TERRITORY
    • Walls built around (X, Y) in the map buffer pointed to by MapOff
  • Returns: -
  • Calls: -
  • Updates map buffer pointed to by MapOff with walls around location (X, Y) if primary key is pressed. Then it removes CurrentPhase from [_Phase] indicating that the player is ready for the next phase. Also updates NumTerritory to reflect the change in amount of territory conquered

void _BuildCannon(dword *MapOff, dword *CannonArray, dword *NumCannon, dword *TotalCannon, word X, word Y, word InputFlags, word Region, word CurrentPhase)

  • Author: initially written by Gihyun Ko, rewritten by Ki Chung
  • Inputs:
    • MapOff - offset of map buffer
    • CannonArray - offset of cannon array
    • NumCannon - number of cannons available for deployment
    • X - x coordinate of location at which to place cannon
    • Y - y coordinate of location at which to place cannon
    • InputFlags - input flags
    • Region - map value of allowed region for cannon placement
  • Outputs:
    • NumCannon - number of cannons available updated if cannon is built
    • [_Phase] - removes CurrentPhase from [_Phase] if number of cannons reaches 0
    • CannonArray - cannon array updated with new cannon
    • TotalCannon - total number of cannons available
    • MapOff - walls built around (X, Y) in the map buffer pointed to by MapOff
  • Returns: -
  • Calls: _BoundaryInRegion
  • First checks to see if the cannon can be built at the location of the cursor. This is done by calling _BoundaryInRegion. If there's nothing in the cannon's way, it updates the map buffer and the cannon array with new cannon

void _BuildWall(dword *MapOff, word X, word Y, dword *Block, word InputFlags)

  • Author: Gihyun Ko
  • Inputs:
    • MapOff - offset of map buffer
    • X - x coordinate of location at which to place block
    • Y - y coordinate of location at which to place block
    • Block - offset of block to place in the map buffer
    • InputFlags - input flags
  • Outputs:
    • MapOff - map buffer updated with new wall piece
    • Block - new wall piece generated and stored in block if old block is used
  • Returns: -
  • Calls: _BlockInRegion, _GenerateRandomBlock, _UpdateEnclosedRegion
  • Takes input InputFlags and checks to see if primary key is pressed. If primary key is pressed then it will check if the land is not occupied for each cell of the 3x3 grid selected for the block. This is done by calling _BlockInRegion. If it's not occupied for cells of the blocks needed, new wall pieces are place by changing the region values and functions _GenerateRandomBlock, and _UpdateEnclosedRegion are called at the end.

void _GenerateRandomBlock(dword *Block)

  • Author: Gihyun Ko
  • Inputs: _BlockArray - array holding different types of blocks
  • Outputs: Block - updated with new wall piece
  • Return: -
  • Calls: _Random
  • Generates a random block by calling the _Random function to find a random value and using it to choose a block from the _BlockArray.

void _UpdateCannonBall(dword *MapOff, dword *CannonBallArray)

  • Author: intially written by Gihyun Ko, rewritten by Ki Chung
  • Inputs:
    • MapOff - offset of map buffer
    • CannonBallArray - offset of array of cannon balls in flight
    • [_CBallTick] - tick counter for cannon ball update
  • Outputs:
    • MapOff - map buffer updated if cannon ball destroys cannon or wall
    • CannonBallArray - updated with new position info
    • _P1CannonArray - P1 cannon array updated if P1 cannon ball landed
    • _P2CannonArray - P2 cannon array updated if P2 cannon ball landed
  • Returns: -
  • Calls: -
  • This function uses Bresenham's line algorithm to make the cannon ball fly in line from source to destination. The function takes three locations from cannon ball array. These are source, destination, and current location. It then calculates the line error from the source and destination locations. The line error is used to calculate the current location and stores the current location back into the array. _DrawCannonBall reads this current location to draw the cannon ball. When the current location is matched with the destination, the function calculates the index in the appropriate cannon array to find out which cannon has fired that cannon ball. Then it resets the cannon enabling it to fire again. The destination is also entered into the explosion array to be displayed on screen. If the destination happens to be a wall piece or a cannon, it assigns proper damage to the object then removes it from the map when the damage reaches the max life of the object.

void _FireCannon(dword *MapOff, dword *CannonArray, dword *CannonBallArray, word X, word Y, word InputFlags)

  • Author: initially written by Gihyun Ko, rewritten by Ki Chung
  • Inputs:
    • MapOff - offset of map buffer
    • CannonArray - offset of array of cannons
    • CannonBallArray - offset of array of cannon balls in flight
    • X - x coordinate of destination for cannon ball
    • Y - y coordinate of destination for cannon ball
    • InputFlags - input flags
  • Outputs:
    • CannonArray - cannon array updated if cannon fires
    • CannonBallArray - cannon ball array updated with new cannon ball
  • Returns: -
  • Calls: -
  • This function first checks if the fire key is pressed. If it is, it searches for cannon that is ready (that does not have a cannon ball that's still flying) in the given cannon array. If it finds one, it sets the cannon as busy, then updates the cannon ball array with three locations, which are source, destination, and current

void _ScrollBanner(dword *BannerX, dword *BannerY)

  • Author: Ki Chung
  • Inputs:
    • BannerX - offset of x coordinate of banner
    • BannerY - offset of y coordinate of banner
    • [_AnimateTick] - tick counter for animation
  • Outputs: banner positions updated
  • Returns: -
  • Calls: -
  • Updates banner position; used when drawing banner across the screen (eg. Deploy, Battle, Rebuild)

dword _Random(word MaxNum)

  • Author: Gihyun Ko
  • Inputs: MaxNum - max value to be generated
  • Outputs: -
  • Returns: random value from 0 to (MaxNum - 1)
  • Calls: -
  • Takes input MaxNum and uses [_TimeTick] and random algorithm to generate random value from 0 to (MaxNum - 1).

void _DrawStatusBar(dword *DestOff, word X, word Y)

  • Author: Ki Chung
  • Inputs:
    • DestOff - offset of destination buffer
    • X - x coordinate of the status bar (use this as reference)
    • Y - y coordinate of the status bar (use this as reference)
    • _StatusBarOff - offset of status bar image
    • _BigNumFontOff - offset of big number font image
    • _SmallNumFontOff - offset of small number font image
    • _BlueNumFontOff - offset of blue number font image
    • [_NumP1Castle] - number of conquered P1 castle
    • [_NumP2Castle] - number of conquered P2 castle
    • [_TimeTick] - tick counter for displaying time left
    • [_AnimateTick] - tick counter for animation
  • Outputs: DestOff - status bar drawn to the destination buffer pointed to by DestOff
  • Returns: -
  • Calls: _CopyBuffer, _DrawImage
  • Draws status bar which shows game info (remaining time time, number of castles conquered by each player, amount of territory conquered by each player, and number of cannons deployed by each player) to the destination buffer pointed to by DestOff

void _DrawMap(dword *DestOff, dword *MapOff)

  • Author: Ki Chung, Seunghoon Kim
  • Inputs:
    • DestOff - offset of destination buffer
    • MapOff - offset of map buffer
    • [_TerrainOff] - offset of terrain image
    • [_FlatCastleOff] - offset of flat castle image
    • [_BattleCastleOff] - offset of battle phase castle image
    • [_FlatCannonOff] - offset of flat cannon image
    • [_BattleCannonOff] - offset of battle phase cannon image
    • [_FlatWallOff] - offset of flat wall image
    • [_BattleWallOff] - offset of battle phase wall image
    • [_DestroyedCannonOff] - offset of destroyed cannon image
    • [_RubblesOff] - offset of rubbles image
    • [_OverlayOff] - offset of overlay buffer
    • [_Phase] - current phase
    • [_AnimateTick] - tick counter for animation
  • Outputs: DestOff - map data drawn to the destination buffer pointed to by DestOff
  • Returns: -
  • Calls: _ClearBuffer, _CopyBuffer, _ComposeBuffer
  • Draws map data to the destination buffer pointed to by DestOff; First colors P1 and P2 regions accordingly, then draws castles, cannons, and walls with the data provided by the game map buffer

void _DrawImage(dword *DestOff, word DestWidth, word DestHeight, dword *ImageOff, word ImageWidth, word ImageHeight, word X, word Y, word NumFrames, word FrameCount, dword AlphaBlend)

  • Author: Ki Chung
  • Inputs:
    • DestOff - offset of destination buffer
    • DestWidth - width of destination buffer
    • DestHeight - height of destination buffer
    • ImageOff - offset of image to draw
    • ImageWidth - width of image buffer
    • ImageHeight - height of image buffer
    • X - x coordinate of cursor in grids
    • Y - y coordinate of cursor in grids
    • NumFrames - number of frames for image
    • FrameCount - counter to be used to determine which frame to be drawn
    • AlphaBlend - if 1, alpha-blend image to destination buffer
    • [_OverlayOff] - offset of overlay buffer
  • Outputs: image drawn on the destination buffer at (X, Y)
  • Returns: -
  • Calls: _ClearBuffer, _CopyBuffer, _ComposeBuffer, _PointInBox
  • Draws image at (X, Y) on the destination buffer pointed to by DestOff and alpha-blends if indicated to do so. This function is a very general function which is used by many other graphics function in the game. It first checks to see if the pixel it's trying to draw is outside the boundary given by DestWidth and DestHeight. This is done by calling _PointInBox. Then it calculates the frame to draw from NumFrames and FrameCount. This calculation is done by dividing FrameCount by NumFrames. The remainder of the division is the frame to draw. So if the calling function wants to draw a particular frame in the image, it just needs to pass that frame number along with the width and height of the frame. If the calling function passes some sort of counting variable for the FrameCount, the end result is an animated object drawn on screen. If AlphaBlend is 1, it draws into overlay buffer instead of DestOff so that it can alpha composed.

void _DrawDeployCursor(dword *DestOff, word X, word Y, dword *CursorImageOff)

  • Author: Ki Chung, Seunghoon Kim
  • Inputs:
    • DestOff - offset of destination buffer
    • X - x coordinate of cursor in grids
    • Y - y coordinate of cursor in grids
    • CursorImageOff - offset of deploy cursor image
    • [_InvalidCannonOff] - offset of invalid cannon image
    • [_OverlayOff] - offset of overlay buffer
    • [_AnimateTick] - tick count that determines which sprite to draw
  • Outputs: deploy cursor drawn on the destination buffer at (X, Y)
  • Returns: -
  • Calls: _BoundaryInRegion, _ClearBuffer, _CopyBuffer, _ComposeBuffer
  • Draws deploy cursor at (X, Y) on the destination buffer pointed to by DestOff, if the cannon lies over where it cannot be placed, it's drawn with invalid cannon image

void _DrawBlock(dword *DestOff, word X, word Y, word Block, dword *BlockImageOff)

  • Author: Seunghoon Kim
  • Inputs:
    • DestOff - offset of destination buffer
    • X - x coordinate of block in grids
    • Y - y coordinate of block in grids
    • Block - block to draw
    • BlockImageOff - offset of block image
    • [_InvalidBlockOff] - offset of invalid block image
    • [_OverlayOff] - offset of overlay buffer
    • [_AnimateTick] - tick count that determines which sprite to draw
  • Outputs: block drawn on the destination buffer at (X, Y)
  • Returns: -
  • Calls: _BlockInRegion, _ClearBuffer, _CopyBuffer, _ComposeBuffer
  • Draws the random blocks created at various positions, respect to the rotations made by the player. The function makes a call to BlockInRegion, to check if the block is in the free region, and in respect to the output returned by BlockInRegion, it will print out the picture on each grid, specified by the block numbers. The block numbers used here are randomly generated from the block array, which holds the bit assignment to each grid in 3x3 image.(The bit will be 1, if a block exists in that grid.) I have written separate function subroutines for each of the nine grids, since each grid holds different coordinate values, and each makes a different function calls.

void _DrawExplosion(dword *DestOff, dword *MapOff, dword *ExplosionArray, dword *ExplosionImage)

  • Author: Ki Chung
  • Inputs:
    • DestOff - offset of destination buffer
    • MapOff - offset of map buffer
    • ExplosionArray - array of explosions to draw
    • ExplosionImage - offset of explosion image
    • [_AnimateTick] - tick count for animation (updates ExplosionArray)
  • Outputs: explosions drawn to the buffer pointed to by DestOff
  • Returns: -
  • Calls: _CopyBuffer
  • Draws explosions on the destination buffer pointed to by DestOff

void _DrawCannonBall(dword *DestOff, dword *CannonBallArray, dword *CannonBallImage)

  • Author: Ki Chung
  • Inputs:
    • DestOff - offset of destination buffer
    • CannonBallArray - array of cannon balls to draw
    • CannonBallImage - offset of cannon ball image
  • Outputs: cannon balls drawn to the buffer pointed to by DestOff
  • Returns: -
  • Calls: _CopyBuffer
  • Draws cannon balls on the destination buffer pointed to by DestOff

void _DimBuffer(dword *DestOff, word DestWidth, word DestHeight, word DimVal)

  • Author: Gihyun Ko
  • Inputs:
    • DestOff - offset of image buffer in memory
    • DestWidth - width of the buffer
    • DestHeight - height of the buffer
    • DimVal - color to clear with
  • Outputs: color copied to buffer
  • Returns: -
  • Calls: -
  • Takes input DimVal and dims the buffer by that constant. Each pixel contains ARGB bytes and each bytes are unpacked into a mmx register and subracted by the DimVal using saturation to prevent the colors going bright again when it goes below 0. Then the mmx register is packed again to dword size and changed for each pixel in the DestOff with width DestWidth and height DestHeight.

dword _InstallTmr( )

  • Author: Ki Chung
  • Inputs: -
  • Outputs: -
  • Returns: 1 if error; 0 otherwise
  • Calls: _LockArea, _Install_Int
  • Installs Timer ISR

void _RemoveTmr( )

  • Author: Ki Chung
  • Inputs: -
  • Outputs: -
  • Returns: -
  • Calls: -
  • Uninstalls Timer ISR

void _TmrISR( )

  • Author: Ki Chung
  • Inputs: -
  • Outputs:
    • [_TimeTick] - interrupt counter for controlling phase duration
    • [_AnimateTick] - counter for animation
    • [_CBallTick] - counter for updating cannon ball array
  • Returns: -
  • Calls: -
  • Increments [_TickCount]

dword _InstallKbd( )

  • Author: Hyun Jeong
  • Inputs: -
  • Outputs: -
  • Returns: 1 if error; 0 otherwise
  • Calls: _LockArea, Install_Int
  • Locks the appropriate memeory using LockArea library function and installs the ISR with Install_Int. This is almost entirely from MP4.

void _RemoveKbd( )

  • Author: Hyun Jeong
  • Inputs: -
  • Outputs: -
  • Returns: -
  • Calls: _Remove_Int
  • Removes the keyboard interrupt handler using the library function, _Remove_Int. Also from MP4.

void _KbdISR( )

  • Author: Hyun Jeong
  • Inputs: key presses waiting at port [_kbPort], [_kbIRQ]
  • Outputs: [_P1InputFlags], [_P2InputFlags], [_Flags]
  • Returns: -
  • Calls: -
  • Handles keyboard input, Very long and tedious function. Takes the scancode from the [kbPort] and compares the scancode with every possible key in this game. There are total of 12 keys, and each press and releases are handled. With presses, sets the appropriate flag to 1. With releases, sets the appropriate flags to 0. The P1 and P2 input flags are updated, along with the general game [_Flag] so it can be used in the game. At the end, it sends acknowledges to the PIC, which was taken from the lab manual.

dword _InstallSound( )

  • Author: Hyun Jeong
  • Inputs: -
  • Outputs: installs sound
  • Returns: 1 if error; 0 otherwise
  • Calls: _SB16_Init, _DMA_Lock_Mem
  • installs the sound. By doing the follwing:
    1. It sets the [_DMA_Mix] flag, which indicates whether to mix the sound effect to the background music.
    2. Initializes the [_ISR_Called] to 0, which counts the number of times the soundISR is called.
    3. The DMA buffer is allocated to the constant "SIZE", first set in the code. The size I ended up using is 4096 bytes. The smallest I was able to use was 2048 bytes. Smaller sizes made heavy pop and crack noises. Perhaps it took too many CPU cycles to correctly refill the buffers on time. There was no difference with 4096 bytes, so it was used to save CPU cycles. After allocating the memory, the selector and the address were stored into [DMASel] and [DMAAddr] respectively.

void _SoundISR( )

  • Author: Hyun Jeong
  • Inputs: -
  • Outputs: sound played
  • Returns: -
  • Calls: _DMA_Refill
  • This is the handler for sounds. This is called regularly after playing specified number of samples indicated in _SB16_Start function. It was set to 2048 samples, which is 2048 bytes for 8-bit sound. It is half the buffer size, so half of the buffer could be refilled each time. The SoundISR simply counts the number of time it's called and calls _DMA_Refill to refill the half of the DMA buffer.

void _DMA_Refill( )

  • Author: Hyun Jeong
  • Inputs: -
  • Outputs: DMA Buffer Refilled, sound mixed
  • Returns: -
  • Calls: _SB16_Start
  • Refilling the buffer - This was the key to playing long sound files with smooth transitions. Each time it is called, it refills half of the buffer with the next half-buffer-length of the sound clip to be played. [_DMA_Refill_Flag] indicates which half to refill. The flag is updated every time one half is filled, so the other half is filled the next time this function is called.
    • The actual refill was just copying the next half-buffer length of the sound clip to the DMA buffer address. The next portion to be played was stored in [BGMPos], which is incremented by SIZE/2 each time. [NextPos] is just a variable starting at 0 incremented each time by SIZE/2 also, which is used for comparing to handle the end case. When [NextPos] is greater than [BGMSize], which is the size of the background music, we know we've reached the end, and jump to the end case.
    • The end case was handled rather crudely. The code was originally written to play the remaining size of the file, switching to single cycle. But with small enough DMA buffer and having the sound file with enough blank at the end, simply not playing small remainder was good enough. Therefore, when the end is reached, the small remainder is ignored and the music starts again from the beginning.
  • Mixing the sounds - Next, it checks the [_DMA_Mix] flag to see whether it needs to mix sound effects to the background music. In this program, background music was always on, so whenever we wanted to play a sound effect, it had to mixed with the background music. To mix the sounds, it loaded data from the buffer it just refilled into an MMX register. It loads the sound effect to be mixed into another MMX register and performs bytewise parallel add. The result is stored back into the buffer and it is repeated until the entire half buffer is filled.
  • [MixCycle] stores how many times cycles (DMA refills) the sound must be mixed. It is initialized in _PlaySFX. It is decremented each time, and when it reaches 0, it jumps to the end case. This end case was also handled similar to the DMA refilling case. Again with small enough buffer and sound files with blanks at the end, I simply set the [_DMA_Mix] flag back to 0.

Functions from MP4

dword _PlayBGM(dword FileOff, dword filesize)

  • Author: Hyun Jeong
  • Inputs: FileOff - offset of the background music to play
  • Outputs: sound played
  • Returns: 1 if error, 0 otherwise
  • Calls: _DMA_Start, _DMA_Stop, _SB16Init, _SB16_GetChannel, _SB16_SetFormat, _SB16_SetMixers, _SB16_Start
  • This function starts the music playing. It sets the appropriate flags and position markers to be used in refilling. It initially fills the entire buffer with the beginning of the sound clip. Then it uses the library functions to initialize sound and to start the DMA transfer, etc.

dword _StopBGM(dword crossfade)

  • Author: Hyun Jeong
  • Inputs: .Crossfade (1 for crossfade and 0 otherwise)
  • Outputs: Stops BGM
  • Returns: 1 if error, 0 otherwise
  • Calls: -
  • Stops the background music This function stops the sound. The "crossfade" decreases the volume gradually then bring the music to stop. It was an attempt to reduce pop or crack noise when stopping the sound. This didn't make any noticeable change. Perhaps the CPU cycle was too fast and each instruction was executed too fast to make it noticeable to our ears. Each decrement in volume could have been made with longer intervals, perhaps using the timer or at the ISR. In theory, when we are near the end of the music (maybe about several buffer lengths from the end), or when this function is called, we can decrease the volume each time the ISR is called. However, there was neither time nor significant reason for this to be implemented.

dword _PlaySFX(dword fileOff2, dword filesize2)

  • Author: Hyun Jeong
  • Inputs: fileOff2 - offset of the sound effect to play, size of the sound clip
  • Outputs: sound played
  • Returns: 1 if error, 0 otherwise
  • Calls: -
  • This function does not actually do any "playing." This just sets the variables used for mixing. Sets the [_DMA_Mix] flag to 1, and sets SFX position markers to the next address to refill. [_MixCycles], the number of buffer-refill cycles is found by dividing the size of the sound effect by the size of the half-buffer. The remainder from this division is the length of the end case, stored in [RemCycle]. Since background music is always on, this does not need to start any DMA transfers.

dword _PointInBox(word X, word Y, word BoxULCornerX, word BoxULCornerY, word BoxLRCornerX, word BoxLRCornerY)

  • Inputs:
    • X - x coordinate of point in question
    • Y - y coordinate of point in question
    • BoxULCornerX - x coordinate of upper-left hand corner of box
    • BoxULCornerY - y coordinate of upper-left hand corner of box
    • BoxLRCornerX - x coordinate of lower_right hand corner of box
    • BoxLRCornerY - y coordinate of lower_right hand corner of box
  • Outputs: -
  • Returns: 1 if BoxULCornerX <= X <= BoxLRCornerX and BoxULCornerY <= Y <= BoxLRCornerY; 0 otherwise
  • Calls: -
  • determines if the point (X, Y) is located in the box formed by the points (BoxULCornerX, BoxULCornerY) and (BoxLRCornerX, BoxLRCornerY)

void _ClearBuffer(dword *DestOff, word DestWidth, word DestHeight, dword Color)

  • Inputs:
    • DestOff - offset of image buffer in memory
    • DestWidth - width of the buffer
    • DestHeight - height of the buffer
    • Color - color to clear with
  • Outputs: color copied to buffer
  • Returns: -
  • Calls: -
  • Clears pointed buffer by filling it with Color

void _CopyBuffer(dword *SrcOff, word SrcWidth, word SrcHeight, dword *DestOff, word DestWidth, word DestHeight, word X, word Y)

  • Inputs:
    • SrcOff - offset of source buffer
    • SrcWidth - width of source buffer
    • SrcHeight - height of source buffer
    • DestOff - offset of destination buffer
    • DestWidth - width of destination buffer
    • DestHeight - height of destination buffer
    • X - x coordinate of start point in destination buffer
    • Y - y coordinate of start point in destination buffer
  • Outputs: source buffer copied to destination buffer
  • Returns: -
  • Calls: -
  • Copies the pointed source buffer to (X,Y) in the pointed destination buffer

void _ComposeBuffers(dword *SrcOff, word SrcWidth, word SrcHeight, dword *DestOff, word DestWidth, word DestHeight, word X, word Y)

  • Inputs:
    • SrcOff - offset of source buffer
    • SrcWidth - width of source buffer
    • SrcHeight - height of source buffer
    • DestOff - offset of destination buffer
    • DestWidth - width of destination buffer
    • DestHeight - height of destination buffer
    • X - x coordinate of start point in destination buffer
    • Y - y coordinate of start point in destination buffer
  • Outputs: source buffer alpha composed onto destination buffer
  • Returns: -
  • Calls: -
  • Alpha composes source buffer onto destination buffer


External Routines(Pmode Lib): _LibInit _LibExit _AllocMem _LoadPNG _OpenFile _ReadFile _CloseFile _FindGraphicsMode _SetGraphicsMode _CopyToScreen _LockArea _Install_Int _Remove_Int _DMA_Allocate_Mem _DMA_Start _DMA_Stop _DMA_Lock_Mem _SB16Init _SB16_GetChannel _SB16_SetFormat _SB16_SetMixers _SB16_Start _SB16_Stop _SB16_Exit


Source Code: final.asm

Game design and data structures

  • Resolution: 640 x 480 pixels
  • Pixels: 4 bytes (ARGB)
  • Map details:
    • Game field: 65 x 60 grids (520 x 480 pixels)
    • Status bar: 15 x 60 grids on the right side of the game field (120 x 480 pixels)
    • Grids: 8 x 8 pixels, 2 byte each
      • Bit 15-8 (while bit 1 = 1): index for [_P1CannonArray] or [_P2CannonArray]
      • Bit 15-8 (while bit 1 = 0):
        • Value 0: unvailable
        • Value 1: empty
        • Value 2: obstacle
        • Value 3: castle
        • Value 4: remains of destroyed cannon
        • Value 5: wall
        • Value 6: wall destroyed
      • Bit 7-4: reserved
      • Bit 3-2: Player indicator
        • Value 0: P1
        • Value 1: P2
      • Bit 1: cannon
      • Bit 0: occupied
    • Castle: 3 x 3 grids
    • Cannon: 2 x 2 grids
    • Cannon ball: 8 bytes
      • Byte 7: x coordinate of source in grid
      • Byte 6: y coordinate of source in grid
      • Byte 5: x coordinate of destination in grid
      • Byte 4: y coordinate of destination in grid
      • Bytes 3-2: x coordinate of current location in pixel
      • Bytes 1-0: y coordinate of current location in pixel
    • Effective range of damage: 1 grid
    • Explosion: 4 bytes
      • Byte 3: reserved (for possible expansion)
      • Byte 2: current frame to display
      • Byte 1: x coordinate of location in grid
      • Byte 0: y coordinate of location in grid
    • Wall piece: 1 to 5 contiguous grids out of 3 x 3 square (2 bytes)
      • Bits 15-9: reserved (for possible expansion)
      • Bits 8-0: wall piece data
      • Using the design shown below, we only need to rotate left low-byte by 2 bits in order to rotate a block counter-clockwise


    Block design
    0 7 6
    1 8 5
    2 3 4
    Bit assignments
    - - - - - - - 8 7 6 5 4 3 2 1 0


  • Game data variables:
    • [_P1X] - x coordinate of P1 cursor (2)
    • [_P1Y] - y coordinate of P1 cursor (2)
    • [_P1_Up_Key] - scancode of P1 'up' key (1)
    • [_P1_Down_Key] - scancode of P1 'down' key (1)
    • [_P1_Left_Key] - scancode of P1 'left' key (1)
    • [_P1_Right_Key] - scancode of P1 'right' key (1)
    • [_P1_Primary_Key] - scancode of P1 'primary' key (1)
    • [_P1_Secondary_Key] - scancode of P1 'secondary' key (1)
    • [_P1Score] - score for P1 (2)
    • [_P1Life] - indicates how many life P1 has (1)
    • [_P1CurrentBlock] - current wall piece for P1 (2)
    • [_NumP1Castle] - number of conquered P1 castle (1)
    • [_NumP1Territory] - number of conquered grid by P1 (2)
    • [_NumP1Cannon] - number of P1 cannons on the map (1)
    • [_NumP1DeployCannon] - number of P1 cannons available for deployment (1)
    • [_P1CastleArray] - array of castles for P1 (10 x 4)
      • Byte 1: x coordinate of castle (FFh if no castle)
      • Byte 0: y coordinate of castle (FFh if no castle)
    • [_P1CannonArray] - array of cannons for P1 (30 x 4)
      • Byte 3: reserved (for possible expansion)
      • Byte 2: damage count
      • Bytes 1-0: location of upper-left part of cannon on map in grids
    • [_P2X] - x coordinate of P2 cursor (2)
    • [_P2Y] - y coordinate of P2 cursor (2)
    • [_P2_Up_Key] - scancode of P2 'up' key (1)
    • [_P2_Down_Key] - scancode of P2 'down' key (1)
    • [_P2_Left_Key] - scancode of P2 'left' key (1)
    • [_P2_Right_Key] - scancode of P2 'right' key (1)
    • [_P2_Primary_Key] - scancode of P2 'primary' key (1)
    • [_P2_Secondary_Key] - scancode of P2 'secondary' key (1)
    • [_P2Score] - score for P2 (2)
    • [_P2Life] - indicates how many life P2 has (1)
    • [_P2CurrentBlock] - current wall piece for P2 (2)
    • [_NumP2Castle] - number of conquered P2 castle (1)
    • [_NumP2Territory] - number of conquered grid by P2 (2)
    • [_NumP2Cannon] - number of P2 cannons on the map (1)
    • [_NumP2DeployCannon] - number of P2 cannons available for deployment (1)
    • [_P2CastleArray] - array of castles for P2 (10 x 2)
      • Same as [_P1CastleArray]
    • [_P2CannonArray] - array of cannons for P2 (30 x 4)
      • Same as [_P1CannonArray]
    • [_CBallArray] - array of cannon balls currently on map (60 x 8)
      • Explained in Cannon ball under Map details
    • [_BlockArray] - array of 19 available wall pieces (19 x 2)
      • Explained in Wall piece under Map details
    • [_ExplosionArray] - array of explosion grids (30 x 4)
      • Explained in Explosion under Map details
    • [_NumTotalCastle] - number of castles available for each player (1)
    • [_NumRounds] - number of game rounds played (1)
    • [_BattleTime] - duration of battle phase (2)
    • [_RebuildTime] - duration of rebuild phase (2)
    • [_DeployTime] - duration of deploy phase (2)
  • Game map and buffer offset variables:</li>
    • [_GameMapOff] - offset of game map buffer (65 x 60 x 1)
    • [_ScreenOff] - offset of screen buffer (640 x 480 x 4)
    • [_AuxScreenOff] - offset of secondary screen buffer (640 x 480 x 4) (for backup purposes)
    • [_MapScreenOff] - offset of map screen buffer (520 x 480 x 4)
    • [_OverlayOff] - offset of overlay buffer (520 x 480 x 4)
  • Image offset variables:</li>
    • _StatusBarOff resd 1 ; offset of status bar image (120 x 480 x 4)
    • _LeadOff resd 1 ; offset of Lead image buffer (20 x 20 x 4)
    • _P1InitCursorOff ; offset of P1 initialize cursor (56 x 72 x 4)
    • _P1DeployCursorOff ; offset of P1 initialize cursor (16 x 16 x 4)
    • _P1BattleCursorOff ; offset of P1 battle cursor (24 x 24 x 4)
    • _P1TerrainOff ; offset of P1 terrain image (8 x 8 x 4)
    • _P1BlockOff ; offset of P1 block image (8 x 8 x 4)
    • _P1FlatWallOff ; offset of P1 flat wall image (8 x 8 x 4)
    • _P1BattleWallOff ; offset of P1 battle wall image (?)
    • _P1FlatCannonOff ; offset of P1 flat cannon image (16 x 16 x 4)
    • _P1BattleCannonOff ; offset of P1 battle cannon image (16 x 16 x 4)
    • _P1FlatCastleOff ; offset of P1 flat castle image (24 x 24 x 4)
    • _P1BattleCastleOff ; offset of P1 battle castle image (24 x 24 x 4)
    • _P1WinBannerOff ; offset of win banner for P1 (378 x 282 x 4)
    • _P2InitCursorOff ; offset of P2 initialize cursor (56 x 72 x 4)
    • _P2DeployCursorOff ; offset of P2 initialize cursor (16 x 16 x 4)
    • _P2BattleCursorOff ; offset of P2 battle cursor (24 x 24 x 4)
    • _P2TerrainOff ; offset of P2 terrain image (8 x 8 x 4)
    • _P2BlockOff ; offset of P2 block image (8 x 8 x 4)
    • _P2FlatWallOff ; offset of P2 flat wall image (8 x 8 x 4)
    • _P2BattleWallOff ; offset of P2 battle wall image (?)
    • _P2FlatCannonOff ; offset of P2 flat cannon image (16 x 16 x 4)
    • _P2BattleCannonOff ; offset of P2 battle cannon image (16 x 16 x 4)
    • _P2FlatCastleOff ; offset of P2 flat castle image (24 x 24 x 4)
    • _P2BattleCastleOff ; offset of P2 battle castle image (24 x 24 x 4)
    • _P2WinBannerOff ; offset of win banner for P2 (378 x 282 x 4)
    • _InvalidBlockOff ; offset of invalid block image (8 x 8 x 4)
    • _InvalidCannonOff ; offset of invalid cannon image (8 x 8 x 4)
    • _CannonBallOff ; offset of cannon ball image offset (240 x 20 x 4)
    • _ExplosionOff ; offset of explosion image offset (256 x 16 x 4)
    • _RubblesOff ; offset of rubbles image offset (8 x 8 x 4)
    • _DestroyedCannonOff ; offset of destroyed cannon image (16 x 16 x 4)
    • _Terrain1Off ; offset of terrain image #1 (520 x 480 x 4)
    • _Terrain2Off ; offset of terrain image #2 (520 x 480 x 4)
    • _TerrainOff ; offset of terrain image selected (520 x 480 x 4)
    • _BigNumFontOff ; offset of big number font image buffer (400 x 55 x 4)
    • _SmallNumFontOff ; offset of small number font image buffer (200 x 21 x 4)
    • _BlueNumFontOff ; offest of blue number font image buffer (100 x 12 x 4)
    • _BattleBannerOff ; offset of battle phase banner image buffer (441 x 119 x 4)
    • _RebuildBannerOff ; offset of rebuild phase banner image buffer (441 x 119 x 4)
    • _DeployBannerOff ; offset of deploy phase banner image buffer (441 x 119 x 4)
    • _MenuOff ; offset of menu screen
    • _PlayBtn0Off ; offset of play game button image buffer
    • _PlayBtn1Off ; offset of play game button image buffer
    • _InstructionBtn0Off ; offset of instruction button image buffer
    • _InstructionBtn1Off ; offset of instruction button image buffer
    • _CreditsBtn0Off ; offset of credits button image buffer
    • _CreditsBtn1Off ; offset of credits button image buffer
    • _ExitBtn0Off ; offset of exit button image buffer
    • _ExitBtn1Off ; offset of exit button image buffer
    • _InstScreen1Off ; offset of instruction screen 1
    • _InstScreen2Off ; offset of instruction screen 2
    • _InstScreen3Off ; offset of instruction screen 3
    • _InstScreen4Off ; offset of instruction screen 4
  • Sound offset variables:</li>
    • _FireSndOff ; offset of cannon firing sound
    • _Fire2SndOff ; offset of cannon firing sound
    • _ExplosionSndOff ; offset of explosion sound
    • _BuildWallSndOff ; offset of placing wall piece sound
    • _BuildCannonSndOff ; offset of placing cannon sound
    • _InvalidSndOff ; offset of invalid move for placing object sound
    • _IntroBGMOff ; offset of intro background music
    • _BattleBGMOff ; offset of battle phase background music
    • _RebuildBGMOff ; offset of rebuild phase background music
    • _DeployBGMOff ; offset of deploy phase background music
    • _FireSndSize ; size of cannon firing sound
    • _Fire2SndSize ; size of cannon firing sound
    • _ExplosionSndSize ; size of explosion sound
    • _BuildWallSndSize ; size of placing wall piece sound
    • _BuildCannonSndSize ; size of placing cannon sound
    • _InvalidSndSize ; size of invalid move for placing object sound
    • _IntroBGMSize ; size of intro background music
    • _BattleBGMSize ; size of battle phase background music
    • _RebuildBGMSize ; size of rebuild phase background music
    • _DeployBGMSize ; size of deploy phase background music
  • Flags and status indicators:</li>
    • [_Flags]: program flags (1)
      • Bit 4: right mouse button pressed down
      • Bit 3: left mouse button pressed down
      • Bit 0: exit
    • [_P1_InputFlags]: player 1 input flags (2)
      • Bit 15-8: reserved (for possible expansion)
      • Bit 7-6: reserved (for possible expansion)
      • Bit 5: 'primary' key
      • Bit 4: 'secondary' key
      • Bit 3: 'up' key
      • Bit 2: 'down' key
      • Bit 1: 'left' key
      • Bit 0: 'right' key
    • [_P2_InputFlags]: player 2 input flags (2)
      • Same as [_P1Flags]
    • [_Phase]: phase indicator(1)
      • Bits 7-5: current phase
        • Value 0: initialize phase
        • Value 1: deploy phase
        • Value 2: battle phase
        • Value 3: rebuild phase
        • Value 4: break phase
      • Bits 4-0 (while Bits 7-5 = 0):
        • Bit 4-2: reserved (for possible expansion)
        • Bit 1: P2 initialize phase
        • Bit 0: P1 initialize phase
      • Bits 4-0 (while Bits 7-5 = 1):
        • Bit 4-2: reserved (for possible expansion)
        • Bit 1: P2 deploy phase
        • Bit 0: P1 deploy phase
      • Bits 4-0 (while Bits 7-5 = 2): unused
      • Bits 4-0 (while Bits 7-5 = 3): unused
      • Bits 4-0 (while Bits 7-5 = 4):
        • Value 0: break phase before deploy
        • Value 1: break phase before battle
        • Value 2: break phase before rebuild
  • Other global variables:</li>
    • [_GraphicsMode] - graphics mode # (2)
    • [_kbINT] - keyboard interrupt # (1)
    • [_kbIRQ] - keyboard IRQ (1)
    • [_kbPort] - keyboard port (2)
    • [_MouseSeg] - real mode segment for MouseCallback (2)
    • [_MouseOff] - real mode offset for MouseCallback (2)
    • [_MouseX] - X coordinate position of mouse on screen (2)
    • [_MouseY] - Y coordinate position of mouse on screen (2)
    • [_RoundingFactor] - rounding factor for alpha-blending (4)
    • [_TimeTick] - tick counter for timer (for controlling time limit in _Game)(2)
    • [_CBallTick] - tick counter for timer (for updating cannon ball array)(1)
    • [_AnimateTick] - tick counter for timer (for animation) (1)
    • [_BannerX] - x coordinate of banner (2)
    • [_BannerY] - y coordinate of banner (2)
  • Constants:</li>
    • TIMER_INT = 1Ch ;timer int #
    • SCREEN_WIDTH = 640 ;width of resolution
    • SCREEN_HEIGHT = 480 ;height of resolution
    • GAME_MAP_WIDTH = 65 ;width of game map in grids
    • GAME_MAP_HEIGHT = 60 ;height of game map in grids
    • MAP_PIXEL_WIDTH = 520 ;width of game map in pixels
    • MAP_PIXEL_HEIGHT = 480 ;height of game map in pixels
    • STATUSBAR_WIDTH = 120 ;width of status bar in pixels
    • STATUSBAR_HEIGHT = 480 ;height of status bar in pixels
    • STATUSBAR_X = 520 ;x coordinate for status bar
    • STATUSBAR_Y = 0 ;y coordinate for status bar
    • WIN_BANNER_WIDTH = 50 ;width of win banner
    • WIN_BANNER_HEIGHT = 50 ;height of win banner
    • WIN_BANNER_X = 50 ;x coordinate for win banner
    • WIN_BANNER_Y = 50 ;y coordinate for win banner
    • DEPLOY_BANNER_WIDTH = 50 ;width of deploy banner
    • DEPLOY_BANNER_HEIGHT = 50 ;height of deploy banner
    • BATTLE_BANNER_WIDTH = 50 ;width of battle banner
    • BATTLE_BANNER_HEIGHT = 50 ;height of battle banner
    • REBUILD_BANNER_WIDTH = 50 ;width of rebuild banner
    • REBUILD_BANNER_HEIGHT = 50 ;height of rebuild banner
    • BANNER_INIT_X = 639 ;x coordinate for initial banner loctaion
    • BANNER_INIT_Y = 100 ;y coordinate for initial banner loctaion
    • CASTLE_WIDTH = 24 ;width of castle image buffer
    • CASTLE_HEIGHT = 24 ;height of castle image buffer
    • WALL_WIDTH = 8 ;width of wall
    • WALL_HEIGHT = 8 ;width of height
    • INIT_CURSOR_WIDTH = 56 ;width of init cursor
    • INIT_CURSOR_HEIGHT = 72 ;height of init cursor
    • BATTLE_CURSOR_WIDTH = 24 ;width of battle cursor
    • BATTLE_CURSOR_HEIGHT = 24 ;height of battle cursor
    • NUM_FRAMES_INIT_CURSOR = 8 ;number of frames for initialize cursor
    • NUM_FRAMES_BATTLE_CURSOR = 8 ;number of frames for battle cursor
    • DIM_VAL = 50 ;value for dimming buffer
    • MAX_CANNON = 30 ;max number of cannons allowed
    • NUM_HANDICAP_CANNON = 4 ;number of deploy cannons given to player who lost
    • NUM_INIT_TERRITORY = 63 ;amount of initial territory in grids
    • NUM_TOTAL_ROUNDS = 10 ;number of total game rounds
    • INIT_PHASE = 00000000b ;phase value for initialize
    • DEPLOY_PHASE = 00100000b ;phase value for deploy
    • BATTLE_PHASE = 01000000b ;phase value for battle
    • REBUILD_PHASE = 01100000b ;phase value for rebuild
    • BREAK_PHASE = 10000000b ;phase value for break
    • BREAK_DEPLOY_PHASE = 10000000b ;phase value for break before deploy
    • BREAK_BATTLE_PHASE = 10000001b ;phase value for break before battle
    • BREAK_REBUILD_PHASE = 10000010b ;phase value for break before rebuild
    • BREAK_TIME = 20 ;duration of break phase
    • OCCUPIED = 10000000b ;map bit for occupied
    • CANNON = 01000000b ;map bit for cannon
    • OBSTACLE = 0 ;map value for unavailable grid
    • CANNON_REMAINS = 1 ;map value for remains of destroyed cannon
    • WALL_HIT = 2 ;map value for wall piece hit by cannon ball
    • P1_REGION = 5 ;map value for P1 region
    • P1_WALL = 11 ;map value for P1 wall
    • P1_CASTLE = 12 ;map value for P1 castle
    • P1_INIT_PHASE = 00000001b ;bit for P1 initialize phase
    • P1_DEPLOY_PHASE = 00100001b ;bit for P1 deploy phase
    • P2_REGION = 6 ;map value for P2 region
    • P2_WALL = 21 ;map value for P2 wall
    • P2_CASTLE = 22 ;map value for P2 castle
    • P2_INIT_PHASE = 00000010b ;bit for P2 initialize phase
    • P2_DEPLOY_PHASE = 00100010b ;bit for P2 deploy phase
    • EXIT_FLAG = 00000001b ;exit flag
    • PRIMARY_FLAG = 00100000b ;'primary' input flag
    • SECONDARY_FLAG = 00010000b ;'secondary' input flag
    • UP_FLAG = 00001000b ;'up' input flag
    • DOWN_FLAG = 00000100b ;'down' input flag
    • LEFT_FLAG = 00000010b ;'left' input flag
    • RIGHT_FLAG = 00000001b ;'right' input flag
ċ
Assembly-program-executables.zip
(353k)
Hoonio,
Feb 11, 2012, 8:59 AM
ċ
final.asm
(146k)
Hoonio,
Feb 11, 2012, 8:58 AM
ċ
mp0.asm
(4k)
Hoonio,
Feb 11, 2012, 8:58 AM
ċ
mp1.asm
(6k)
Hoonio,
Feb 11, 2012, 8:58 AM
ċ
mp2.asm
(17k)
Hoonio,
Feb 11, 2012, 8:58 AM
ċ
mp3.asm
(18k)
Hoonio,
Feb 11, 2012, 8:58 AM
ċ
mp4.asm
(43k)
Hoonio,
Feb 11, 2012, 8:58 AM
Comments