OBJECTIVE
Since graphics plays a very important role in modern computer application, it is important to know more information about its hardware and software operations. Despite the simplicity of the concepts required for this assignment, upon completion of this lab, you will be able to:
1) Understand what a video buffer is and how to access it.
2) Understand how to control the contents of the video buffer.
3) Use the computer's onboard clock.
4) Detect keyboard and mouse clicks and decode the pressed key.
5) Write user defined interrupt service routines (ISR).
BACKGROUND
1) Software Interrupts
Software interrupts are generated by programs, not by hardware. Since the first PCs with DOS and BIOS functions, all modern operating systems, including MS Vista, Linux, etc., still use software interrupts to access libraries, invoke systems functions, and connect with the operating system kernel. Software interrupts are caused by a program issuing the software interrupt instruction to make the processor act as though it receives a hardware interrupt. This method is convenient for accessing OS services independently of any program location in memory. Although software interrupts may not be as critical as hardware interrupts, they are called frequently and must be efficiently implemented. A software interrupt can be seen as an indirect call to a procedure. In this case, the data structure were the address of such procedure is stored is called interrupt vector table, or just interrupt vector. The interrupt vector is stored in the lowest 1024 bytes of system memory with 4 bytes (CS and IP) reserved per interrupt, for a total of 256 distinct interrupt vector entries. Upon examining this table, one will notice that there are some interrupts which are not used. Those vacancies are reserved for the users. In other words, INT 60H to INT 67H are reserved for the user defined interrupt services. Because modifying an interrupt vector directly is not compliant with the specifications of any OS, the MSDOS also provides a safe way to consult and change the interrupt vector entries using interrupt 21H function numbers 25H and 35H. Here is what they look like:
a) Set interrupt vector:
i) Calling registers:
(1) AH: 25H
(2) AL: Interrupt number
(3) DS: Code segment of the interrupt service routine
(4) DX: Offset of the interrupt service routine
ii) Return registers: None
b) Get interrupt vector:
i) Calling registers:
(1) AH: 35H
(2) AL: Interrupt number
ii) Return registers:
(1) ES: Code segment of the interrupt service routine
(2) BX: Offset of the interrupt service routine
2) Real Time Clock
Many applications require some sort of Idle Loop in order to "kill time" while the user is preparing to hit a key, move the mouse, etc... Most of the times, the OS itself will need to use such an idle loop for its internal operation (e.g. mouse movements).
For this assignment we will use a BIOS system call for obtaining the system time in the form of a counter that is incremented every 55ms. This system function is:
a) INT 1Ah, with calling register AH = 00h and return registers:
i) AL: Midnight flag, 1 if 24 hours passed since reset
ii) CX: high order word of tick count
iii) DX: low order word of tick count
b) INT 1Ah, with calling register AH = 02h and return registers:
i) CH: Hour (BCD)
ii) CL: Minute (BCD)
iii) DH: Second (BCD)
iv) CF: 0, Clock is working; 1, No clock.
c) There is a DOS function call - service 2Ch - which can also yield the system time. However, we don't use it in TSRs because of DOS reentrance considerations.
d) To create delay, you can also create nested loops. You can calculate the delay based on the number of clock cycles needed to execute a command and the number of cycles per second.
e) There is also a function in int 15h that can be used to wait.
3) Keyboard Detection
From previous labs, we are familiar with keyboard character/string input. However, for some situations, we may need to detect if there is any input/keyboard hit, rather than wait until an input has been actually made. For that, we will use INT 16h.
4) Mouse Detection
For mouse functions we use INT 33h.
5) Video Buffer
The video buffer is accessed via INT 10h. Video RAM is an element common to all video cards, whether this is programmed in text mode or graphics mode. While video RAM is organized differently for different video cards when this is set to graphics mode, the structure is virtually identical for all video cards when in text mode. Video cards only need memory segments A and B, starting at segment addresses A000H and B000H, which requires only 64K. Monochrome video cards (i.e., MDA and Hercules) use video RAM in the range B000:0000 to B000:7FFF. Color Cards (i.e., CGA, EGA and VGA) use video RAM beginning at physical address B8000H or A0000H. In DOS and Win98, whenever the card is in text mode (i.e. inside the command prompt for Win98), the screen is divided into 80 columns. For Win XP and up, one is able to change the number of columns and the number of rows. The top-left most character on the screen starts at RAM address 0B8000:0. Each character uses 2 bytes, 16 bits. The first four bits are used to indicate background color. The second four indicate foreground color. The final byte indicates the ASCII character. The four color bits are (most to least significant) brightness, red, green, and blue.
LABORATORY
This experiment consists of two parts. First, write a set of 80x86 assembly language routines and a main program that can be installed as a TSR (Terminate and Stay Resident) program and that defines an user-defined ISR so it can be called by other programs. This user-defined interrupt service should contain the following functions:
1) INT 63H
a) Function 01H ==> clear screen
b) You may add other functions
2) INT 64H
a) Function 03H ==> draw a rectangle
b) Call registers:
i) AH: 03H
ii) AL: color code
iii) CX: X coordinate of the starting point
iv) DX: Y coordinate of the starting point
v) SI: X coordinate of the ending point
vi) DI: Y coordinate of the ending point
3) INT 65H ==> restore the original interrupt vector.
4) The interrupt service routine you write should become part of the Disk Operating System (DOS). In other words, it has to reside in the memory and become a Terminate and Stay Resident (TSR) program. INT 27H is recommended for this purpose:
a) Calling registers:
i) DX Offset of last byte plus 1 of the program to remain resident
ii) CS Segment of Program Segment Prefix
b) Search "INT 27" or "Terminate and Stay Resident" on the internet for more info.
When a user-defined interrupt service (or any ISR) is called, the DS (data segment) is unchanged at the entry point of the ISR. Therefore, it will point to the original data segment of the calling function. As a result, the DS must be manually initialized before access to any data inside the ISR can be made. Also, remember that an ISR is supposed to save ("PUSH") all registers that it will modify during the course of its execution. These registers must be restored at exit. For the second part of the lab, extend your original ISR to perform the following services:
1) Clear screen.
2) Display the game field and the score on the top right corner of the screen.
3) Start the game by shooting multiple projectiles from a random location on the edge of the screen.
4) The space ship, in the middle of the screen, aims, controlled by mouse location, can shoot the projectiles to destroy them using a mouse click to fire bullets in that direction.
5) Each projectile that is destroyed is worth a certain number of points.
6) If the player is hit by a projectile, the space ship loses one of 3 life points.
7) Game would stop when you get hit 3 times.
8) After the game ends it should go back to the game menu. In the next lab we will add
high score functionality here.
9) Before exiting, the game should:
a) Clear the screen,
b) Restore the original interrupt vector table and return to DOS.
10) All BIOS function calls (macros) must be included in a macro, BIOS.MAC, while the DOS function calls (macros) must be included in DOS.MAC.
The following program structure is suggested:
code SEGMENT
ASSUME cs:code, ds:code, ss:code
ORG 100h
begin:
jmp initialize
; needed memory data here
;
; begin resident program here
int63 PROC FAR
.
.
iret
int63 ENDP
;
int64 PROC FAR
.
iret
int64 ENDP
;
int65 PROC FAR
.
iret
int65 ENDP
; end resident program here
initialize:
ASSUME cs:code, ds:code, ss:code
.
.
lea dx, initialize
int 27h
code ENDS
END begin