home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 5
/
ctrom5b.zip
/
ctrom5b
/
CT
/
CT9404
/
TTDEMO
/
SOURCE.ZIP
/
PAGER.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1994-02-01
|
20KB
|
683 lines
(***********************************************************************
* pager.pas *
* Copyright (c) 1993 QQS - All rights reserved *
* This file is donated to the public domain *
* *
* version 1.0 November 24, 1993 *
* convert teletext pages & display as text *
***********************************************************************)
program pager;
uses dos, strings, crt, tttest, tunefunc;
type
bufrow = array[0..39] of byte;
RecPtr = ^rec;
rec = record
page : array[0..25, 0..39] of byte; (* teletext page, control in [0][0..1]
pagenum in [0][2..3], subnum in [0][4..5] *)
next : RecPtr;
end;
Queue = record
head, tail : RecPtr; (* dequeue from head, enqueue on tail *)
end;
chapr = record
rec : RecPtr;
pagenum : word; (* pagenumber of this page *)
numlines : word; (* number of lines put in this page *)
lastline : word; (* last teletext line received on this page *)
countdown : byte; (* countdown for acceptance *)
special : boolean; (* newsflash or subtitle page? *)
potkilled : boolean; (* page must be killed if potkilled and
line <> lastline + 1 *)
usedpage : boolean; (* page in use? *)
contentsset : boolean; (* boolean whether contents is received *)
end;
var
Hamming : array[0..255] of integer; (* translates hammingcodes, if Hamming[x]
in 0..15 -> Hamming[x] is value for x, Hamming[x] == -1 -> x is an error
value *)
OddParity : array[0..255] of boolean; (* OddParity denotes whether char ii is
odd *)
tuneval : longint; (* tune frequency (default = direct video) *)
hardirq : word; (* card address *)
hardaddr : word;
oldmask : byte; (* old interrupt mask *)
oldvector : pointer; (* old int vector *)
Filled, Avail : Queue; (* queue of filled and available pages *)
(* locals for main program *)
remain : byte;
ok : boolean;
ii, jj : word;
ll : longint;
argv : string;
pageinfo : RecPtr;
control, pagenum, subnum : word;
graphics : boolean;
ch : byte;
finished : boolean;
wordptr : ^word;
(* static locals for interrupt *)
buf : bufrow; (* store lines here *)
semaphore : boolean; (* prevents interrupt overrun *)
vidline : byte; (* television videoline with teletext *)
numlines, parerror : word;
br, ag, se, st, me, mt, ue, ut, ca, cb : integer;
chapter, line : integer; (* teletext line number *)
intrpagenum, intrcontrol, intrmin, intrhour, intrii : word;
ChapRec : array[0..16] of chapr; (* info for each teletext + interrupt page
+ subtitle *)
isint : array[0..7] of boolean; (* determines whether chapter currently is
an interrupt page *)
subtitle : word; (* 0..7 -> chapter is subtitle use chaprec[16], *)
(* 8 -> no subtitle *)
intrready : boolean;
intrwordptr : ^word;
(* page processing functions called from within interrupt, at end of file *)
function DeQueue(var queue : Queue) : RecPtr; forward;
procedure EnQueue(var queue : Queue; rec : RecPtr); forward;
procedure KillChapter(chapter : word); forward;
procedure TeletextInterrupt(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES,
BP : word); interrupt; forward;
procedure InitHam;
const
valid : array[0..15] of integer = ($15, $02, $49, $5e, $64, $73, $38,
$2f, $d0, $c7, $8c, $9b, $a1, $b6, $fd, $ea);
(* valid hammingcodes 0..15 *)
var
ii : integer;
begin
for ii := 0 to 255 do
Hamming[ii] := -1;
for ii := 0 to 15 do
Hamming[valid[ii]] := ii;
end;
procedure InitOddParity;
label even;
var
ii : integer;
begin
for ii := 0 to 255 do
begin
asm
mov AL, byte ptr [ii]
or AL, AL
jp even
end;
OddParity[ii] := true;
continue;
even:
OddParity[ii] := false;
end;
end;
procedure StartInt;
(* set interrupt for teletext *)
var
pat : byte;
begin
if(hardirq < 10) then
begin
GetIntVec(hardirq + 8, oldvector);
SetIntVec(hardirq + 8, @TeletextInterrupt);
pat := not (1 shl hardirq);
asm
in al, 21h
mov [oldmask], al
and al, [pat] (* enable hard int *)
out 21h, al
end;
end
else
begin
GetIntVec(hardirq + $68, oldvector);
SetIntVec(hardirq + $68, @TeletextInterrupt);
pat := not (1 shl (hardirq - 8));
asm
in al, 0a1h
mov [oldmask], al
and al, [pat] (* enable hard int *)
out 0a1h, al
end;
end;
port[hardaddr + 1] := 0; (* reset ram linenr & busybit *)
end;
procedure StopInt;
(* restore setting of interrupt after teletext *)
var
pat : byte;
begin
port[hardaddr + 2] := 0; (* deselect tuner, int off *)
if(hardirq < 10) then
begin
pat := (1 shl hardirq) and oldmask;
asm
in al, 21h
or al, [pat] (* set bit int to old value *)
out 21h, al
end;
SetIntVec(8 + hardirq, oldvector);
end
else
begin
pat := (1 shl (hardirq - 8)) and oldmask;
asm
in al, 0a1h
or al,[pat] (* set bit int to old value *)
out 0a1h, al
end;
SetIntVec($68 + hardirq, oldvector);
end;
end;
procedure initialize;
(* set up datastructure *)
var
ii : integer;
mem : RecPtr;
begin
for ii := 0 to 16 do
new(ChapRec[ii].rec);
Filled.head := nil;
Filled.tail := nil;
Avail.head := nil;
Avail.tail := nil;
for ii := 0 to 25 do
begin
new(mem);
EnQueue(Avail, mem);
end;
for ii := 0 to 16 do
KillChapter(ii);
for ii := 0 to 7 do;
isint[ii] := false;
end;
(* teletext interrupt handler & functions called from within interrupt
use as little as possible local variables, because they are put on an
unknown stack. *)
{$S-} (* no stack check *)
(* queue functions *)
function DeQueue(var queue : Queue) : RecPtr;
(* return dequeued value from queue. pre: queue isn't empty. *)
var
rec : RecPtr;
begin
rec := queue.head;
if (queue.head = queue.tail) then
begin
queue.head := nil;
queue.tail := nil;
end
else
queue.head := queue.head^.next;
Dequeue := rec;
end;
procedure EnQueue(var queue : Queue; rec : RecPtr);
(* enqueue record in queue *)
begin
rec^.next := nil;
if (queue.tail = nil) then
queue.head := rec
else
queue.tail^.next := rec;
queue.tail := rec;
end;
procedure PrioEnQueue(var queue : Queue; rec : RecPtr);
(* enqueue record in queue with priority at start of queue *)
begin
rec^.next := queue.head;
queue.head := rec;
if (queue.tail = nil) then
queue.tail := rec;
end;
(* functions making complete page of teletext lines *)
procedure KillChapter(chapter : word);
begin
ChapRec[chapter].usedpage := false;
end;
procedure PotKillChapter(chapter : word);
begin
ChapRec[chapter].potkilled := true;
end;
procedure AcceptChapter(chapter : word);
begin
if (ChapRec[chapter].usedpage and ChapRec[chapter].contentsset and
(not chapRec[chapter].potkilled or (ChapRec[chapter].lastline > 22) or
ChapRec[chapter].special)) then
begin
(* process completed page *)
if (Avail.head <> nil) then
begin
if (ChapRec[chapter].special) then
PrioEnQueue(Filled, ChapRec[chapter].rec)
else
EnQueue(Filled, ChapRec[chapter].rec);
ChapRec[chapter].rec := DeQueue(Avail);
end;
end;
ChapRec[chapter].usedpage := false;
ChapRec[chapter].countdown := 0;
end;
procedure StartChapter(chapter, control, pagenum, subnum : word; buffer :
bufrow);
begin
intrready := false;
if (subtitle <> 8) then
begin
AcceptChapter(16);
subtitle := 8;
end;
if (control and $0400) <> 0 then
begin (* interrupt bit is on (this is a interrupt page) *)
if (isint[chapter]) then (* interrupt on interrupt *)
begin
chapter := chapter + 8;
if (ChapRec[chapter].pagenum = pagenum) and (ChapRec[chapter].lastline <=
22) and (not ChapRec[chapter].special) then
intrready := true; (* continue page *)
if not intrready then
begin
if (not ChapRec[chapter].special) and ((control and $c0) <> 0) then
begin
subtitle := chapter - 8;
chapter := 16;
end
else
AcceptChapter(chapter); (* accept previous interrupt page *)
end;
end
else
begin
isint[chapter] := true;
chapter := chapter + 8;
end;
if not intrready then
ChapRec[chapter].potkilled := false;
end
else
begin
if (isint[chapter]) then (* but last page was interrupt page *)
begin
AcceptChapter(8 + chapter);
isint[chapter] := false;
end;
if (ChapRec[chapter].pagenum = pagenum) and (ChapRec[chapter].lastline <=
22) and (not ChapRec[chapter].special) then
begin
if (ChapRec[chapter].usedpage) then
intrready := true (* continue page *)
else
ChapRec[chapter].potkilled := true; (* page must start with line 1 *)
end
else
begin
AcceptChapter(chapter);
ChapRec[chapter].potkilled := false;
end;
end;
if not intrready then
begin
ChapRec[chapter].special := (control and $c0) <> 0; (* newsfl./subtitle *)
ChapRec[chapter].pagenum := pagenum;
ChapRec[chapter].numlines := 0;
ChapRec[chapter].contentsset := false;
ChapRec[chapter].usedpage := true;
Fillchar(ChapRec[chapter].rec^.page, 25 * 40,' ');
(* put in array as backwords *)
ChapRec[chapter].rec^.page[0][0] := control and $ff;
ChapRec[chapter].rec^.page[0][1] := control shr 8;
ChapRec[chapter].rec^.page[0][2] := pagenum and $ff;
ChapRec[chapter].rec^.page[0][3] := pagenum shr 8;
ChapRec[chapter].rec^.page[0][4] := subnum and $ff;
ChapRec[chapter].rec^.page[0][5] := subnum shr 8;
Move(buf[8], ChapRec[chapter].rec^.page[0][8], 32);
end;
end;
procedure TeletextInterrupt;
label next, nopar;
begin
asm
mov al,20h
out 20h, al (* give end of interrupt *)
end;
if(hardirq > 8) then
asm
mov al,20h
out 0a0h, al
end;
if(not semaphore) then
begin
semaphore := true;
asm
sti
cld
end;
for intrii := 0 to 15 do
if (ChapRec[intrii].countdown <> 0) then
begin
ChapRec[intrii].countdown := ChapRec[intrii].countdown - 1;
if (ChapRec[intrii].countdown = 0) then
AcceptChapter(intrii);
end;
numlines := port[hardaddr + 1] and $7f;
asm
db 0ebh, 0 (* jmp short $+2, short wait *)
end;
port[hardaddr + 1] := 0;
while(numlines > 0) do
begin
vidline := port[hardaddr];
br := Hamming[port[hardaddr]];
ag := Hamming[port[hardaddr]];
if((br <> -1) and (ag <> -1)) then
begin
line := (br shr 3) + (ag shl 1);
chapter := br and $7;
if (line > 24) then
begin
numlines := numlines - 1;
port[hardaddr + 0] := 0;
continue;
end;
parerror := 0;
for intrii := 0 to 39 do
begin
buf[intrii] := port[hardaddr];
if (not OddParity[buf[intrii]]) then
begin
parerror := parerror + 1;
buf[intrii] := 27; (* Esc char *)
end;
end;
if (line = 0) then
begin
(* page header *)
se := Hamming[buf[0]];
st := Hamming[buf[1]];
me := Hamming[buf[2]];
mt := Hamming[buf[3]];
ue := Hamming[buf[4]];
ut := Hamming[buf[5]];
ca := Hamming[buf[6]];
cb := Hamming[buf[7]];
if (se <> -1) and (st <> -1) and (me <> -1) and (mt <> -1) and
(ue <> -1) and (ut <> -1) and (ca <> -1) and (cb <> -1) then
begin
intrpagenum := st * 10 + se;
if (intrpagenum < 100) then
begin
intrcontrol := ((mt and $08 (* c4 *)) shl 2) or ((ut and $0c
(* c6/5 *)) shl 4) or ((ca and $0f (* c10..7 *)) shl 8) or
((cb and $0f (* c14..11 *)) shl 12);
intrmin := (mt and $07) * 10 + me;
intrhour := (ut and $03) * 10 + ue;
if (chapter = 0) then
intrpagenum := intrpagenum + 800
else
intrpagenum := intrpagenum + chapter * 100;
StartChapter(chapter, intrcontrol, intrpagenum, intrhour * 100 +
intrmin, buf);
if (isint[chapter]) then
if (chapter = subtitle) then
chapter := 16
else
chapter := chapter + 8;
if (parerror <> 0) then
(* parity error in header, just started page is bad *)
KillChapter(chapter);
ChapRec[chapter].lastline := line;
end
else
begin
if isint[chapter] then
begin
if (ChapRec[chapter + 8].special) then
AcceptChapter(chapter + 8);
end
else
begin
if (ChapRec[chapter].special) then
AcceptChapter(chapter);
end;
end;
end
else
begin
if isint[chapter] then
begin
PotKillChapter(chapter + 8);
AcceptChapter(chapter + 8);
end
else
begin
PotKillChapter(chapter);
AcceptChapter(chapter);
end;
end;
end
else
begin
if (isint[chapter]) then
if (chapter = subtitle) then
chapter := 16
else
chapter := chapter + 8;
if (not ChapRec[chapter].usedpage) then
begin
numlines := numlines - 1;
port[hardaddr] := 0;
continue;
end;
Move(buf, ChapRec[chapter].rec^.page[line], 40);
if ChapRec[chapter].potkilled and (line <> ChapRec[chapter].lastline
+ 1) then
KillChapter(chapter)
else
begin
(* accept line *)
if (ChapRec[chapter].numlines > 40) then
KillChapter(chapter); (* too many lines, probably header missed *)
ChapRec[chapter].numlines := ChapRec[chapter].numlines + 1;
ChapRec[chapter].contentsset := true;
ChapRec[chapter].potkilled := false;
ChapRec[chapter].countdown := 10; (* accept after 10 interrupts *)
ChapRec[chapter].lastline := line;
end;
end;
numlines := numlines - 1;
port[hardaddr] := 0;
end;
end;
port[hardaddr + 1] := 0;
semaphore := false;
end;
end;
begin
(* initialize global variables *)
tuneval := 0;
hardirq := 11;
hardaddr := $130;
(* initialize global variable for interrupt *)
semaphore := false;
if not cardtest(hardaddr) then
writeln('No teletext card detected')
else
begin
(* load frequency if given as parameter *)
ok := true;
if (ParamCount > 0) then
begin
if (ParamCount > 1) then
ok := false
else
begin
argv := ParamStr(1);
for ii := 1 to Length(argv) do
begin
if (argv[ii] < '0') or (argv[ii] > '9') then
break;
tuneval := tuneval * 10 + ord(argv[ii]) - ord('0');
end;
tuneval := tuneval * 1000000;
if (argv[ii] = '.') then
begin
ii := ii + 1;
ll := 0;
jj := 0;
while (ii + jj <= Length(argv)) and (argv[ii + jj] >= '0') and
(argv[ii + jj] <= '9') do
begin
ll := ll * 10 + ord(argv[ii + jj]) - ord('0');
jj := jj + 1;
end;
ii := ii + jj;
while (jj < 6) do
begin
ll := ll * 10;
jj := jj + 1;
end;
tuneval := tuneval + ll;
end;
if(ii <> (Length(argv) + 1)) then
ok := false;
end;
if not ok then
begin
writeln('Usage: PAGER <frequency in MHz (default = 0, direct video)>');
halt;
end;
end;
writeln('Press Esc to exit program...');
initialize;
TunefuncSetAddr(hardaddr);
SelectChannel(tuneval); (* you can use 0 or a value 46000000L..870000000L *)
InitHam;
InitOddParity;
StartInt;
remain := port[hardaddr + 2] and $2; (* get tuner/video selection bit *)
port[hardaddr + 2] := remain or 1; (* interrupts on *)
(* Insert teletext active code here *)
finished := false;
while true do
begin
if finished or KeyPressed and (ord(ReadKey) = $1b) then
break; (* Esc pressed, stop *)
if (Filled.head <> nil) then (* teletext page filled *)
begin
asm
cli
end;
pageinfo := DeQueue(Filled);
asm
sti
end;
(* we have a page in pageinfo, process it.
pages have same format as 'BINAIR' saved page in TT program *)
(* as an example, we display the page here as text *)
(* get from in array as backwords *) (* control is not used *)
control := pageinfo^.page[0][0] or pageinfo^.page[0][1] shl 8;
pagenum := pageinfo^.page[0][2] or pageinfo^.page[0][3] shl 8;
subnum := pageinfo^.page[0][4] or pageinfo^.page[0][5] shl 8;
(* strip parity *)
for ii := 0 to 24 do
for jj := 0 to 39 do
if (pageinfo^.page[ii][jj] = 27) then
pageinfo^.page[ii][jj] := ord(' ') (* parity character *)
else
pageinfo^.page[ii][jj] := (pageinfo^.page[ii][jj] and $7f);
(* display page as text *)
write(pagenum : 3, '/', subnum);
if subnum < 1000 then
begin
write(' ');
if subnum < 100 then
begin
write(' ');
if subnum < 10 then
write(' ');
end;
end;
for ii := 0 to 24 do
begin
if KeyPressed and (ord(ReadKey) = $1b) then (* Esc pressed, stop *)
begin
finished := true;
break;
end;
graphics := false;
if ii = 0 then (* line 0 starts at 8 *)
jj := 8
else
jj := 0;
while jj < 40 do
begin
ch := pageinfo^.page[ii][jj];
if (ch < 8) then
graphics := false
else
if (ch >= 16) and (ch < 23) then
graphics := true;
if graphics or (ord(ch) < 32) then
ch := ord(' '); (* display space instead of control character *)
write(chr(ch));
jj := jj + 1;
end;
writeln;
end;
(* make the pageinfo available again, so it can be recycled *)
asm
cli
end;
EnQueue(Avail, pageinfo);
asm
sti
end;
end;
end;
StopInt;
end;
end.