| /* |
| Copyright (C) 1994-1995 Apogee Software, Ltd. |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| |
| See the GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| |
| */ |
| /********************************************************************** |
| module: TASK_MAN.C |
| |
| author: James R. Dose |
| date: July 25, 1994 |
| |
| Low level timer task scheduler. |
| |
| (c) Copyright 1994 James R. Dose. All Rights Reserved. |
| **********************************************************************/ |
| |
| #define TRUE ( 1 == 1 ) |
| #define FALSE ( !TRUE ) |
| |
| //#define USESTACK |
| #define LOCKMEMORY |
| #define NOINTS |
| #define USE_USRHOOKS |
| |
| #include <stdlib.h> |
| #include <dos.h> |
| #include <conio.h> |
| #include <string.h> |
| #include "interrup.h" |
| #include "linklist.h" |
| #include "task_man.h" |
| |
| #ifdef USESTACK |
| #include "dpmi.h" |
| #endif |
| #ifdef LOCKMEMORY |
| #include "dpmi.h" |
| #endif |
| |
| #ifdef USE_USRHOOKS |
| #include "usrhooks.h" |
| #define FreeMem( ptr ) USRHOOKS_FreeMem( ( ptr ) ) |
| #else |
| #define FreeMem( ptr ) free( ( ptr ) ) |
| #endif |
| |
| typedef struct |
| { |
| task *start; |
| task *end; |
| } tasklist; |
| |
| |
| /*--------------------------------------------------------------------- |
| Global variables |
| ---------------------------------------------------------------------*/ |
| |
| #ifdef USESTACK |
| |
| // adequate stack size |
| #define kStackSize 2048 |
| |
| static unsigned short StackSelector = NULL; |
| static unsigned long StackPointer; |
| |
| static unsigned short oldStackSelector; |
| static unsigned long oldStackPointer; |
| |
| #endif |
| |
| static task HeadTask; |
| static task *TaskList = &HeadTask; |
| |
| static void ( __interrupt __far *OldInt8 )( void ); |
| |
| static volatile long TaskServiceRate = 0x10000L; |
| static volatile long TaskServiceCount = 0; |
| |
| #ifndef NOINTS |
| static volatile int TS_TimesInInterrupt; |
| #endif |
| |
| static char TS_Installed = FALSE; |
| |
| volatile int TS_InInterrupt = FALSE; |
| |
| /*--------------------------------------------------------------------- |
| Function prototypes |
| ---------------------------------------------------------------------*/ |
| |
| static void TS_FreeTaskList( void ); |
| static void TS_SetClockSpeed( long speed ); |
| static long TS_SetTimer( long TickBase ); |
| static void TS_SetTimerToMaxTaskRate( void ); |
| static void __interrupt __far TS_ServiceSchedule( void ); |
| static void __interrupt __far TS_ServiceScheduleIntEnabled( void ); |
| static void TS_AddTask( task *ptr ); |
| static int TS_Startup( void ); |
| static void RestoreRealTimeClock( void ); |
| |
| // These declarations are necessary to use the inline assembly pragmas. |
| |
| extern void GetStack(unsigned short *selptr,unsigned long *stackptr); |
| extern void SetStack(unsigned short selector,unsigned long stackptr); |
| |
| // This function will get the current stack selector and pointer and save |
| // them off. |
| #pragma aux GetStack = \ |
| "mov [edi],esp" \ |
| "mov ax,ss" \ |
| "mov [esi],ax" \ |
| parm [esi] [edi] \ |
| modify [eax esi edi]; |
| |
| // This function will set the stack selector and pointer to the specified |
| // values. |
| #pragma aux SetStack = \ |
| "mov ss,ax" \ |
| "mov esp,edx" \ |
| parm [ax] [edx] \ |
| modify [eax edx]; |
| |
| |
| /********************************************************************** |
| |
| Memory locked functions: |
| |
| **********************************************************************/ |
| |
| |
| #define TS_LockStart TS_FreeTaskList |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_FreeTaskList |
| |
| Terminates all tasks and releases any memory used for control |
| structures. |
| ---------------------------------------------------------------------*/ |
| |
| static void TS_FreeTaskList |
| ( |
| void |
| ) |
| |
| { |
| task *node; |
| task *next; |
| unsigned flags; |
| |
| flags = DisableInterrupts(); |
| |
| node = TaskList->next; |
| while( node != TaskList ) |
| { |
| next = node->next; |
| FreeMem( node ); |
| node = next; |
| } |
| |
| TaskList->next = TaskList; |
| TaskList->prev = TaskList; |
| |
| RestoreInterrupts( flags ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_SetClockSpeed |
| |
| Sets the rate of the 8253 timer. |
| ---------------------------------------------------------------------*/ |
| |
| static void TS_SetClockSpeed |
| ( |
| long speed |
| ) |
| |
| { |
| unsigned flags; |
| |
| flags = DisableInterrupts(); |
| |
| if ( ( speed > 0 ) && ( speed < 0x10000L ) ) |
| { |
| TaskServiceRate = speed; |
| } |
| else |
| { |
| TaskServiceRate = 0x10000L; |
| } |
| |
| outp( 0x43, 0x36 ); |
| outp( 0x40, TaskServiceRate ); |
| outp( 0x40, TaskServiceRate >> 8 ); |
| |
| RestoreInterrupts( flags ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_SetTimer |
| |
| Calculates the rate at which a task will occur and sets the clock |
| speed if necessary. |
| ---------------------------------------------------------------------*/ |
| |
| static long TS_SetTimer |
| ( |
| long TickBase |
| ) |
| |
| { |
| long speed; |
| |
| speed = 1192030L / TickBase; |
| if ( speed < TaskServiceRate ) |
| { |
| TS_SetClockSpeed( speed ); |
| } |
| |
| return( speed ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_SetTimerToMaxTaskRate |
| |
| Finds the fastest running task and sets the clock to operate at |
| that speed. |
| ---------------------------------------------------------------------*/ |
| |
| static void TS_SetTimerToMaxTaskRate |
| ( |
| void |
| ) |
| |
| { |
| task *ptr; |
| long MaxServiceRate; |
| unsigned flags; |
| |
| flags = DisableInterrupts(); |
| |
| MaxServiceRate = 0x10000L; |
| |
| ptr = TaskList->next; |
| while( ptr != TaskList ) |
| { |
| if ( ptr->rate < MaxServiceRate ) |
| { |
| MaxServiceRate = ptr->rate; |
| } |
| |
| ptr = ptr->next; |
| } |
| |
| if ( TaskServiceRate != MaxServiceRate ) |
| { |
| TS_SetClockSpeed( MaxServiceRate ); |
| } |
| |
| RestoreInterrupts( flags ); |
| } |
| |
| |
| #ifdef NOINTS |
| /*--------------------------------------------------------------------- |
| Function: TS_ServiceSchedule |
| |
| Interrupt service routine |
| ---------------------------------------------------------------------*/ |
| |
| static void __interrupt __far TS_ServiceSchedule |
| ( |
| void |
| ) |
| |
| { |
| task *ptr; |
| task *next; |
| |
| |
| TS_InInterrupt = TRUE; |
| |
| #ifdef USESTACK |
| // save stack |
| GetStack( &oldStackSelector, &oldStackPointer ); |
| |
| // set our stack |
| SetStack( StackSelector, StackPointer ); |
| #endif |
| |
| ptr = TaskList->next; |
| while( ptr != TaskList ) |
| { |
| next = ptr->next; |
| |
| if ( ptr->active ) |
| { |
| ptr->count += TaskServiceRate; |
| //JIM |
| // if ( ptr->count >= ptr->rate ) |
| while( ptr->count >= ptr->rate ) |
| { |
| ptr->count -= ptr->rate; |
| ptr->TaskService( ptr ); |
| } |
| } |
| ptr = next; |
| } |
| |
| #ifdef USESTACK |
| // restore stack |
| SetStack( oldStackSelector, oldStackPointer ); |
| #endif |
| |
| TaskServiceCount += TaskServiceRate; |
| if ( TaskServiceCount > 0xffffL ) |
| { |
| TaskServiceCount &= 0xffff; |
| _chain_intr( OldInt8 ); |
| } |
| |
| outp( 0x20,0x20 ); |
| |
| TS_InInterrupt = FALSE; |
| } |
| |
| #else |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_ServiceScheduleIntEnabled |
| |
| Interrupt service routine with interrupts enabled. |
| ---------------------------------------------------------------------*/ |
| |
| static void __interrupt __far TS_ServiceScheduleIntEnabled |
| ( |
| void |
| ) |
| |
| { |
| task *ptr; |
| task *next; |
| |
| TS_TimesInInterrupt++; |
| TaskServiceCount += TaskServiceRate; |
| if ( TaskServiceCount > 0xffffL ) |
| { |
| TaskServiceCount &= 0xffff; |
| _chain_intr( OldInt8 ); |
| } |
| |
| outp( 0x20,0x20 ); |
| |
| if ( TS_InInterrupt ) |
| { |
| return; |
| } |
| |
| TS_InInterrupt = TRUE; |
| _enable(); |
| |
| #ifdef USESTACK |
| // save stack |
| GetStack( &oldStackSelector, &oldStackPointer ); |
| |
| // set our stack |
| SetStack( StackSelector, StackPointer ); |
| #endif |
| |
| while( TS_TimesInInterrupt ) |
| { |
| ptr = TaskList->next ; |
| while( ptr != TaskList ) |
| { |
| next = ptr->next; |
| |
| if ( ptr->active ) |
| { |
| ptr->count += TaskServiceRate; |
| if ( ptr->count >= ptr->rate ) |
| { |
| ptr->count -= ptr->rate; |
| ptr->TaskService( ptr ); |
| } |
| } |
| ptr = next; |
| } |
| TS_TimesInInterrupt--; |
| } |
| |
| _disable(); |
| |
| #ifdef USESTACK |
| // restore stack |
| SetStack( oldStackSelector, oldStackPointer ); |
| #endif |
| |
| TS_InInterrupt = FALSE; |
| } |
| #endif |
| |
| |
| #ifdef USESTACK |
| |
| /*--------------------------------------------------------------------- |
| Function: allocateTimerStack |
| |
| Allocate a block of memory from conventional (low) memory and return |
| the selector (which can go directly into a segment register) of the |
| memory block or 0 if an error occured. |
| ---------------------------------------------------------------------*/ |
| |
| static unsigned short allocateTimerStack |
| ( |
| unsigned short size |
| ) |
| |
| { |
| union REGS regs; |
| |
| // clear all registers |
| memset( ®s, 0, sizeof( regs ) ); |
| |
| // DPMI allocate conventional memory |
| regs.w.ax = 0x100; |
| |
| // size in paragraphs |
| regs.w.bx = ( size + 15 ) / 16; |
| |
| int386( 0x31, ®s, ®s ); |
| if (!regs.w.cflag) |
| { |
| // DPMI call returns selector in dx |
| // (ax contains real mode segment |
| // which is ignored here) |
| |
| return( regs.w.dx ); |
| } |
| |
| // Couldn't allocate memory. |
| return( NULL ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: deallocateTimerStack |
| |
| Deallocate a block of conventional (low) memory given a selector to |
| it. Assumes the block was allocated with DPMI function 0x100. |
| ---------------------------------------------------------------------*/ |
| |
| static void deallocateTimerStack |
| ( |
| unsigned short selector |
| ) |
| |
| { |
| union REGS regs; |
| |
| if ( selector != NULL ) |
| { |
| // clear all registers |
| memset( ®s, 0, sizeof( regs ) ); |
| |
| regs.w.ax = 0x101; |
| regs.w.dx = selector; |
| int386( 0x31, ®s, ®s ); |
| } |
| } |
| |
| #endif |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_Startup |
| |
| Sets up the task service routine. |
| ---------------------------------------------------------------------*/ |
| |
| static int TS_Startup |
| ( |
| void |
| ) |
| |
| { |
| if ( !TS_Installed ) |
| { |
| #ifdef LOCKMEMORY |
| |
| int status; |
| |
| status = TS_LockMemory(); |
| if ( status != TASK_Ok ) |
| { |
| TS_UnlockMemory(); |
| return( status ); |
| } |
| |
| #endif |
| |
| #ifdef USESTACK |
| |
| StackSelector = allocateTimerStack( kStackSize ); |
| if ( StackSelector == NULL ) |
| { |
| |
| #ifdef LOCKMEMORY |
| |
| TS_UnlockMemory(); |
| |
| #endif |
| return( TASK_Error ); |
| } |
| |
| // Leave a little room at top of stack just for the hell of it... |
| StackPointer = kStackSize - sizeof( long ); |
| |
| #endif |
| |
| //static const task *TaskList = &HeadTask; |
| TaskList->next = TaskList; |
| TaskList->prev = TaskList; |
| |
| TaskServiceRate = 0x10000L; |
| TaskServiceCount = 0; |
| |
| #ifndef NOINTS |
| TS_TimesInInterrupt = 0; |
| #endif |
| |
| OldInt8 = _dos_getvect( 0x08 ); |
| #ifdef NOINTS |
| _dos_setvect( 0x08, TS_ServiceSchedule ); |
| #else |
| _dos_setvect( 0x08, TS_ServiceScheduleIntEnabled ); |
| #endif |
| |
| TS_Installed = TRUE; |
| } |
| |
| return( TASK_Ok ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_Shutdown |
| |
| Ends processing of all tasks. |
| ---------------------------------------------------------------------*/ |
| |
| void TS_Shutdown |
| ( |
| void |
| ) |
| |
| { |
| if ( TS_Installed ) |
| { |
| TS_FreeTaskList(); |
| |
| TS_SetClockSpeed( 0 ); |
| |
| _dos_setvect( 0x08, OldInt8 ); |
| |
| #ifdef USESTACK |
| |
| deallocateTimerStack( StackSelector ); |
| StackSelector = NULL; |
| |
| #endif |
| |
| // Set Date and Time from CMOS |
| // RestoreRealTimeClock(); |
| |
| #ifdef LOCKMEMORY |
| |
| TS_UnlockMemory(); |
| |
| #endif |
| TS_Installed = FALSE; |
| } |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_ScheduleTask |
| |
| Schedules a new task for processing. |
| ---------------------------------------------------------------------*/ |
| |
| task *TS_ScheduleTask |
| ( |
| void ( *Function )( task * ), |
| int rate, |
| int priority, |
| void *data |
| ) |
| |
| { |
| task *ptr; |
| |
| #ifdef USE_USRHOOKS |
| int status; |
| |
| ptr = NULL; |
| |
| status = USRHOOKS_GetMem( &ptr, sizeof( task ) ); |
| if ( status == USRHOOKS_Ok ) |
| #else |
| ptr = malloc( sizeof( task ) ); |
| if ( ptr != NULL ) |
| #endif |
| { |
| if ( !TS_Installed ) |
| { |
| status = TS_Startup(); |
| if ( status != TASK_Ok ) |
| { |
| FreeMem( ptr ); |
| return( NULL ); |
| } |
| } |
| |
| ptr->TaskService = Function; |
| ptr->data = data; |
| ptr->rate = TS_SetTimer( rate ); |
| ptr->count = 0; |
| ptr->priority = priority; |
| ptr->active = FALSE; |
| |
| TS_AddTask( ptr ); |
| } |
| |
| return( ptr ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_AddTask |
| |
| Adds a new task to our list of tasks. |
| ---------------------------------------------------------------------*/ |
| |
| static void TS_AddTask |
| ( |
| task *node |
| ) |
| |
| { |
| LL_SortedInsertion( TaskList, node, next, prev, task, priority ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_Terminate |
| |
| Ends processing of a specific task. |
| ---------------------------------------------------------------------*/ |
| |
| int TS_Terminate |
| ( |
| task *NodeToRemove |
| ) |
| |
| { |
| task *ptr; |
| task *next; |
| unsigned flags; |
| |
| flags = DisableInterrupts(); |
| |
| ptr = TaskList->next; |
| while( ptr != TaskList ) |
| { |
| next = ptr->next; |
| |
| if ( ptr == NodeToRemove ) |
| { |
| LL_RemoveNode( NodeToRemove, next, prev ); |
| NodeToRemove->next = NULL; |
| NodeToRemove->prev = NULL; |
| FreeMem( NodeToRemove ); |
| |
| TS_SetTimerToMaxTaskRate(); |
| |
| RestoreInterrupts( flags ); |
| |
| return( TASK_Ok ); |
| } |
| |
| ptr = next; |
| } |
| |
| RestoreInterrupts( flags ); |
| |
| return( TASK_Warning ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_Dispatch |
| |
| Begins processing of all inactive tasks. |
| ---------------------------------------------------------------------*/ |
| |
| void TS_Dispatch |
| ( |
| void |
| ) |
| |
| { |
| task *ptr; |
| unsigned flags; |
| |
| flags = DisableInterrupts(); |
| |
| ptr = TaskList->next; |
| while( ptr != TaskList ) |
| { |
| ptr->active = TRUE; |
| ptr = ptr->next; |
| } |
| |
| RestoreInterrupts( flags ); |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_SetTaskRate |
| |
| Sets the rate at which the specified task is serviced. |
| ---------------------------------------------------------------------*/ |
| |
| void TS_SetTaskRate |
| ( |
| task *Task, |
| int rate |
| ) |
| |
| { |
| unsigned flags; |
| |
| flags = DisableInterrupts(); |
| |
| Task->rate = TS_SetTimer( rate ); |
| TS_SetTimerToMaxTaskRate(); |
| |
| RestoreInterrupts( flags ); |
| } |
| |
| |
| #ifdef LOCKMEMORY |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_LockEnd |
| |
| Used for determining the length of the functions to lock in memory. |
| ---------------------------------------------------------------------*/ |
| |
| static void TS_LockEnd |
| ( |
| void |
| ) |
| |
| { |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_UnlockMemory |
| |
| Unlocks all neccessary data. |
| ---------------------------------------------------------------------*/ |
| |
| void TS_UnlockMemory |
| ( |
| void |
| ) |
| |
| { |
| DPMI_UnlockMemoryRegion( TS_LockStart, TS_LockEnd ); |
| DPMI_Unlock( TaskList ); |
| DPMI_Unlock( OldInt8 ); |
| DPMI_Unlock( TaskServiceRate ); |
| DPMI_Unlock( TaskServiceCount ); |
| DPMI_Unlock( TS_Installed ); |
| |
| #ifndef NOINTS |
| DPMI_Unlock( TS_TimesInInterrupt ); |
| #endif |
| |
| #ifdef USESTACK |
| DPMI_Unlock( StackSelector ); |
| DPMI_Unlock( StackPointer ); |
| DPMI_Unlock( oldStackSelector ); |
| DPMI_Unlock( oldStackPointer ); |
| #endif |
| } |
| |
| |
| /*--------------------------------------------------------------------- |
| Function: TS_LockMemory |
| |
| Locks all neccessary data. |
| ---------------------------------------------------------------------*/ |
| |
| int TS_LockMemory |
| ( |
| void |
| ) |
| |
| { |
| int status; |
| |
| status = DPMI_LockMemoryRegion( TS_LockStart, TS_LockEnd ); |
| status |= DPMI_Lock( TaskList ); |
| status |= DPMI_Lock( OldInt8 ); |
| status |= DPMI_Lock( TaskServiceRate ); |
| status |= DPMI_Lock( TaskServiceCount ); |
| status |= DPMI_Lock( TS_Installed ); |
| |
| #ifndef NOINTS |
| status |= DPMI_Lock( TS_TimesInInterrupt ); |
| #endif |
| |
| #ifdef USESTACK |
| status |= DPMI_Lock( StackSelector ); |
| status |= DPMI_Lock( StackPointer ); |
| status |= DPMI_Lock( oldStackSelector ); |
| status |= DPMI_Lock( oldStackPointer ); |
| #endif |
| |
| if ( status != DPMI_Ok ) |
| { |
| TS_UnlockMemory(); |
| return( TASK_Error ); |
| } |
| |
| return( TASK_Ok ); |
| } |
| |
| #endif |