home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 5
/
ctrom5b.zip
/
ctrom5b
/
CT
/
CT9404
/
TTDEMO
/
SOURCE.ZIP
/
PAGER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-01
|
16KB
|
554 lines
/***********************************************
* pager.c *
* Copyright (c) 1993 QQS - All rights reserved *
* This file is donated to the public domain *
* *
* version 1.0 May 10, 1992 *
* convert teletext pages & display as text *
* last update: 1.6 November 16, 1993 *
***********************************************/
#include <stdio.h>
#include <dos.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <vector.h>
#include <tunefunc.h>
#include <tttest.h>
#define TRUE 1
#define FALSE 0
extern void interrupt TeletextInterrupt(void); // at end of file
// datatypes & datastuctures for page processing
typedef struct rec
{
unsigned char page[25][40]; /* teletext page, control in [0][0..1],
pagenum in [0][2..3], subnum in [0][4..5] */
struct rec* next; /* link to next in queue */
} *RecPtr;
typedef struct queue
{
RecPtr head, tail; /* dequeue from head, enqueue on tail */
} *Queue;
typedef struct
{
RecPtr record;
unsigned int pagenum; /* pagenumber of this page */
unsigned int numlines; /* number of lines put in this page */
unsigned int lastline; /* last teletext line received on this page */
unsigned char countdown; /* countdown for acceptance */
int special; /* newsflash or subtitle page? */
int potkilled; /* page must be killed if potkilled && line != lastline + 1 */
int usedpage; /* page in use? */
int contentsset; /* boolean whether contents is received */
} chaprec;
struct queue Filled, Avail; /* queue of filled and available pages */
chaprec ChapRec[17]; /* info for each teletext + interrupt page + subtitle */
int isint[8]; /* determines whether chapter currently is an interrupt page */
unsigned int subtitle; /* 0..7 -> chapter is subtitle use chaprec[16], */
/* 8 -> no subtitle */
// page processing functions called from within interrupt, at end of file
extern RecPtr DeQueue(Queue queue);
extern void EnQueue(Queue queue, RecPtr record);
extern void KillChapter(unsigned int chapter);
signed char Hamming[256]; /* translates hammingcodes, if Hamming[x] in 0..15 ->
Hamming[x] is value for x, Hamming[x] == -1 -> x is an error value. */
void InitHam(void)
/* init for hamming check (teletext control characters are hamming encoded) */
{
unsigned char valid[16] = {0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f,
0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea}; // valid hammingcodes 0..15
int ii;
for(ii = 0; ii < 256; ii++)
Hamming[ii] = -1;
for(ii = 0; ii < 16; ii++)
Hamming[valid[ii]] = ii;
}
int OddParity[256]; // OddParity denotes whether char ii is odd
void InitOddParity(void)
/* init for odd parity (teletext characters should be odd parity) */
{
int ii;
for(ii = 0; ii < 256; ii++)
{
asm mov al,byte ptr [ii]
asm or al,al
asm jp even
OddParity[ii] = 1;
continue;
even:
OddParity[ii] = 0;
}
}
unsigned long tuneval = 0; // tune frequency (default = direct video)
unsigned int hardirq = 11, hardaddr = 0x130; // card address
unsigned char oldmask; /* old interrupt mask */
void far* oldvector; /* old int vector */
void StartInt(void)
/* set interrupt for teletext */
{
unsigned char pat;
if(hardirq < 10)
{
oldvector = GetInterruptVector(hardirq + 8);
SetInterruptVector(hardirq + 8, TeletextInterrupt);
pat = ~(1 << hardirq);
asm in al,21h
asm mov [oldmask],al
asm and al, [pat] // enable hard int
asm out 21h,al
}
else
{
oldvector = GetInterruptVector(hardirq + 0x68);
SetInterruptVector(hardirq + 0x68, TeletextInterrupt);
pat = ~(1 << (hardirq - 8));
asm in al,0a1h
asm mov [oldmask],al
asm and al, [pat] // enable hard int
asm out 0a1h,al
}
outportb(hardaddr + 1, 0); // reset ram linenr & busybit
}
void StopInt(void)
/* restore setting of interrupt after teletext */
{
unsigned char pat;
outportb(hardaddr + 2, 0); // deselect tuner, int off
if(hardirq < 10)
{
pat = (1 << hardirq) & oldmask;
asm in al,21h
asm or al,[pat] // set bit int to old value
asm out 21h,al
SetInterruptVector(8 + hardirq, oldvector);
}
else
{
pat = (1 << (hardirq - 8)) & oldmask;
asm in al,0a1h
asm or al,[pat] // set bit int to old value
asm out 0a1h,al
SetInterruptVector(0x68 + hardirq, oldvector);
}
}
void initialize(void)
// set up datastructure
{
int ii;
void* mem;
for(ii = 0; ii < 17; ii++)
if((ChapRec[ii].record = malloc(sizeof(struct rec))) == NULL)
printf("Not enough memory\n"), exit(-1);
Filled.head = Filled.tail = Avail.head = Avail.tail = NULL;
for(ii = 0; ii < 25; ii++)
{
if((mem = malloc(sizeof(struct rec))) == NULL)
printf("Not enough memory\n"), exit(-1);
EnQueue(&Avail, mem);
}
for(ii = 0; ii < 17; ii++)
KillChapter(ii);
for(ii = 0; ii < 8; ii++)
isint[ii] = FALSE;
}
void main(int argc, char** argv)
{
unsigned char remain;
RecPtr pageinfo;
unsigned int control, pagenum, subnum;
unsigned char (*page)[40];
int graphics; // currently display graphics as spaces?
int ii, jj, ch, ok;
long ll;
if(!cardtest())
printf("No teletext card detected\n"), exit(-1);
// load frequency if given as parameter
if(argc > 1)
{
if(argc > 2)
ok = FALSE;
else
{
for(ii = 0; ii < strlen(argv[1]); ii++)
{
if(argv[1][ii] < '0' || argv[1][ii] > '9')
break;
tuneval *= 10;
tuneval += argv[1][ii] - '0';
}
tuneval *= 1000000L;
if(argv[1][ii++] == '.')
{
ll = 0;
for(jj = 0; argv[1][ii + jj] >= '0' && argv[1][ii + jj] <= '9'; jj++)
{
ll *= 10;
ll += argv[1][ii + jj] - '0';
}
ii += jj;
while(jj++ < 6)
ll *= 10;
tuneval += ll;
}
if(argv[1][ii] != '\0')
ok = FALSE;
}
if(!ok)
{
printf("Usage: PAGER <frequency in MHz (default = 0, direct video)>\n");
exit(-1);
}
}
fprintf(stderr, "Press Esc to exit program...\n");
initialize();
SelectChannel(tuneval); // you can use 0 or a value 46000000L..870000000L
InitHam();
InitOddParity();
StartInt();
remain = inportb(hardaddr + 2) & 0x2; // get tuner/video selection bit
outportb(hardaddr + 2, remain | 1); // interrupts on
for(;;)
{
if(kbhit() && getch() == 0x1b) // Esc pressed, stop execution
break;
if(Filled.head != NULL) // teletext page filled ?
{
asm cli
pageinfo = DeQueue(&Filled);
asm sti
// 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.
page = pageinfo->page;
control = *(unsigned int *)(page[0] + 0); // not used here
pagenum = *(unsigned int *)(page[0] + 2);
subnum = *(unsigned int *)(page[0] + 4);
// strip parity
for(ii = 0; ii < 25; ii++)
for(jj = 0; jj < 40; jj++)
if(page[ii][jj] == 27)
page[ii][jj] = ' '; // parity character
else
page[ii][jj] &= 0x7f;
// display page as text
printf("%3d/%-4d", pagenum, subnum);
for(ii = 0; ii < 25; ii++)
{
if(kbhit() && getch() == 0x1b) // Esc pressed, stop execution
break;
graphics = FALSE;
for(jj = (ii == 0 ? 8 : 0); jj < 40; jj++) // line 0 starts at 8
{
ch = page[ii][jj];
if(ch < 8)
graphics = FALSE;
else
if(ch >= 16 && ch < 23)
graphics = TRUE;
if(graphics || ch < 32)
ch = ' '; // display space instead of strange control character
printf("%c", ch);
}
printf("\n");
}
if(ii < 25)
break; // Esc pressed
// make the pageinfo available again, so it can be recycled
asm cli
EnQueue(&Avail, pageinfo);
asm sti
}
}
StopInt();
}
/* teletext interrupt handler & functions called from within interrupt */
#pragma option -N- // no stack check
#pragma option -r- // no register vars
// queue functions
RecPtr DeQueue(Queue queue)
/* return dequeued value from queue. pre: queue isn't empty. */
{
RecPtr record;
record = queue->head;
if(queue->head == queue->tail)
queue->head = queue->tail = NULL;
else
queue->head = queue->head->next;
return(record);
}
void EnQueue(Queue queue, RecPtr record)
/* enqueue record in queue */
{
record->next = NULL;
if(queue->tail == NULL)
queue->head = record;
else
queue->tail->next = record;
queue->tail = record;
}
void PrioEnQueue(Queue queue, RecPtr record)
/* enqueue record in queue with priority at start of queue */
{
record->next = queue->head;
queue->head = record;
if(queue->tail == NULL)
queue->tail = record;
}
// functions making complete page of teletext lines
void KillChapter(unsigned int chapter)
{
ChapRec[chapter].usedpage = FALSE;
}
void PotKillChapter(unsigned int chapter)
{
ChapRec[chapter].potkilled = TRUE;
}
void AcceptChapter(unsigned int chapter)
{
if(ChapRec[chapter].usedpage && ChapRec[chapter].contentsset &&
(!ChapRec[chapter].potkilled || ChapRec[chapter].lastline > 22 ||
ChapRec[chapter].special))
{
/* process completed page */
if(Avail.head != NULL)
{
if(ChapRec[chapter].special)
PrioEnQueue(&Filled, ChapRec[chapter].record);
else
EnQueue(&Filled, ChapRec[chapter].record);
ChapRec[chapter].record = DeQueue(&Avail);
}
}
ChapRec[chapter].usedpage = FALSE;
ChapRec[chapter].countdown = 0;
}
void StartChapter(unsigned int chapter, unsigned int control, unsigned int
pagenum, unsigned int subnum, unsigned char* buffer)
{
if(subtitle != 8)
AcceptChapter(16), subtitle = 8;
if(control & 0x0400) /* interrupt bit is on (this is a interrupt page) */
{
if(isint[chapter]) /* interrupt on interrupt */
{
chapter += 8;
if(ChapRec[chapter].pagenum == pagenum && ChapRec[chapter].lastline <= 22
&& !ChapRec[chapter].special)
return; /* continue page, unless finished or newsflash or subtitle */
if(!ChapRec[chapter].special && (control & 0xc0) != 0)
subtitle = chapter - 8, chapter = 16;
else
AcceptChapter(chapter); /* accept previous interrupt page */
}
else
isint[chapter] = TRUE, chapter += 8;
ChapRec[chapter].potkilled = FALSE;
}
else
{
if(isint[chapter]) /* but last page was interrupt page */
AcceptChapter(8 + chapter), isint[chapter] = FALSE;
if(ChapRec[chapter].pagenum == pagenum && ChapRec[chapter].lastline <= 22 &&
!ChapRec[chapter].special)
{
if(ChapRec[chapter].usedpage)
return; /* continue page */
ChapRec[chapter].potkilled = TRUE; /* make sure page start with line 1 */
}
else
{
AcceptChapter(chapter);
ChapRec[chapter].potkilled = FALSE;
}
}
ChapRec[chapter].special = (control & 0xc0) != 0; /* newsflash or subtitle */
ChapRec[chapter].pagenum = pagenum;
ChapRec[chapter].numlines = 0;
ChapRec[chapter].contentsset = FALSE, ChapRec[chapter].usedpage = TRUE;
*(unsigned int*)(ChapRec[chapter].record->page[0] + 0) = control;
*(unsigned int*)(ChapRec[chapter].record->page[0] + 2) = pagenum;
*(unsigned int*)(ChapRec[chapter].record->page[0] + 4) = subnum;
memset(ChapRec[chapter].record->page + 1, ' ', 24 * 40);
memcpy(ChapRec[chapter].record->page[0] + 8, buffer + 8, 32);
}
void interrupt TeletextInterrupt(void)
/* hardware interrupt handler */
{
static unsigned char buf[40];
static int semaphore = FALSE;
static unsigned vidline, numlines, parerror;
static int br, ag, se, st, me, mt, ue, ut, ca, cb, chapter, line;
static unsigned int pagenum, control, min, hour, ii;
asm mov al,20h
asm out 20h, al // give end of interrupt
if(hardirq > 8)
{
asm mov al,20h
asm out 0a0h, al
}
if(semaphore)
return;
semaphore = TRUE;
asm sti
asm cld
for(ii = 0; ii < 16; ii++)
if(ChapRec[ii].countdown != 0 && --ChapRec[ii].countdown == 0)
AcceptChapter(ii);
numlines = inportb(hardaddr + 1) & 0x7f;
asm db 0ebh, 0 ; /* jmp short $+2, short wait */
outportb(hardaddr + 1, 0);
for(; numlines > 0; --numlines, outportb(hardaddr + 0, 0))
{
vidline = inportb(hardaddr);
br = Hamming[inportb(hardaddr)], ag = Hamming[inportb(hardaddr)];
if(br != -1 && ag != -1)
{
line = (br >> 3) + (ag << 1), chapter = br & 0x7;
if(line > 24)
continue;
parerror = 0;
// assembler used for extra speed
_DX = hardaddr;
asm mov bx,di // save di
asm mov cx,40
asm mov di,offset buf
asm mov ax,ds
asm mov es,ax
next:
asm in al,dx
asm or al,al
asm jnp nopar
parerror++;
asm mov al,27 // parity error character is replaced by Esc char
nopar:
asm stosb
asm loop next
asm mov di,bx
if(line == 0)
{
/* 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 && st != -1 && me != -1 && mt != -1 && ue != -1 &&
ut != -1 && ca != -1 && cb != -1)
{
pagenum = st * 10 + se;
if(pagenum < 100)
{
control = ((mt & 0x08 /* c4 */) << 2) | ((ut & 0x0c /* c6/5 */)
<< 4) | ((ca & 0x0f /* c10..7 */) << 8) | ((cb & 0x0f
/* c14..11 */) << 12);
min = (mt & 0x07) * 10 + me, hour = (ut & 0x03) * 10 + ue;
pagenum += (chapter == 0 ? 8 : chapter) * 100;
StartChapter(chapter, control, pagenum, hour * 100 + min, buf);
if(isint[chapter])
if(chapter == subtitle)
chapter = 16;
else
chapter += 8;
if(parerror != 0)
/* parity error in header, just started page is bad */
KillChapter(chapter);
ChapRec[chapter].lastline = line;
}
else
if(ChapRec[chapter + (isint[chapter] != 0 ? 8 : 0)].special)
AcceptChapter(chapter + (isint[chapter] ? 8 : 0));
}
else
{
PotKillChapter(chapter + (isint[chapter] ? 8 : 0));
AcceptChapter(chapter + (isint[chapter] ? 8 : 0));
}
}
else
{
if(isint[chapter])
if(chapter == subtitle)
chapter = 16;
else
chapter += 8;
if(!ChapRec[chapter].usedpage)
continue;
memcpy(ChapRec[chapter].record->page[line], buf, 40);
if(ChapRec[chapter].potkilled && line != ChapRec[chapter].lastline + 1)
KillChapter(chapter);
else
{
/* accept line */
if(ChapRec[chapter].numlines > 40)
KillChapter(chapter); /* too many lines, probably header missed */
ChapRec[chapter].numlines++;
ChapRec[chapter].contentsset = TRUE;
ChapRec[chapter].potkilled = FALSE;
ChapRec[chapter].countdown = 10; /* accept after 10 interrupts */
ChapRec[chapter].lastline = line;
}
}
}
}
outportb(hardaddr + 1, 0); // reset ram
semaphore = FALSE;
}