home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Teach Yourself Game Programming in 21 Days
/
TYGAMES_R.ISO
/
source
/
day_11
/
mtask.c
< prev
next >
Wrap
Text File
|
1994-07-05
|
18KB
|
708 lines
// I N C L U D E S ////////////////////////////////////////////////////////////
#include <io.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <bios.h>
#include <fcntl.h>
#include <memory.h>
#include <malloc.h>
#include <math.h>
#include <string.h>
#include "graph3.h" // include graphics our stuff
#include "graph4.h"
#include "graph5.h"
#include "graph6.h"
// D E F I N E S //////////////////////////////////////////////////////////////
// timer defines
#define CONTROL_8253 0x43 // the 8253's control register
#define CONTROL_WORD 0x3C // the control word to set mode 2,binary least/most
#define COUNTER_0 0x40 // counter 0
#define COUNTER_1 0x41 // counter 1
#define COUNTER_2 0x42 // counter 2
#define TIMER_60HZ 0x4DAE // 60 hz
#define TIMER_50HZ 0x5D37 // 50 hz
#define TIMER_40HZ 0x7486 // 40 hz
#define TIMER_30HZ 0x965C // 30 hz
#define TIMER_20HZ 0xE90B // 20 hz
#define TIMER_18HZ 0xFFFF // 18.2 hz (the standard count and the slowest possible)
// interrupt table defines
#define TIME_KEEPER_INT 0x1C // the time keeper interrupt
// multi-tasking kernal defines
#define MAX_TASKS 16 // this should be enough to turn your brains to mush
#define TASK_INACTIVE 0 // this is an inactive task
#define TASK_ACTIVE 1 // this is an active task
// defines for demo tasks
#define NUM_ATOMS 30
#define NUM_STARS 50
// M A C R O S ///////////////////////////////////////////////////////////////
#define LOW_BYTE(n) (n & 0x00ff) // extracts the low-byte of a word
#define HI_BYTE(n) ((n>>8) & 0x00ff) // extracts the hi-byte of a word
// S T U C T U R E S /////////////////////////////////////////////////////////
// this is a single task structure
typedef struct task_typ
{
int id; // the id number for this task
int state; // the state of this task
void (far *task)(); // the function pointer to the task itself
} task, *task_ptr;
// structures for demo tasks
typedef struct particle_typ
{
int x,y; // position of particle
int xv,yv; // velocity of particle
unsigned char color; // color of particle
} particle, *particle_ptr;
// P R O T O T Y P E S ///////////////////////////////////////////////////////
void Change_Timer(unsigned int new_count);
// multi-tasking stuff
void Initialize_Kernal(void);
void Start_Kernal(void);
void Stop_Kernal(void);
int Add_Task(void (far *function)());
int Delete_Task(int id);
void _interrupt far Multi_Kernal(void);
// G L O B A L S /////////////////////////////////////////////////////////////
void (_interrupt far *Old_Time_Isr)(); // used to hold old interrupt vector
// multi-tasking stuff
task tasks[MAX_TASKS]; // this is the task list for the system
int num_tasks = 0; // tracks number of active tasks
// globals for demo tasks
particle atoms[NUM_ATOMS]; // the balls
particle starfield[NUM_STARS]; // the star field
int star_id, mirror_id, ball_id; // used to hold id's so that tasks can be
// terminated later
// F U N C T I O N S //////////////////////////////////////////////////////////
void Initialize_Kernal(void)
{
// this function will set up the task list and prepare for it to be populated
int index; // loop variable
for (index=0; index<MAX_TASKS; index++)
{
// set id to current location in list
tasks[index].id = index;
// set to inactive
tasks[index].state = TASK_INACTIVE;
// set function pointer to NULL;
tasks[index].task = NULL;
} // end for index
} // end Initialize_Kernal
///////////////////////////////////////////////////////////////////////////////
void Start_Kernal(void)
{
// install our time keeper ISR while saving old one
Old_Time_Isr = _dos_getvect(TIME_KEEPER_INT);
_dos_setvect(TIME_KEEPER_INT, Multi_Kernal);
} // end Start_Kernal
///////////////////////////////////////////////////////////////////////////////
void Stop_Kernal(void)
{
// replace old time keeper ISR
_dos_setvect(TIME_KEEPER_INT, Old_Time_Isr);
} // end Stop_Kernal
//////////////////////////////////////////////////////////////////////////////
int Add_Task(void (far *function)())
{
// this function will add the task to the task list and return it's id number
// which can be used to delete it. If the function returns -1 then the
// task list is full and no more tasks can be added
int index;
for (index=0; index<MAX_TASKS; index++)
{
// try and find an inactive task
if (tasks[index].state == TASK_INACTIVE)
{
// load new task into this position
tasks[index].state = TASK_ACTIVE;
tasks[index].id = index;
tasks[index].task = function; // assign function pointer
// adjust global task monitor
num_tasks++;
// return id to caller
return(tasks[index].id);
} // end if found an inactive task
} // end for index
// if we got this far then there are no free spots...bummer
return(-1);
} // end Add_Task
///////////////////////////////////////////////////////////////////////////////
int Delete_Task(int id)
{
// this function will try to delete a task from the task list, if the function
// is successful, it will return 1 else it will return 0.
if (tasks[id].state == TASK_ACTIVE)
{
// kill task and return success
tasks[id].task = NULL;
tasks[id].state = TASK_INACTIVE;
// decrement number of active tasks
num_tasks--;
return(1);
} // end if task can be deleted
else
{
// couldn't delete task
return(0);
} // end task already dead
} // end Delete_Task
///////////////////////////////////////////////////////////////////////////////
void _interrupt far Multi_Kernal(void)
{
// this function will call all of the task in a round robin manner such that
// only one task will be called per interrupt. note: ther must be at least
// one active task in the task list
static int current_task=0; // current_task to be executed by kernal
// test if there are any tasks at all
if (num_tasks>0)
{
// find an active task
while(tasks[current_task].state!=TASK_ACTIVE)
{
// move to next task and round robin if at end of task list
if (++current_task>=MAX_TASKS)
current_task=0;
} // end search for active task
// at this point we have an active task so call it
tasks[current_task].task(); // weird looking huh!
// now we need to move to the next possible task
if (++current_task>=MAX_TASKS)
current_task=0;
} // end if there are any tasks
// chain to old ISR (play nice with the other children)
Old_Time_Isr();
} // end Multi_Kernal
///////////////////////////////////////////////////////////////////////////////
void Change_Timer(unsigned int new_count)
{
// send the control word, mode 2, binary, least/most load sequence
_outp(CONTROL_8253, CONTROL_WORD);
// now write the least significant byte to the counter register
_outp(COUNTER_0,LOW_BYTE(new_count));
// and now the the most significant byte
_outp(COUNTER_0,HI_BYTE(new_count));
} // end Change_Timer
// D E M O T A S K S /////////////////////////////////////////////////////////
void Rectangle(int xo,int yo,int x1,int y1,unsigned char color)
{
// draw a rectangle using the Bline function
Bline(xo,yo,x1,yo,color);
Bline(x1,yo,x1,y1,color);
Bline(x1,y1,xo,y1,color);
Bline(xo,y1,xo,yo,color);
} // end Rectangle
///////////////////////////////////////////////////////////////////////////////
void Stars(void)
{
// this function will animate a star field
int index; // loop variable
static int initialized=0; // this is the local static state variable
if (!initialized)
{
// initialize all the stars
for (index=0; index<NUM_STARS; index++)
{
// initialize each star to a velocity, position and color
starfield[index].x = 226 + rand()%70;
starfield[index].y = 26 + rand()%70;
// decide what star plane the star is in
switch(rand()%3)
{
case 0: // plane 1- the farthest star plane
{
// set velocity and color
starfield[index].xv = 2;
starfield[index].color = 8;
} break;
case 1: // plane 2-The medium distance star plane
{
starfield[index].xv = 4;
starfield[index].color = 7;
} break;
case 2: // plane 3-The nearest star plane
{
starfield[index].xv = 6;
starfield[index].color = 15;
} break;
} // end switch
} // end for index
// draw working window
Rectangle(225,25,225+75,25+75,9);
// set variable to move to next processing state
initialized=1;
} // end if being initialized
else
{ // must be nth time in, so do the usual
// process each star
for (index=0; index<NUM_STARS; index++)
{
// E R A S E ///////////////////////////////////////////////////////////
Plot_Pixel_Fast(starfield[index].x,starfield[index].y,0);
// M O V E /////////////////////////////////////////////////////////////
if ( (starfield[index].x+=starfield[index].xv)>=225+75 )
starfield[index].x = 226;
// D R A W /////////////////////////////////////////////////////////////
Plot_Pixel_Fast(starfield[index].x,starfield[index].y,
starfield[index].color);
} // end for index
} // end else
} // end Stars
///////////////////////////////////////////////////////////////////////////////
void Mirror(void)
{
// this function will draw a mirrored pixel image
int x,y;
unsigned char color;
static int initialized=0; // this is the local static state variable
if (!initialized)
{
// draw working window
Rectangle(125,25,125+75,25+75,9);
// set variable to move to next processing state
initialized=1;
} // end if not intialized
else
{
// D R A W /////////////////////////////////////////////////////////////////
// draw a mirrored image
x = rand()%38;
y = rand()%38;
color = (unsigned char)(rand()%256);
Plot_Pixel_Fast(x+125,y+25,color);
Plot_Pixel_Fast((75-1)-x+125,y+25,color);
Plot_Pixel_Fast(x+125,(75-1)-y+25,color);
Plot_Pixel_Fast((75-1)-x+125,(75-1)-y+25,color);
} // end else
} // end Mirror
///////////////////////////////////////////////////////////////////////////////
void Balls(void)
{
// this function will bounce a collection of balls around
int index; // used for looping
static int initialized=0; // this is the local static state variable
if (!initialized)
{
// initialize all structures
for (index=0; index<NUM_ATOMS; index++)
{
// select a random position and trajectory for each atom
// their background
atoms[index].x = 26 + rand()%70;
atoms[index].y = 26 + rand()%70;
atoms[index].xv = -2 + rand()%4;
atoms[index].yv = -2 + rand()%4;
} // end for index
// draw working window
Rectangle(25,25,25+75,25+75,9);
// set initialized flag so process can switch states
initialized = 1;
} // end if need to initialize
else
{ // do normal processing
// E R A S E /////////////////////////////////////////////////////////////
// loop through the atoms and erase them
for (index=0; index<NUM_ATOMS; index++)
{
Plot_Pixel_Fast(atoms[index].x, atoms[index].y, 0);
} // end for index
// M O V E ////////////////////////////////////////////////////////////////
// loop through the atom array and move each atom also check collsions
// with the walls of the container
for (index=0; index<NUM_ATOMS; index++)
{
// move the atoms
atoms[index].x+=atoms[index].xv;
atoms[index].y+=atoms[index].yv;
// did the atom hit a wall, if so reflect the velocity vector
if (atoms[index].x > 98 || atoms[index].x < 27)
{
atoms[index].xv=-atoms[index].xv;
atoms[index].x+=atoms[index].xv;
} // end if hit a vertical wall
if (atoms[index].y > 98 || atoms[index].y < 28)
{
atoms[index].yv=-atoms[index].yv;
atoms[index].y+=atoms[index].yv;
} // end if hit a horizontal wall
} // end for index
// D R A W /////////////////////////////////////////////////////////////////
// loop through the atoms and draw them
for (index=0; index<NUM_ATOMS; index++)
{
Plot_Pixel_Fast(atoms[index].x, atoms[index].y, 10);
} // end for index
} // end else normal processing
} // end Balls
// M A I N ////////////////////////////////////////////////////////////////////
void main(void)
{
int trate=20; // initial timer rate
int done=0; // exit flag
char string[80]; // used for printing
// SECTION 1 //////////////////////////////////////////////////////////////////
// set video mode to 320x200 256 color mode
Set_Video_Mode(VGA256);
// initialize the multi-tasking system
Initialize_Kernal();
// load in some processes and save their id's
star_id = Add_Task(Stars);
ball_id = Add_Task(Balls);
mirror_id = Add_Task(Mirror);
// SECTION 2 //////////////////////////////////////////////////////////////////
// set timer rate to 20Hz
Change_Timer(TIMER_20HZ);
// turn on the multi-tasking kernal, each task will be executed every 3
// interrupts since there is a round robin scheduler in place
Start_Kernal();
// SECTION 3 //////////////////////////////////////////////////////////////////
// now do main processing in parallel with other tasks
// draw menu
Blit_String(10,105,10, "Multi-Tasking Control Menu",1);
Blit_String(10,110+10,2,"Press (2-6) to Change interrupt rate.",1);
Blit_String(10,110+20,2,"Press 'B' to kill ball task.",1);
Blit_String(10,110+30,2,"Press 'S' to kill stars task.",1);
Blit_String(10,110+40,2,"Press 'M' to kill mirror task.",1);
Blit_String(10,110+50,2,"Press 'Q' to exit.",1);
Blit_String(25,10,10,"Balls",0);
Blit_String(125,10,10,"Mirror",0);
Blit_String(225,10,10,"Star Field",0);
// SECTION 4 //////////////////////////////////////////////////////////////////
// main event loop
while(!done)
{
// test if key was hit
if (kbhit())
{
// get the character and test it
switch(getch())
{
case '2': // set system timer to 20hz
{
Change_Timer(TIMER_20HZ);
trate = 20;
} break;
case '3': // set system timer to 30hz
{
Change_Timer(TIMER_30HZ);
trate = 30;
} break;
case '4': // set system timer to 40hz
{
Change_Timer(TIMER_40HZ);
trate = 40;
} break;
case '5': // set system timer to 50hz
{
Change_Timer(TIMER_50HZ);
trate = 50;
} break;
case '6': // set system timer to 60hz
{
Change_Timer(TIMER_60HZ);
trate = 60;
} break;
// SECTION 5 //////////////////////////////////////////////////////////////////
case 'b': // kill the ball task
{
Delete_Task(ball_id);
Blit_String(25,10,12,"INACTIVE ",0);
} break;
case 's': // kill the star field task
{
Delete_Task(star_id);
Blit_String(225,10,12,"INACTIVE ",0);
} break;
case 'm': // kill the mirror task
{
Delete_Task(mirror_id);
Blit_String(125,10,12,"INACTIVE ",0);
} break;
case 'q': done=1; break;
default:break;
} // end switch
} // end if kbhit
// display info
sprintf(string,"System timer at %dHZ ",trate);
Blit_String(10,190,15,string,0);
} // end while
// SECTION 6 //////////////////////////////////////////////////////////////////
// turn off the multi-tasking kernal
Stop_Kernal();
// reset system timer to 18.2
Change_Timer(TIMER_18HZ);
// reset the video mode back to text
Set_Video_Mode(TEXT_MODE);
} // end main