blob: c6e2cd6e9e53c1104df644fe6a141baf90291763 [file] [log] [blame]
Franklin Weia855d622017-01-21 15:18:31 -05001/*
2Copyright (C) 1994-1995 Apogee Software, Ltd.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19*/
20/**********************************************************************
21 module: TASK_MAN.C
22
23 author: James R. Dose
24 date: July 25, 1994
25
26 Low level timer task scheduler.
27
28 (c) Copyright 1994 James R. Dose. All Rights Reserved.
29**********************************************************************/
30
31#define TRUE ( 1 == 1 )
32#define FALSE ( !TRUE )
33
34//#define USESTACK
35#define LOCKMEMORY
36#define NOINTS
37#define USE_USRHOOKS
38
39#include <stdlib.h>
40#include <dos.h>
41#include <conio.h>
42#include <string.h>
43#include "interrup.h"
44#include "linklist.h"
45#include "task_man.h"
46
47#ifdef USESTACK
48#include "dpmi.h"
49#endif
50#ifdef LOCKMEMORY
51#include "dpmi.h"
52#endif
53
54#ifdef USE_USRHOOKS
55#include "usrhooks.h"
56#define FreeMem( ptr ) USRHOOKS_FreeMem( ( ptr ) )
57#else
58#define FreeMem( ptr ) free( ( ptr ) )
59#endif
60
61typedef struct
62 {
63 task *start;
64 task *end;
65 } tasklist;
66
67
68/*---------------------------------------------------------------------
69 Global variables
70---------------------------------------------------------------------*/
71
72#ifdef USESTACK
73
74// adequate stack size
75#define kStackSize 2048
76
77static unsigned short StackSelector = NULL;
78static unsigned long StackPointer;
79
80static unsigned short oldStackSelector;
81static unsigned long oldStackPointer;
82
83#endif
84
85static task HeadTask;
86static task *TaskList = &HeadTask;
87
88static void ( __interrupt __far *OldInt8 )( void );
89
90static volatile long TaskServiceRate = 0x10000L;
91static volatile long TaskServiceCount = 0;
92
93#ifndef NOINTS
94static volatile int TS_TimesInInterrupt;
95#endif
96
97static char TS_Installed = FALSE;
98
99volatile int TS_InInterrupt = FALSE;
100
101/*---------------------------------------------------------------------
102 Function prototypes
103---------------------------------------------------------------------*/
104
105static void TS_FreeTaskList( void );
106static void TS_SetClockSpeed( long speed );
107static long TS_SetTimer( long TickBase );
108static void TS_SetTimerToMaxTaskRate( void );
109static void __interrupt __far TS_ServiceSchedule( void );
110static void __interrupt __far TS_ServiceScheduleIntEnabled( void );
111static void TS_AddTask( task *ptr );
112static int TS_Startup( void );
113static void RestoreRealTimeClock( void );
114
115// These declarations are necessary to use the inline assembly pragmas.
116
117extern void GetStack(unsigned short *selptr,unsigned long *stackptr);
118extern void SetStack(unsigned short selector,unsigned long stackptr);
119
120// This function will get the current stack selector and pointer and save
121// them off.
122#pragma aux GetStack = \
123 "mov [edi],esp" \
124 "mov ax,ss" \
125 "mov [esi],ax" \
126 parm [esi] [edi] \
127 modify [eax esi edi];
128
129// This function will set the stack selector and pointer to the specified
130// values.
131#pragma aux SetStack = \
132 "mov ss,ax" \
133 "mov esp,edx" \
134 parm [ax] [edx] \
135 modify [eax edx];
136
137
138/**********************************************************************
139
140 Memory locked functions:
141
142**********************************************************************/
143
144
145#define TS_LockStart TS_FreeTaskList
146
147
148/*---------------------------------------------------------------------
149 Function: TS_FreeTaskList
150
151 Terminates all tasks and releases any memory used for control
152 structures.
153---------------------------------------------------------------------*/
154
155static void TS_FreeTaskList
156 (
157 void
158 )
159
160 {
161 task *node;
162 task *next;
163 unsigned flags;
164
165 flags = DisableInterrupts();
166
167 node = TaskList->next;
168 while( node != TaskList )
169 {
170 next = node->next;
171 FreeMem( node );
172 node = next;
173 }
174
175 TaskList->next = TaskList;
176 TaskList->prev = TaskList;
177
178 RestoreInterrupts( flags );
179 }
180
181
182/*---------------------------------------------------------------------
183 Function: TS_SetClockSpeed
184
185 Sets the rate of the 8253 timer.
186---------------------------------------------------------------------*/
187
188static void TS_SetClockSpeed
189 (
190 long speed
191 )
192
193 {
194 unsigned flags;
195
196 flags = DisableInterrupts();
197
198 if ( ( speed > 0 ) && ( speed < 0x10000L ) )
199 {
200 TaskServiceRate = speed;
201 }
202 else
203 {
204 TaskServiceRate = 0x10000L;
205 }
206
207 outp( 0x43, 0x36 );
208 outp( 0x40, TaskServiceRate );
209 outp( 0x40, TaskServiceRate >> 8 );
210
211 RestoreInterrupts( flags );
212 }
213
214
215/*---------------------------------------------------------------------
216 Function: TS_SetTimer
217
218 Calculates the rate at which a task will occur and sets the clock
219 speed if necessary.
220---------------------------------------------------------------------*/
221
222static long TS_SetTimer
223 (
224 long TickBase
225 )
226
227 {
228 long speed;
229
230 speed = 1192030L / TickBase;
231 if ( speed < TaskServiceRate )
232 {
233 TS_SetClockSpeed( speed );
234 }
235
236 return( speed );
237 }
238
239
240/*---------------------------------------------------------------------
241 Function: TS_SetTimerToMaxTaskRate
242
243 Finds the fastest running task and sets the clock to operate at
244 that speed.
245---------------------------------------------------------------------*/
246
247static void TS_SetTimerToMaxTaskRate
248 (
249 void
250 )
251
252 {
253 task *ptr;
254 long MaxServiceRate;
255 unsigned flags;
256
257 flags = DisableInterrupts();
258
259 MaxServiceRate = 0x10000L;
260
261 ptr = TaskList->next;
262 while( ptr != TaskList )
263 {
264 if ( ptr->rate < MaxServiceRate )
265 {
266 MaxServiceRate = ptr->rate;
267 }
268
269 ptr = ptr->next;
270 }
271
272 if ( TaskServiceRate != MaxServiceRate )
273 {
274 TS_SetClockSpeed( MaxServiceRate );
275 }
276
277 RestoreInterrupts( flags );
278 }
279
280
281#ifdef NOINTS
282/*---------------------------------------------------------------------
283 Function: TS_ServiceSchedule
284
285 Interrupt service routine
286---------------------------------------------------------------------*/
287
288static void __interrupt __far TS_ServiceSchedule
289 (
290 void
291 )
292
293 {
294 task *ptr;
295 task *next;
296
297
298 TS_InInterrupt = TRUE;
299
300 #ifdef USESTACK
301 // save stack
302 GetStack( &oldStackSelector, &oldStackPointer );
303
304 // set our stack
305 SetStack( StackSelector, StackPointer );
306 #endif
307
308 ptr = TaskList->next;
309 while( ptr != TaskList )
310 {
311 next = ptr->next;
312
313 if ( ptr->active )
314 {
315 ptr->count += TaskServiceRate;
316//JIM
317// if ( ptr->count >= ptr->rate )
318 while( ptr->count >= ptr->rate )
319 {
320 ptr->count -= ptr->rate;
321 ptr->TaskService( ptr );
322 }
323 }
324 ptr = next;
325 }
326
327 #ifdef USESTACK
328 // restore stack
329 SetStack( oldStackSelector, oldStackPointer );
330 #endif
331
332 TaskServiceCount += TaskServiceRate;
333 if ( TaskServiceCount > 0xffffL )
334 {
335 TaskServiceCount &= 0xffff;
336 _chain_intr( OldInt8 );
337 }
338
339 outp( 0x20,0x20 );
340
341 TS_InInterrupt = FALSE;
342 }
343
344#else
345
346/*---------------------------------------------------------------------
347 Function: TS_ServiceScheduleIntEnabled
348
349 Interrupt service routine with interrupts enabled.
350---------------------------------------------------------------------*/
351
352static void __interrupt __far TS_ServiceScheduleIntEnabled
353 (
354 void
355 )
356
357 {
358 task *ptr;
359 task *next;
360
361 TS_TimesInInterrupt++;
362 TaskServiceCount += TaskServiceRate;
363 if ( TaskServiceCount > 0xffffL )
364 {
365 TaskServiceCount &= 0xffff;
366 _chain_intr( OldInt8 );
367 }
368
369 outp( 0x20,0x20 );
370
371 if ( TS_InInterrupt )
372 {
373 return;
374 }
375
376 TS_InInterrupt = TRUE;
377 _enable();
378
379 #ifdef USESTACK
380 // save stack
381 GetStack( &oldStackSelector, &oldStackPointer );
382
383 // set our stack
384 SetStack( StackSelector, StackPointer );
385 #endif
386
387 while( TS_TimesInInterrupt )
388 {
389 ptr = TaskList->next ;
390 while( ptr != TaskList )
391 {
392 next = ptr->next;
393
394 if ( ptr->active )
395 {
396 ptr->count += TaskServiceRate;
397 if ( ptr->count >= ptr->rate )
398 {
399 ptr->count -= ptr->rate;
400 ptr->TaskService( ptr );
401 }
402 }
403 ptr = next;
404 }
405 TS_TimesInInterrupt--;
406 }
407
408 _disable();
409
410 #ifdef USESTACK
411 // restore stack
412 SetStack( oldStackSelector, oldStackPointer );
413 #endif
414
415 TS_InInterrupt = FALSE;
416 }
417#endif
418
419
420#ifdef USESTACK
421
422/*---------------------------------------------------------------------
423 Function: allocateTimerStack
424
425 Allocate a block of memory from conventional (low) memory and return
426 the selector (which can go directly into a segment register) of the
427 memory block or 0 if an error occured.
428---------------------------------------------------------------------*/
429
430static unsigned short allocateTimerStack
431 (
432 unsigned short size
433 )
434
435 {
436 union REGS regs;
437
438 // clear all registers
439 memset( &regs, 0, sizeof( regs ) );
440
441 // DPMI allocate conventional memory
442 regs.w.ax = 0x100;
443
444 // size in paragraphs
445 regs.w.bx = ( size + 15 ) / 16;
446
447 int386( 0x31, &regs, &regs );
448 if (!regs.w.cflag)
449 {
450 // DPMI call returns selector in dx
451 // (ax contains real mode segment
452 // which is ignored here)
453
454 return( regs.w.dx );
455 }
456
457 // Couldn't allocate memory.
458 return( NULL );
459 }
460
461
462/*---------------------------------------------------------------------
463 Function: deallocateTimerStack
464
465 Deallocate a block of conventional (low) memory given a selector to
466 it. Assumes the block was allocated with DPMI function 0x100.
467---------------------------------------------------------------------*/
468
469static void deallocateTimerStack
470 (
471 unsigned short selector
472 )
473
474 {
475 union REGS regs;
476
477 if ( selector != NULL )
478 {
479 // clear all registers
480 memset( &regs, 0, sizeof( regs ) );
481
482 regs.w.ax = 0x101;
483 regs.w.dx = selector;
484 int386( 0x31, &regs, &regs );
485 }
486 }
487
488#endif
489
490/*---------------------------------------------------------------------
491 Function: TS_Startup
492
493 Sets up the task service routine.
494---------------------------------------------------------------------*/
495
496static int TS_Startup
497 (
498 void
499 )
500
501 {
502 if ( !TS_Installed )
503 {
504#ifdef LOCKMEMORY
505
506 int status;
507
508 status = TS_LockMemory();
509 if ( status != TASK_Ok )
510 {
511 TS_UnlockMemory();
512 return( status );
513 }
514
515#endif
516
517#ifdef USESTACK
518
519 StackSelector = allocateTimerStack( kStackSize );
520 if ( StackSelector == NULL )
521 {
522
523#ifdef LOCKMEMORY
524
525 TS_UnlockMemory();
526
527#endif
528 return( TASK_Error );
529 }
530
531 // Leave a little room at top of stack just for the hell of it...
532 StackPointer = kStackSize - sizeof( long );
533
534#endif
535
536//static const task *TaskList = &HeadTask;
537 TaskList->next = TaskList;
538 TaskList->prev = TaskList;
539
540 TaskServiceRate = 0x10000L;
541 TaskServiceCount = 0;
542
543#ifndef NOINTS
544 TS_TimesInInterrupt = 0;
545#endif
546
547 OldInt8 = _dos_getvect( 0x08 );
548 #ifdef NOINTS
549 _dos_setvect( 0x08, TS_ServiceSchedule );
550 #else
551 _dos_setvect( 0x08, TS_ServiceScheduleIntEnabled );
552 #endif
553
554 TS_Installed = TRUE;
555 }
556
557 return( TASK_Ok );
558 }
559
560
561/*---------------------------------------------------------------------
562 Function: TS_Shutdown
563
564 Ends processing of all tasks.
565---------------------------------------------------------------------*/
566
567void TS_Shutdown
568 (
569 void
570 )
571
572 {
573 if ( TS_Installed )
574 {
575 TS_FreeTaskList();
576
577 TS_SetClockSpeed( 0 );
578
579 _dos_setvect( 0x08, OldInt8 );
580
581#ifdef USESTACK
582
583 deallocateTimerStack( StackSelector );
584 StackSelector = NULL;
585
586#endif
587
588 // Set Date and Time from CMOS
589// RestoreRealTimeClock();
590
591#ifdef LOCKMEMORY
592
593 TS_UnlockMemory();
594
595#endif
596 TS_Installed = FALSE;
597 }
598 }
599
600
601/*---------------------------------------------------------------------
602 Function: TS_ScheduleTask
603
604 Schedules a new task for processing.
605---------------------------------------------------------------------*/
606
607task *TS_ScheduleTask
608 (
609 void ( *Function )( task * ),
610 int rate,
611 int priority,
612 void *data
613 )
614
615 {
616 task *ptr;
617
618#ifdef USE_USRHOOKS
619 int status;
620
621 ptr = NULL;
622
623 status = USRHOOKS_GetMem( &ptr, sizeof( task ) );
624 if ( status == USRHOOKS_Ok )
625#else
626 ptr = malloc( sizeof( task ) );
627 if ( ptr != NULL )
628#endif
629 {
630 if ( !TS_Installed )
631 {
632 status = TS_Startup();
633 if ( status != TASK_Ok )
634 {
635 FreeMem( ptr );
636 return( NULL );
637 }
638 }
639
640 ptr->TaskService = Function;
641 ptr->data = data;
642 ptr->rate = TS_SetTimer( rate );
643 ptr->count = 0;
644 ptr->priority = priority;
645 ptr->active = FALSE;
646
647 TS_AddTask( ptr );
648 }
649
650 return( ptr );
651 }
652
653
654/*---------------------------------------------------------------------
655 Function: TS_AddTask
656
657 Adds a new task to our list of tasks.
658---------------------------------------------------------------------*/
659
660static void TS_AddTask
661 (
662 task *node
663 )
664
665 {
666 LL_SortedInsertion( TaskList, node, next, prev, task, priority );
667 }
668
669
670/*---------------------------------------------------------------------
671 Function: TS_Terminate
672
673 Ends processing of a specific task.
674---------------------------------------------------------------------*/
675
676int TS_Terminate
677 (
678 task *NodeToRemove
679 )
680
681 {
682 task *ptr;
683 task *next;
684 unsigned flags;
685
686 flags = DisableInterrupts();
687
688 ptr = TaskList->next;
689 while( ptr != TaskList )
690 {
691 next = ptr->next;
692
693 if ( ptr == NodeToRemove )
694 {
695 LL_RemoveNode( NodeToRemove, next, prev );
696 NodeToRemove->next = NULL;
697 NodeToRemove->prev = NULL;
698 FreeMem( NodeToRemove );
699
700 TS_SetTimerToMaxTaskRate();
701
702 RestoreInterrupts( flags );
703
704 return( TASK_Ok );
705 }
706
707 ptr = next;
708 }
709
710 RestoreInterrupts( flags );
711
712 return( TASK_Warning );
713 }
714
715
716/*---------------------------------------------------------------------
717 Function: TS_Dispatch
718
719 Begins processing of all inactive tasks.
720---------------------------------------------------------------------*/
721
722void TS_Dispatch
723 (
724 void
725 )
726
727 {
728 task *ptr;
729 unsigned flags;
730
731 flags = DisableInterrupts();
732
733 ptr = TaskList->next;
734 while( ptr != TaskList )
735 {
736 ptr->active = TRUE;
737 ptr = ptr->next;
738 }
739
740 RestoreInterrupts( flags );
741 }
742
743
744/*---------------------------------------------------------------------
745 Function: TS_SetTaskRate
746
747 Sets the rate at which the specified task is serviced.
748---------------------------------------------------------------------*/
749
750void TS_SetTaskRate
751 (
752 task *Task,
753 int rate
754 )
755
756 {
757 unsigned flags;
758
759 flags = DisableInterrupts();
760
761 Task->rate = TS_SetTimer( rate );
762 TS_SetTimerToMaxTaskRate();
763
764 RestoreInterrupts( flags );
765 }
766
767
768#ifdef LOCKMEMORY
769
770/*---------------------------------------------------------------------
771 Function: TS_LockEnd
772
773 Used for determining the length of the functions to lock in memory.
774---------------------------------------------------------------------*/
775
776static void TS_LockEnd
777 (
778 void
779 )
780
781 {
782 }
783
784
785/*---------------------------------------------------------------------
786 Function: TS_UnlockMemory
787
788 Unlocks all neccessary data.
789---------------------------------------------------------------------*/
790
791void TS_UnlockMemory
792 (
793 void
794 )
795
796 {
797 DPMI_UnlockMemoryRegion( TS_LockStart, TS_LockEnd );
798 DPMI_Unlock( TaskList );
799 DPMI_Unlock( OldInt8 );
800 DPMI_Unlock( TaskServiceRate );
801 DPMI_Unlock( TaskServiceCount );
802 DPMI_Unlock( TS_Installed );
803
804#ifndef NOINTS
805 DPMI_Unlock( TS_TimesInInterrupt );
806#endif
807
808#ifdef USESTACK
809 DPMI_Unlock( StackSelector );
810 DPMI_Unlock( StackPointer );
811 DPMI_Unlock( oldStackSelector );
812 DPMI_Unlock( oldStackPointer );
813#endif
814 }
815
816
817/*---------------------------------------------------------------------
818 Function: TS_LockMemory
819
820 Locks all neccessary data.
821---------------------------------------------------------------------*/
822
823int TS_LockMemory
824 (
825 void
826 )
827
828 {
829 int status;
830
831 status = DPMI_LockMemoryRegion( TS_LockStart, TS_LockEnd );
832 status |= DPMI_Lock( TaskList );
833 status |= DPMI_Lock( OldInt8 );
834 status |= DPMI_Lock( TaskServiceRate );
835 status |= DPMI_Lock( TaskServiceCount );
836 status |= DPMI_Lock( TS_Installed );
837
838#ifndef NOINTS
839 status |= DPMI_Lock( TS_TimesInInterrupt );
840#endif
841
842#ifdef USESTACK
843 status |= DPMI_Lock( StackSelector );
844 status |= DPMI_Lock( StackPointer );
845 status |= DPMI_Lock( oldStackSelector );
846 status |= DPMI_Lock( oldStackPointer );
847#endif
848
849 if ( status != DPMI_Ok )
850 {
851 TS_UnlockMemory();
852 return( TASK_Error );
853 }
854
855 return( TASK_Ok );
856 }
857
858#endif