High Speed IRQ Timer/Clock in C

I found this excellent High Speed Timer (in Pascal). I compiled it (using Turbo Pascal 7 and it runs fine):

http://www.sorucevap.com/bilisimteknolojisi/programcilik/pascal/ders.asp?207995
and same High Speed Timer here too:
http://groups.google.com/group/comp.lang.pascal/browse_thread/thread/92e9398f16c10ba4/e67ff3cf587648ef?lnk=st&q=inline(%24CD)+inline(%241C)+inline(%249C)&rnum=1&hl=en#e67ff3cf587648ef

I converted it to C (using p2c), compiled it using Borland C++ 4.5 and it runs. But it crashes when it gets to setvect(...) in TimerOn.

Does anyone know how IRQ programming works in C? If you know IRQ timer/clock please contact me. I need urgent help in understanding why it's not working.

Comments

  • Here is the converted code to C. It's suppose to cycle through NewInt08, NewInt1C repeatedly. Yes, behaviour of the program is erratic, sometimes it prints out about 20 NewInt08, NewInt1C before it crashes, other times it crashes straight away. I just want someone who has done IRQ programming before to see if they can spot any mistakes am making.
    [code]
    #define MaxRate 1193180L


    void interrupt(__far *OldInt08)();//Static _PROCEDURE OldInt08, OldInt1C;
    void interrupt(__far *OldInt1C)();
    Static unsigned short IntCount08, Trigger;
    Static boolean TimerAlreadySet;
    Static unsigned short Frequency;

    void GetIntVec(int a, void interrupt(__far *b)())
    {
    b = getvect(a);
    }

    void SetIntVec(int a, void interrupt(__far *b)())
    {
    setvect(a, b);
    }


    Static Void IrqOn()
    {
    asm{sti;}//asm(" inline $FB");
    }


    Static Void IrqOff()
    {
    asm{cli;}//asm(" inline $FA");
    }

    /*$F+*/
    void interrupt NewInt1C()//Static Void NewInt1C()
    {printf("
    NewInt1C()");
    ClockTicks++;
    }

    /*$F-*/

    /*$F+*/
    void interrupt NewInt08()//Static Void NewInt08()
    {printf("
    NewInt08()");
    IrqOff();
    asm{int 1Ch;}//asm(" inline $CD");
    //asm(" inline $1C"); /*Generate INT 1Ch instruction to call interrupt 1Ch*/
    if (IntCount08 == Trigger) {
    IntCount08 = 0;
    asm{pushf;}//asm(" inline $9C");
    OldInt08();/*if (OldInt08.link != NULL)
    (*(Void(*) PP((Anyptr _link)))OldInt08.proc)(OldInt08.link);
    else
    (*(Void(*) PV())OldInt08.proc)();*/
    } else
    IntCount08++;
    outportb( 0x20, 0x20 );//PORT(0x20) = 0x20; /*Sends non-specific EOI to the PIC*/
    IrqOn();
    }


    /*$F-*/

    Void TimerOn(Freq)
    long Freq;
    {
    LONGINT Temp = MaxRate;
    unsigned short Count;
    _PROCEDURE TEMP1;
    printf("
    TimerOn()");
    if (TimerAlreadySet)
    return;
    ClockTicks = 0;
    IntCount08 = 0;
    Frequency = Freq;
    Trigger = (long)(Freq / 18.2);
    Temp = (long)((double)Temp / Freq);
    Count = Temp;
    GetIntVec(0x8, OldInt08);
    TEMP1.proc = (Anyptr)NewInt08;
    TEMP1.link = (Anyptr)NULL;
    SetIntVec(0x8, NewInt08);//SetIntVec(0x8, TEMP1);
    GetIntVec(0x1c, OldInt1C);
    TEMP1.proc = (Anyptr)NewInt1C;
    TEMP1.link = (Anyptr)NULL;
    SetIntVec(0x1c, NewInt1C);//SetIntVec(0x1c, TEMP1);
    outportb( 0x43, 0xb6);
    outportb( 0x40, Count & 255);
    outportb( 0x40, Count >> 8);
    TimerAlreadySet = true;
    }


    Void TimerOff()
    {printf("
    TimerOff()");
    if (!TimerAlreadySet)
    return;
    outportb( 0x43, 0xb6);
    outportb( 0x40, 0xff);
    outportb( 0x40, 0xff);
    SetIntVec(0x8, OldInt08);
    SetIntVec(0x1c, OldInt1C);
    TimerAlreadySet = false;
    }


    Void ResetTimer()
    {
    ClockTicks = 0;
    }


    double TimeElapsed()
    {
    return ((double)ClockTicks / Frequency);
    }


    void _Timer_init()
    {
    static int _was_initialized = 0;
    if (_was_initialized++)
    return;
    TimerAlreadySet = false;
    }

    void main()
    {
    int i;
    _Timer_init();

    TimerOn(546);
    for(i=0; i<100000; i++) {
    if (i%10000==0)
    printf("
    ... %d", i);
    }
    TimerOff();

    }
    [/code]
  • The thing is, it worked perfectly from a Pascal compiled source. But from C, I get this error:

    16 bit MS-DOS Subsystem (DIALOG)
    C:TIMER imer.exe
    The NTVDM CPU has encountered an illegal instruction.
    CS:0008 IP:08f1 OP:0f 00 74 01 c3 Choose 'Close' to terminate the application.
    ----------------

    Surely if one is allowed to do IRQ programming in Pascal and XP allows it executed 100%, then it should also work under C too.

    Am just looking for oldskool experienced C IRQ programmers who is familiar with this timer code.
  • : Here I converted to C but it crashes:
    : [code]:
    : #include "p2c.h"
    : #include "extra.h"
    :
    : #define TIMER_G
    : #include "timer.h"
    :
    :
    :
    : #define MaxRate 1193180L
    :
    :
    : void interrupt(__far *OldInt08)();//Static _PROCEDURE OldInt08, OldInt1C;
    : void interrupt(__far *OldInt1C)();
    : Static unsigned short IntCount08, Trigger;
    : Static boolean TimerAlreadySet;
    : Static unsigned short Frequency;
    :
    :
    : Static Void IrqOn()
    : {
    : /* p2c: timer1.pas, line 37:
    : * Note: Inline assembly language encountered [254] */
    : asm{sti;}//asm(" inline $FB");
    : }
    :
    :
    : Static Void IrqOff()
    : {
    : /* p2c: timer1.pas, line 40:
    : * Note: Inline assembly language encountered [254] */
    : asm{sti;}//asm(" inline $FA");
    : }
    : /* p2c: timer1.pas, line 43: Note: Ignoring INTERRUPT keyword [258] */
    :
    :
    : /*$F+*/
    : void interrupt NewInt1C()//Static Void NewInt1C()
    : {
    : ClockTicks++;
    : }
    : /* p2c: timer1.pas, line 50: Note: Ignoring INTERRUPT keyword [258] */
    :
    :
    : /*$F-*/
    :
    : /*$F+*/
    : void interrupt NewInt08()//Static Void NewInt08()
    : {
    : IrqOff();
    : /* p2c: timer1.pas, line 53:
    : * Note: Inline assembly language encountered [254] */
    : asm{int 1Ch;}//asm(" inline $CD");
    : //asm(" inline $1C"); /*Generate INT 1Ch instruction to call interrupt 1Ch*/
    : if (IntCount08 == Trigger) {
    : IntCount08 = 0;
    : /* p2c: timer1.pas, line 57:
    : * Note: Inline assembly language encountered [254] */
    : asm{pushf;}//asm(" inline $9C");
    : /*if (OldInt08.link != NULL)
    : (*(Void(*) PP((Anyptr _link)))OldInt08.proc)(OldInt08.link);
    : else
    : (*(Void(*) PV())OldInt08.proc)();*/
    : } else
    : IntCount08++;
    : outp( 0x20, 0x20 );//PORT(0x20) = 0x20; /*Sends non-specific EOI to the PIC*/
    : /* p2c: timer1.pas, line 64: Note: Reference to PORT [191] */
    : /* p2c: timer1.pas, line 64: Warning: Invalid assignment [168] */
    : IrqOn();
    : }
    :
    :
    : /*$F-*/
    :
    : Void TimerOn(Freq)
    : long Freq;
    : {
    : LONGINT Temp = MaxRate;
    : unsigned short Count;
    : _PROCEDURE TEMP1;
    :
    : if (TimerAlreadySet)
    : return;
    : ClockTicks = 0;
    : IntCount08 = 0;
    : Frequency = Freq;
    : Trigger = (long)(Freq / 18.2);
    : Temp = (long)((double)Temp / Freq);
    : Count = Temp;
    : GetIntVec(0x8, OldInt08);
    : TEMP1.proc = (Anyptr)NewInt08;
    : TEMP1.link = (Anyptr)NULL;
    : /* p2c: timer1.pas, line 83:
    : * Warning: Symbol 'GETINTVEC' is not defined [221] */
    : SetIntVec(0x8, NewInt08);//SetIntVec(0x8, TEMP1);
    : /* p2c: timer1.pas, line 84:
    : * Warning: Symbol 'SETINTVEC' is not defined [221] */
    : GetIntVec(0x1c, OldInt1C);
    : TEMP1.proc = (Anyptr)NewInt1C;
    : TEMP1.link = (Anyptr)NULL;
    : /* p2c: timer1.pas, line 85:
    : * Warning: Symbol 'GETINTVEC' is not defined [221] */
    : SetIntVec(0x1c, NewInt1C);//SetIntVec(0x1c, TEMP1);
    : /* p2c: timer1.pas, line 86:
    : * Warning: Symbol 'SETINTVEC' is not defined [221] */
    : outp( 0x43, 0xb6);
    : /* p2c: timer1.pas, line 87: Note: Reference to PORT [191] */
    : /* p2c: timer1.pas, line 87: Warning: Invalid assignment [168] */
    : outp( 0x40, Count & 255);
    : /* p2c: timer1.pas, line 88: Note: Reference to PORT [191] */
    : /* p2c: timer1.pas, line 88: Warning: Invalid assignment [168] */
    : outp( 0x40, Count >> 8);
    : /* p2c: timer1.pas, line 89: Note: Reference to PORT [191] */
    : /* p2c: timer1.pas, line 89: Warning: Invalid assignment [168] */
    : TimerAlreadySet = true;
    : }
    :
    :
    : Void TimerOff()
    : {
    : if (!TimerAlreadySet)
    : return;
    : outp( 0x43, 0xb6);
    : /* p2c: timer1.pas, line 98: Note: Reference to PORT [191] */
    : /* p2c: timer1.pas, line 98: Warning: Invalid assignment [168] */
    : outp( 0x40, 0xff);
    : /* p2c: timer1.pas, line 99: Note: Reference to PORT [191] */
    : /* p2c: timer1.pas, line 99: Warning: Invalid assignment [168] */
    : outp( 0x40, 0xff);
    : /* p2c: timer1.pas, line 100: Note: Reference to PORT [191] */
    : /* p2c: timer1.pas, line 100: Warning: Invalid assignment [168] */
    : SetIntVec(0x8, OldInt08);
    : /* p2c: timer1.pas, line 101:
    : * Warning: Symbol 'SETINTVEC' is not defined [221] */
    : SetIntVec(0x1c, OldInt1C);
    : /* p2c: timer1.pas, line 102:
    : * Warning: Symbol 'SETINTVEC' is not defined [221] */
    : TimerAlreadySet = false;
    : }
    :
    :
    : Void ResetTimer()
    : {
    : ClockTicks = 0;
    : }
    :
    :
    : double TimeElapsed()
    : {
    : return ((double)ClockTicks / Frequency);
    : }
    :
    :
    : void _Timer_init()
    : {
    : static int _was_initialized = 0;
    : if (_was_initialized++)
    : return;
    : TimerAlreadySet = false;
    : }
    : /* p2c: Note: Remember to call _Timer_init() in main program [215] */
    :
    :
    :
    : /* End. */
    :
    : void main()
    : {
    : _Timer_init();
    : }
    :
    : [/code]:
    :
    It is not possible to change a procedure in a method, and then use it as a callback function. Callback functions always have very specific signatures, and any deviation from that will cause the code to fail.
    [code]
    SetIntVec($08,Addr(NewInt08));
    [/code]
    is not the same as
    [code]
    SetIntVec(0x8, NewInt08);
    [/code]
    since the first gives the pointer to the entry point of NewInt08() to the IntVec, while the latter gives the entry point itself (or perhaps the result of) NewInt08() to the IntVec.
    Also where is the call to the OldInt08()?
  • I found the closest Turbo C DOS timer source code written by David Oshinsky at:

    http://www.bookcase.com/library/software/msdos.devel.apps.turbo-c.html (TIMERTST)

    To get my own timer (original Pascal port) I had to comment out asm{ int 1Ch;} & asm{pushf;} in NewInt08() function. Also I had to omit all debugging printfs in the interrupt functions. Afterwards my program doesn't crash anymore.

    What am wondering is:

    1. In Pascal version it uses inline($CD / $1C); & inline($9C); before the OldInt08() call. I though I could call the equivalent asm{ int 1Ch;} & asm{pushf;} but I guess this was wrong since it crashed my program. Is there an inline assembly call in C?

    2. David's initializing the timer code uses
    [code]
    /* Set up 8259 PIC chip to allow INT0 interrupt. */
    outportb(0x21, inportb(0x21) & 0xfe);

    /* issue command to 8253: counter 0, binary counter, rate generator */
    /* (mode 2), load least significant byte of counter followed by */
    /* most significant byte */
    outportb(0x43, 0x34);

    /* Timer is set for 0x4cd * 813.8 ns = 1 ms (LSB followed by MSB). */
    outportb(0x40, 0xcd); /* least significant byte of timer count */
    outportb(0x40, 0x04); /* most significant byte of timer count */
    [/code]
    But mine uses
    [code]
    outportb( 0x43, 0xb6);
    outportb( 0x40, Count & 255);
    outportb( 0x40, Count >> 8);
    [/code]
    What is the difference? Even though both works.

    3. Same goes for the clean up code:
    His is:
    [code]
    /* restore 8253 to original state set during PC boot */
    /* NOTE: this program leaves 8259 mask register with */
    /* least significant bit clear (i.e., INT0 enabled). */
    outportb(0x43, 0x34);
    outportb(0x40, 0);
    outportb(0x40, 0);
    [/code]
    Mine is:
    [code]
    outportb( 0x43, 0xb6);
    outportb( 0x40, 0xff);
    outportb( 0x40, 0xff);
    [/code]
    Again both works but I don't understand why different?
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories