[Back to MISC SWAG index] [Back to Main SWAG index] [Original]

    {
     by Sean L. Palmer
     Public Domain

     This is a 'multitasking' Program in the sense that it hooks into
     the timer interrupt, but what that interrupt ends up actually
     doing is controlled by the current value in SaveAdr, which
     changes With each interrupt as the routine passes control back
     to the tick handler not by Exiting normally, but by an explicit
     transfer of control.
     The end result of this is that you can Write a state-driven
     interrupt handler
     The included example is RealLY simplistic, and barely tested.
     I intend to use this to Write a comm port driver that
     parses the incoming data as it receives it which would
     be nice in a communications Program that shells to Dos, as
     the incoming Chars could be saved to disk in the background
     With buffered ZModem or something...
    }

    Program intTest;

    Uses
      Dos;

    Var
      saveAdr : Word;  {offset in this code segment of where we are now}
      active  : Boolean;  {to avoid re-entrancy}

    Procedure intHandler; Far; Assembler;
    Asm
      pusha
      mov  ax, seg @DATA
      mov  ds, ax

      {anything you need to do before continuing (reading port data?), do here}

      in   al, $61  {click speaker as an example}
      xor  al, 2
      out  $61, al

      test active, $FF  {exit now if interrupted ourselves}
      jz   @OK
      popa
      iret

     @OK:
      inc Byte ptr active
      sti
      jmp [saveAdr]  {near jump to continue where handler last left off}
    end;

    {call this Procedure from StateHandler to suspend execution Until next time}

    Procedure wait; near; Assembler;
    Asm {wait For next interrupt}
      pop Word ptr saveAdr  {save where to continue next time}
      dec Byte ptr active
      popa                  {restore caller regs}
      iret
    end;

    Const
      c : Char = '.';

    Procedure stateHandler;
    begin
    {
     a stateHandler Procedure should never ever Exit (only by calling 'wait'),
     shouldn't have any local Variables or parameters, and shouldn't call
     'wait' With anything on the stack (like from a subroutine).
     This routine is using the caller's (interrupted Program's) stack, so be
     very very careful}

     Asm
       pop bp  {clean up stack mess left by Turbo's Procedure header}
     end;
     {^ alternative method here is to init saveAdr to offset(proc)+3 and skip
      the push bp; mov bp,sp altogether}

      Repeat  {this is an example only}
        c := '@';
        wait;
        c := '.';
        wait;
      Until False;                {don't let it return normally!!}
    end;

    Var
      oldHook : Procedure;
      i       : Integer;

    begin
      saveAdr := ofs(stateHandler);
      getIntVec($1C, @oldHook);
      setIntVec($1C, @intHandler);
      For i := 1 to 1500 do
        Write(c);
      setIntVec($1C, @oldHook);
    end.

[Back to MISC SWAG index] [Back to Main SWAG index] [Original]