program DDS;
{DDS-Funktionsgenerator mit AD9833}
{(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de}
{LCDisplay mit 2x16 Zeichen, für Preset- und Default-Anzeige}
{5 Taster zur Bedienung, Up/Down, Left/Right, Aux}

{$NOSHADOW}
{ $W+ Warnings}            {Warnings off}
{$TYPEDCONST OFF}
{$DEBDELAY}

{Defines aktivieren durch Entfernen des 1. Leerzeichens!}

{Auswahl der Features. Es ist möglich, anstelle der eigenen Scan-Routine}
{eine MIDI-Merge-Funktion einzubinden, etwa wenn ein Masterkeyboard}
{ohne Bedienelemente (E-Piano o.dgl.) vorhanden ist. Nur eine Auswahl zulässig}

{$DEFINE IncrEnc}         {Incremental-Encoder statt Up/Down?}
{$DEFINE LM1972}          {statt LM1971, hat bis -48 dB 0,5-dB-Sprünge}
{ $DEFINE ADJ1972}         {statt Burst Adjust-Wert für LM1972}

Device = mega8, VCC = 5;

Import SysTick, TWImaster, LCDmultiPort;   // I2Cexpand,
From System Import LongInt;

Define
  ProcClock      = 16000000;        {Hertz}
  SysTick        = 1;              {msec}
  StackSize      = $0080, iData;
  FrameSize      = $0060, iData;

  TWIpresc       = TWI_BR400;
  LCDmultiPort   = I2C_TWI;
  LCDTYPE_M      = 44780;          // 66712;
  LCDrows_M      = 2;              {2 rows}
  LCDcolumns_M   = 16;             {16 chars per line}
//  I2Cexpand = I2C_TWI, $38; {use TWIport, 9554A}
//  I2CexpPorts = Port0,Port1; {use Port0}

Implementation

{--------------------------------------------------------------}
{ Type Declarations }
type
  TEditWhich     = (Patch, dB, Fr1, Fr2, Fr3, Fr4, Fr5, Fr6, Fr7, FrDec, Outp, Goff, Brst, Wv, Preset);

{--------------------------------------------------------------}
{ Const Declarations }
const
  ItemOffset                 = 2; // Abstand Patch-Fr1
{ Keyboard-Interface, Taster und LEDs PortBits }

  DDRBinit                   = %00111111;            {PortB dir }
  PortBinit                  = %11101111;            {PortB, b_STROBE=0 }

  DDRCinit                   = %00001111;            {PortC dir }
  PortCinit                  = %11110000;            {PortC }

  DDRDinit                   = %00000100;            {PortD dir }
  PortDinit                  = %11111100;            {PortD }

  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  LEDOutPort                 = @PortD;   {LED-Port}
  DDSOutPort                 = @PortB;   {DDS-Port}

{Konstanten für Frequenzen, addiert für DDS, abhängig von CLK, hier 4.194304 Mhz}
  fhz            : Table[0..7] of LongInt = (64000000, 6400000, 640000, 64000, 6400, 640, 64, 6);
{Dezimalstelle gerundet, nur die ersten 10 benutzt}
  fdhz           : Table[0..15] of LongInt = (0, 6, 13, 19, 26, 32, 38, 45, 51, 58, 0, 6, 13, 19, 26, 32);

  dBfac          : Table[0..31] of Word = (
{$I dB775mV.txt}
  );
  dBadj          : Table[0..63] of byte = (
{$I dBadj.txt}
  );

{Lineal     .----.----.----.----.----.----.----.----.----}
  Vers1Str                   = 'DSS-FG1  V#0.950';    //Vers1
  Vers2Str                   =                   'C. Meyer 10/05'+char(0);    //Vers2

  InitStr                    =                    'Initialising... ';     //InitStr
  MemStr                     =                     'Memory '+char(0);     //MemStr
  PresetStr                  =                  'Preset '+char(0);     //MemStr
  HzStr                      =                      ' Hz'+char(0);
  dBStr                      =                      ' dB'+char(0);
  SinStr                     =                      'Sin'+char(0);
  SquStr                     =                      'Sqr'+char(0);
  TriStr                     =                      'Tri'+char(0);
  ExtStr                     =                      'Ext'+char(0);
  PwrStr                     =                      'Pwr'+char(0);
  NrmStr                     =                      'Nrm'+char(0);

  EmptyItemStr               = '-*-'+char(0);
  FrequStr                   = 'Frequency Hz    ';
  OptStr                     = 'Out dB Brst Wav ';
  high                       = true;
  low                        = false;
//     Bit    5432109876543210
  reset                      = %0010000100000000;
//     Bit              O D M
  sine                       = %0010000000000000;
  triangle                   = %0010000000000010;
  square                     = %0010000000101000;

//Preset: Terzfrequenzen
//20, 25, 31,5, 40, 50, 63, 80, 100, 125, 160, 250, 315, 400, 500,
//630, 800, 1k, 1,25k, 1,6k, 2k, 2,5k, 3,15k, 4k, 5k, 6,3k, 8k, 10k, 12.5k, 16k, 20k
  TerzArray      : Table[0..31] of integer =
  (
  1000,
  20,
  25,
  31,
  40,
  50,
  63,
  80,
  100,
  125,
  160,
  200,
  250,
  315,
  400,
  500,
  630,
  800,
  1000,
  1250,
  1600,
  2000,
  2500,
  3150,
  4000,
  5000,
  6300,
  8000,
  10000,
  12500,
  16000,
  20000
  );

//structconst Default-EEPROM-Werte:
{$EEPROM}
structconst

  EEprime        : word      = $FFFF;
  EElastPatch    : Byte      = 1;
  EEaux          : Byte      = $FF;


  EEPatchArray   : Array[0..15] of Byte =
  (
  0, 0, 0, 1, 0, 0, 0, 0,    //Frequ
{$IFDEF ADJ1972}
  0, 0, 64, 0, 70, 0, 0, 0   //Outp Gain ADJ1972 Wv dB
{$ELSE}
  0, 0, 0, 0, 70, 0, 0, 0   //Outp Gain Burst Wv dB
{$ENDIF}
  );


{--------------------------------------------------------------}
{ Var Declarations }

var
{$EEPROM}

{$DATA} {Schnelle Register-Variablen}
  i, j, k, m, n  : Byte;

  b_SCLK[DDSOutPort, 0]      : bit;
  b_SDATA[DDSOutPort, 1]     : bit;
  b_FSYNC[DDSOutPort, 2]     : bit;
  b_LOADPOT[DDSOutPort, 3]   : bit;
  b_STROBE[DDSOutPort, 4]    : bit;


{$IDATA}  {Langsamere SRAM-Variablen}

  RelaisOutPort  : Byte; //Zwischenspeicher für 4094 SR
  SquareSw[@RelaisOutPort, 0]: bit;
  GainSw[@RelaisOutPort, 1]  : bit;
  SymSw[@RelaisOutPort, 2]   : bit;
  ExtSource[@RelaisOutPort, 3]: bit;

  BtnInPort      : byte;  //Sample-Wert der Panel-Tasten
  _TasteRechts[@BtnInPort, 4]: bit; {Bit 0 ist Taste Enter, ganz rechts}
  _TasteAb[@BtnInPort, 6]    : bit; {Bit 6 ist Taste Down}
  _TasteAuf[@BtnInPort, 5]   : bit; {Bit 5 ist Taste Up}
  _TasteLinks[@BtnInPort, 3] : bit; {Bit 3 ist Taste Esc, ganz links}
  _TasteAux[@BtnInPort, 7]   : bit; {Bit 7 ist Taste Aux, separat}

  _LEDaux[LEDOutPort, 2]     : bit; {Bit 2 ist LED, separat}

  EditWhich      : TEditWhich;
  dss_cmd        : word;
  wave_cmd       : word;
  DisplayValue, EditValue,
  ItemUpperLimit, ItemLowerLimit, x, y, {x,y Koordinaten LCD}
  digits,
  ScanSema, BtnTime, BtnTime2, BurstCount: Byte;
  Modified, aBool, PresetOn, BurstPrime, prime: boolean;

{Edit-Item-Reihenfolge}

  PatchNum       : byte;
  PresetNum      : byte;

  EditArray      : Table[0..15] of Byte;
  Frequ1[@EditArray]   : byte;      // 1 Mhz
  Frequ2[@Frequ1+1]    : byte;       // 100kHz
  Frequ3[@Frequ1+2]    : byte;
  Frequ4[@Frequ1+3]    : byte;
  Frequ5[@Frequ1+4]    : byte;
  Frequ6[@Frequ1+5]    : byte;
  Frequ7[@Frequ1+6]    : byte;       //1 Hz
  FrequDez[@Frequ1+7]  : byte;     //0.1 Hz

  OutputMode[@FrequDez+1]    : byte;
  GainOffset[@OutputMode+1]  : byte;
  Burstmode[@GainOffset+1]   : byte;
  Wave[@Burstmode+1]   : byte;
  LeveldB[@Wave+1]     : byte;        //0...100= -70 ... +30dB

  LevelStr       : string[7];
  FreqStr        : String[8];

  IncrTimeOut, BurstTimer     : SysTimer8;


const
  LevelArrOffs               = @OutputMode - @EditArray;
  EditArrayLen               = @LeveldB - @EditArray;

{###########################################################################}

{ $NOSAVE}

procedure SendSR;  //Sende RelaisOutPort an den 4094

begin
  b_STROBE:= high;
  b_SCLK:= low;
  m:= RelaisOutPort;
  for i := 0 to 7 do
    b_SDATA:= bit(m, 7);
    b_SCLK:= high;
    m := m shl 1;
    b_SCLK:= low;
  endfor;
  b_STROBE:= low;
  b_SCLK:= high;
end;

{DDS functions }
procedure SendDSS;  //Sende ein Wort an den DSS-Chip

begin
  b_FSYNC:= low;
  for i := 0 to 15 do
    if bit(dss_cmd, 15)then
      b_SDATA:= high;
    else
      b_SDATA:= low;
    endif;
    b_SCLK:= low;
    dss_cmd := dss_cmd shl 1;
    b_SCLK:= high;
  endfor;
  b_FSYNC:= high;
end;

procedure SetFrequ;
var
  freq, fac, acc : LongInt;
begin
  freq:= 0;
  for i := 0 to 6 do                //Einzelne Stellen aufaddieren
    fac:= LongInt(gettable(EditArray, (i)));
    acc:= gettable(fhz, i);
    freq:= freq + (acc*fac);
  endfor;
  acc:= LongInt(gettable(fdhz, FrequDez)); //Dezihertz
  freq:= freq + acc;
  dss_cmd:= loword(freq) AND %0011111111111111;
  dss_cmd:= dss_cmd OR %0100000000000000;
  SendDSS;
  freq:= freq shl 2;
  dss_cmd:= hiword(freq) AND %0011111111111111;
  dss_cmd:= dss_cmd OR %0100000000000000;
  SendDSS;
  dss_cmd:= wave_cmd;
  SendDSS;

  PortC:= OutputMode;  //RelaisOutPort
end;

procedure SendLevel(step, channel : byte);
begin
  //Sende Adresse 0 und ein Byte an den Poti-Chip LM1971
  b_FSYNC:= high;
  b_SDATA:= low;
  b_SCLK:= low;
  b_LOADPOT:= low;
  for i := 0 to 6 do
    b_SCLK:= high;
    nop;
    b_SCLK:= low;
  endfor;
  if channel = 1 then
    b_SDATA:= high;
    nop;
  endif;
  b_SCLK:= high;
  nop;
  b_SCLK:= low;
  for i := 0 to 7 do
    if bit(step, 7)then
      b_SDATA:= high;
    else
      b_SDATA:= low;
    endif;
    nop;
    b_SCLK:= high;
    step := step shl 1;
    b_SCLK:= low;
  endfor;
  b_LOADPOT:= high;
  b_SDATA:= high;
  b_SCLK:= high;
end;

procedure SetLevel;
begin
// - (256-80, 80=Anzahl eigener Stufen bis Vollaussteuerung +10 dB)
  k:= - LeveldB - 176;
  j:= k;

  if LeveldB < 20 then
//Relais für Abschwächer
    GainSw:= high;
    k:= k - 20; //-20dB
  else
    GainSw:= low;
  endif;

{wenn LM1972,  bis -48 dB 0,5-dB-Sprünge}
{$IFDEF LM1972}
  if k < 48 then
    k := k shl 1;      // *2
  else
    k:= k + 48;
  endif;
{$ENDIF}
  SendLevel(k, 0);

{$IFDEF ADJ1972}
  LCDxy_M(LCD_m1, 0, 1);
  inc(j); // für Zeile der Tabelle, besser lesbar im Texteditor
  write(lcdout_m, bytetostr(j : 3));
  k:= Burstmode;
{$ELSE}
  k:= Frequ2 shl 4; // Frequenzgangkorrektur
  k:= k + gettable(dBadj, j);
{$ENDIF}

  SendLevel(k, 1);
  SendSR;
  PortC:= OutputMode;  //RelaisOutPort
end;

{###########################################################################}

procedure PresetCopyFromROM;
{Frequenz aus ROM holen}
begin
  FreqStr:= inttostr(gettable(TerzArray, PresetNum) : 7);
  FreqStr[8]:= #48;   //Zehntel
  for i := 0 to 7 do
    k:= byte(FreqStr[i + 1]);
    if k = 32 then  //führende Leerzeichen?
      k := 48;
    endif;
    settable(EditArray, i, k - 48);
  endfor;
end;

procedure PatchCopyFromEE;
{Frequenz und Settings aus EEPROM holen}
var myPatchAdr: Word;
begin
  for i := 0 to EditArrayLen do
    myPatchAdr := word(i) + ((word(PatchNum)) shl 4);  //*16
    settable(EditArray, i, (EEPatchArray[myPatchAdr]));
  endfor;
end;

procedure PatchCopyToEE;
{Frequenz und Settings in EEPROM speichern}
var myPatchAdr: Word;
begin
  for i := 0 to EditArrayLen do
    myPatchAdr := word(i) + ((word(PatchNum)) shl 4);  //*16
    EEPatchArray[myPatchAdr]:= gettable(EditArray, i);
  endfor;
end;


{###########################################################################}
{Menü-Prozeduren}
procedure LCD1xy;
begin
  LCDxy_M(LCD_m1, x, y);
end;

procedure LCD1x0y0;
begin
  x:= 0;
  y:= 0;
  LCD1xy;
end;

procedure LCD1x0y1;
begin
  x:= 0;
  y:= 1;
  LCD1xy;
end;

{$I buttons.pas}

procedure CheckItemLimits;
{EditValue eingrenzen}
begin
  if EditValue = $ff then
    EditValue := 0;
  endif;
  if EditValue > ItemUpperLimit then
    EditValue := ItemUpperLimit;
  endif;
  if EditValue < ItemlowerLimit then
    EditValue := ItemlowerLimit;
  endif;
end;

procedure SetItemLimPos;
{Item-Grenzen je nach EditWhich setzen}
begin
  ItemLowerLimit:= 0;
  ItemUpperLimit:= 9;
  digits:= 3;
  y:= 1;
  case EditWhich of
    Patch      :
                 digits:= 2;
                 ItemUpperLimit:= 31;
                 if PresetOn then
                   dec(ItemUpperLimit);
                 endif;
                 x:= 7;
                 y:= 0;
               |
    dB         :
                 digits:= 4;
                 ItemUpperLimit:= 80;   // 80dB eigener Regelbereich
                 x:= 9;
                 y:= 0;
               |
    Fr1..FrDec :
                 digits:= 1;
                 x:= (ord(EditWhich) - ItemOffset) AND 7;
                 x:= x shl 1;
                 y:= 1;
                 if EditWhich = Fr1 then
                   ItemUpperLimit:= 1;
                 endif;
               |
    Outp       :
                 x:= 0;
                 ItemUpperLimit:= 1;
               |
    Goff       :
                 digits:= 1;
                 x:= 4;
                 ItemUpperLimit:= 2;
               |
    Brst       :
                 digits:= 2;
                 x:= 8;
                 ItemUpperLimit:= 99;
               |
    Wv         :
                 x:= 12;
                 ItemUpperLimit:= 3;
               |
  endcase;
end;

procedure SetCursor;
begin
  SetItemLimPos;
  LCDxy_M(LCD_m1, x + digits, y);
  LCDout_M(#0);
end;

procedure WriteWave;
begin
  case DisplayValue of
    0 :
        ROMStr16Out(@SinStr);
        wave_cmd:= sine;
        SquareSw:= low;
      |
    1 :
        ROMStr16Out(@TriStr);
        wave_cmd:= triangle;
        SquareSw:= low;
        ExtSource:= low;
      |
    2 :
        ROMStr16Out(@SquStr);
        wave_cmd:= square;
        SquareSw:= high;
        ExtSource:= low;
      |
    3 : //externe Quelle, DSS abschalten
        ROMStr16Out(@ExtStr);
        wave_cmd:= reset;
        SquareSw:= low;
        ExtSource:= high;
      |
  endcase;
  dss_cmd:= wave_cmd;
  SendDSS;
  SendSR;
end;

procedure WriteOutput;
begin
  If DisplayValue = 0 then
    ROMStr16Out(@NrmStr);
  else
    ROMStr16Out(@PwrStr);
  endif;
end;

procedure WriteItem;
{einzelnen Wert DisplayValue darstellen}
begin
//Zahlenausgabe ein- bis dreistellig, VIEL platzsparender als ByteToStr:
//  write(lcdout_m, ByteToStr(DisplayValue:digits));

  if digits > 1 then
    k:= DisplayValue mod 100;
    k:= (k div 10);
    LCDout_M(char(k + 48));
  endif;
  k:= DisplayValue mod 10;
  LCDout_M(char(k + 48));

end;

procedure WriteFrequArrayData;
{Teil des EditArray darstellen}
begin
  LCD1x0y1;
  for i:= 0 to 7 do
    k:= gettable(EditArray, i);
    LCDout_M(char(k + 48));
    if i = 6 then
      LCDout_M(#46); //Dezimalpunkt nach 7. Stelle
    else
      LCDout_M(#32);
    endif;
  endfor;
end;

procedure WriteOptionsArrayData;
{Teil des EditArray darstellen, Anzahl <items>}
begin
  LCD1x0y1;
  digits:= 2;
  DisplayValue:= OutputMode;
  WriteOutput;
  LCDout_M(#32);
  DisplayValue:= GainOffset;
  digits:= 1;
  WriteItem;
  LCDout_M(#48);
  LCDout_M(#32);
  LCDout_M(#32);
  digits:= 2;
  DisplayValue:= Burstmode;
  WriteItem;
  LCDout_M(#32);
  LCDout_M(#32);
  DisplayValue:= Wave;
  WriteWave;
  LCDout_M(#32);
end;

procedure WriteFrequency;
var blank: boolean;
{Teil des EditArray darstellen, Anzahl 8}
begin
  blank := true;
  for i:= 0 to 6 do
    DisplayValue:= gettable(EditArray, i);
    if DisplayValue <> 0 then
      blank:= false;
    endif;
    if (DisplayValue = 0) and blank then
      LCDout_M(#32);
    else
      LCDout_M(char(DisplayValue + 48));
    endif;
  endfor;
  LCDout_M(#46);   //Dezimalpunkt nach 7. Stelle
  DisplayValue:= FrequDez;
  LCDout_M(char(DisplayValue + 48));
  ROMStr16Out(@HzStr);
end;

procedure WriteLevelmV;
var blank: boolean;
  dBtab          : byte;
  Prefix         : char;
{mV aus dB ausrechnen}
begin
  dBtab := LeveldB + (10*GainOffset) + 10;
  j:= dBtab mod 20;
  k:= ((dBtab + 10) div 20) + 2;  //Komma-Position ermitteln
  Prefix:= 'm';   // für mV
  LevelStr:= inttostr(gettable(dBfac, j) : 6);

  //hier: 10...110 = -70 ... +30dB
  //eigener Regelbereich 0...80 = -70 ... +10dB
  case dBtab of
    10..29  :      //-70..-51
              k:= k + 3;
              insert('.', LevelStr, k);
              Prefix:= #228;   // für uV
            |
    30..89  :     //-50..+9
              insert('.', LevelStr, k);
            |
    90..109 :     //+10..+29
              insert('.', LevelStr, 4);
              Prefix:= #32;   //kein Prefix
            |
    110     :     //+30
              insert('.', LevelStr, 5);
              Prefix:= #32;   //kein Prefix
            |
  endcase;
  LCDout_M(#32);   //Leerzeichen
  LCDout_M(#32);   //Leerzeichen
  write(lcdout_m, LevelStr);
  LCDout_M(#32);   //Leerzeichen
  LCDout_M(Prefix);
  LCDout_M(#86);
end;

procedure WriteLeveldB;
var dBval: Integer;
begin
  //0...100 = -70 ... +30dB
  //eigener Regelbereich 0...80 = -70 ... +10dB
  dBval:= integer(LeveldB) - 70 + integer(GainOffset*10);
  write(lcdout_m, IntToStr(dBval : 4));
  ROMStr16Out(@dBStr);
end;

function EditItem : boolean;
var altered: boolean;
{Wert in EditValue, Limit in ItemLimit}
begin
  altered:= false;
  while UpDownButton do
{$IFDEF IncrEnc}
    setSystimer(IncrTimeOut, 200);
{$ENDIF}
    altered:= true;
    if not _TasteAuf then
      inc(EditValue);
    endif;
    if not _TasteAb then
      dec(EditValue);
    endif;
{$IFDEF IncrEnc}
    repeat
      mdelay(4);
    until (not UpDownButton);
{$ENDIF}
    CheckItemLimits;
    LCD1xy;
    DisplayValue:= EditValue;

    case EditWhich of
      Wv   :
             WriteWave;
           |
      Outp :
             WriteOutput;
           |
      dB   :
             WriteLeveldB;
           |
    else
      WriteItem;
    endcase;

{$IFNDEF IncrEnc}
    FastRepeatRelease;
{$ENDIF}
  endwhile;
  if BurstMode <> 0 then
    if isSysTimerZero(BurstTimer) then
      setSystimer(BurstTimer, 10);
      dec(BurstCount);
      if BurstCount = 0 then
        dss_cmd:= reset;
        SendDSS;
        BurstCount:= BurstMode + 1;
        BurstPrime:= true;
      endif;
    endif;
    if BurstCount = 1 then
      if BurstPrime then
        dss_cmd:= wave_cmd;
        SendDSS;
        BurstPrime:= false;
      endif;
    endif;
  endif;
  return (altered);
end;


procedure UpDownData;
begin
  EditValue:= gettable(EditArray, ord(EditWhich) - ItemOffset);
  SetItemLimPos;
  Modified:= EditItem; // wird noch gebraucht
  if Modified then
{neuen Patch senden}
    settable(EditArray, ord(EditWhich) - ItemOffset, EditValue);
    if not PresetOn then
      PatchCopyToEE;
    endif;

    SetFrequ;
    SetLevel;
  endif;
end;

procedure ShowDefaultDisp;
{Darstellung der PerfPatch-Daten}
begin
  LCD1x0y0;
  if PresetOn Then
    ROMStr16Out(@PresetStr);
    DisplayValue:= PresetNum;
  else
    ROMStr16Out(@MemStr);
    DisplayValue:= PatchNum;
  endif;
  x:= 7;
  LCD1xy;
  digits:= 2;
  WriteItem;
  LCDout_M(#32);

  x:= 9;
  LCD1xy;
  WriteLeveldB;
  LCD1x0y1;
  LCDclrEOL_M(LCD_m1);
  DisplayValue:= Wave;
  WriteWave;

  x:= 4;
  LCD1xy;
  if EditWhich = dB Then
    WriteLevelmV;
  else
    WriteFrequency;
  endif;
  SetCursor;
end;

procedure WaitForDisp;
begin
  if isSysTimerZero(IncrTimeOut) AND prime then
    prime:= false;     // einmal darstellen
    ShowDefaultDisp;
  endif;
end;

{###########################################################################}

procedure initall;
{nach Reset aufgerufen}
begin
  DDRB:=  DDRBinit;            {PortB dir}
  PortB:= PortBinit;           {PortB}

  DDRC:=  DDRCinit;            {PortC dir}
  PortC:= PortCinit;           {PortC}

  DDRD:=  DDRDinit;            {PortD dir}
  PortD:= PortDinit;           {PortD}

  TCNT2:= 0;

  if LCDsetup_M(LCD_m1) then
    LCDcursor_M(LCD_m1, false, false);
    LCDCharSet_M(LCD_m1, #0, $02, $06, $0E, $1E, $0E, $06, $02, $00); {"<" Cursor}
//    LCDCharSet_M(LCD_m1, #1, $00, $00, $08, $15, $02, $00, $00, $00); {Normal}
//    LCDCharSet_M(LCD_m1, #2, $08, $15, $02, $00, $02, $15, $08, $00); {Symmetrisch}
//    LCDCharSet_M(LCD_m1, #2, $08, $15, $02, $00, $08, $15, $02, $00); {2kanalig}

    LCDclr_M(LCD_m1);
    ROMStr16Out(@Vers1Str);
    LCD1x0y1;
    ROMStr16Out(@Vers2Str);
  endif;
  mdelay(1500);

  PatchNum:= 0;
  PatchCopyFromEE;       // Optionen-Defaults
  BtnInPort:= PinD;

  dss_cmd:= reset; //DSS-reset
  SendDSS;
  wave_cmd:= sine;

  if (not _TasteAux) or (EEprime <> $aa55) then
{EEPROM-Table erstmals mit Patch 1 initialisieren, wenn Taste Aux gedrückt}
    LCD1x0y1;
    ROMStr16Out(@InitStr);
    for PatchNum:= 0 to 30 do
      PresetNum:= PatchNum;
      _LEDaux := not _LEDaux;
      PresetCopyFromROM;
      PatchCopyToEE;
    endfor;
    _LEDaux := high;
    EEprime:= $aa55;
    PatchNum:= 0;
    EElastpatch:= PatchNum;
  endif;

  PatchNum:= EElastpatch;
  PatchCopyFromEE;
  PresetNum:= 0;
  PresetOn:= true;
  BurstPrime:= False;
  LCDclr_M(LCD_m1);
  Modified:= false;
  EnableInts;
end;

{###########################################################################}
{ Main Program }
{$IDATA}

begin
  Initall;
  SetLevel;
  SetFrequ;
  prime:= false;
  loop
    SetItemLimPos;
    BurstCount:= 1;
    case EditWhich of
      Patch      :
                   ShowDefaultDisp;
                   SlowRepeatRelease;
                   if PresetOn then
                     EditValue:= PresetNum;
                   else
                     EditValue:= PatchNum;
                   endif;
                   SetItemLimPos;
                   repeat
                     if EditItem then
                       prime:= true;
                       if PresetOn then
                         PresetNum:= EditValue;
                         PresetCopyFromROM;
                       else
                         PatchNum:= EditValue;
                         PatchCopyFromEE;
                       endif;
                       SetFrequ;
                       SetLevel;
                     endif;
                     WaitForDisp;
                   until NaviButton;
                 |
      dB         :
                   ShowDefaultDisp;
                   SlowRepeatRelease;
                   EditValue:= LeveldB;
                   SetItemLimPos;
                   repeat
                     if EditItem then
                       prime:= true;
                       LeveldB:= EditValue;
                       SetLevel;
                       if not PresetOn then
                         PatchCopyToEE;
                       endif;
                     endif;
                     WaitForDisp;
                   until NaviButton;
                 |
      Fr1..FrDec :
                   LCD1x0y0;
                   ROMStr16Out(@FrequStr);
                   WriteFrequArrayData; //Index steht auf 4 Werte im ProgArray
                   SetCursor;
                   SlowRepeatRelease;
                   repeat
                     UpDownData;
                   until NaviButton;
                 |
      Outp..Wv   :
                   LCD1x0y0;
                   ROMStr16Out(@OptStr);
                   WriteOptionsArrayData; //Index steht auf 4 Werte im ProgArray
                   SetCursor;
                   SlowRepeatRelease;
                   repeat
                     UpDownData;
                   until NaviButton;
                 |
    endcase;

    if not _TasteLinks then
      if EditWhich = Patch then
        EditWhich:= Wv;
      else
        EditWhich:= pred(EditWhich);
      endif;
      EElastPatch:= PatchNum;
    endif;
    if not _TasteRechts then
      EElastPatch:= PatchNum;
      if EditWhich = Wv then
        EditWhich:= Patch;
      else
        EditWhich:= succ(EditWhich);
      endif;
    endif;
    if not _TasteAux then
      if PresetOn then
        PresetOn:= false;
        _LEDaux := low;
        PatchCopyFromEE;
      else
        PresetOn:= true;
        _LEDaux := high;
        PresetCopyFromROM;
      endif;
    endif;
  endloop;
end DDS.

