www.digitalmars.com         C & C++   DMDScript  

c++.announce - Preemptive multitasker for DM - mtask2.dm.zip

reply hee fibertel.com.ar writes:
This multitasking library is a port of something I originally wrote for
real-mode DOS with Borland C. Porting it to DM allowed me to break the 640K
barrier and it was an easy and pleasant job due to the excellent compilerīs
features. It was originally written for educational purposes, since I use it for
teaching operating systems and IPC primitives. However, I think now it is
perfectly usable for professional applications.

This software is public domain. Please donīt hesitate to send me any feedback,
whether criticism, bug reports or improvements.


Hugo Etchegoyen
Buenos Aires, Argentina
hee fibertel.com.ar
hetchegoyen hasar.com
Sep 20 2003
next sibling parent "Walter" <walter digitalmars.com> writes:
<hee fibertel.com.ar> wrote in message
news:bkj3vu$ht5$1 digitaldaemon.com...
 This multitasking library is a port of something I originally wrote for
 real-mode DOS with Borland C. Porting it to DM allowed me to break the

 barrier and it was an easy and pleasant job due to the excellent

 features. It was originally written for educational purposes, since I use

 teaching operating systems and IPC primitives. However, I think now it is
 perfectly usable for professional applications.

Great! The documentation is here: www.digitalmars.com/user/mtask.html
Sep 21 2003
prev sibling parent reply "KarL" <someone somewhere.org> writes:
Looks nice.  Certainly better than a couple of method I have seen
before (e.g. J.English, S. Kofoed etc)

Since you are using timer int, it is relatively easier to change the
counter value to achieve something like a task switch of say
1000Hz.  In this way, you can also have  true milliseconds sleep
function.

To set clock rate (real mode) - originally published in Byte.com:

static void setclkrate(unsigned short timerval)
{
    __asm   mov     al, 0x36
    __asm   out     0x43, al
    __asm   mov     ax, timerval
    __asm   out     0x40, al
    __asm   mov     al, ah
    __asm   out     0x40, al
}

Then have a couple of counters to track original 55ms

static unsigned short clkdiv, clkdivh, clkdivl, clkmod;

static void __interrupt clkint()
{

    // Call the clock interrrupt here;;;;

    __asm   mov     ax, clkdivl
    __asm   add     clkmod, ax
    __asm   mov     ax, clkdivh
    __asm   adc     ax, 0
    __asm   jnz     cint8
    __asm   mov     al, 0x20
    __asm   out     020H, al
    __asm   jmp     cint7

cint8:
    (*oldvec)();        // Old clock interrupt

cint7:
    __asm   mov     ax, clkdiv
    __asm   cmp     ax, clkdivl
    __asm   je      exit
    __asm   push    ax
    __asm   call    setclkrate
    __asm   pop     ax
    __asm   mov     clkdivl, ax
    __asm   cmp     ax, 1
    __asm   mov     clkdivh, 0
    __asm   adc     clkdivh, 0
exit: ;
}

<hee fibertel.com.ar> wrote in message news:bkj3vu$ht5$1 digitaldaemon.com...
 This multitasking library is a port of something I originally wrote for
 real-mode DOS with Borland C. Porting it to DM allowed me to break the 640K
 barrier and it was an easy and pleasant job due to the excellent compilerīs
 features. It was originally written for educational purposes, since I use it
for
 teaching operating systems and IPC primitives. However, I think now it is
 perfectly usable for professional applications.

 This software is public domain. Please donīt hesitate to send me any feedback,
 whether criticism, bug reports or improvements.


 Hugo Etchegoyen
 Buenos Aires, Argentina
 hee fibertel.com.ar
 hetchegoyen hasar.com

Sep 21 2003
parent reply Hugo Etchegoyen <Hugo_member pathlink.com> writes:
In article <bklr7g$290s$1 digitaldaemon.com>, KarL says...
Looks nice.  Certainly better than a couple of method I have seen
before (e.g. J.English, S. Kofoed etc)

Since you are using timer int, it is relatively easier to change the
counter value to achieve something like a task switch of say
1000Hz.  In this way, you can also have  true milliseconds sleep
function.

To set clock rate (real mode) - originally published in Byte.com:

static void setclkrate(unsigned short timerval)
{
    __asm   mov     al, 0x36
    __asm   out     0x43, al
    __asm   mov     ax, timerval
    __asm   out     0x40, al
    __asm   mov     al, ah
    __asm   out     0x40, al
}

Then have a couple of counters to track original 55ms

static unsigned short clkdiv, clkdivh, clkdivl, clkmod;

static void __interrupt clkint()
{

    // Call the clock interrrupt here;;;;

    __asm   mov     ax, clkdivl
    __asm   add     clkmod, ax
    __asm   mov     ax, clkdivh
    __asm   adc     ax, 0
    __asm   jnz     cint8
    __asm   mov     al, 0x20
    __asm   out     020H, al
    __asm   jmp     cint7

cint8:
    (*oldvec)();        // Old clock interrupt

cint7:
    __asm   mov     ax, clkdiv
    __asm   cmp     ax, clkdivl
    __asm   je      exit
    __asm   push    ax
    __asm   call    setclkrate
    __asm   pop     ax
    __asm   mov     clkdivl, ax
    __asm   cmp     ax, 1
    __asm   mov     clkdivh, 0
    __asm   adc     clkdivh, 0
exit: ;
}

You are right, Karl, there is a lot to be improved in the handling of the timer interrupt. Certainly a 1 kHz interrupt calling the old vector once every 55 counts would be ideal, since 'timer ticks' would be the same as 'milliseconds', my only concern here is whether the overhead of 1k interrupts per second would not be too much. Probably a configurable time granularity would be best, then you could set it to the best compromise according to your needs and to your hardware. Also trapping the real time interrupt from the application level is not possible right now, since it is trapped in the kernel. It would be nice if the application could install callbacks to be called from the timer interrupt every so many milliseconds, something like the poll feature in Unix. I don't promise dates, but I will try to include these features. If someone does it before, please tell me. Thank you, Hugo.
Sep 22 2003
next sibling parent reply chris elliott <biol75 york.ac.uk> writes:
You should look into the mmtimer of windows, which has a rate of up to 
1ms , is very accurate and can call code in a dll you supply,

chris

Hugo Etchegoyen wrote:

 In article <bklr7g$290s$1 digitaldaemon.com>, KarL says...
 
Looks nice.  Certainly better than a couple of method I have seen
before (e.g. J.English, S. Kofoed etc)

Since you are using timer int, it is relatively easier to change the
counter value to achieve something like a task switch of say
1000Hz.  In this way, you can also have  true milliseconds sleep
function.

To set clock rate (real mode) - originally published in Byte.com:

static void setclkrate(unsigned short timerval)
{
   __asm   mov     al, 0x36
   __asm   out     0x43, al
   __asm   mov     ax, timerval
   __asm   out     0x40, al
   __asm   mov     al, ah
   __asm   out     0x40, al
}

Then have a couple of counters to track original 55ms

static unsigned short clkdiv, clkdivh, clkdivl, clkmod;

static void __interrupt clkint()
{

   // Call the clock interrrupt here;;;;

   __asm   mov     ax, clkdivl
   __asm   add     clkmod, ax
   __asm   mov     ax, clkdivh
   __asm   adc     ax, 0
   __asm   jnz     cint8
   __asm   mov     al, 0x20
   __asm   out     020H, al
   __asm   jmp     cint7

cint8:
   (*oldvec)();        // Old clock interrupt

cint7:
   __asm   mov     ax, clkdiv
   __asm   cmp     ax, clkdivl
   __asm   je      exit
   __asm   push    ax
   __asm   call    setclkrate
   __asm   pop     ax
   __asm   mov     clkdivl, ax
   __asm   cmp     ax, 1
   __asm   mov     clkdivh, 0
   __asm   adc     clkdivh, 0
exit: ;
}

You are right, Karl, there is a lot to be improved in the handling of the timer interrupt. Certainly a 1 kHz interrupt calling the old vector once every 55 counts would be ideal, since 'timer ticks' would be the same as 'milliseconds', my only concern here is whether the overhead of 1k interrupts per second would not be too much. Probably a configurable time granularity would be best, then you could set it to the best compromise according to your needs and to your hardware. Also trapping the real time interrupt from the application level is not possible right now, since it is trapped in the kernel. It would be nice if the application could install callbacks to be called from the timer interrupt every so many milliseconds, something like the poll feature in Unix. I don't promise dates, but I will try to include these features. If someone does it before, please tell me. Thank you, Hugo.

Sep 22 2003
parent "KarL" <someone somewhere.org> writes:
Sorry Chris,  we are talking about DOSX running in embedded systems.

"chris elliott" <biol75 york.ac.uk> wrote in message
news:bkms4k$1r5e$1 digitaldaemon.com...
 You should look into the mmtimer of windows, which has a rate of up to
 1ms , is very accurate and can call code in a dll you supply,

 chris

 Hugo Etchegoyen wrote:

 In article <bklr7g$290s$1 digitaldaemon.com>, KarL says...

Looks nice.  Certainly better than a couple of method I have seen
before (e.g. J.English, S. Kofoed etc)

Since you are using timer int, it is relatively easier to change the
counter value to achieve something like a task switch of say
1000Hz.  In this way, you can also have  true milliseconds sleep
function.

To set clock rate (real mode) - originally published in Byte.com:

static void setclkrate(unsigned short timerval)
{
   __asm   mov     al, 0x36
   __asm   out     0x43, al
   __asm   mov     ax, timerval
   __asm   out     0x40, al
   __asm   mov     al, ah
   __asm   out     0x40, al
}

Then have a couple of counters to track original 55ms

static unsigned short clkdiv, clkdivh, clkdivl, clkmod;

static void __interrupt clkint()
{

   // Call the clock interrrupt here;;;;

   __asm   mov     ax, clkdivl
   __asm   add     clkmod, ax
   __asm   mov     ax, clkdivh
   __asm   adc     ax, 0
   __asm   jnz     cint8
   __asm   mov     al, 0x20
   __asm   out     020H, al
   __asm   jmp     cint7

cint8:
   (*oldvec)();        // Old clock interrupt

cint7:
   __asm   mov     ax, clkdiv
   __asm   cmp     ax, clkdivl
   __asm   je      exit
   __asm   push    ax
   __asm   call    setclkrate
   __asm   pop     ax
   __asm   mov     clkdivl, ax
   __asm   cmp     ax, 1
   __asm   mov     clkdivh, 0
   __asm   adc     clkdivh, 0
exit: ;
}

You are right, Karl, there is a lot to be improved in the handling of the timer interrupt. Certainly a 1 kHz interrupt calling the old vector once every 55 counts would be ideal, since 'timer ticks' would be the same as 'milliseconds', my only concern here is whether the overhead of 1k interrupts per second would not be too much. Probably a configurable time granularity would be best, then you could set it to the best compromise according to your needs and to your hardware. Also trapping the real time interrupt from the application level is not possible right now, since it is trapped in the kernel. It would be nice if the application could install callbacks to be called from the timer interrupt every so many milliseconds, something like the poll feature in Unix. I don't promise dates, but I will try to include these features. If someone does it before, please tell me. Thank you, Hugo.


Sep 22 2003
prev sibling next sibling parent "KarL" <someone somewhere.org> writes:
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

Actually, I do have one.  I modified S Kofoed's original version
available from Nov. 1995 DDJ to include a few features:

1) =B1 1ms resolution sleep(n) function.
2) Real time callback at 1ms.
3) Actived time log.

I found the 1kHz thread switch doesn't cause much overheads.
In fact, if I increase the clock to 5kHz, I get better timing resolution
without compromising speed.  For what I need, my timing
requirements are about 2 to 3 milliseconds.  With the 1kHz
callback, I can drive DAC output or sound chip waveform controls
quite accurately.

It is not pre-emptive but with good coorporative thread design, I
am able to use it to run on embedded PC-104 board beautifully.
It is not DOSX yet. I might port it one day but since you have
done the pre-emptive version, I might leave it as that.

I also have a couple of graphics libraries, serial comms, associated
with that library.

If Walter thinks it is a good idea, I would eventually document them
and put it on my web site for all.  I haven't done so is because I
am too embarass I still don't write good template class. So those
code still has some ugly C stuff.

Here's the header file for the portable thread:
// Thread.h//// Based on S. Kofoed, Doctor Dobb's Journal Nov. 1995//// =
Patched by Kar G Lim to include:////  1. 1 ms Real Time callback Task//  =
2. Sleep function with 1 ms resolution//  3. Active milliseconds lapsed =
record//// Fixed critical section bug
#ifndef THREAD_H#define THREAD_H
#include <setjmp.h>
class Thread {
friend class Queue, Semaphore;
    bool            m_bAllocated;       // used or free    unsigned      =
  m_nBlocksize;       // block size
    void (*m_pThreadFn)(const void *);  // pointer to thread function    =
static void (*sm_pmstask)();        // pointer to millisecond task =
function
    void           *m_pFnArg;           // argument to thread function   =
 unsigned        m_nStackSze;        // requested stack size
    volatile unsigned m_nSleepTime;    volatile unsigned m_nActiveTime;
protected:    jmp_buf         m_jmpb;             // non-local jumpbuf   =
 Thread         *m_pNextThread;      // pointer to next control block    =
Thread         *m_pChain;           // next thread in ready or semaphore =
queue
    static void     schedule();    static void     stackalloc(Thread *, =
unsigned);    static void     timekeeper();
public:
    static void     sleep(unsigned N);  // relinquish executing control =
for next N ms    static unsigned activetime();       // get the number =
of ms current thread has used
    static void     yield();            // Voluntary relinquish =
executing control                                        // to allow =
other thread to execute
    static void     init(unsigned, unsigned);    // To be called once
    // Creates a new Thread with runnable function, initial argument and =
stacksize    static bool     create(void (*)(const void *), void *, =
unsigned);
    // Set real time millisecond task - use NULL to clear millisecond =
task    static void     setmstask(void (*)(void));};
class Queue {      // Simple Queue implementationfriend class =
Thread;protected:    Thread *m_pHead, *m_pTail;          // pointer to =
first and last element
public:    Queue() { m_pHead =3D NULL; }
    void append(Thread *p);
    Thread *getfirst();};
class Semaphore : private Queue       // Simple Semaphore =
implementation{ private:    int             m_nCount;
 public:    Semaphore() { m_nCount =3D 0; }
    void signal();    void wait();};
#endif  // THREAD_H

"Hugo Etchegoyen" <Hugo_member pathlink.com> wrote in message =
news:bkmq3h$1ckf$1 digitaldaemon.com...

 You are right, Karl, there is a lot to be improved in the handling of =

 interrupt.=20
=20
 Certainly a 1 kHz interrupt calling the old vector once every 55 =

 ideal, since 'timer ticks' would be the same as 'milliseconds', my =

 here is whether the overhead of 1k interrupts per second would not be =

 Probably a configurable time granularity would be best, then you could =

 the best compromise according to your needs and to your hardware.=20
=20
 Also trapping the real time interrupt from the application level is =

 right now, since it is trapped in the kernel. It would be nice if the
 application could install callbacks to be called from the timer =

 so many milliseconds, something like the poll feature in Unix.
=20
 I don't promise dates, but I will try to include these features. If =

 it before, please tell me.
=20
 Thank you,
=20
 Hugo.
=20
=20

Sep 22 2003
prev sibling parent reply roland <--rv ronetech.com> writes:
Hi,

Nice, very nice.
I know on DOSX and pure dos mode, tick interrupt can go as fast as 40KHz 
with a reliability of around 2-3 micoseconds.
For that, when the tick is running that fast, the program must _never_ 
go 16 bit and back to 32 bit that means it must _never_ call bios:
- mouse driver must be rewritten 32 bits, or lock mouse interrupt
- keyboard driver must be rewritten 32 bits or lock keyboard interrupt,
- no disk access

I have some "raw" source to deal with the timer, int 8 and int 1ch.
Comments are in french and it may be usable only after a lot of work on it.
Just ask, I can give them and answer precise questions.

Just in case ..

roland
Sep 23 2003
parent reply Hugo Etchegoyen <hee fibertel.com.ar> writes:
Hi everybody

Thanks Karl, Roland and Chris. Iīve been working on your proposals and I 
have a new version of the multitasker (DM 2.01). I think I improved time 
handling substancially by making the following changes:

- The kernel now measures times directly in milliseconds - no more ticks.

- InitMtask() now has two parameters: the timer frequency in 
milliseconds per tick (range 1-55), which is used to program the real 
time counter, and the time slice in milliseconds. If you pass 0 for 
these arguments the default (version 2.00) values are used (55 and 110 
msecs. respectively). Upon program termination the DOS timer frecuency 
(55 msecs.) is restored.

- I added a GetTime() function that returns the elapsed time in 
milliseconds fron MTask initialization.

- I added a DelayUntil() primitive that allows precise timing of 
periodic tasks. I also added a test program (ptime.exe) to show the 
improvement.

- I have not added timer callbacks yet. Precisely timed, high-priority 
periodic tasks offer more or less the same functionality (except that 
they can be defeated by another task getting into non-preemptive mode, 
timer callbacks can only be defeated by disabling interrupts or IRQ 0). 
I will probably add them in 2.02.

- The timer interrupt is trapped by the kernel and cannot be hooked by 
the application, but version 2.00 did not enforce that. In 2.01, if you 
try to trap IRQ 0 you get a Panic().

Walter, I donīt want to clobber the newsgroup with big postings. I got 
the idea that I should post the zip file and the documentation in 
www.digitalmars.com/users and then place a link here, but I donīt know 
how to do that. Please advise.

Hugo.
Sep 24 2003
parent Jan Knepper <jan smartsoft.us> writes:
 Walter, I donīt want to clobber the newsgroup with big postings. I got 
 the idea that I should post the zip file and the documentation in 
 www.digitalmars.com/users and then place a link here, but I donīt know 
 how to do that. Please advise.

Yes, I could setup a user page for you. Thanks! ManiaC++ Jan Knepper
Sep 25 2003