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.π