home *** CD-ROM | disk | FTP | other *** search
- UNIT cct; {$project vt }
- { Programmierung des CCT-Chips (Computer Controlled Teletext) SAA5246 }
-
- INTERFACE; FROM vt USES global;
- {$incl "i2c.lib" }
-
- CONST PF_SERMAG = $0800; { C11 offizielle Seitenflags }
- PF_RENEW = $0100; { C8 }
- PF_STITLE = $0040; { C6 }
- PF_HEADLN = $0020; { C5 }
- PF_ISMAP = $0004; { inoffizielle Flags für meine eigenen Zwecke, }
- PF_PSEUDO = $0002; { "C3 bis C0", existieren im }
- PF_LOCAL = $0001; { Standard aber auch nicht }
- CONST maxerror=9;
- VAR i2c_error: ARRAY[0..maxerror] OF String[30];
- VAR i2c_status, tv_status: Integer;
-
- PROCEDURE busmaster(really: Boolean);
- PROCEDURE display_select(speicher: Byte);
- PROCEDURE TV_display(mode: Byte);
- FUNCTION VTstat: Byte;
- PROCEDURE sperren(speicher: Byte);
- PROCEDURE anfordern(speicher: Byte; page, subpage: Integer; mask: Byte);
- PROCEDURE init_CCT;
- FUNCTION seite_da(speicher: Byte): Boolean;
- PROCEDURE gettime(VAR zeit: Str80);
- PROCEDURE gethead(VAR head: Str80);
- PROCEDURE getpage(speicher: Byte; seite: p_onepage; ganz: Boolean);
- PROCEDURE add_sp_number(seite: p_onepage);
- PROCEDURE char_to_page(x,y: Integer; c: Char; seite: p_onepage);
- PROCEDURE write_to_page(x,y: Integer; txt: Str80; vgf,hgf: Integer;
- seite: p_onepage);
- PROCEDURE init_page(seite: p_onepage);
-
- { ---------------------------------------------------------------------- }
-
- IMPLEMENTATION;
-
- {$opt q,s+,i+ } { keine Laufzeitprüfungen außer Stack und Feldindizes }
-
- CONST SAA5246 = $22;
- PBLF = $20;
- DOCARE = $10;
- NOTHOLD = $08;
-
- VAR i2cdata: ARRAY[1..20] OF Byte; { kleiner Puffer für Kommandos }
- aktspeicher: Integer; { der Speicher, in dem die VT-Uhr läuft }
-
- PROCEDURE busmaster{(really: Boolean)};
- VAR l: Long;
- BEGIN
- IF really THEN l := BringBackI2C
- ELSE ShutDownI2C;
- END;
-
- PROCEDURE send(addr: Byte; anz: Word; buffer: Ptr);
- { Dummy-Prozedur, um den Fehler "keine Library" aufzufangen }
- BEGIN
- IF I2CBase=Nil THEN
- i2c_status := maxerror
- ELSE
- i2c_status := (SendI2C(addr,anz,buffer) SHR 8) AND $FF;
- END;
-
- PROCEDURE receive(addr: Byte; anz: Word; buffer: Ptr);
- BEGIN
- IF I2CBase=Nil THEN
- i2c_status := maxerror
- ELSE
- i2c_status := (ReceiveI2C(addr,anz,buffer) SHR 8) AND $FF;
- END;
-
- PROCEDURE setregister(addr,reg,value: Byte);
- { Häufig benötigter Vorgang: ein einzelnes Register am I²C-Bus beschreiben. }
- BEGIN
- i2cdata[1] := reg; i2cdata[2] := value;
- send(addr,2,^i2cdata);
- END;
-
- FUNCTION getregister(addr,reg: Byte): Byte;
- { Etwas umständlicher, wird auch seltener benötigt: ein einzelnes Register }
- { auslesen. NICHT schleifenweise aufrufen, um mehrere Bytes zu lesen! }
- BEGIN
- send(addr,1,^reg);
- receive(addr,1,^i2cdata);
- getregister := i2cdata[1];
- END;
-
- PROCEDURE display_select{(speicher: Byte)};
- { Einen der 8 Seitenspeicher zur Anzeige auswählen }
- BEGIN
- setregister(SAA5246,4,speicher);
- aktspeicher := speicher;
- END;
-
- PROCEDURE TV_display{(mode: Byte)};
- { Art der Anzeige am Fernseher auswählen. }
- BEGIN
- i2cdata[1] := 5;
- CASE mode OF
- 2: { Normalseiten bildfüllend, Schlagzeilen als Boxen }
- BEGIN i2cdata[2] := %11001100; i2cdata[3] := %01001111; END;
- 1: { Normalseiten als Boxen, Schlagzeilen transparent }
- BEGIN i2cdata[2] := %11001111; i2cdata[3] := %00001111; END;
- OTHERWISE { Anzeige aus }
- BEGIN i2cdata[2] := %00000011; i2cdata[3] := %00000011; END;
- END;
- i2cdata[4] := %00000111;
- send(SAA5246,4,^i2cdata);
- tv_status := mode;
- END;
-
- FUNCTION VTstat{: Byte};
- { Liest aus dem CCT das Register R11B. Bedeutung: bit0 - Videosignal vorh., }
- { bit1 - Textsignal vorh., bit2-5 - ROM-Version }
- BEGIN
- setregister(SAA5246,0,%00000001); { R11B aktivieren }
- VTstat := getregister(SAA5246,11); { Status lesen }
- setregister(SAA5246,0,%00000000); { R11 aktivieren }
- END;
-
- PROCEDURE sperren{(speicher: Byte)};
- { Seitensuche eines Empfangskreises anhalten, damit nicht evtl. niederpriore }
- { Empfangskreise behindert werden. Die Sperre wird bei Programmierung einer }
- { neuen Seitenanforderung automatisch aufgehoben. }
- { Kreis 3 (der mit der niedrigsten Priorität) wird *nicht* gesperrt, da das }
- { unnötig ist. Stattdessen wird er auf Seite *** programmiert, so daß seine }
- { Titelzeile ständig durchläuft. }
- BEGIN
- IF speicher=3 THEN
- anfordern(speicher, $100, 0, %000)
- ELSE BEGIN
- i2cdata[1] := 2;
- i2cdata[2] := speicher*16;
- i2cdata[3] := 0;
- send(SAA5246,3,^i2cdata);
- END;
- END;
-
- PROCEDURE anfordern{(speicher: Byte; page, subpage: Integer; mask: Byte)};
- { Einlesen einer Seite in einen Seitenspeicher 0..7 anfordern, Unterseite nur }
- { direkt anfordern, wenn subpage > 0 }
- { <mask> ist eine dreistellige Binärzahl, wo eine '0' steht, wird in der }
- { entsprechenden Ziffer der angeforderten Seitennummer das DOCARE-Bit }
- { gelöscht. }
- VAR lauf: Integer;
- BEGIN
- { Seitenanforderung beschreiben: }
- i2cdata[1] := 2;
- i2cdata[2] := speicher*16;
- i2cdata[3] := ((page SHR 8) AND $7) OR NOTHOLD;
- i2cdata[4] := (page SHR 4) AND $F;
- i2cdata[5] := page AND $F;
- IF page > 0 THEN
- FOR lauf := 3 TO 5 DO
- IF Odd(mask SHR (5-lauf)) THEN
- i2cdata[lauf] := i2cdata[lauf] OR DOCARE;
- i2cdata[6] := (subpage SHR 12) AND $3;
- i2cdata[7] := (subpage SHR 8) AND $F;
- i2cdata[8] := (subpage SHR 4) AND $7
- i2cdata[9] := subpage AND $F;
- IF subpage > 0 THEN
- FOR lauf := 6 TO 9 DO i2cdata[lauf] := i2cdata[lauf] OR DOCARE;
- send(SAA5246,9,^i2cdata);
- END;
-
- PROCEDURE init_CCT;
- VAR i: Byte;
- BEGIN
- { Wichtig in R1: bit5 - ACQ OFF, Decoder-NotAus; bit2 - TCS ON, }
- { RGB-Ausgangssignal wird mit Fernsehbild synchronisiert. }
- setregister(SAA5246,1,%00000100);
- { Seitensuche sperren: }
- FOR i := 0 TO maxactive-1 DO BEGIN
- sperren(i); activejobs[i].pg := 0;
- END;
- { alle Seitenspeicher löschen: }
- FOR i := 0 TO 7 DO BEGIN
- setregister(SAA5246,8,%00001000 OR i); Delay(2);
- { Löschvorgang dauert bis zu 22 ms, darum mal jeweils 40 ms warten. }
- END;
- { R11 aktivieren }
- setregister(SAA5246,0,%00000000);
- { Fernsehdarstellung einschalten }
- display_select(aktspeicher);
- TV_display(tv_status);
- END;
-
- FUNCTION seite_da{(speicher: Byte): Boolean};
- { Überprüft, ob die für einen Speicher 0..7 angeforderte Seite bereits }
- { eingetroffen ist. Das Statusflag PBLF, das in diesem Fall gelöscht ist, }
- { muß danach wieder gesetzt werden. Das geschieht entweder implizit beim }
- { Programmieren einer neuen Seitenanforderung oder explizit durch }
- { Schreibzugriff, in diesem Programm in getpage(). }
- { Problem: das Statusflag PBLF bezieht sich auf den Empfang der Kopfzeile, }
- { nicht etwa den Empfang der kompletten Seite. Genauer gesagt: für letzteres }
- { existiert überhaupt kein Statusflag! }
- { Nun ist das nicht ganz so tragisch, nach Empfang der Kopfzeile kann man }
- { bereits bedenkenlos die Statuszeile 25 mit Steuerbits und Seitennummer }
- { lesen. Und in vielen Fällen geht es sogar gut, sofort mit dem Einlesen }
- { der gesamten Seite zu beginnen, da sich nämlich maximal 11 Seiten pro }
- { Sekunde über den I²C-Bus schaufeln lassen, während der Videotext selbst }
- { mit ca. 13 Seiten pro Sekunde einrollt: Die noch leere Seite wird }
- { schneller aufgefüllt, als sie ausgelesen werden kann. }
- { Probleme entstehen natürlich, wenn der Empfang der VT-Seite länger als }
- { ca. 1/10 s dauert. Das ist möglich, wenn der Sender nicht alle zur }
- { VT-Übertragung zulässigen Rasterzeilen ausnutzt, oder wenn er gar den }
- { Magazin-gemischten Übertragungsmodus verwendet. Letzteres ist aber an }
- { einem Statusflag (C11) vom Programm aus erkennbar, hm ... }
- BEGIN
- seite_da := False;
- { Byte 9 der Statuszeile (Zeile 25) adressieren ... }
- i2cdata[1] := 8;
- i2cdata[2] := speicher;
- i2cdata[3] := 25; i2cdata[4] := 9;
- send(SAA5246,4,^i2cdata);
- IF i2c_status<>0 THEN Exit;
- { ... und auslesen: }
- receive(SAA5246,1,^i2cdata);
- IF i2c_status<>0 THEN Exit;
- seite_da := (i2cdata[1] AND PBLF)=0;
- END;
-
- PROCEDURE gettime{(VAR zeit: Str80)};
- { liest aus der zur Anzeige ausgewählten Speicherseite die Uhrzeit }
- VAR i: Integer;
- BEGIN
- zeit := ' ';
- { Uhrzeit aus der Seite im CCT lesen: Zeile 0, Spalte 32 }
- i2cdata[1] := 8;
- i2cdata[2] := aktspeicher;
- i2cdata[3] := 0; i2cdata[4] := 32;
- send(SAA5246,4,^i2cdata);
- IF i2c_status<>0 THEN Exit;
- { Uhrzeit lesen: }
- receive(SAA5246,8,^i2cdata);
- IF i2c_status<>0 THEN Exit;
- FOR i := 1 TO 8 DO
- IF i2cdata[i] IN [32..127] THEN zeit[i] := Chr(i2cdata[i]);
- END;
-
- PROCEDURE gethead{(VAR head: Str80)};
- { liest aus Speicher 3 die 24 Byte Titelzeile zwischen Statusfeld und }
- { Uhrzeit, Steuerzeichen werden entfernt }
- VAR i: Integer;
- BEGIN
- head := '';
- { Seitenspeicher addressieren: Zeile 0, Spalte 8 }
- i2cdata[1] := 8;
- i2cdata[2] := 3; { Speicher }
- i2cdata[3] := 0; i2cdata[4] := 8;
- send(SAA5246,4,^i2cdata);
- IF i2c_status=0 THEN
- receive(SAA5246,24,^head); { Zeile lesen }
- FOR i := 1 TO 24 DO
- IF NOT ((i2c_status=0) AND (head[i] IN [' '..'~'])) THEN
- head[i] := ' ';
- head[25] := #0;
- END;
-
- PROCEDURE getpage{(speicher: Byte; seite: p_onepage; ganz: Boolean)};
- { Liest eine Speicherseite (0..7) aus dem CCT-Baustein aus und speichert }
- { diese Seite im Amiga. Für <ganz>=false werden nur die Statusinformationen }
- { aus Zeile 25 gelesen. }
- VAR i,h: Integer;
- status: ARRAY [0..9] OF Byte;
- s: str80;
- BEGIN
- seite^.pg := 0; seite^.sp := 0; seite^.cbits := 0;
- { Status aus der Seite im CCT lesen: Zeile 25, Spalte 0 }
- i2cdata[1] := 8;
- i2cdata[2] := speicher;
- i2cdata[3] := 25; i2cdata[4] := 0;
- send(SAA5246,4,^i2cdata);
- IF i2c_status<>0 THEN Exit;
- receive(SAA5246,10,^status);
- IF i2c_status<>0 THEN Exit;
- seite^.pg := (status[0] AND $F) + (status[1] AND $F) SHL 4
- + (status[8] AND $7) SHL 8;
- IF seite^.pg<$100 THEN seite^.pg := seite^.pg + $800;
- seite^.sp := (status[2] AND $F) + (status[3] AND $7) SHL 4
- + (status[4] AND $F) SHL 8 + (status[5] AND $3) SHL 12;
- seite^.cbits := (status[3] AND $08) SHL 1 { C4 }
- OR (status[5] AND $0C) SHL 3 { C5, C6 }
- OR (status[6] AND $0F) SHL 7 { C7, C8, C9, C10 }
- OR (status[7] AND $0F) SHL 11 { C11, C12..C14 }
- IF make_bcd(get_bcd(seite^.pg))<>seite^.pg THEN
- seite^.cbits := seite^.cbits OR PF_PSEUDO;
- IF ganz THEN BEGIN
- seite^.dejavu := False; { als Neuigkeit kennzeichnen }
- { Seite einlesen. Richtigen Speicher, Zeile 0, Spalte 0 adressieren: }
- i2cdata[1] := 8;
- i2cdata[2] := speicher;
- i2cdata[3] := 0; i2cdata[4] := 0;
- send(SAA5246,4,^i2cdata);
- IF i2c_status<>0 THEN Exit;
- { Seite einlesen: }
- receive(SAA5246,960,^seite^.chars);
- IF i2c_status<>0 THEN Exit;
- { aus dem zur Anzeige aktivierten Seitenspeicher die Uhrzeit holen: }
- IF speicher<>aktspeicher THEN BEGIN
- gettime(s);
- FOR i := 1 TO 8 DO
- seite^.chars[31+i] := Ord(s[i]);
- END;
- END;
- END;
-
- { Jetzt noch ein paar Routinen, die zwar nicht auf den I²C-Bus zugreifen, }
- { aber wie getpage() dem Ausfüllen von VT-Seiten dienen: }
-
- PROCEDURE add_sp_number{(seite: p_onepage)};
- { In den freien Anfang der 1. Zeile die Unterseitennummer eintragen, }
- VAR i: Integer;
- s: str80;
- BEGIN
- s := '('+hexstr(seite^.sp,4)+') ';
- IF (seite^.cbits AND PF_SERMAG)=0 THEN BEGIN { Magazin-gemischte Übertragung, }
- s[1] := '<'; s[6] := '>'; { Modus für den Benutzer erkennbar machen }
- END;
- FOR i := 1 TO 7 DO
- seite^.chars[i] := Ord(s[i]);
- END;
-
- PROCEDURE char_to_page{(x,y: Integer; c: Char; seite: p_onepage)};
- BEGIN
- IF (y IN [0..23]) AND (x IN [0..39]) THEN
- seite^.chars[x+40*y] := Ord(c);
- END;
-
- PROCEDURE write_to_page{(x,y: Integer; txt: Str80; vgf,hgf: Integer;
- seite: p_onepage)};
- { Text in eine Videotextseite schreiben. }
- { x und y werden ab 0 gezählt. Umlaute werden konvertiert. Wenn vgf oder }
- { hgf -1 sind, werden keine Farbsteuerzeichen erzeugt -> Platzersparnis. }
- VAR i,j: Integer;
- BEGIN
- IF y>23 THEN Exit;
- i := x + 40*y;
- IF hgf=0 THEN BEGIN
- seite^.chars[i] := 28; Inc(i);
- END;
- IF hgf>0 THEN BEGIN
- seite^.chars[i] := hgf; Inc(i);
- seite^.chars[i] := 29; Inc(i);
- END;
- IF vgf>=0 THEN BEGIN
- seite^.chars[i] := vgf; Inc(i);
- END;
- FOR j := 1 TO Length(txt) DO BEGIN
- CASE txt[j] OF
- 'ä': seite^.chars[i] := Ord('{');
- 'ö': seite^.chars[i] := Ord('|');
- 'ü': seite^.chars[i] := Ord('}');
- 'ß': seite^.chars[i] := Ord('~');
- 'Ä': seite^.chars[i] := Ord('[');
- 'Ö': seite^.chars[i] := Ord('\');
- 'Ü': seite^.chars[i] := Ord(']');
- OTHERWISE seite^.chars[i] := Ord(txt[j]);
- END;
- IF i<959 THEN Inc(i);
- END;
- END;
-
- PROCEDURE init_page{(seite: p_onepage)};
- { gerade mit New() erzeugte Seite zum Selbstbeschreiben vorbereiten. }
- { Nur pg und sp müssen bereits eingetragen sein. }
- VAR i: Integer;
- s: Str80;
- BEGIN
- seite^.dejavu := False; { als Neuheit kennzeichnen }
- FOR i := 0 TO 959 DO seite^.chars[i] := 32;
- seite^.cbits := $4000 OR PF_SERMAG OR PF_LOCAL; { dt. Zeichensatz! }
- IF seite^.pg>0 THEN BEGIN
- s := '('+hexstr(seite^.sp,4)+') '+hexstr(seite^.pg,3);
- write_to_page(0,0,s,7,-1,seite);
- write_to_page(16,0,'lokal erzeugte VT-Seite',5,-1,seite);
- END;
- END;
-
- LIBRARY SysBase:
- -414 : PROCEDURE CloseLibrary(A1: Ptr);
- -552 : FUNCTION OpenLibrary(A1: Str; D0: Long): Ptr;
- END;
-
- PROCEDURE retreat;
- BEGIN
- IF I2CBase<>Nil THEN CloseLibrary(I2CBase);
- END;
-
- VAR i: Integer;
-
- BEGIN { Initialisierungsteil }
- aktspeicher := 0; tv_status := 2;
- AddExitServer(retreat); I2CBase := OpenLibrary('i2c.library',39);
- FOR i := 0 TO maxerror-1 DO
- IF I2CBase<>Nil THEN i2c_error[i] := I2CErrText(i SHL 8)
- ELSE i2c_error[i] := '';
- i2c_error[maxerror] := 'need i2c.library V39+';
- END.
-