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 LM1971}

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,Swp,Gn10,Brst,Wv,Preset);

{--------------------------------------------------------------}
{ Const Declarations }
const
  ItemOffset = 2; // Abstand Patch-Fr1
{ Keyboard-Interface, Taster und LEDs PortBits }

  DDRBinit                   = %00111111;            {PortB dir }
  PortBinit                  = %11111111;            {PortB }

  DDRCinit                   = %00001111;            {PortC dir }
  PortCinit                  = %11111111;            {PortC }
  
  DDRDinit                   = %00000100;            {PortD dir }
  PortDinit                  = %11111100;            {PortD }

  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  LEDOutPort                 = @PortD;   {LED-Port}
  DDSOutPort                 = @PortB;   {DDS-Port}

  SCLK_pin                  = 0;
  SDATA_pin                 = 1;
  FSYNC_pin                 = 2;
  LOADS_pin                 = 3;
  GainSwitch_pin            = 4;
  SquareSwitch_pin          = 5;
  
{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..127] of Word = (
{$I dB775mV.txt}
  );
  dBadj   : Table[0..127] of byte = (
{$I dBadj.txt}
  );

{Lineal     .----.----.----.----.----.----.----.----.----}
  Vers1Str                   = 'AD9833 DDS 1.020';    //Vers1
  Vers2Str =                   'by C. Meyer/c''t'+char(0);    //Vers2

  InitStr =                    'Initialising... ';     //InitStr
  MemStr =                     'Memory '+char(0);     //MemStr
  PresetStr =                  'Preset '+char(0);     //MemStr
  HzStr =                      ' Hz'+char(0);
  mVStr =                      ' mV'+char(0);
  dBStr =                      ' dB'+char(0);
  SinStr =                      'Sin'+char(0);
  SquStr =                      'Sqr'+char(0);
  TriStr =                      'Tri'+char(0);

  EmptyItemStr = '-*-'+char(0);
  FrequStr                    = 'Frequency Hz    ';
  LevelStr                    = 'Swp 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..255] of Byte =
  (
  0,0,0,1,0,0,0,0,
  0,0,0,0,0,2,0,0,
  0,0,0,0,0,2,5,0,
  0,0,0,0,0,3,1,5,
  0,0,0,0,0,4,0,0,
  0,0,0,0,0,5,0,0,
  0,0,0,0,0,6,3,0,
  0,0,0,0,0,8,0,0,
  0,0,0,0,1,0,0,0,
  0,0,0,0,1,2,5,0,
  0,0,0,0,1,6,0,0,
  0,0,0,0,2,5,0,0,
  0,0,0,0,3,1,5,0,
  0,0,0,0,4,0,0,0,
  0,0,0,0,5,0,0,0,
  0,0,0,0,6,3,0,0,
  0,0,0,0,8,0,0,0,
  0,0,0,1,0,0,0,0,
  0,0,0,1,2,5,0,0,
  0,0,0,1,6,0,0,0,
  0,0,0,2,0,0,0,0,
  0,0,0,2,5,0,0,0,
  0,0,0,3,1,5,0,0,
  0,0,0,4,0,0,0,0,
  0,0,0,5,0,0,0,0,
  0,0,0,6,3,0,0,0,
  0,0,0,8,0,0,0,0,
  0,0,1,0,0,0,0,0,
  0,0,1,2,5,0,0,0,
  0,0,1,6,0,0,0,0,
  0,0,2,0,0,0,0,0,
  0,0,2,5,0,0,0,0
  );


//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,60,0,0,0   //Swp Gain ADJ1972 Wv dB
{$ELSE}
  0,0,0,0,60,0,0,0   //Swp Gain Burst Wv dB
{$ENDIF}
 );


{--------------------------------------------------------------}
{ Var Declarations }

var
{$EEPROM}

{$DATA} {Schnelle Register-Variablen}
  i, j, k, m       : Byte;
  SCLK[DDSOutPort, SCLK_pin]: bit;
  SDATA[DDSOutPort, SDATA_pin]: bit;
  FSYNC[DDSOutPort, FSYNC_pin]: bit;
  LOADPOT[DDSOutPort, LOADS_pin]: bit;
  GainSw[DDSOutPort, GainSwitch_pin]: bit;
  SquareSw[DDSOutPort, SquareSwitch_pin]: bit;
  
{$IDATA}  {Langsamere SRAM-Variablen}

  EditWhich      : TEditWhich;
  dss_cmd: word;
  wave_cmd: word;
  DisplayValue, EditValue,
  ItemUpperLimit, ItemLowerLimit, x, y, {x,y Koordinaten LCD}
  digits,
  BtnTime,BtnTime2       : Byte;
  Modified,aBool,PresetOn : 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;
  FrequDez[@Frequ1+7]: byte;
  
  Sweepmode[@FrequDez+1]: byte;
  Gain10dB[@Sweepmode+1]: byte;
  Burstmode[@Gain10dB+1]: byte;
  Wave[@Burstmode+1]: byte;
  LeveldB[@Wave+1]: byte;

  Timer1,Timer2 : SysTimer8;
  LongTimer: Systimer;

{$IDATA}  {Langsamere SRAM-Variablen}

  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}

const
  LevelArrOffs = @Sweepmode - @EditArray;
  EditArrayLen = @LeveldB - @EditArray;

{###########################################################################}

{DDS functions }

{$NOSAVE}
procedure SendDSS;  //Sende ein Wort an den DSS-Chip

begin
  FSYNC:=low;
  for i := 0 to 15 do
    if bit(dss_cmd,15)then
      SDATA:=high;
    else
      SDATA:=low;
    endif;
    SCLK:=low;
    dss_cmd := dss_cmd shl 1;
    SCLK:=high;
  endfor;
  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;
end;

procedure SendLevel(step, channel:byte);
begin
  //Sende Adresse 0 und ein Byte an den Poti-Chip LM1971
  FSYNC:=high;
  SDATA:=low;
  SCLK:=low;
  LOADPOT:=low;
  for i := 0 to 6 do
    SCLK:=high;
    nop;
    SCLK:=low;
  endfor;
  if channel=1 then
      SDATA:=high;
      nop;
  endif;
  SCLK:=high;
  nop;
  SCLK:=low;
  for i := 0 to 7 do
    if bit(step,7)then
      SDATA:=high;
    else
      SDATA:=low;
    endif;
    nop;
    SCLK:=high;
    step := step shl 1;
    SCLK:=low;
  endfor;
  LOADPOT:=high;
  SDATA:=high;
  SCLK:=high;
end;

procedure SetLevel;
begin
  k:= -LeveldB-186;    // - (256-Anzahl Stufen)
  j:=k;

{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);

//Relais für externe Endstufe
  GainSw:=boolean(Gain10dB);
  
end;

{###########################################################################}

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 PresetCopyFromROM;
{Frequenz aus ROM holen}
var myPatchAdr: Word;
begin
  for i := 0 to 7 do
    myPatchAdr := word(i)+((word(PresetNum)) shl 3);  //*8
    settable(EditArray, i, (TerzArray[myPatchAdr]));
  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 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:= 30;
    x:=7;
    y:=0;
    |
  dB:
    digits:=1;
    ItemLowerLimit:= 0;
    ItemUpperLimit:= 70;
    x:=12;
    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;
    |
  Swp:
    x:=0;
    ItemLowerLimit:= 0;
    ItemUpperLimit:= 100;
    |
  Gn10:
    digits:=1;
    x:=4;
    ItemLowerLimit:= 0;
    ItemUpperLimit:= 1;
    |
  Brst:
    x:=7;
    ItemLowerLimit:= 0;
    ItemUpperLimit:= 255;
    |
  Wv:
    x:=12;
    ItemLowerLimit:= 0;
    ItemUpperLimit:= 2;
    |
  endcase;
end;

procedure SetCursor;
begin
  SetItemLimPos;
  LCDxy_M(LCD_m1, x+digits, y);
  LCDout_M(#0);
end;

procedure WriteWave;
var blank:boolean;
{Teil des EditArray darstellen, Anzahl 8}
begin
  case DisplayValue of
  0:
    ROMStr16Out(@SinStr);
    wave_cmd:=sine;
    SquareSw:=low;
    |
  1:
    ROMStr16Out(@TriStr);
    wave_cmd:=triangle;
    SquareSw:=low;
    |
  2:
    ROMStr16Out(@SquStr);
    wave_cmd:=square;
    SquareSw:=high;
    |
  endcase;
  dss_cmd:=wave_cmd;
  SendDSS;
end;

procedure WriteItem;
{einzelnen Wert DisplayValue darstellen}
begin
//Zahlenausgabe ein- bis dreistellig, VIEL platzsparender als ByteToStr:
//  write(lcdout_m, ByteToStr(DisplayValue:digits));

  if digits = 3 then
    k:=DisplayValue div 100;
    LCDout_M(char(k+48));
  endif;
  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
    DisplayValue:= gettable(EditArray, i);
    WriteItem;
    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:=3;
  DisplayValue:= Sweepmode;
  WriteItem;
  LCDout_M(#32);
  DisplayValue:= Gain10dB;
  digits:=1;
  WriteItem;
  LCDout_M(#48);
  LCDout_M(#32);
  digits:=3;
  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;
 mVint:Word;
 mVdez:byte;
{mV aus dB ausrechnen}
begin
  i := (10*Gain10dB)+LeveldB+10;     //fängt mit +10  dB an
  mVint:=gettable(dBfac,i);
  if i>80 then
    mVdez:=0;
  else
    mVdez:=byte(mVint mod 10);
    mVint:= mVint div 10;
  endif;
  write(lcdout_m, inttostr(mVint:7));
  LCDout_M(#46);   //Dezimalpunkt nach 7. Stelle
  LCDout_M(char(mVdez+48));
  ROMStr16Out(@mVStr);
end;

function EditItem : boolean;
var altered: boolean;
{Wert in EditValue, Limit in ItemLimit}
begin
  altered:= false;
{$IFDEF IncrEnc}
  repeat
{$ENDIF}
    while UpDownButton do
{$IFDEF IncrEnc}
      setSystimer(Timer1,25);
{$ENDIF}
      altered:= true;
      if not _TasteAuf then
        inc(EditValue);
      endif;
      if not _TasteAb then
        dec(EditValue);
      endif;
{$IFDEF IncrEnc}
      repeat
        mdelay(1);
      until not UpDownButton;
{$ENDIF}
      CheckItemLimits;
      LCD1xy;
      DisplayValue:=EditValue;
      
      if EditWhich = Wv then
        WriteWave;
      else
        WriteItem;
      endif;

{$IFNDEF IncrEnc}
      FastRepeatRelease;
{$ENDIF}
    endwhile;
{$IFDEF IncrEnc}
  until isSysTimerZero(Timer1);
{$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;
    SetLevel;
    SetFrequ;
  endif;
end;


procedure ShowDefaultDisp;
var dBval: Integer;
{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);
  LCDclrEOL_M(LCD_m1);
  
  x:=9;
  LCD1xy;
  dBval:= integer(LeveldB)-60+integer(Gain10dB*10);
  
  write(lcdout_m, IntToStr(dBval:4));
  ROMStr16Out(@dBStr);
  
  LCD1x0y1;
  LCDclrEOL_M(LCD_m1);
  DisplayValue:= Wave;
  WriteWave;
  x:=4;
  LCD1xy;
  if EditWhich=dB Then
    WriteLevelmV;
  else
    WriteFrequency;
  endif;
  SetFrequ;
  SetLevel;
  SetCursor;
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;

//  TIMSK:= TIMSK OR %10000000;  {OCIE2=1}
//  ADMUX:= ADMUX OR %11000000;  {Internal ADC Reference}

  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}
    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;
  LCDclr_M(LCD_m1);
  Modified:= false;
  EnableInts;
end;

{###########################################################################}
{ Main Program }
{$IDATA}

begin
  Initall;
{Setup-Menü wenn Taste Aux beim Einschalten festgehalten}
  loop
    SetItemLimPos;
    case EditWhich of
      Patch :
         ShowDefaultDisp;
         SlowRepeatRelease;
         if PresetOn then
           EditValue:= PresetNum;
         else
           EditValue:= PatchNum;
         endif;
         SetItemLimPos;
         repeat
          if EditItem then
            if PresetOn then
              PresetNum:=EditValue;
              PresetCopyFromROM;
            else
              PatchNum:=EditValue;
              PatchCopyFromEE;
            endif;
            ShowDefaultDisp;
          endif;
        until AnyButton;
       |
      dB :
         ShowDefaultDisp;
         SlowRepeatRelease;
         EditValue:= LeveldB;
         SetItemLimPos;
         repeat
           if EditItem then
             LeveldB:=EditValue;
             ShowDefaultDisp;
             if not PresetOn then
               PatchCopyToEE;
             endif;
          endif;
         until AnyButton;
       |
      Fr1..FrDec :
         LCD1x0y0;
         ROMStr16Out(@FrequStr);
         WriteFrequArrayData; //Index steht auf 4 Werte im ProgArray
         SetCursor;
         SlowRepeatRelease;
         repeat
           UpDownData;
         until AnyButton;
       |
      Swp..Wv :
         LCD1x0y0;
         ROMStr16Out(@LevelStr);
         WriteOptionsArrayData; //Index steht auf 4 Werte im ProgArray
         SetCursor;
         SlowRepeatRelease;
         repeat
           UpDownData;
         until AnyButton;
       |
    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.

