home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 5 / ctrom5b.zip / ctrom5b / CT / CT9404 / TTDEMO / SOURCE.ZIP / PAGER.PAS < prev    next >
Pascal/Delphi Source File  |  1994-02-01  |  20KB  |  683 lines

  1. (***********************************************************************
  2. * pager.pas                                                            *
  3. * Copyright (c) 1993 QQS - All rights reserved                         *
  4. * This file is donated to the public domain                            *
  5. *                                                                      *
  6. * version 1.0 November 24, 1993                                        *
  7. * convert teletext pages & display as text                             *
  8. ***********************************************************************)
  9.  
  10. program pager;
  11.  
  12. uses dos, strings, crt, tttest, tunefunc;
  13.  
  14. type
  15.   bufrow = array[0..39] of byte;
  16.   RecPtr = ^rec;
  17.   rec = record
  18.     page : array[0..25, 0..39] of byte; (* teletext page, control in [0][0..1]
  19.                                    pagenum in [0][2..3], subnum in [0][4..5] *)
  20.     next : RecPtr;
  21.   end;
  22.   Queue = record
  23.     head, tail : RecPtr; (* dequeue from head, enqueue on tail *)
  24.   end;
  25.   chapr = record
  26.     rec : RecPtr;
  27.     pagenum : word; (* pagenumber of this page *)
  28.     numlines : word; (* number of lines put in this page *)
  29.     lastline : word; (* last teletext line  received on this page *)
  30.     countdown : byte; (* countdown for acceptance *)
  31.     special : boolean; (* newsflash or subtitle page? *)
  32.     potkilled : boolean; (* page must be killed if potkilled and
  33.                             line <> lastline + 1 *)
  34.     usedpage : boolean; (* page in use? *)
  35.     contentsset : boolean; (* boolean whether contents is received *)
  36.   end;
  37.  
  38. var
  39.   Hamming : array[0..255] of integer; (* translates hammingcodes, if Hamming[x]
  40.       in 0..15 -> Hamming[x] is value for x, Hamming[x] == -1 -> x is an error
  41.       value *)
  42.   OddParity : array[0..255] of boolean; (* OddParity denotes whether char ii is
  43.                                            odd *)
  44.   tuneval : longint; (* tune frequency (default = direct video) *)
  45.   hardirq : word; (* card address *)
  46.   hardaddr : word;
  47.   oldmask : byte; (* old interrupt mask *)
  48.   oldvector : pointer; (* old int vector *)
  49.   Filled, Avail : Queue;  (* queue of filled and available pages *)
  50.  
  51.   (* locals for main program *)
  52.   remain : byte;
  53.   ok : boolean;
  54.   ii, jj : word;
  55.   ll : longint;
  56.   argv : string;
  57.   pageinfo : RecPtr;
  58.   control, pagenum, subnum : word;
  59.   graphics : boolean;
  60.   ch : byte;
  61.   finished : boolean;
  62.   wordptr : ^word;
  63.  
  64.   (* static locals for interrupt *)
  65.   buf : bufrow; (* store lines here *)
  66.   semaphore : boolean; (* prevents interrupt overrun *)
  67.   vidline : byte; (* television videoline with teletext *)
  68.   numlines, parerror : word;
  69.   br, ag, se, st, me, mt, ue, ut, ca, cb : integer;
  70.   chapter, line : integer; (* teletext line number *)
  71.   intrpagenum, intrcontrol, intrmin, intrhour, intrii : word;
  72.  
  73.   ChapRec : array[0..16] of chapr; (* info for each teletext + interrupt page
  74.                                         + subtitle *)
  75.   isint : array[0..7] of boolean; (* determines whether chapter currently is
  76.                                      an interrupt page *)
  77.   subtitle : word; (* 0..7 -> chapter is subtitle use chaprec[16], *)
  78.                    (* 8 -> no subtitle *)
  79.   intrready : boolean;
  80.   intrwordptr : ^word;
  81.  
  82. (* page processing functions called from within interrupt, at end of file *)
  83. function DeQueue(var queue : Queue) : RecPtr; forward;
  84. procedure EnQueue(var queue : Queue; rec : RecPtr); forward;
  85. procedure KillChapter(chapter : word); forward;
  86.  
  87. procedure TeletextInterrupt(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES,
  88.     BP : word); interrupt; forward;
  89.  
  90. procedure InitHam;
  91. const
  92.   valid : array[0..15] of integer = ($15, $02, $49, $5e, $64, $73, $38,
  93.       $2f, $d0, $c7, $8c, $9b, $a1, $b6, $fd, $ea);
  94.       (* valid hammingcodes 0..15 *)
  95. var
  96.   ii : integer;
  97. begin
  98.   for ii := 0 to 255 do
  99.     Hamming[ii] := -1;
  100.   for ii := 0 to 15 do
  101.     Hamming[valid[ii]] := ii;
  102. end;
  103.  
  104. procedure InitOddParity;
  105. label even;
  106. var
  107.   ii : integer;
  108. begin
  109.   for ii := 0 to 255 do
  110.   begin
  111.     asm
  112.       mov AL, byte ptr [ii]
  113.       or AL, AL
  114.       jp even
  115.     end;
  116.     OddParity[ii] := true;
  117.     continue;
  118. even:
  119.     OddParity[ii] := false;
  120.   end;
  121. end;
  122.  
  123. procedure StartInt;
  124. (* set interrupt for teletext *)
  125. var
  126.   pat : byte;
  127. begin
  128.   if(hardirq < 10) then
  129.   begin
  130.     GetIntVec(hardirq + 8, oldvector);
  131.     SetIntVec(hardirq + 8, @TeletextInterrupt);
  132.     pat := not (1 shl hardirq);
  133.     asm
  134.       in al, 21h
  135.       mov [oldmask], al
  136.       and al, [pat]   (* enable hard int *)
  137.       out 21h, al
  138.     end;
  139.   end
  140.   else
  141.   begin
  142.     GetIntVec(hardirq + $68, oldvector);
  143.     SetIntVec(hardirq + $68, @TeletextInterrupt);
  144.     pat := not (1 shl (hardirq - 8));
  145.     asm
  146.       in al, 0a1h
  147.       mov [oldmask], al
  148.       and al, [pat]   (* enable hard int *)
  149.       out 0a1h, al
  150.     end;
  151.   end;
  152.   port[hardaddr + 1] := 0; (* reset ram linenr & busybit *)
  153. end;
  154.  
  155. procedure StopInt;
  156. (* restore setting of interrupt after teletext *)
  157. var
  158.   pat : byte;
  159. begin
  160.   port[hardaddr + 2] :=  0; (* deselect tuner, int off *)
  161.   if(hardirq < 10) then
  162.   begin
  163.     pat := (1 shl hardirq) and oldmask;
  164.     asm
  165.       in al, 21h
  166.       or al, [pat]     (* set bit int to old value *)
  167.       out 21h, al
  168.     end;
  169.     SetIntVec(8 + hardirq, oldvector);
  170.   end
  171.   else
  172.   begin
  173.     pat := (1 shl (hardirq - 8)) and oldmask;
  174.     asm
  175.       in al, 0a1h
  176.       or al,[pat]     (* set bit int to old value *)
  177.       out 0a1h, al
  178.     end;
  179.     SetIntVec($68 + hardirq, oldvector);
  180.   end;
  181. end;
  182.  
  183. procedure initialize;
  184. (* set up datastructure *)
  185. var
  186.   ii : integer;
  187.   mem : RecPtr;
  188. begin
  189.  
  190.   for ii := 0 to 16 do
  191.     new(ChapRec[ii].rec);
  192.   Filled.head := nil;
  193.   Filled.tail := nil;
  194.   Avail.head := nil;
  195.   Avail.tail := nil;
  196.   for ii := 0 to 25 do
  197.   begin
  198.     new(mem);
  199.     EnQueue(Avail, mem);
  200.   end;
  201.   for ii := 0 to 16 do
  202.     KillChapter(ii);
  203.   for ii := 0 to 7 do;
  204.     isint[ii] := false;
  205. end;
  206.  
  207. (* teletext interrupt handler & functions called from within interrupt
  208.    use as little as possible local variables, because they are put on an
  209.    unknown stack. *)
  210. {$S-} (* no stack check *)
  211.  
  212. (* queue functions *)
  213.  
  214. function DeQueue(var queue : Queue) : RecPtr;
  215. (* return dequeued value from queue. pre: queue isn't empty. *)
  216. var
  217.   rec : RecPtr;
  218. begin
  219.   rec := queue.head;
  220.   if (queue.head = queue.tail) then
  221.   begin
  222.     queue.head := nil;
  223.     queue.tail := nil;
  224.   end
  225.   else
  226.     queue.head := queue.head^.next;
  227.   Dequeue := rec;
  228. end;
  229.  
  230. procedure EnQueue(var queue : Queue; rec : RecPtr);
  231. (* enqueue record in queue *)
  232. begin
  233.   rec^.next := nil;
  234.   if (queue.tail = nil) then
  235.     queue.head := rec
  236.   else
  237.     queue.tail^.next := rec; 
  238.   queue.tail := rec;
  239. end;
  240.  
  241. procedure PrioEnQueue(var queue : Queue; rec : RecPtr);
  242. (* enqueue record in queue with priority at start of queue *)
  243. begin
  244.   rec^.next := queue.head;
  245.   queue.head := rec;
  246.   if (queue.tail = nil) then
  247.     queue.tail := rec;
  248. end;
  249.  
  250. (* functions making complete page of teletext lines *)
  251.  
  252. procedure KillChapter(chapter : word);
  253. begin
  254.   ChapRec[chapter].usedpage := false;
  255. end;
  256.  
  257. procedure PotKillChapter(chapter : word);
  258. begin
  259.   ChapRec[chapter].potkilled := true;
  260. end;
  261.  
  262. procedure AcceptChapter(chapter : word);
  263. begin
  264.   if (ChapRec[chapter].usedpage and ChapRec[chapter].contentsset and
  265.      (not chapRec[chapter].potkilled or (ChapRec[chapter].lastline > 22) or
  266.      ChapRec[chapter].special)) then
  267.   begin
  268.     (* process completed page *)
  269.     if (Avail.head <> nil) then
  270.     begin
  271.       if (ChapRec[chapter].special) then
  272.         PrioEnQueue(Filled, ChapRec[chapter].rec)
  273.       else
  274.         EnQueue(Filled, ChapRec[chapter].rec);
  275.       ChapRec[chapter].rec := DeQueue(Avail);
  276.     end;
  277.   end;
  278.   ChapRec[chapter].usedpage := false;
  279.   ChapRec[chapter].countdown := 0;
  280. end;
  281.  
  282. procedure StartChapter(chapter, control, pagenum, subnum : word; buffer :
  283.     bufrow);
  284. begin
  285.   intrready := false;
  286.   if (subtitle <> 8) then
  287.   begin
  288.     AcceptChapter(16);
  289.     subtitle := 8;
  290.   end;
  291.   if (control and $0400) <> 0 then
  292.   begin (* interrupt bit is on (this is a interrupt page) *)
  293.     if (isint[chapter]) then (* interrupt on interrupt *)
  294.     begin
  295.       chapter := chapter + 8;
  296.       if (ChapRec[chapter].pagenum = pagenum) and (ChapRec[chapter].lastline <=
  297.           22) and (not ChapRec[chapter].special) then
  298.         intrready := true; (* continue page *)
  299.       if not intrready then
  300.       begin
  301.         if (not ChapRec[chapter].special) and ((control and $c0) <> 0) then
  302.         begin
  303.           subtitle := chapter - 8;
  304.           chapter := 16;
  305.         end
  306.         else
  307.           AcceptChapter(chapter); (* accept previous interrupt page *)
  308.       end;
  309.     end
  310.     else
  311.     begin
  312.       isint[chapter] := true;
  313.       chapter := chapter + 8;
  314.     end;
  315.     if not intrready then
  316.       ChapRec[chapter].potkilled := false;
  317.   end
  318.   else
  319.   begin
  320.     if (isint[chapter]) then (* but last page was interrupt page *)
  321.     begin
  322.       AcceptChapter(8 + chapter);
  323.       isint[chapter] := false;
  324.     end;
  325.     if (ChapRec[chapter].pagenum = pagenum) and (ChapRec[chapter].lastline <=
  326.         22) and (not ChapRec[chapter].special) then
  327.     begin
  328.       if (ChapRec[chapter].usedpage) then
  329.         intrready := true  (* continue page *)
  330.       else
  331.         ChapRec[chapter].potkilled := true; (* page must start with line 1 *)
  332.     end
  333.     else
  334.     begin
  335.       AcceptChapter(chapter);
  336.       ChapRec[chapter].potkilled := false;
  337.     end;
  338.   end;
  339.   if not intrready then
  340.   begin
  341.     ChapRec[chapter].special := (control and $c0) <> 0; (* newsfl./subtitle *)
  342.     ChapRec[chapter].pagenum := pagenum;
  343.     ChapRec[chapter].numlines := 0;
  344.     ChapRec[chapter].contentsset := false;
  345.     ChapRec[chapter].usedpage := true;
  346.     Fillchar(ChapRec[chapter].rec^.page, 25 * 40,' ');
  347.     (* put in array as backwords *)
  348.     ChapRec[chapter].rec^.page[0][0] := control and $ff;
  349.     ChapRec[chapter].rec^.page[0][1] := control shr 8;
  350.     ChapRec[chapter].rec^.page[0][2] := pagenum and $ff;
  351.     ChapRec[chapter].rec^.page[0][3] := pagenum shr 8;
  352.     ChapRec[chapter].rec^.page[0][4] := subnum and $ff;
  353.     ChapRec[chapter].rec^.page[0][5] := subnum shr 8;
  354.     Move(buf[8], ChapRec[chapter].rec^.page[0][8], 32);
  355.   end;
  356. end;
  357.  
  358. procedure TeletextInterrupt;
  359. label next, nopar;
  360. begin
  361.   asm
  362.     mov al,20h
  363.     out 20h, al  (* give end of interrupt *)
  364.   end;
  365.   if(hardirq > 8) then
  366.     asm
  367.       mov al,20h
  368.       out 0a0h, al
  369.     end;
  370.   if(not semaphore) then
  371.   begin
  372.     semaphore := true;
  373.     asm
  374.       sti
  375.       cld
  376.     end;
  377.  
  378.     for intrii := 0 to 15 do
  379.       if (ChapRec[intrii].countdown <> 0) then
  380.       begin
  381.         ChapRec[intrii].countdown := ChapRec[intrii].countdown - 1;
  382.         if (ChapRec[intrii].countdown = 0) then
  383.           AcceptChapter(intrii);
  384.       end;
  385.  
  386.     numlines := port[hardaddr + 1] and $7f;
  387.     asm
  388.       db 0ebh, 0 (* jmp short $+2, short wait *)
  389.     end;
  390.     port[hardaddr + 1] := 0;
  391.     while(numlines > 0) do
  392.     begin
  393.       vidline := port[hardaddr];
  394.       br := Hamming[port[hardaddr]];
  395.       ag := Hamming[port[hardaddr]];
  396.       if((br <> -1) and (ag <> -1)) then
  397.       begin
  398.  
  399.         line := (br shr 3) + (ag shl 1);
  400.         chapter := br and $7;
  401.         if (line > 24) then
  402.         begin
  403.           numlines := numlines - 1;
  404.           port[hardaddr + 0] := 0;
  405.           continue;
  406.         end;
  407.         parerror := 0;
  408.  
  409.         for intrii := 0 to 39 do
  410.         begin
  411.           buf[intrii] := port[hardaddr];
  412.           if (not OddParity[buf[intrii]]) then
  413.           begin
  414.             parerror := parerror + 1;
  415.             buf[intrii] := 27; (* Esc char *)
  416.           end;
  417.         end;
  418.  
  419.         if (line = 0) then
  420.         begin
  421.           (* page header *)
  422.           se := Hamming[buf[0]];
  423.           st := Hamming[buf[1]];
  424.           me := Hamming[buf[2]];
  425.           mt := Hamming[buf[3]];
  426.           ue := Hamming[buf[4]];
  427.           ut := Hamming[buf[5]];
  428.           ca := Hamming[buf[6]];
  429.           cb := Hamming[buf[7]];
  430.           if (se <> -1) and (st <> -1) and (me <> -1) and (mt <> -1) and
  431.              (ue <> -1) and (ut <> -1) and (ca <> -1) and (cb <> -1) then
  432.           begin
  433.             intrpagenum := st * 10 + se;
  434.             if (intrpagenum < 100) then
  435.             begin
  436.               intrcontrol := ((mt and $08 (* c4 *)) shl 2) or ((ut and $0c
  437.                  (* c6/5 *)) shl 4) or ((ca and $0f (* c10..7 *)) shl 8) or
  438.                  ((cb and $0f (* c14..11 *)) shl 12);
  439.               intrmin := (mt and $07) * 10 + me;
  440.               intrhour := (ut and $03) * 10 + ue;
  441.               if (chapter = 0) then
  442.                 intrpagenum := intrpagenum + 800
  443.               else
  444.                 intrpagenum := intrpagenum + chapter * 100;
  445.               StartChapter(chapter, intrcontrol, intrpagenum, intrhour * 100 +
  446.                   intrmin, buf);
  447.               if (isint[chapter]) then
  448.                 if (chapter = subtitle) then
  449.                   chapter := 16
  450.                 else
  451.                   chapter := chapter + 8;
  452.               if (parerror <> 0) then
  453.                 (* parity error in header, just started page is bad *)
  454.                 KillChapter(chapter);
  455.               ChapRec[chapter].lastline := line;
  456.             end
  457.             else
  458.             begin
  459.               if isint[chapter] then
  460.               begin
  461.                 if (ChapRec[chapter + 8].special) then
  462.                   AcceptChapter(chapter + 8);
  463.               end
  464.               else
  465.               begin
  466.                 if (ChapRec[chapter].special) then
  467.                   AcceptChapter(chapter);
  468.               end;
  469.             end;
  470.           end
  471.           else
  472.           begin
  473.             if isint[chapter] then
  474.             begin
  475.               PotKillChapter(chapter + 8);
  476.               AcceptChapter(chapter + 8);
  477.             end
  478.             else
  479.             begin
  480.               PotKillChapter(chapter);
  481.               AcceptChapter(chapter);
  482.             end;
  483.           end;
  484.         end
  485.         else
  486.         begin
  487.           if (isint[chapter]) then
  488.             if (chapter = subtitle) then
  489.               chapter := 16
  490.             else
  491.               chapter := chapter + 8;
  492.           if (not ChapRec[chapter].usedpage) then
  493.           begin
  494.             numlines := numlines - 1;
  495.             port[hardaddr] := 0;
  496.             continue;
  497.           end;
  498.           Move(buf, ChapRec[chapter].rec^.page[line], 40);
  499.           if ChapRec[chapter].potkilled and (line <> ChapRec[chapter].lastline
  500.               + 1) then
  501.             KillChapter(chapter)
  502.           else
  503.           begin
  504.             (* accept line *)
  505.             if (ChapRec[chapter].numlines > 40) then
  506.               KillChapter(chapter); (* too many lines, probably header missed *)
  507.             ChapRec[chapter].numlines := ChapRec[chapter].numlines + 1;
  508.             ChapRec[chapter].contentsset := true;
  509.             ChapRec[chapter].potkilled := false;
  510.             ChapRec[chapter].countdown := 10; (* accept after 10 interrupts *)
  511.             ChapRec[chapter].lastline := line;
  512.           end;
  513.         end;
  514.         numlines := numlines - 1;
  515.         port[hardaddr] := 0;
  516.       end;
  517.     end;
  518.     port[hardaddr + 1] := 0;
  519.     semaphore := false;
  520.   end;
  521. end;
  522.  
  523. begin
  524.   (* initialize global variables *)
  525.   tuneval := 0;
  526.   hardirq := 11;
  527.   hardaddr := $130;
  528.   (* initialize global variable for interrupt *)
  529.   semaphore := false;
  530.  
  531.   if not cardtest(hardaddr) then
  532.     writeln('No teletext card detected')
  533.   else
  534.   begin
  535.     (* load frequency if given as parameter *)
  536.     ok := true;
  537.     if (ParamCount > 0) then
  538.     begin
  539.       if (ParamCount > 1) then
  540.         ok := false
  541.       else
  542.       begin
  543.         argv := ParamStr(1);
  544.  
  545.         for ii := 1 to Length(argv) do
  546.         begin
  547.           if (argv[ii] < '0') or (argv[ii] > '9') then
  548.             break;
  549.           tuneval := tuneval * 10 + ord(argv[ii]) - ord('0');
  550.         end;
  551.         tuneval := tuneval * 1000000;
  552.         if (argv[ii] = '.') then
  553.         begin
  554.           ii := ii + 1;
  555.           ll := 0;
  556.           jj := 0;
  557.           while (ii + jj <= Length(argv)) and (argv[ii + jj] >= '0') and
  558.               (argv[ii + jj] <= '9') do
  559.           begin
  560.             ll := ll * 10 + ord(argv[ii + jj]) - ord('0');
  561.             jj := jj + 1;
  562.           end;
  563.           ii := ii + jj;
  564.           while (jj < 6) do
  565.           begin
  566.             ll := ll * 10;
  567.             jj := jj + 1;
  568.           end;
  569.           tuneval := tuneval + ll;
  570.         end;
  571.         if(ii <> (Length(argv) + 1)) then
  572.           ok := false;
  573.       end;
  574.       if not ok then
  575.       begin
  576.         writeln('Usage: PAGER <frequency in MHz (default = 0, direct video)>');
  577.         halt;
  578.       end;
  579.     end;
  580.  
  581.     writeln('Press Esc to exit program...');
  582.  
  583.     initialize;
  584.  
  585.     TunefuncSetAddr(hardaddr);
  586.     SelectChannel(tuneval); (* you can use 0 or a value 46000000L..870000000L *)
  587.  
  588.     InitHam;
  589.     InitOddParity;
  590.     StartInt;
  591.  
  592.     remain := port[hardaddr + 2] and $2; (* get tuner/video selection bit *)
  593.     port[hardaddr + 2] := remain or 1; (* interrupts on *)
  594.  
  595.     (* Insert teletext active code here *)
  596.     finished := false;
  597.     while true do
  598.     begin
  599.       if finished or KeyPressed and (ord(ReadKey) = $1b) then
  600.         break; (* Esc pressed, stop *)
  601.       if (Filled.head <> nil) then (* teletext page filled *)
  602.       begin
  603.         asm
  604.           cli
  605.         end;
  606.         pageinfo := DeQueue(Filled);
  607.         asm
  608.           sti
  609.         end;
  610.  
  611.         (* we have a page in pageinfo, process it.
  612.          pages have same format as 'BINAIR' saved page in TT program *)
  613.  
  614.         (* as an example, we display the page here as text *)
  615.  
  616.         (* get from in array as backwords *) (* control is not used *)
  617.         control := pageinfo^.page[0][0] or pageinfo^.page[0][1] shl 8;
  618.         pagenum := pageinfo^.page[0][2] or pageinfo^.page[0][3] shl 8;
  619.         subnum := pageinfo^.page[0][4] or pageinfo^.page[0][5] shl 8;
  620.  
  621.         (* strip parity *)
  622.         for ii := 0 to 24 do
  623.           for jj := 0 to 39 do
  624.             if (pageinfo^.page[ii][jj] = 27) then
  625.               pageinfo^.page[ii][jj] := ord(' ') (* parity character *)
  626.             else
  627.               pageinfo^.page[ii][jj] := (pageinfo^.page[ii][jj] and $7f);
  628.  
  629.  
  630.         (* display page as text *)
  631.         write(pagenum : 3, '/', subnum);
  632.         if subnum < 1000 then
  633.         begin
  634.           write(' ');
  635.           if subnum < 100 then
  636.           begin
  637.             write(' ');
  638.             if subnum < 10 then
  639.               write(' ');
  640.           end;
  641.         end;
  642.         for ii := 0 to 24 do
  643.         begin
  644.           if KeyPressed and (ord(ReadKey) = $1b) then (* Esc pressed, stop *)
  645.           begin
  646.             finished := true;
  647.             break;
  648.           end;
  649.           graphics := false;
  650.           if ii = 0 then (* line 0 starts at 8 *)
  651.             jj := 8
  652.           else
  653.             jj := 0;
  654.           while jj < 40 do
  655.           begin
  656.             ch := pageinfo^.page[ii][jj];
  657.             if (ch < 8) then
  658.               graphics := false
  659.             else
  660.             if (ch >= 16) and (ch < 23) then
  661.               graphics := true;
  662.             if graphics or (ord(ch) < 32) then
  663.               ch := ord(' '); (* display space instead of control character *)
  664.             write(chr(ch));
  665.             jj := jj + 1;
  666.           end;
  667.           writeln;
  668.         end;
  669.  
  670.         (* make the pageinfo available again, so it can be recycled *)
  671.         asm
  672.           cli
  673.         end;
  674.         EnQueue(Avail, pageinfo);
  675.         asm
  676.           sti
  677.         end;
  678.       end;
  679.     end;
  680.     StopInt;
  681.   end;
  682. end.
  683.