
{$R+,S+,I+,N+,E+} {$IFDEF VER70} {$Q+,G-} {$ENDIF}
(* Compiled with TurboPascal 5.00, 7.01 for 8086 without 8087 *)
{$M 4096, 0, 0}
program INT70AT2 ;
(* DEMO of use of programmable interrupt timer in PC-AT -
  remember also the 8253 at $40. *)
(* J R Stockton, www.merlyn.demon.co.uk *)

uses Dos, Crt ;

const
RTCsel = $70 { RTC chip base address in I/O space} ;
RTCdat = RTCsel+1 ;
RegA = $0A ;
RegB = $0B ;
RegC = $0C ;

AT_int = $70 ;

IC1 = $20 { Interrupt Controller Chip 1 base address in I/O space} ;
IC2 = $A0 { Interrupt Controller Chip 2 base address in I/O space - AT only} ;

PIE = $40 ;

var
JJ : longint ;

procedure PIintHandler{(Flags,cs,ip,ax,bx,cx,dx,si,di,ds,es,bp : word)} ;
{ Int $70 on PC-AT only; "1024Hz"; 1024*42/43÷1000.186 }
interrupt ;
const Q : byte = 21 ; EOI = $20 ;
var TV : byte ;
begin
  Port[RTCsel] := RegC ; TV := Port[RTCdat] { read polls/clears flags } ;
  Inc(Q) ; if Q>42 then Q := 0 else Inc(JJ) { Adjust 1024 to nigh 1000 } ;
  Port[IC2+0] := EOI ;
  Port[IC1+0] := EOI ;
  end {PIH} ;
{ Needs Bit 6 in Reg B of RTC chip to have been set }
{ Needs 8259(s) to have been enabled on right bit(s) }

var SavePIvec, ExitSave : pointer ; XP : byte ;


{$F+} procedure PIoff {$F-} ;
var xxx: byte;
begin Writeln(' PI off') ;
  Port[RTCsel] := RegB ; xxx := Port[RTCdat];
  Port[RTCsel] := RegB; Port[RTCdat] := xxx and not PIE ;
  Port[IC2+1] := XP ;
  SetIntVec(AT_int, SavePIvec) ;
  ExitProc := ExitSave ;
  Write(' <CR>') ; Readln ;
  end {PIoff} ;



const Rate = $26 { not $22 even on T486/33!; $23=8192Hz .. $2F=2Hz } ;
procedure PIon { HD146818-type RTC + CMOS RAM does this } ;
{ Rate $26 = 38 for 1024Hz : MSB is UIP bit : figs on PPC640 :
  ?  first field B6..B4=DV2..DV0 - freq 2=32768; 1=1024; 0,3,4,5=256; 6,7=0 ??
  ?  second field B3..B0=RS3..RS0 - freqv2^(f-1), 4 OK }
const Mask1 = $01 (** See D4:\CFS\DHP\INTERRUP.DOC ; IAR's AnnaBook p.53 **) ;
var xxx: byte;
begin Writeln(' PI on') ;
  ExitSave := ExitProc ; ExitProc := @PIoff { Ensure OFF at end } ;
  GetIntVec(AT_int, SavePIvec) ; SetIntVec(AT_int, @PIintHandler) ;
  XP := Port[IC2+1] ;
  Port[IC2+1] := Port[IC2+1] and not Mask1 { Enable } ;
  Port[RTCsel] := RegA ; Port[RTCdat] := Rate ;
  Port[RTCsel] := RegB ; xxx := Port[RTCdat];
  Port[RTCsel] := RegB; Port[RTCdat] := xxx or PIE ;
  Port[RTCsel] := RegC ; xxx := Port[RTCdat];  { dummy read access to unlock int }
  end {PIon} ;


var BB : byte ; C : char ; Freq : real ; K, KL : longint ;
T : byte absolute $0040:$006C { increments at ~ 18.2Hz } ;


BEGIN ; Writeln ;
Writeln('INT70-AT.PAS : Expects a PC-AT+') ;
Writeln('Hogs Interrupt ', AT_int, '; side-effects are uncertain.') ;
Writeln('PC-AT+ only    - Int $70 = 112 - seems OK now') ;

JJ := 0 ;
PIon ;

BB := (Rate and $7F) shr 4 ; C := char(Ord('0')+BB) ;
case BB of { as observed on PPC640 }
  2 :       Freq := 32768.0 { Tandon PacII 486/33 : 32768.0 } ;
  1 :       Freq :=  1024.0 { Tandon PacII 486/33 : 0 } ;
  0,3,4,5 : Freq :=   256.0 { Tandon PacII 486/33 : 0 } ;
  6,7 :     Freq :=     0.0 end ;
BB := Rate and $0F ;
for KL := 2 to BB do Freq := Freq / 2.0 ;
if BB>9 then BB := BB+7 ;
Writeln(' Rate = ', Rate, ' = $', C, char(Ord('0')+BB),
  '; Freq = ', Freq:0:3, 'Hz *42/43;') ;
Freq := Freq * 42/43 ;
Writeln(' Too much interrupt processing slows Crt.Delay!') ;
Write(' CRT.DELAY(1000{ms}) ;  : ') ; JJ := 0 ; Delay(1000) ; KL := JJ ;
Writeln(KL:7, ', should be about ', Round(Freq)) ;
Write(' Using 5s @ 18.2Hz = 91 : ') ;
repeat KL := T until (KL<160) and (KL<T) ; KL := T+91 ;
JJ := 0 ; repeat until T>=KL ; KL := JJ ;
Writeln(KL:7, ', should be about ', Round(5*Freq)) ;
Write(^G) ;
END.


There was a "FAQ"-posting (now in JRS's file RTC-FAQ) :

From: chris@pfsparc02.phil15.uni-sb.de (Christian Blum)
Newsgroups: comp.os.msdos.programmer
Subject: The Real Time Clock - Answers, not questions :)
Date: 7 Feb 1994 20:34:08 GMT
Organization: CIP-Pool, Media Centre, University of Saarland at Saarbruecken

and it was his E-mail help which finally removed all known errors
in my code.

JRS.
