home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 16 / CD_ASCQ_16_0994.iso / maj / swag / isr.swg < prev    next >
Text File  |  1993-05-28  |  19KB  |  1 lines

  1. SWAGOLX.EXE (c) 1993 GDSOFT  ALL RIGHTS RESERVED 00008         ISR HANDLING ROUTINES                                             1      05-28-9313:49ALL                      SWAG SUPPORT TEAM        CLOCK.PAS                IMPORT              21          {$M 2000, 0, 0}                         {  When writing TSR's you have to   }πProgram Clock;                          {  use the $M directive             }πUses Dos;ππ Varπ   oldint: Procedure;ππProcedure Write2scr (s: String; color, x, y: Byte);π Varπ   counter1,π   counter2: Byte;π begin                                             { In TSR's you always }π    counter1 := 2 * (x + y * 80);                  { need to use direct  }π    For counter2 := 1 to ord(s[0]) do              { screen Writes.      }π     begin                                         {                     }π      mem [$b800: counter1] := ord(s[counter2]);π      inc (counter1);π      mem [$b800: counter1] := color;π      inc (counter1);π     end;  {do}ππ end;      {Write2SCR}ππ{$F+}                   { All Procedures will now be Far Procedures }ππProcedure int_Hook; interrupt;πVarπ   hour,                { Where the Hour will be stored }π   min: Word;           { "  "  " " minute "          " }π   hS,                  { Where STR Hour will be stored }π   MS: String[2];       {       STR Min                 }πbeginπ     hour := memW[$0000:$046e];π     min := (memW[$0000:$046c] div 18) div 60;ππ{ The above 2 lines of code give the hour & minute.. How?? The firstπ  memory location gives the hour thats easy, but the minutes are aπ  little more tricky... The interrupt i'm gonna hook into is int 8π  ... it is called approximately 18.2 times/second. When its called,π  it increments 0000:046c hex. When it overflows, it inc's 0000:046eπ  (which is the hour in 24 hr Format) so, dividing by 18 would giveπ  us the approximate second in the hour, div'ding by 60 then givesπ  the hour                                                              }ππ     if hour > 12 then dec(hour, 12);           { Converts from 24 hr Format }π     str(min, MS);π     str(hour, HS);π     Write2scr (HS, 9, 77 - ord(hs[0]), 0);     { Writes to screen }π     Write2scr (MS, 12, 78, 0);π     Write2scr (':',10, 77, 0);π     Inline ($9c);                              { Push the flags ( you have }π     oldint;                                    { to do this beFore any int }π                                                { call like this            }πend;   {inT_HOOK}ππ{$F-}                                           { No more Far Procedures }ππbeginπ  getintvec (8, @oldint);                       { Hooks the interrupt }π  setintvec (8, addr(int_hook));π  keep (0);                                     { Makes it stay resident }πend.ππ                                                         2      05-28-9313:49ALL                      SWAG SUPPORT TEAM        DELAYHK.PAS              IMPORT              10          {πREYNIR STEFANSSONππ> I need a Procedure in the form of:π> Type DelayHook = Function : Boolean;π> Procedure DelayIt(S : Word; Hook : DelayHook);ππ> What it needs to do is keep calling the hook Function Until it returnsπ> True or the number of 1/100th's of seconds, S, is up.ππ> Any ideas?  I know how to call the Hook Function, but I am concerned Withπ> how to go about keeping up With the time Without using the Crt.Delayπ> Procedure. I am using this to play a tune (with Sound and NoSound) throughπ> the speaker and quit when the user presses a key.  The tune is read fromπ> a Text File of unknown length.  HELP!ππ{ More or less outta my head... }ππUsesπ  Dos;ππTypeπ  Reg       : Registers;π  DelayHook : Function : Boolean;ππ{π   This proc Uses the AT BIOS' Wait Function: INT 15h, FUNC 86h. It'sπ   called With a LongInt in CX:DX. Its resolution is ca. 1 microsecond.π}ππProcedure DelayIt(S : Word; Hook : DelayHook);πVarπ  dly : LongInt;π  bdy : Boolean;πbeginπ  Repeatπ    Reg.AH := $86;π    Reg.CX := 0;π    Reg.DX := 10000; { Wait 0.01 sec. }π    Intr($15, Reg);π  Until Hook;πend;π                                                            3      05-28-9313:49ALL                      SWAG SUPPORT TEAM        ISRDEMO.PAS              IMPORT              23          {πIt can make sense to Write Method-ISR's. Two examples:ππ1. Implementation of a timer-IRQ-triggered eventQueue (a body of such anπObject I append to this mail).ππ2. Objects For serial-IO. You can have up to 8 or 16 serial channels (withπspecial hardware). The priciples of encapsulation and instances suggestsπan OOPS-solution! Why not including the ISR-Routine into the Object?πI'm not happy about the intentional misunderstandings to your requestπin this area :-|ππHowever, the next 2K are my act of revenge to your repliers: :-Dπ(Gentlemen: No flames please, I know that the demo itself makes no senseπand procedural Programming For such a simple output would be easierπand shorter!!! All I want to show, is a toRSO of an OOPS-solution For ISR's.)π}ππProgram isrDemo;π{$F+,S-}πUsesπ    Crt,Dos;πTypeπ    timerObj  =    Objectπ                        saveInt,π                        myjob     :    Pointer;π                        stopped   :    Boolean;π                        Constructor Init(job:Pointer);π                        Destructor  DeInit;π                        PRIVATEπ                        Procedure   timerInt;π                   end;πConstπ    timerSelf :    Pointer   =    NIL;ππConstructor timerObj.Init(job:Pointer);πbeginπ    if timerSelf<>NIL then FAIL;            { only one instance in this demo }π    timerSelf:=@self;π    myjob:=job;π    stopped:=False;π    getintvec($1C,saveInt);π    setintvec($1C,@timerObj.timerInt);πend;ππDestructor timerObj.DeInit;πbeginπ    setintvec($1C,saveint); timerSelf:=NIL;πend;ππProcedure timerObj.timerInt; Assembler;πLabelπ    _10;πAsmπ    pop  bp                           { Compiler inserts PUSH BP - restore it }π    push axπ    push bxπ    push cxπ    push dxπ    push siπ    push diπ    push dsπ    push esπ    push bpπ    mov  al,20h                        { send EOI }π    out  20h,alπ    stiππ    mov  bp,spπ    mov  ax,SEG @DATAπ    mov  ds,axπ    les  di,[offset timerSelf]         { only one instance in this demo! }π    cmp  es:[di+stopped],0             { prevents IRQ-overruns           }π    jne  _10π    inc  es:[di+stopped]π    call dWord ptr es:[di+offset myjob]; { no test of NIL implemented    }π    les  di,[offset timerSelf]π    dec  es:[di+stopped]ππ   _10:π    call dWord ptr es:[di+saveInt]     { call original inT-Proc }π    mov  sp,bpπ    pop  bpπ    pop  esπ    pop  dsπ    pop  diπ    pop  siπ    pop  dxπ    pop  cxπ    pop  bxπ    pop  axπ    iretπend;π(*********************** DemoShell **************************)πVarπ    timer     :    timerObj;ππProcedure helloHerb;πbeginπ    Write('.');πend;ππbeginπ    if timer.Init(@helloHerb) thenπ    beginπ         Delay(5000);π         timer.DeInit;π    end;πend.ππ                                                                                                          4      05-28-9313:49ALL                      SWAG SUPPORT TEAM        ISRDEMO1.PAS             IMPORT              12          {π│- Also, is there anyway of making "HOT-KEYS" without using ReadKey f│π│  CharS?  I want it For Integers or can I have CharS as a RANdoM #? │π│  PROBLEMO!                                                         │ππ> Unless you want to Write an ISR (initiate and stay resident) routine   │π> that traps keyboard interrupts and either preprocesses them or passes  │π> them on to your routine, ReadKey is the only way. (Writing an ISR      │π> is not a simple task.)                                                 │ππActualy it is not that difficult in pascal:π}πUsesπ  Dos;ππConstπ  end_eks : Boolean = False;ππVarπ  IntVec  : Pointer;ππProcedure Keybd; Interrupt;πVarπ  Key : Byte;πbeginπ  Asmπ    cliπ  end;π  Key := Port[$60];ππ  Case Key ofπ    1   : end_eks := True;π    57  : Writeln(' You have pressed Space');π    75  : Writeln(' Left Arrow');π    77  : Writeln(' Right Arrow');π    203,π    205 : Writeln(' You have released an Arrow key');π  end;ππ  if not end_eks thenπ  Asmπ    mov ah,0chπ    int 21hπ    call IntVec  { Call original int 9 handler }π  end;π  { port[$20]:=$20} { if you dont call the original handlerπ                      you need to uncomment this }πend;ππbeginπ  GetIntVec($09,Intvec);π  SetIntVec($09,@Keybd);π  Writeln(' Press <ESC> to end Program ');ππ  Repeat Until end_eks;ππ  SetIntVec($09,IntVec);ππ  Writeln(' Program terminatet');ππend.                               5      05-28-9313:49ALL                      SWAG SUPPORT TEAM        ISRINFO.PAS              IMPORT              17          { What is an ISR?? Are there several things you have to know to createπ one in Pascal?? Thanks.ππ        ISR stands For interrupt service routine (I think; Hey, Iπ        just remember the abbriveation) :) But what it does isπ        changes an interrupt vector to the address of a routineπ        of yours then, your routine calls the actual interrupt code.ππ        In the next message, I'll post some heavily commented codeπ        that is a time TSR, But what is a TSR? Just a residentπ        ISR. (By the way, The TSR screws up Blue Wave when resident)ππ        ---=== Extremely simplified version of how an ISR works ===---ππ        Assuming you know what an interrupt is (You called it a hardwareπ        command) ... When you call an interrupt (TP: Intr, Asm: int) theπ        CPU stops what its doing and calls up a routine at a certainπ        memory address (Which is called the interrupt's vector). Youπ        can get the address of the routine by using GETinTVEC. Nowπ        if you have this codeπ}π          Uses Dos;π          Varπ             the_inTERRUPT: Procedure;π          beginπ             getintvec (--Interrupt num--, @the_inTERRUPT);π          end.π{π        it will store the vector of the interrupt into @the_interruptπ        (if you dont know what a Pointer is, go back to the manual andπ        read the section on them)π        So, Everytime you call the_inTERRUPT it will actually call whatπ        ever interrupt you made the_interrupt point to. on the sameπ        note SETinTVEC (--int num--, @your_Routine) will set it whereπ        when ever you call that interrupt it will execute your routine.ππ        What the ISR does is gets the vector of the interrupt youπ        want to 'Latch' onto, puts it into a Procedure (As shownπ        above) then, Uses SETinTVEC to set the ISR routine insideπ        that interrupt. The ISR routine then calls the Procedureπ        that points to the old interrupt.π}                                                                                                        6      05-28-9313:49ALL                      SWAG SUPPORT TEAM        MYCHECK.PAS              IMPORT              31          {$ifDEF VER70}π{$A+,B-,D+,E-,F-,G+,I-,L+,N-,O-,P-,Q-,R-,S+,T-,V-,X+}π{$else}π{$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,R-,S+,V-,X-}π{$endif}π{$DEFinE COLor}πUnit MyCheck;π{π  Version: 2.0 (8 jan 1993).ππ             TeeCee     Bob Swart  Saved:π  Code size: 514 Bytes  472 Bytes  42 Bytesπ  Data size:  32 Bytes   32 Bytes   0 Bytesππ  Here is the $1C ISR that I will add (unless you wish to do that).ππ  Some changes were made, which resulted in less code and data size, aπ  little more speed, and display of the progress Variable on screen isπ  made 'ticking' each second by changing the colour from white on blueπ  to gray on blue and back With each update.π  Also, the Variable Test8086 is set to 0 when the ISR in entered, andπ  reset to the value Save8086 (initialized at startup) on Exit. Herebyπ  we elimiate potential BTP7 problems With using LongInts in ISRs, andπ  not saving Extended Registers properly.π}πInterfaceππVar progress: LongInt Absolute $0040:$00F0;ππImplementationπ{ Everything is private to this Unit }πUses Dos;ππConstπ  Line      = 0;    { Change as required For position of display on screen }π  Column    = 72;                                 { top left corner is 0,0 }π  ScreenPos = (line * 80 * 2) + (column * 2);π  Colour: Byte = $1F;                                 { White/Gray on Blue }ππTypeπ  TimeStr = Array[0..15] of Char;π  TimePtr = ^TimeStr;ππVarπ  {$ifDEF COLor}π  Time: TimeStr Absolute $B800:ScreenPos;  { Assume colour display adaptor }π  {$else}π  Time: TimeStr Absolute $B000:ScreenPos; { otherwise mono display adaptor }π  {$endif}π  OldInt1C: Pointer;π  ExitSave: Pointer;π  Save8086: Byte;πππ{$F+}πProcedure Int1CISR; Interrupt;π{ This will be called every clock tick by hardware interrupt $08 }πConst DisplayTickCount = 20;π      TickCount: LongInt = DisplayTickCount;π      HexChars: Array[$0..$F] of Char = '0123456789ABCDEF';πVar HexA: Array[0..3] of Byte Absolute progress;πbeginπ  {$ifDEF VER70}π  Test8086 := 0;π  {$endif}π  Asmπ    cliπ  end;π  inc(TickCount);π  if TickCount > DisplayTickCount then { ticks to update the display }π  beginπ    TickCount := 0;        { equality check and assignment faster than mod }π            { The following statements actually display the on-screen time }π    Colour := Colour xor $08;        { Swap between white and gray on blue }π    FillChar(Time[1],Sizeof(Time)-1,Colour);π    Time[00] := HexChars[HexA[3] SHR 4];π    Time[02] := HexChars[HexA[3] and $F];π    Time[04] := HexChars[HexA[2] SHR 4];π    Time[06] := HexChars[HexA[2] and $F];π    Time[08] := HexChars[HexA[1] SHR 4];π    Time[10] := HexChars[HexA[1] and $F];π    Time[12] := HexChars[HexA[0] SHR 4];π    Time[14] := HexChars[HexA[0] and $F]π  end { if TickCount > DisplayTickCount };π  Asmπ    stiπ    pushf                                  { push flags to set up For IRET }π    call  OldInt1C                              { Call old ISR entry point }π  end;π  {$ifDEF VER70}π  Test8086 := Save8086π  {$endif}πend {Int1CISR};π{$F-}πππProcedure ClockExitProc; Far;π{ This Procedure is VERY important as you have hooked the timer interrupt  }π{ and thereFore if this is omitted when the Unit is terminated your        }π{ system will crash in an unpredictable and possibly damaging way.         }πbeginπ  ExitProc := ExitSave;π  SetIntVec($1C,OldInt1C);               { This "unhooks" the timer vector }πend {ClockExitProc};πππbeginπ  progress := 0;π  {$ifDEF VER70}π  Save8086 := Test8086;π  {$endif}π  ExitSave := ExitProc;                          { Save old Exit Procedure }π  ExitProc := @ClockExitProc;                 { Setup a new Exit Procedure }π  GetIntVec($1C,OldInt1C);              { Get old timer vector and save it }π  SetIntVec($1C,@Int1CISR);   { Hook the timer vector to the new Procedure }πend.π                                                                        7      05-28-9313:49ALL                      SWAG SUPPORT TEAM        RUNINBCK.PAS             IMPORT              10          {π> How do you have a Procedure running Constantly While others things areπ> happening??ππWell, to have them run at the exact same time isn't possible. You can swapπback and forth pretty quick though. The basic idea is that when theπcomputer is idle (waiting For a key, displaying a Text File, etc) you haveπit jump to your routine.π}πProgram Test1;π(* This will wait For a key and display the time *)πUsesπ  Dos, Crt;ππProcedure WriteTime;πVarπ  CurX,π  CurY,π  CurA  : Byte;π  H, M,π  S, MS : Word;πbeginπ  CurX := WhereX;π  CurY := WhereY;π  CurA := TextAttr;π  TextColor(7);π  GotoXy(60, 1);π  GetTime(H, M, S, MS);π  Write(H, ':', M, ':', S, '.', MS);π  TextAttr := CurA;π  GotoXy(CurX, CurY);πend;ππ{ Uncomment this For Keyboard IDLE Demo }πVar Ch : Char;π    Done : Boolean;πbeginπ  Repeatπ    Repeatπ      WriteTimeπ    Until KeyPressed;π    Ch := ReadKey;π    Done := (Ch = #27);π  Until Done;πend.ππ{ Uncomment this For TextFile IDLE Demo }π{πVar T : Text;π    Ts : String;πbeginπ  Assign(T,'BBS.NFO');π  Reset(T);π  While Not Eof(T) Do beginπ    ReadLn(T,Ts);π    WriteTime;π    WriteLn(Ts);π  end;π  Close(T);πend.π}                        8      05-28-9313:49ALL                      SWAG SUPPORT TEAM        WATCHDOG.PAS             IMPORT              24           BC> I have the desire to create a Type of "watch dog" Procedure For a Programπ BC> I am writing. It has become increasingly attractive For me to create thisπ BC> Procedure to activate at certain times every hour - i.e. at 15 past and 45π BC> past the hour.ππPerhaps if you insert this code into your Program. It works With the ISR 1Cπcalled by the timerinterrupt 8 periodically. For testing reasons it calls theπbatchFile at 15 and 45 seconds. Change finally the line "if(s=15) or (s=45)" toπ"if (m=15) or (m=45)" to test the minutes.ππCode written in TP6.0 :ππ{$M 16384,0,16384} (*Adjust to your requirements*)πUses Dos,Crt;ππConstπ  WDTryTime = 18; (*18 ticks = about every second*)π  BatchFileName = 'woufwouf.bat'; (*BatchFile to start*) <<<your BatchFilenameπVarπ  WDCount : Integer;π  WDBusy,WdEvent : Boolean;π  WDSave1c,WDSaveExit: Pointer;π  DosReentrant : ^Byte;π  IntVec : Array[0..255] of Pointer Absolute 0:0;ππProcedure STI;πInline($FB);ππProcedure CLI;πInline($FA);ππ(*Do not use Turbo-Pascal I/O Routines in here.*)π(*Be also sure not to use GetTime/Date at the same time as this routine*)ππProcedure WatchDog;interrupt;πVar h,m,s,c : Word;πbeginπ  if not WDBusy thenπ  beginπ    WDBusy:=True;π    inc(WDCount);π    if DosReentrant^=0 then      (*No Dos op's in work ?*)π      if WDCount>=WdTryTime then (*Test only every second to prevent*)π      begin                      (*big loss in perFormance.        *)π        WDCount:=0;π        GetTime(h,m,s,c);              (*Get Time*)ππhere>>> if(s=15) or (s=45) then (*Call on 15 minutes and 45 minutes*)ππ        beginπ          Cli;IntVec[$1c]:=WDSave1C;Sti;π          Port[$20]:=$a0;(*Report TimerInt finished to Interrupt-contr.*)π          Port[$20]:=$20;π          SwapVectors;      (*Execute COMMand.COM + batchFile*)π          Exec(GetEnv('COMSPEC'),'/C '+batchFilename);π          SwapVectors;π          Cli;IntVec[$1c]:=@Watchdog;Sti;π          WDEvent:=True;π        end;π      end;π    WDBusy:=False;π  end;πend;ππProcedure WDRemove;πbeginπ  SetIntVec($1C,WDSave1c); (*Restore old 1c routine*)π  ExitProc:=WDSaveExit;π  Writeln('Exiting Watchdog');π  Halt(Exitcode);πend;ππProcedure WDInstall;πVar Regs:Registers;πbeginπ  With Regs doπ  beginπ    ah:=$34;π    MsDos(regs);π    DosReentrant:=ptr(es,bx); (*Get Reentrant Pointer*);π  end;π  WdCount:=0;π  WDBusy:=False;π  WDEvent:=False;π  GetIntVec($1C,WDSave1C); (*Save old 1c routine*)π  SetIntVec($1C,@Watchdog); (*Assign Watchdog to int 1c*)π  WDSaveExit:=ExitProc;π  ExitProc:=@WDRemove;π  Writeln('Watchdog installed');πend;ππVar c:Char;ππbeginπ  WDInstall;π  Repeat (*Example Main Loop*)π    Write('Hello');π    if WDEvent then  (*Watch sign of Watchdog*)π    beginπ      Writeln;Writeln('Event occured');π      WDEvent:=False;π    end;π    Delay(1000);π  Until KeyPressed;π  c:=ReadKey;πend.π