home *** CD-ROM | disk | FTP | other *** search
/ The Best of Mecomp Multimedia 1 / Mecomp-CD.iso / amiga / tools / misc / videotext5.41 / src / cct.p < prev    next >
Encoding:
Text File  |  1997-06-15  |  13.4 KB  |  401 lines

  1. UNIT cct; {$project vt }
  2. { Programmierung des CCT-Chips (Computer Controlled Teletext) SAA5246 }
  3.  
  4. INTERFACE; FROM vt USES global;
  5. {$incl "i2c.lib" }
  6.  
  7. CONST PF_SERMAG = $0800; { C11  offizielle Seitenflags }
  8.       PF_RENEW  = $0100; { C8 }
  9.       PF_STITLE = $0040; { C6 }
  10.       PF_HEADLN = $0020; { C5 }
  11.       PF_ISMAP  = $0004; { inoffizielle Flags für meine eigenen Zwecke, }
  12.       PF_PSEUDO = $0002; { "C3 bis C0", existieren im }
  13.       PF_LOCAL  = $0001; { Standard aber auch nicht }
  14. CONST maxerror=9;
  15. VAR i2c_error: ARRAY[0..maxerror] OF String[30];
  16. VAR i2c_status, tv_status: Integer;
  17.  
  18. PROCEDURE busmaster(really: Boolean);
  19. PROCEDURE display_select(speicher: Byte);
  20. PROCEDURE TV_display(mode: Byte);
  21. FUNCTION VTstat: Byte;
  22. PROCEDURE sperren(speicher: Byte);
  23. PROCEDURE anfordern(speicher: Byte; page, subpage: Integer; mask: Byte);
  24. PROCEDURE init_CCT;
  25. FUNCTION seite_da(speicher: Byte): Boolean;
  26. PROCEDURE gettime(VAR zeit: Str80);
  27. PROCEDURE gethead(VAR head: Str80);
  28. PROCEDURE getpage(speicher: Byte; seite: p_onepage; ganz: Boolean);
  29. PROCEDURE add_sp_number(seite: p_onepage);
  30. PROCEDURE char_to_page(x,y: Integer; c: Char; seite: p_onepage);
  31. PROCEDURE write_to_page(x,y: Integer; txt: Str80; vgf,hgf: Integer;
  32.                        seite: p_onepage);
  33. PROCEDURE init_page(seite: p_onepage);
  34.  
  35. { ---------------------------------------------------------------------- }
  36.  
  37. IMPLEMENTATION;
  38.  
  39. {$opt q,s+,i+ } { keine Laufzeitprüfungen außer Stack und Feldindizes }
  40.  
  41. CONST SAA5246 = $22;
  42.       PBLF = $20;
  43.       DOCARE =  $10;
  44.       NOTHOLD = $08;
  45.  
  46. VAR i2cdata: ARRAY[1..20] OF Byte;    { kleiner Puffer für Kommandos }
  47.     aktspeicher: Integer;   { der Speicher, in dem die VT-Uhr läuft }
  48.  
  49. PROCEDURE busmaster{(really: Boolean)};
  50. VAR l: Long;
  51. BEGIN
  52.   IF really THEN l := BringBackI2C
  53.     ELSE ShutDownI2C;
  54. END;
  55.  
  56. PROCEDURE send(addr: Byte; anz: Word; buffer: Ptr);
  57. { Dummy-Prozedur, um den Fehler "keine Library" aufzufangen }
  58. BEGIN
  59.   IF I2CBase=Nil THEN
  60.     i2c_status := maxerror
  61.   ELSE
  62.     i2c_status := (SendI2C(addr,anz,buffer) SHR 8) AND $FF;
  63. END;
  64.  
  65. PROCEDURE receive(addr: Byte; anz: Word; buffer: Ptr);
  66. BEGIN
  67.   IF I2CBase=Nil THEN
  68.     i2c_status := maxerror
  69.   ELSE
  70.     i2c_status := (ReceiveI2C(addr,anz,buffer) SHR 8) AND $FF;
  71. END;
  72.  
  73. PROCEDURE setregister(addr,reg,value: Byte);
  74. { Häufig benötigter Vorgang: ein einzelnes Register am I²C-Bus beschreiben. }
  75. BEGIN
  76.   i2cdata[1] := reg; i2cdata[2] := value;
  77.   send(addr,2,^i2cdata);
  78. END;
  79.  
  80. FUNCTION getregister(addr,reg: Byte): Byte;
  81. { Etwas umständlicher, wird auch seltener benötigt: ein einzelnes Register }
  82. { auslesen. NICHT schleifenweise aufrufen, um mehrere Bytes zu lesen! }
  83. BEGIN
  84.   send(addr,1,^reg);
  85.   receive(addr,1,^i2cdata);
  86.   getregister := i2cdata[1];
  87. END;
  88.  
  89. PROCEDURE display_select{(speicher: Byte)};
  90. { Einen der 8 Seitenspeicher zur Anzeige auswählen }
  91. BEGIN
  92.   setregister(SAA5246,4,speicher);
  93.   aktspeicher := speicher;
  94. END;
  95.  
  96. PROCEDURE TV_display{(mode: Byte)};
  97. { Art der Anzeige am Fernseher auswählen. }
  98. BEGIN
  99.   i2cdata[1] := 5;
  100.   CASE mode OF
  101.     2: { Normalseiten bildfüllend, Schlagzeilen als Boxen }
  102.       BEGIN  i2cdata[2] := %11001100; i2cdata[3] := %01001111; END;
  103.     1: { Normalseiten als Boxen, Schlagzeilen transparent }
  104.       BEGIN  i2cdata[2] := %11001111; i2cdata[3] := %00001111; END;
  105.     OTHERWISE  { Anzeige aus }
  106.       BEGIN  i2cdata[2] := %00000011; i2cdata[3] := %00000011; END;
  107.   END;
  108.   i2cdata[4] := %00000111;
  109.   send(SAA5246,4,^i2cdata);
  110.   tv_status := mode;
  111. END;
  112.  
  113. FUNCTION VTstat{: Byte};
  114. { Liest aus dem CCT das Register R11B. Bedeutung: bit0 - Videosignal vorh., }
  115. { bit1 - Textsignal vorh., bit2-5 - ROM-Version }
  116. BEGIN
  117.   setregister(SAA5246,0,%00000001);   { R11B aktivieren }
  118.   VTstat := getregister(SAA5246,11);    { Status lesen }
  119.   setregister(SAA5246,0,%00000000);   { R11 aktivieren }
  120. END;
  121.  
  122. PROCEDURE sperren{(speicher: Byte)};
  123. { Seitensuche eines Empfangskreises anhalten, damit nicht evtl. niederpriore }
  124. { Empfangskreise behindert werden. Die Sperre wird bei Programmierung einer }
  125. { neuen Seitenanforderung automatisch aufgehoben. }
  126. { Kreis 3 (der mit der niedrigsten Priorität) wird *nicht* gesperrt, da das }
  127. { unnötig ist. Stattdessen wird er auf Seite *** programmiert, so daß seine }
  128. { Titelzeile ständig durchläuft. }
  129. BEGIN
  130.   IF speicher=3 THEN
  131.     anfordern(speicher, $100, 0, %000)
  132.   ELSE BEGIN
  133.     i2cdata[1] := 2;
  134.     i2cdata[2] := speicher*16;
  135.     i2cdata[3] := 0;
  136.     send(SAA5246,3,^i2cdata);
  137.   END;
  138. END;
  139.  
  140. PROCEDURE anfordern{(speicher: Byte; page, subpage: Integer; mask: Byte)};
  141. { Einlesen einer Seite in einen Seitenspeicher 0..7 anfordern, Unterseite nur }
  142. { direkt anfordern, wenn subpage > 0 }
  143. { <mask> ist eine dreistellige Binärzahl, wo eine '0' steht, wird in der }
  144. { entsprechenden Ziffer der angeforderten Seitennummer das DOCARE-Bit }
  145. { gelöscht. }
  146. VAR lauf: Integer;
  147. BEGIN
  148.   { Seitenanforderung beschreiben: }
  149.   i2cdata[1] := 2;
  150.   i2cdata[2] := speicher*16;
  151.   i2cdata[3] := ((page SHR 8) AND $7) OR NOTHOLD;
  152.   i2cdata[4] := (page SHR 4) AND $F;
  153.   i2cdata[5] := page AND $F;
  154.   IF page > 0 THEN
  155.     FOR lauf := 3 TO 5 DO
  156.       IF Odd(mask SHR (5-lauf)) THEN
  157.         i2cdata[lauf] := i2cdata[lauf] OR DOCARE;
  158.   i2cdata[6] := (subpage SHR 12) AND $3;
  159.   i2cdata[7] := (subpage SHR 8) AND $F;
  160.   i2cdata[8] := (subpage SHR 4) AND $7
  161.   i2cdata[9] := subpage AND $F;
  162.   IF subpage > 0 THEN
  163.     FOR lauf := 6 TO 9 DO  i2cdata[lauf] := i2cdata[lauf] OR DOCARE;
  164.   send(SAA5246,9,^i2cdata);
  165. END;
  166.  
  167. PROCEDURE init_CCT;
  168. VAR i: Byte;
  169. BEGIN
  170.   { Wichtig in R1: bit5 - ACQ OFF, Decoder-NotAus; bit2 - TCS ON, }
  171.   { RGB-Ausgangssignal wird mit Fernsehbild synchronisiert. }
  172.   setregister(SAA5246,1,%00000100);
  173.   { Seitensuche sperren: }
  174.   FOR i := 0 TO maxactive-1 DO BEGIN
  175.     sperren(i); activejobs[i].pg := 0;
  176.   END;
  177.   { alle Seitenspeicher löschen: }
  178.   FOR i := 0 TO 7 DO BEGIN
  179.     setregister(SAA5246,8,%00001000 OR i); Delay(2);
  180.     { Löschvorgang dauert bis zu 22 ms, darum mal jeweils 40 ms warten. }
  181.   END;
  182.   { R11 aktivieren }
  183.   setregister(SAA5246,0,%00000000);
  184.   { Fernsehdarstellung einschalten }
  185.   display_select(aktspeicher);
  186.   TV_display(tv_status);
  187. END;
  188.  
  189. FUNCTION seite_da{(speicher: Byte): Boolean};
  190. { Überprüft, ob die für einen Speicher 0..7 angeforderte Seite bereits }
  191. { eingetroffen ist. Das Statusflag PBLF, das in diesem Fall gelöscht ist, }
  192. { muß danach wieder gesetzt werden. Das geschieht entweder implizit beim }
  193. { Programmieren einer neuen Seitenanforderung oder explizit durch }
  194. { Schreibzugriff, in diesem Programm in getpage(). }
  195. { Problem: das Statusflag PBLF bezieht sich auf den Empfang der Kopfzeile, }
  196. { nicht etwa den Empfang der kompletten Seite. Genauer gesagt: für letzteres }
  197. { existiert überhaupt kein Statusflag! }
  198. { Nun ist das nicht ganz so tragisch, nach Empfang der Kopfzeile kann man }
  199. { bereits bedenkenlos die Statuszeile 25 mit Steuerbits und Seitennummer }
  200. { lesen. Und in vielen Fällen geht es sogar gut, sofort mit dem Einlesen }
  201. { der gesamten Seite zu beginnen, da sich nämlich maximal 11 Seiten pro }
  202. { Sekunde über den I²C-Bus schaufeln lassen, während der Videotext selbst }
  203. { mit ca. 13 Seiten pro Sekunde einrollt: Die noch leere Seite wird }
  204. { schneller aufgefüllt, als sie ausgelesen werden kann. }
  205. { Probleme entstehen natürlich, wenn der Empfang der VT-Seite länger als }
  206. { ca. 1/10 s dauert. Das ist möglich, wenn der Sender nicht alle zur }
  207. { VT-Übertragung zulässigen Rasterzeilen ausnutzt, oder wenn er gar den }
  208. { Magazin-gemischten Übertragungsmodus verwendet. Letzteres ist aber an }
  209. { einem Statusflag (C11) vom Programm aus erkennbar, hm ... }
  210. BEGIN
  211.   seite_da := False;
  212.   { Byte 9 der Statuszeile (Zeile 25) adressieren ... }
  213.   i2cdata[1] := 8;
  214.   i2cdata[2] := speicher;
  215.   i2cdata[3] := 25; i2cdata[4] := 9;
  216.   send(SAA5246,4,^i2cdata);
  217.   IF i2c_status<>0 THEN Exit;
  218.   { ... und auslesen: }
  219.   receive(SAA5246,1,^i2cdata);
  220.   IF i2c_status<>0 THEN Exit;
  221.   seite_da := (i2cdata[1] AND PBLF)=0;
  222. END;
  223.  
  224. PROCEDURE gettime{(VAR zeit: Str80)};
  225. { liest aus der zur Anzeige ausgewählten Speicherseite die Uhrzeit }
  226. VAR i: Integer;
  227. BEGIN
  228.   zeit := '        ';
  229.   { Uhrzeit aus der Seite im CCT lesen: Zeile 0, Spalte 32 }
  230.   i2cdata[1] := 8;
  231.   i2cdata[2] := aktspeicher;
  232.   i2cdata[3] := 0; i2cdata[4] := 32;
  233.   send(SAA5246,4,^i2cdata);
  234.   IF i2c_status<>0 THEN Exit;
  235.   { Uhrzeit lesen: }
  236.   receive(SAA5246,8,^i2cdata);
  237.   IF i2c_status<>0 THEN Exit;
  238.   FOR i := 1 TO 8 DO
  239.     IF i2cdata[i] IN [32..127] THEN zeit[i] := Chr(i2cdata[i]);
  240. END;
  241.  
  242. PROCEDURE gethead{(VAR head: Str80)};
  243. { liest aus Speicher 3 die 24 Byte Titelzeile zwischen Statusfeld und }
  244. { Uhrzeit, Steuerzeichen werden entfernt }
  245. VAR i: Integer;
  246. BEGIN
  247.   head := '';
  248.   { Seitenspeicher addressieren: Zeile 0, Spalte 8 }
  249.   i2cdata[1] := 8;
  250.   i2cdata[2] := 3; { Speicher }
  251.   i2cdata[3] := 0; i2cdata[4] := 8;
  252.   send(SAA5246,4,^i2cdata);
  253.   IF i2c_status=0 THEN
  254.     receive(SAA5246,24,^head);  { Zeile lesen }
  255.   FOR i := 1 TO 24 DO
  256.     IF NOT ((i2c_status=0) AND (head[i] IN [' '..'~'])) THEN
  257.       head[i] := ' ';
  258.   head[25] := #0;
  259. END;
  260.  
  261. PROCEDURE getpage{(speicher: Byte; seite: p_onepage; ganz: Boolean)};
  262. { Liest eine Speicherseite (0..7) aus dem CCT-Baustein aus und speichert }
  263. { diese Seite im Amiga. Für <ganz>=false werden nur die Statusinformationen }
  264. { aus Zeile 25 gelesen. }
  265. VAR i,h: Integer;
  266.     status: ARRAY [0..9] OF Byte;
  267.     s: str80;
  268. BEGIN
  269.   seite^.pg := 0; seite^.sp := 0; seite^.cbits := 0;
  270.   { Status aus der Seite im CCT lesen: Zeile 25, Spalte 0 }
  271.   i2cdata[1] := 8;
  272.   i2cdata[2] := speicher;
  273.   i2cdata[3] := 25; i2cdata[4] := 0;
  274.   send(SAA5246,4,^i2cdata);
  275.   IF i2c_status<>0 THEN Exit;
  276.   receive(SAA5246,10,^status);
  277.   IF i2c_status<>0 THEN Exit;
  278.   seite^.pg := (status[0] AND $F) + (status[1] AND $F) SHL 4
  279.                                   + (status[8] AND $7) SHL 8;
  280.   IF seite^.pg<$100 THEN seite^.pg := seite^.pg + $800;
  281.   seite^.sp := (status[2] AND $F)       + (status[3] AND $7) SHL 4
  282.              + (status[4] AND $F) SHL 8 + (status[5] AND $3) SHL 12;
  283.   seite^.cbits := (status[3] AND $08) SHL 1   { C4 }
  284.                OR (status[5] AND $0C) SHL 3   { C5, C6 }
  285.                OR (status[6] AND $0F) SHL 7   { C7, C8, C9, C10 }
  286.                OR (status[7] AND $0F) SHL 11  { C11, C12..C14 }
  287.   IF make_bcd(get_bcd(seite^.pg))<>seite^.pg THEN
  288.     seite^.cbits := seite^.cbits OR PF_PSEUDO;
  289.   IF ganz THEN BEGIN
  290.     seite^.dejavu := False; { als Neuigkeit kennzeichnen }
  291.     { Seite einlesen. Richtigen Speicher, Zeile 0, Spalte 0 adressieren: }
  292.     i2cdata[1] := 8;
  293.     i2cdata[2] := speicher;
  294.     i2cdata[3] := 0; i2cdata[4] := 0;
  295.     send(SAA5246,4,^i2cdata);
  296.     IF i2c_status<>0 THEN Exit;
  297.     { Seite einlesen: }
  298.     receive(SAA5246,960,^seite^.chars);
  299.     IF i2c_status<>0 THEN Exit;
  300.     { aus dem zur Anzeige aktivierten Seitenspeicher die Uhrzeit holen: }
  301.     IF speicher<>aktspeicher THEN BEGIN
  302.       gettime(s);
  303.       FOR i := 1 TO 8 DO
  304.         seite^.chars[31+i] := Ord(s[i]);
  305.     END;
  306.   END;
  307. END;
  308.  
  309. { Jetzt noch ein paar Routinen, die zwar nicht auf den I²C-Bus zugreifen, }
  310. { aber wie getpage() dem Ausfüllen von VT-Seiten dienen: }
  311.  
  312. PROCEDURE add_sp_number{(seite: p_onepage)};
  313. { In den freien Anfang der 1. Zeile die Unterseitennummer eintragen, }
  314. VAR i: Integer;
  315.     s: str80;
  316. BEGIN
  317.   s := '('+hexstr(seite^.sp,4)+') ';
  318.   IF (seite^.cbits AND PF_SERMAG)=0 THEN BEGIN { Magazin-gemischte Übertragung, }
  319.     s[1] := '<'; s[6] := '>';     { Modus für den Benutzer erkennbar machen }
  320.   END;
  321.   FOR i := 1 TO 7 DO
  322.     seite^.chars[i] := Ord(s[i]);
  323. END;
  324.  
  325. PROCEDURE char_to_page{(x,y: Integer; c: Char; seite: p_onepage)};
  326. BEGIN
  327.   IF (y IN [0..23]) AND (x IN [0..39]) THEN 
  328.     seite^.chars[x+40*y] := Ord(c);
  329. END;
  330.  
  331. PROCEDURE write_to_page{(x,y: Integer; txt: Str80; vgf,hgf: Integer;
  332.                        seite: p_onepage)};
  333. { Text in eine Videotextseite schreiben. }
  334. { x und y werden ab 0 gezählt. Umlaute werden konvertiert. Wenn vgf oder }
  335. { hgf -1 sind, werden keine Farbsteuerzeichen erzeugt -> Platzersparnis. }
  336. VAR i,j: Integer;
  337. BEGIN
  338.   IF y>23 THEN Exit;
  339.   i := x + 40*y;
  340.   IF hgf=0 THEN BEGIN
  341.     seite^.chars[i] := 28; Inc(i);
  342.   END;
  343.   IF hgf>0 THEN BEGIN
  344.     seite^.chars[i] := hgf; Inc(i);
  345.     seite^.chars[i] := 29; Inc(i);
  346.   END;
  347.   IF vgf>=0 THEN BEGIN
  348.     seite^.chars[i] := vgf; Inc(i);
  349.   END;
  350.   FOR j := 1 TO Length(txt) DO BEGIN
  351.     CASE txt[j] OF
  352.       'ä': seite^.chars[i] := Ord('{');
  353.       'ö': seite^.chars[i] := Ord('|');
  354.       'ü': seite^.chars[i] := Ord('}');
  355.       'ß': seite^.chars[i] := Ord('~');
  356.       'Ä': seite^.chars[i] := Ord('[');
  357.       'Ö': seite^.chars[i] := Ord('\');
  358.       'Ü': seite^.chars[i] := Ord(']');
  359.       OTHERWISE seite^.chars[i] := Ord(txt[j]);
  360.     END;
  361.     IF i<959 THEN Inc(i);
  362.   END;
  363. END;
  364.  
  365. PROCEDURE init_page{(seite: p_onepage)};
  366. { gerade mit New() erzeugte Seite zum Selbstbeschreiben vorbereiten. }
  367. { Nur pg und sp müssen bereits eingetragen sein. }
  368. VAR i: Integer;
  369.     s: Str80;
  370. BEGIN
  371.   seite^.dejavu := False; { als Neuheit kennzeichnen }
  372.   FOR i := 0 TO 959 DO seite^.chars[i] := 32;
  373.   seite^.cbits := $4000 OR PF_SERMAG OR PF_LOCAL; { dt. Zeichensatz! }
  374.   IF seite^.pg>0 THEN BEGIN
  375.     s := '('+hexstr(seite^.sp,4)+') '+hexstr(seite^.pg,3);
  376.     write_to_page(0,0,s,7,-1,seite);
  377.     write_to_page(16,0,'lokal erzeugte VT-Seite',5,-1,seite);
  378.   END;
  379. END;
  380.  
  381. LIBRARY SysBase:
  382. -414 : PROCEDURE CloseLibrary(A1: Ptr);
  383. -552 : FUNCTION  OpenLibrary(A1: Str; D0: Long): Ptr;
  384. END;
  385.  
  386. PROCEDURE retreat;
  387. BEGIN
  388.   IF I2CBase<>Nil THEN CloseLibrary(I2CBase);
  389. END;
  390.                
  391. VAR i: Integer;
  392.  
  393. BEGIN  { Initialisierungsteil }
  394.   aktspeicher := 0; tv_status := 2;
  395.   AddExitServer(retreat); I2CBase := OpenLibrary('i2c.library',39);
  396.   FOR i := 0 TO maxerror-1 DO
  397.     IF I2CBase<>Nil THEN i2c_error[i] := I2CErrText(i SHL 8)
  398.     ELSE i2c_error[i] := '';
  399.   i2c_error[maxerror] := 'need i2c.library V39+';
  400. END.
  401.