UNIT cct; {$project vt }
{ Programmierung des CCT-Chips (Computer Controlled Teletext) SAA5246 }

INTERFACE; FROM vt USES global,i2c_serial;

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: Str);
PROCEDURE init_CCT;
FUNCTION seite_da(speicher: Byte): Boolean;
PROCEDURE gettime(speicher: Byte; VAR zeit: Str80);
PROCEDURE gethead(speicher: Byte; VAR head: Str80);
PROCEDURE getpage(speicher: Byte; seite: p_onepage; ganz: Boolean);

{ ---------------------------------------------------------------------- }

IMPLEMENTATION;
{$opt b-}

CONST SAA5246 = $22;
      PBLF = $20;
      DOCARE =  $10;
      NOTHOLD = $08;
      SERMAG = $0800; { C11 }

VAR i2cdata: Array[1..20] of Byte;    { kleiner Puffer für Kommandos }

PROCEDURE display_select{(speicher: Byte)};
{ Einen der 8 Seitenspeicher zur Anzeige auswählen }
begin
  setregister(SAA5246,4,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;
  i2cbusIO(SAA5246,^i2cdata,4);
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. }
begin
  i2cdata[1] := 2;
  i2cdata[2] := speicher*16;
  i2cdata[3] := 0;
  i2cbusIO(SAA5246,^i2cdata,3);
end;

PROCEDURE anfordern{(speicher: Byte; page, subpage: Integer; _mask: Str)};
{ Einlesen einer Seite in einen Seitenspeicher 0..7 anfordern, Unterseite nur }
{ direkt anfordern, wenn subpage <> 0 }
{ <_mask> ist ein dreistelliger String, wo ein '*' steht, wird in der }
{ entsprechenden Ziffer der angeforderten Seitennummer das DOCARE-Bit }
{ gelöscht. }
VAR lauf: integer;
    mask: String[10];
begin
  mask := _mask;
  { Seitenanforderung beschreiben: }
  i2cdata[1] := 2;
  i2cdata[2] := speicher*16;
  i2cdata[3] := ((page div 100) mod 8) OR NOTHOLD;
  i2cdata[4] := (page div 10) mod 10;
  i2cdata[5] := page mod 10;
  if page <> 0 then
    for lauf := 3 to 5 do
      if mask[lauf-2]<>'*' then
        i2cdata[lauf] := i2cdata[lauf] or DOCARE;
  i2cdata[6] := subpage div 1000;
  i2cdata[7] := (subpage div 100) mod 10;
  i2cdata[8] := (subpage div 10) mod 10;
  i2cdata[9] := subpage mod 10;
  if subpage <> 0 then
    for lauf := 6 to 9 do  i2cdata[lauf] := i2cdata[lauf] or DOCARE;
  i2cbusIO(SAA5246,^i2cdata,9)
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  sperren(i);
  { 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(0);
  TV_display(2);
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;
  i2cbusIO(SAA5246,^i2cdata,4);
  if i2c_status<>0 then
    exit; { Schreibfehler }
  { ... und auslesen: }
  i2cbusIO(SAA5246,^i2cdata,-1);
  if i2c_status<>0 then
    exit; { Lesefehler }
  seite_da := (i2cdata[1] AND PBLF)=0;
end;

PROCEDURE gettime{(speicher: Byte; VAR zeit: Str80)};
{ liest aus einer Speicherseite 0..7 des CCT-Bausteins die Uhrzeit }
var i: integer;
begin
  zeit := '00:00:00';
  { Uhrzeit aus der Seite im CCT lesen: Zeile 0, Spalte 32 }
  i2cdata[1] := 8;
  i2cdata[2] := speicher;
  i2cdata[3] := 0; i2cdata[4] := 32;
  i2cbusIO(SAA5246,^i2cdata,4);
  if i2c_status<>0 then
    exit; { Schreibfehler }
  { Uhrzeit lesen: }
  i2cbusIO(SAA5246,^i2cdata,-8);
  if i2c_status<>0 then
    exit; { Lesefehler }
  for i := 1 to 8 do
    IF i2cdata[i] IN [32..127] THEN zeit[i] := chr(i2cdata[i]);
end;

PROCEDURE gethead{(speicher: Byte; VAR head: Str80)};
{ liest die 24 Byte Titelzeile zwischen Statusfeld und Uhrzeit }
var i: integer;
begin
  head := '';
  { Seitenspeicher addressieren: Zeile 0, Spalte 8 }
  i2cdata[1] := 8;
  i2cdata[2] := speicher;
  i2cdata[3] := 0; i2cdata[4] := 8;
  i2cbusIO(SAA5246,^i2cdata,4);
  if i2c_status<>0 then
    exit; { Schreibfehler }
  { Zeile lesen: }
  i2cbusIO(SAA5246,^head,-24);
  if i2c_status<>0 then
    exit; { Lesefehler }
  for i := 1 to 24 do
    IF NOT (ord(head[i]) IN [32..127]) 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. }
{ Abschließend wird das Statusflag PBLF wieder gesetzt! }
VAR i,h: Integer;
    status: ARRAY [0..9] OF Byte;
    s: str80;
BEGIN
  { Status aus der Seite im CCT lesen: Zeile 25, Spalte 0 }
  i2cdata[1] := 8;
  i2cdata[2] := speicher;
  i2cdata[3] := 25; i2cdata[4] := 0;
  i2cbusIO(SAA5246,^i2cdata,4);
  if i2c_status<>0 then
    exit; { Schreibfehler }
  i2cbusIO(SAA5246,^status,-10);
  if i2c_status<>0 then
    exit; { Lesefehler }
  seite^.pg := (status[0] and $F) + 10*(status[1] and $F) + 100*(status[8] and $7);
  if seite^.pg<100 then seite^.pg := seite^.pg + 800;
  seite^.sp := (status[2] and $F) + 10 * (status[3] and $7)
      + 100 * (status[4] and $F) + 1000 * (status[5] and $3);
  seite^.cbits := (status[3] AND $08) SHL 1   { C4 }
                + (status[5] AND $0C) SHL 3   { C5, C6 }
                + (status[6] AND $0F) SHL 7   { C7, C8, C9, C10 }
                + (status[7] AND $0F) SHL 11  { C11, C12..C14 }
  if ganz then begin
    { Seite aus CCT lesen. Richtigen Speicher, Zeile 0, Spalte 0 adressieren: }
    i2cdata[1] := 8;
    i2cdata[2] := speicher;
    i2cdata[3] := 0; i2cdata[4] := 0;
    i2cbusIO(SAA5246,^i2cdata,4);
    if i2c_status<>0 then begin
      exit; { Schreibfehler }
    end;
    { Seite einlesen: }
    i2cbusIO(SAA5246,^seite^.chars,-960);
    if i2c_status<>0 then begin
      exit; { Lesefehler }
    end;
    { in den freien Anfang der 1. Zeile die Unterseitennummer eintragen }
    seite^.chars[1] := ord('(');
    seite^.chars[2] := (status[5] and $3)+ord('0');
    seite^.chars[3] := (status[4] and $F)+ord('0');
    seite^.chars[4] := (status[3] and $7)+ord('0');
    seite^.chars[5] := (status[2] and $F)+ord('0');
    seite^.chars[6] := ord(')');
    seite^.chars[7] := ord(' ');
    IF status[7] AND $01=0 THEN BEGIN  { C11=0: Magazin-gemischte Übertragung, }
      seite^.chars[1] := ord('<');  { Modus für den Benutzer erkennbar machen }
      seite^.chars[6] := ord('>');
    END;
    { aus dem zur Anzeige aktivierten Seitenspeicher die Uhrzeit holen: }
    if speicher<>aktspeicher then begin
      gettime(aktspeicher,s);
      for i := 1 to 8 do
        seite^.chars[31+i] := ord(s[i]);
    END;
  END;
END;

BEGIN  { Initialisierungsteil }
END.
