home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 5 / ctrom5b.zip / ctrom5b / CT / CT9404 / TTDEMO / SOURCE.ZIP / PAGER.C < prev    next >
C/C++ Source or Header  |  1994-02-01  |  16KB  |  554 lines

  1. /***********************************************
  2. * pager.c                                      *
  3. * Copyright (c) 1993 QQS - All rights reserved *
  4. * This file is donated to the public domain    *
  5. *                                              *
  6. * version 1.0 May 10, 1992                     *
  7. * convert teletext pages & display as text     *
  8. * last update:   1.6 November 16, 1993         *
  9. ***********************************************/
  10.  
  11. #include <stdio.h>
  12. #include <dos.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <conio.h>
  16.  
  17. #include <vector.h>
  18. #include <tunefunc.h>
  19. #include <tttest.h>
  20.  
  21. #define TRUE  1
  22. #define FALSE 0
  23.  
  24. extern void interrupt TeletextInterrupt(void); // at end of file
  25.  
  26. // datatypes & datastuctures for page processing
  27.  
  28. typedef struct rec
  29. {
  30.   unsigned char page[25][40]; /* teletext page, control in [0][0..1], 
  31.       pagenum in [0][2..3], subnum in [0][4..5] */
  32.   struct rec* next; /* link to next in queue */
  33. } *RecPtr;
  34. typedef struct queue
  35. {
  36.   RecPtr head, tail; /* dequeue from head, enqueue on tail */
  37. } *Queue;
  38. typedef struct
  39. {
  40.   RecPtr record;
  41.   unsigned int pagenum; /* pagenumber of this page */
  42.   unsigned int numlines; /* number of lines put in this page */
  43.   unsigned int lastline; /* last teletext line received on this page */
  44.   unsigned char countdown; /* countdown for acceptance */
  45.   int special; /* newsflash or subtitle page? */
  46.   int potkilled; /* page must be killed if potkilled && line != lastline + 1 */
  47.   int usedpage; /* page in use? */
  48.   int contentsset; /* boolean whether contents is received */
  49. } chaprec;
  50.  
  51. struct queue Filled, Avail; /* queue of filled and available pages */
  52. chaprec ChapRec[17]; /* info for each teletext + interrupt page + subtitle */
  53. int isint[8]; /* determines whether chapter currently is an interrupt page */
  54. unsigned int subtitle; /* 0..7 -> chapter is subtitle use chaprec[16], */
  55.                        /* 8 -> no subtitle */
  56.  
  57. // page processing functions called from within interrupt, at end of file
  58. extern RecPtr DeQueue(Queue queue);
  59. extern void EnQueue(Queue queue, RecPtr record);
  60. extern void KillChapter(unsigned int chapter);
  61.  
  62.  
  63. signed char Hamming[256]; /* translates hammingcodes, if Hamming[x] in 0..15 ->
  64.     Hamming[x] is value for x, Hamming[x] == -1 -> x is an error value. */
  65.  
  66. void InitHam(void)
  67. /* init for hamming check (teletext control characters are hamming encoded) */
  68. {
  69.   unsigned char valid[16] = {0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f,
  70.     0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea}; // valid hammingcodes 0..15
  71.   int ii;
  72.  
  73.   for(ii = 0; ii < 256; ii++)
  74.     Hamming[ii] = -1;
  75.   for(ii = 0; ii < 16; ii++)
  76.       Hamming[valid[ii]] = ii;
  77. }
  78.  
  79. int OddParity[256]; // OddParity denotes whether char ii is odd
  80.  
  81. void InitOddParity(void)
  82. /* init for odd parity (teletext characters should be odd parity) */
  83. {
  84.   int ii;
  85.  
  86.   for(ii = 0; ii < 256; ii++)
  87.   {
  88.     asm mov al,byte ptr [ii]
  89.     asm or al,al
  90.     asm jp even
  91.     OddParity[ii] = 1;
  92.     continue;
  93. even:
  94.     OddParity[ii] = 0;
  95.   }
  96. }
  97.  
  98. unsigned long tuneval = 0; // tune frequency (default = direct video)
  99. unsigned int hardirq = 11, hardaddr = 0x130; // card address
  100.  
  101. unsigned char oldmask; /* old interrupt mask */
  102. void far* oldvector; /* old int vector */
  103.  
  104. void StartInt(void)
  105. /* set interrupt for teletext */
  106. {
  107.   unsigned char pat;
  108.  
  109.   if(hardirq < 10)
  110.   {
  111.     oldvector = GetInterruptVector(hardirq + 8);
  112.     SetInterruptVector(hardirq + 8, TeletextInterrupt);
  113.     pat = ~(1 << hardirq);
  114.     asm in al,21h
  115.     asm mov [oldmask],al
  116.     asm and al, [pat]   // enable hard int
  117.     asm out 21h,al
  118.   }
  119.   else
  120.   {
  121.     oldvector = GetInterruptVector(hardirq + 0x68);
  122.     SetInterruptVector(hardirq + 0x68, TeletextInterrupt);
  123.     pat = ~(1 << (hardirq - 8));
  124.     asm in al,0a1h
  125.     asm mov [oldmask],al
  126.     asm and al, [pat]   // enable hard int
  127.     asm out 0a1h,al
  128.   }
  129.   outportb(hardaddr + 1, 0); // reset ram linenr & busybit
  130. }
  131.  
  132. void StopInt(void)
  133. /* restore setting of interrupt after teletext */
  134. {
  135.   unsigned char pat;
  136.  
  137.   outportb(hardaddr + 2, 0); // deselect tuner, int off
  138.   if(hardirq < 10)
  139.   {
  140.     pat = (1 << hardirq) & oldmask;
  141.     asm in al,21h
  142.     asm or al,[pat]     // set bit int to old value
  143.     asm out 21h,al
  144.     SetInterruptVector(8 + hardirq, oldvector);
  145.   }
  146.   else
  147.   {
  148.     pat = (1 << (hardirq - 8)) & oldmask;
  149.     asm in al,0a1h
  150.     asm or al,[pat]     // set bit int to old value
  151.     asm out 0a1h,al
  152.     SetInterruptVector(0x68 + hardirq, oldvector);
  153.   }
  154. }
  155.  
  156. void initialize(void)
  157. // set up datastructure
  158. {
  159.   int ii;
  160.   void* mem;
  161.  
  162.   for(ii = 0; ii < 17; ii++)
  163.     if((ChapRec[ii].record = malloc(sizeof(struct rec))) == NULL)
  164.       printf("Not enough memory\n"), exit(-1);
  165.   Filled.head = Filled.tail = Avail.head = Avail.tail = NULL;
  166.   for(ii = 0; ii < 25; ii++)
  167.   {
  168.     if((mem = malloc(sizeof(struct rec))) == NULL)
  169.       printf("Not enough memory\n"), exit(-1);
  170.     EnQueue(&Avail, mem);
  171.   }
  172.   for(ii = 0; ii < 17; ii++)
  173.     KillChapter(ii);
  174.   for(ii = 0; ii < 8; ii++)
  175.     isint[ii] = FALSE;
  176. }
  177.  
  178. void main(int argc, char** argv)
  179. {
  180.   unsigned char remain;
  181.   RecPtr pageinfo;
  182.   unsigned int control, pagenum, subnum;
  183.   unsigned char (*page)[40];
  184.   int graphics; // currently display graphics as spaces?
  185.   int ii, jj, ch, ok;
  186.   long ll;
  187.  
  188.   if(!cardtest())
  189.     printf("No teletext card detected\n"), exit(-1);
  190.  
  191.   // load frequency if given as parameter
  192.   if(argc > 1)
  193.   {
  194.     if(argc > 2)
  195.       ok = FALSE;
  196.     else
  197.     {
  198.       for(ii = 0; ii < strlen(argv[1]); ii++)
  199.       {
  200.         if(argv[1][ii] < '0' || argv[1][ii] > '9')
  201.           break;
  202.         tuneval *= 10;
  203.         tuneval += argv[1][ii] - '0';
  204.       }
  205.       tuneval *= 1000000L;
  206.       if(argv[1][ii++] == '.')
  207.       {
  208.         ll = 0;
  209.         for(jj = 0; argv[1][ii + jj] >= '0' && argv[1][ii + jj] <= '9'; jj++)
  210.         {
  211.           ll *= 10;
  212.           ll += argv[1][ii + jj] - '0';
  213.         }
  214.         ii += jj;
  215.         while(jj++ < 6)
  216.           ll *= 10;
  217.         tuneval += ll;
  218.       }
  219.       if(argv[1][ii] != '\0')
  220.         ok = FALSE;
  221.     }
  222.     if(!ok)
  223.     {
  224.       printf("Usage: PAGER <frequency in MHz (default = 0, direct video)>\n");
  225.       exit(-1);
  226.     }
  227.   }
  228.  
  229.   fprintf(stderr, "Press Esc to exit program...\n");
  230.  
  231.   initialize();
  232.  
  233.   SelectChannel(tuneval); // you can use 0 or a value 46000000L..870000000L
  234.  
  235.   InitHam();
  236.   InitOddParity();
  237.   StartInt();
  238.  
  239.   remain = inportb(hardaddr + 2) & 0x2; // get tuner/video selection bit
  240.   outportb(hardaddr + 2, remain | 1); // interrupts on
  241.  
  242.   for(;;)
  243.   {
  244.     if(kbhit() && getch() == 0x1b) // Esc pressed, stop execution
  245.       break;
  246.     if(Filled.head != NULL) // teletext page filled ?
  247.     {
  248.       asm cli
  249.       pageinfo = DeQueue(&Filled);
  250.       asm sti
  251.  
  252.       // we have a page in pageinfo, process it.
  253.       // pages have same format as 'BINAIR' saved page in TT program
  254.  
  255.  
  256.       // as an example, we display the page here as text.
  257.  
  258.       page = pageinfo->page;
  259.       control = *(unsigned int *)(page[0] + 0); // not used here
  260.       pagenum = *(unsigned int *)(page[0] + 2);
  261.       subnum = *(unsigned int *)(page[0] + 4);
  262.  
  263.       // strip parity
  264.       for(ii = 0; ii < 25; ii++)
  265.         for(jj = 0; jj < 40; jj++)
  266.           if(page[ii][jj] == 27)
  267.             page[ii][jj] = ' '; // parity character
  268.           else
  269.             page[ii][jj] &= 0x7f;
  270.  
  271.  
  272.       // display page as text
  273.       printf("%3d/%-4d", pagenum, subnum);
  274.       for(ii = 0; ii < 25; ii++)
  275.       {
  276.         if(kbhit() && getch() == 0x1b) // Esc pressed, stop execution
  277.           break;
  278.         graphics = FALSE;
  279.         for(jj = (ii == 0 ? 8 : 0); jj < 40; jj++) // line 0 starts at 8
  280.         {
  281.           ch = page[ii][jj];
  282.           if(ch < 8)
  283.             graphics = FALSE;
  284.           else
  285.           if(ch >= 16 && ch < 23)
  286.             graphics = TRUE;
  287.           if(graphics || ch < 32)
  288.             ch = ' '; // display space instead of strange control character
  289.           printf("%c", ch);
  290.         }
  291.         printf("\n");
  292.       }
  293.       if(ii < 25)
  294.         break; // Esc pressed
  295.  
  296.  
  297.  
  298.       // make the pageinfo available again, so it can be recycled
  299.       asm cli
  300.       EnQueue(&Avail, pageinfo);
  301.       asm sti
  302.     }
  303.   }
  304.   StopInt();
  305. }
  306.  
  307. /* teletext interrupt handler & functions called from within interrupt */
  308.  
  309. #pragma option -N-  // no stack check
  310. #pragma option -r-  // no register vars
  311.  
  312. // queue functions
  313.  
  314. RecPtr DeQueue(Queue queue)
  315. /* return dequeued value from queue. pre: queue isn't empty. */
  316. {
  317.   RecPtr record;
  318.  
  319.   record = queue->head;
  320.   if(queue->head == queue->tail)
  321.     queue->head = queue->tail = NULL;
  322.   else
  323.     queue->head = queue->head->next;
  324.   return(record);
  325. }
  326.  
  327. void EnQueue(Queue queue, RecPtr record)
  328. /* enqueue record in queue */
  329. {
  330.   record->next = NULL;
  331.   if(queue->tail == NULL)
  332.     queue->head = record;
  333.   else
  334.     queue->tail->next = record; 
  335.   queue->tail = record;
  336. }
  337.  
  338. void PrioEnQueue(Queue queue, RecPtr record)
  339. /* enqueue record in queue with priority at start of queue */
  340. {
  341.   record->next = queue->head;
  342.   queue->head = record;
  343.   if(queue->tail == NULL)
  344.     queue->tail = record;
  345. }
  346.  
  347. // functions making complete page of teletext lines
  348.  
  349. void KillChapter(unsigned int chapter)
  350. {
  351.   ChapRec[chapter].usedpage = FALSE;
  352. }
  353.  
  354. void PotKillChapter(unsigned int chapter)
  355. {
  356.   ChapRec[chapter].potkilled = TRUE;
  357. }
  358.  
  359. void AcceptChapter(unsigned int chapter)
  360. {
  361.   if(ChapRec[chapter].usedpage && ChapRec[chapter].contentsset &&
  362.      (!ChapRec[chapter].potkilled || ChapRec[chapter].lastline > 22 ||
  363.      ChapRec[chapter].special))
  364.   {
  365.     /* process completed page */
  366.     if(Avail.head != NULL)
  367.     {
  368.       if(ChapRec[chapter].special)
  369.         PrioEnQueue(&Filled, ChapRec[chapter].record);
  370.       else
  371.         EnQueue(&Filled, ChapRec[chapter].record);
  372.       ChapRec[chapter].record = DeQueue(&Avail);
  373.     }
  374.   }
  375.   ChapRec[chapter].usedpage = FALSE;
  376.   ChapRec[chapter].countdown = 0;
  377. }
  378.  
  379. void StartChapter(unsigned int chapter, unsigned int control, unsigned int
  380.     pagenum, unsigned int subnum, unsigned char* buffer)
  381. {
  382.   if(subtitle != 8)
  383.     AcceptChapter(16), subtitle = 8;
  384.   if(control & 0x0400) /* interrupt bit is on (this is a interrupt page) */
  385.   {
  386.     if(isint[chapter]) /* interrupt on interrupt */
  387.     {
  388.       chapter += 8;
  389.       if(ChapRec[chapter].pagenum == pagenum && ChapRec[chapter].lastline <= 22
  390.           && !ChapRec[chapter].special)
  391.         return; /* continue page, unless finished or newsflash or subtitle */
  392.       if(!ChapRec[chapter].special && (control & 0xc0) != 0)
  393.         subtitle = chapter - 8, chapter = 16;
  394.       else
  395.         AcceptChapter(chapter); /* accept previous interrupt page */
  396.     }
  397.     else
  398.       isint[chapter] = TRUE, chapter += 8;
  399.     ChapRec[chapter].potkilled = FALSE;
  400.   }
  401.   else
  402.   {
  403.     if(isint[chapter])  /* but last page was interrupt page */
  404.       AcceptChapter(8 + chapter), isint[chapter] = FALSE;
  405.     if(ChapRec[chapter].pagenum == pagenum && ChapRec[chapter].lastline <= 22 &&
  406.         !ChapRec[chapter].special)
  407.     {
  408.       if(ChapRec[chapter].usedpage)
  409.         return;  /* continue page */
  410.       ChapRec[chapter].potkilled = TRUE; /* make sure page start with line 1 */
  411.     }
  412.     else
  413.     {
  414.       AcceptChapter(chapter);
  415.       ChapRec[chapter].potkilled = FALSE;
  416.     }
  417.   }
  418.   ChapRec[chapter].special = (control & 0xc0) != 0; /* newsflash or subtitle */
  419.   ChapRec[chapter].pagenum = pagenum;
  420.   ChapRec[chapter].numlines = 0;
  421.   ChapRec[chapter].contentsset = FALSE, ChapRec[chapter].usedpage = TRUE;
  422.   *(unsigned int*)(ChapRec[chapter].record->page[0] + 0) = control;
  423.   *(unsigned int*)(ChapRec[chapter].record->page[0] + 2) = pagenum;
  424.   *(unsigned int*)(ChapRec[chapter].record->page[0] + 4) = subnum;
  425.   memset(ChapRec[chapter].record->page + 1, ' ', 24 * 40);
  426.   memcpy(ChapRec[chapter].record->page[0] + 8, buffer + 8, 32);
  427. }
  428.  
  429. void interrupt TeletextInterrupt(void)
  430. /* hardware interrupt handler */
  431. {
  432.   static unsigned char buf[40];
  433.   static int semaphore = FALSE;
  434.   static unsigned vidline, numlines, parerror;
  435.   static int br, ag, se, st, me, mt, ue, ut, ca, cb, chapter, line;
  436.   static unsigned int pagenum, control, min, hour, ii;
  437.  
  438.   asm mov al,20h
  439.   asm out 20h, al  // give end of interrupt
  440.   if(hardirq > 8)
  441.   {
  442.     asm mov al,20h
  443.     asm out 0a0h, al
  444.   }
  445.   if(semaphore)
  446.     return;
  447.   semaphore = TRUE;
  448.   asm sti
  449.   asm cld
  450.   for(ii = 0; ii < 16; ii++)
  451.     if(ChapRec[ii].countdown != 0 && --ChapRec[ii].countdown == 0)
  452.       AcceptChapter(ii);
  453.   numlines = inportb(hardaddr + 1) & 0x7f;
  454.   asm db 0ebh, 0 ; /* jmp short $+2, short wait */
  455.   outportb(hardaddr + 1, 0);
  456.   for(; numlines > 0; --numlines, outportb(hardaddr + 0, 0))
  457.   {
  458.     vidline = inportb(hardaddr);
  459.     br = Hamming[inportb(hardaddr)], ag = Hamming[inportb(hardaddr)];
  460.     if(br != -1 && ag != -1)
  461.     {
  462.       line = (br >> 3) + (ag << 1), chapter = br & 0x7;
  463.       if(line > 24)
  464.         continue;
  465.       parerror = 0;
  466.  
  467.       // assembler used for extra speed
  468.       _DX = hardaddr;
  469.       asm mov bx,di       // save di
  470.       asm mov cx,40
  471.       asm mov di,offset buf
  472.       asm mov ax,ds
  473.       asm mov es,ax
  474. next:
  475.       asm in al,dx
  476.       asm or al,al
  477.       asm jnp nopar
  478.       parerror++;
  479.       asm mov al,27  // parity error character is replaced by Esc char
  480. nopar:
  481.       asm stosb
  482.       asm loop next
  483.       asm mov di,bx
  484.  
  485.       if(line == 0)
  486.       {
  487.         /* page header */
  488.         se = Hamming[buf[0]], st = Hamming[buf[1]];
  489.         me = Hamming[buf[2]], mt = Hamming[buf[3]];
  490.         ue = Hamming[buf[4]], ut = Hamming[buf[5]];
  491.         ca = Hamming[buf[6]], cb = Hamming[buf[7]];
  492.         if(se != -1 && st != -1 && me != -1 && mt != -1 && ue != -1 &&
  493.            ut != -1 && ca != -1 && cb != -1)
  494.         {
  495.           pagenum = st * 10 + se;
  496.           if(pagenum < 100)
  497.           {
  498.             control = ((mt & 0x08 /* c4 */) << 2) | ((ut & 0x0c /* c6/5 */)
  499.                << 4) | ((ca & 0x0f /* c10..7 */) << 8) | ((cb & 0x0f
  500.                 /* c14..11 */) << 12);
  501.             min = (mt & 0x07) * 10 + me, hour = (ut & 0x03) * 10 + ue;
  502.             pagenum += (chapter == 0 ? 8 : chapter) * 100;
  503.             StartChapter(chapter, control, pagenum, hour * 100 + min, buf);
  504.             if(isint[chapter])
  505.               if(chapter == subtitle)
  506.                 chapter = 16;
  507.               else
  508.                 chapter += 8;
  509.             if(parerror != 0)
  510.               /* parity error in header, just started page is bad */
  511.               KillChapter(chapter);
  512.             ChapRec[chapter].lastline = line;
  513.           }
  514.           else
  515.           if(ChapRec[chapter + (isint[chapter] != 0 ? 8 : 0)].special) 
  516.             AcceptChapter(chapter + (isint[chapter] ? 8 : 0));
  517.         }
  518.         else
  519.         {
  520.           PotKillChapter(chapter + (isint[chapter] ? 8 : 0));
  521.           AcceptChapter(chapter + (isint[chapter] ? 8 : 0));
  522.         }
  523.       }
  524.       else
  525.       {
  526.         if(isint[chapter])
  527.           if(chapter == subtitle)
  528.             chapter = 16;
  529.           else
  530.             chapter += 8;
  531.         if(!ChapRec[chapter].usedpage)
  532.           continue;
  533.         memcpy(ChapRec[chapter].record->page[line], buf, 40);
  534.         if(ChapRec[chapter].potkilled && line != ChapRec[chapter].lastline + 1)
  535.           KillChapter(chapter);
  536.         else
  537.         {
  538.           /* accept line */
  539.           if(ChapRec[chapter].numlines > 40)
  540.             KillChapter(chapter); /* too many lines, probably header missed */
  541.           ChapRec[chapter].numlines++;
  542.           ChapRec[chapter].contentsset = TRUE;
  543.           ChapRec[chapter].potkilled = FALSE;
  544.           ChapRec[chapter].countdown = 10; /* accept after 10 interrupts */
  545.           ChapRec[chapter].lastline = line;
  546.         }
  547.       }
  548.     }
  549.   }
  550.   outportb(hardaddr + 1, 0); // reset ram
  551.   semaphore = FALSE;
  552. }
  553.  
  554.