Computer Engineering II
Potts, Summer 2000

Machine Problem 3: Text-Mode Mines

Assigned Thursday, July 6, 2000
Purpose Text-Mode Graphics, Timer and Mouse Interrupts
Points 50
Checkpoint Date July 12 - 5:00pm
Due Date July 20 - 5:00pm


Who says you need windows to play a good game of Mines?  Whoever it was, they're wrong.  Here we will implement a game of Mines, much like the Windows MineSweeper. It will have two modes: Beginner and Advanced, defined as 10x10 and 30x30 playfields. To support this on the same text screen, the Beginner field must be stretched so each Mine square is 3x3 screen characters.

Problem Description

Hopefully you already know how Mines is played.  If not, take an opportunity to try out MineSweeper, in Start>Programs>Accessories>Games. The playfield is full of occasional mines, which you have to locate. Your only clue is when you are next to one or more mines, you know how many are in adjacent squares. Using only this information, you can locate all mines in virtually any randomly generated minefield.

For this MP, you will be implementing a version of this game in text mode video. You will also support the mouse: right-clicking a location will cycle between No Idea, Mine, and Not Sure; left-clicking will check the spot for an actual mine -- if there is, then you lose.


Callbacks and Interrupt Service Routines

Hardware Interrupts are generated when various hardware events occur.  Examples include the timer, which triggers 18.2 times per second, the keyboard, which triggers for every key press and release, and the mouse which triggers for any movement or button press or release. These interrupts are the hardware's way of saying "I need attention," so that the rest of the time the CPU can completely ignore such devices, and just execute code.

When the interrupt happens, the CPU is informed.  The current opcode finishes execution, and then the current program is interrupted. This is when your Interrupt Service Routine (ISR) is executed. As it happens inbetween effectively random lines of your program, it needs to save all registers, including the flags. The hardware takes care of half of this (thus pushing of the flags); the other half you must take care of by using IRET instead of RET (to pop the flags).

But watch out - this wonderful piece of advice is only necessarily useful when you are directly writing the ISR. In the case of the mouse, you are writing a callback routine ( has some good information on this, if you search for mouse, or interrupt 33h) so you only need to preserve your registers.  The actual ISR which is calling your callback will preserve the flags, and call your procedure as a FAR procedure.

The Timer

The timer will be used to keep track of how long the player has been playing. This is generally used to then form a high score table, but we'll just use it to report back to the user. Every time your timer ISR is called, increment the tick counter, and then use it to calculate the total time. Once calculated, display it to the screen using BINASC and a custom implementation of DSPMSG that draws directly to the screen (in DrawScreen, not TimerISR). While technically incrementing the second counter for every 18 ticks will yield an incorrect time, implementing this truly correctly is optional.

The Mouse

When your mouse handler is called, some important information will be in the registers. Unfortunately, it's not quite in the format you want. While you can take the mouse button status MouseClick directly from AL, the MouseX and MouseY from CX and DX need to be changed. The ISR will supply you with the pixel positions of the mouse cursor, but it only draws the cursor in increments of text characters, which are 8x8 pixels in the 80x50 character mode we are using. Not only that, HandleInput routine expects the X and Y coordinates to be relative to the upper-left corner of the board, which is not the same as the screen's upper-left corner.

The Field (Internal)

To keep track of the data in this game of mines, we have created two arrays. The first, called PlayField, is a series of spaces, words, and lines that represent everything on the screen except the actual mines - think of it as equivalent the window border. Since we carefully do not modify any of this area during the remainder of the program, we can display this once and be done with it. Display it with the attributes as defined as PLAYFIELD_ATTR.

The second array is a 30x30 array, called MineField, defined as EMPTY. What is EMPTY though? If you look at the Constants section, we have declared quite a lot of them this time:

EMPTY       EQU 0

If you consider this as a bitfield within the byte, we have several separate options:


You need all of these because a given location can be a mine, or not, can be visible or not, and if invisible, it can have one of three attributes set: no guess, guess of a mine or unknown. The lower four bits are used to make our life easier later on - when displaying the number of mines, or recursing to display the entire empty area - by holding a nybble sized number.  Beware before you start adding this number to '0' that you have limited it to 0-15 by somehow chopping off the high bits.

For example, an undisplayed mine that hasn't been guessed at will hold 0001xxxxb; a displayed non-mine with 3 adjacent mines will hold xx100011b; and an undisplayed non-mine with 5 adjacent mines, guessed as a mine will hold 01000101b.

The Field (External)

The player will not want to read the memory locations directly. Not only would it be a pain, he could see from the beginning where the mines were, and the game would no longer be a game. To make it actually entertaining, the board must be drawn to the screen. Drawing it properly, you will find, is a complicated procedure. These are the steps you must implicitly follow. although turning this into code is for you to do:

One suggestion for later debugging is to override the visible bit in your DrawField routine, thus enabling you to verify your map was properly generated.  It has limited use, however, so you are encouraged to find more methods for debugging your errors.  Codeview can be used, but you must invoke it with the /m argument to tell it not to use the mouse since your program needs it: CV /50 /m mp3.












Preliminary Procedure Monitor the newsgroup and this on-line section for revisions to the MP or to the write-up

Final Steps

  1. Verify that your program meets all requirements for handin.
  2. Print a copy of the MP3 grading sheet.
  3. Demonstrate MP3.EXE to a TA or to the instructor.
  4. Note that there are two due-dates.  If everything is working at the first, you may turn it all in.  Similarly, late points for the first checkpoint's procedures apply even if everything is ready by the final due date.
  5. Be prepared to answer questions about any aspect of the operation of your program. The TAs will not accept an MP if you cannot fully explain all operations of your code.
  6. Handin in your program with a TA at
  7. Print MP3.ASM. Make sure that you print out your code 4 pages per page and double sided. If you don't know how to do this ask a TA for assistance.
  8. Staple the MP3 grading sheet to the front of your MP3.ASM file and give both to the same TA that approved your demonstration.