program SoftDDS;
{DDS-Funktionsgenerator mit R2R-Netzwerk und MP3-Spieler}
{(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?}

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                  = %11100000;            {PortB, R2R-Netz}

  DDRCinit                   = %00001111;            {PortC dir }
  PortCinit                  = %11111011;            {PortC, 4094 SR}
  
  DDRDinit                   = %00000100;            {PortD dir }
  PortDinit                  = %11111111;            {PortD, Taster, LEDS}

  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  LEDOutPort                 = @PortD;   {LED-Port}
  R2ROutPort                 = @PortB;   {DDS-Port}
  MP3OutPort                 = @PortC;   {DDS-Port}

{Konstanten für Frequenzen, addiert für DDS, abhängig von ATmega-CLK/DDS-Zyklen}
  fhz     : Table[0..7] of LongInt = (12582912,1258291,125829,12583,1258,126,13,1);
{
1
13
126
1258
12583
125829
1258291
12582912
}
{Dezimalstelle gerundet, nur die ersten 10 benutzt}
  fdhz     : Table[0..15] of LongInt = (0,1,2,3,5,6,7,8,10,12,0,0,0,0,0,0);

{Sinus-Tabelle für Soft-DDS, *muss* auf Seiten-Anfang liegen}
  sinus[@$1f00]     : Table[0..255] of byte = (
  {$I sinus.txt}
  );
  
{Lineal     .----.----.----.----.----.----.----.----.----}
  Vers1Str                   = 'MP3-FG1  V#0.101';    //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;

  MP3playstop = 1;
  MP3rev = 2;
  MP3forw = 3;
  MP3repeat = 4;
  MP3volplus = 5;
  MP3equ = 6;
  MP3inh = 8;

//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
  0,0,0,0,6,0,0,0     //Outp Gain Burst Wv dB - - -
 );


{--------------------------------------------------------------}
{ Var Declarations }

var
{$EEPROM}

{$DATA} {Schnelle Register-Variablen}
  i, k  : Byte;
  m,DDSL,DDSM,DDSH: Byte;

  SCLK[MP3OutPort, 0]: bit;
  SDATA[MP3OutPort, 1]: bit;
  STROBE[MP3OutPort, 2]: bit;


{$IDATA}  {Langsamere SRAM-Variablen}

  MP3Port              : Byte; //Zwischenspeicher für 4094 SR

  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;
  DisplayValue, EditValue,
  ItemUpperLimit, ItemLowerLimit, x, y, {x,y Koordinaten LCD}
  digits,
  BtnTime,BtnTime2, BurstCount       : Byte;
  Modified,aBool,PresetOn,BurstPrime,prime : boolean;
  MP3command: Byte;
  
{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...7  = -60 ... +10dB

  FreqStr:String[8];

  IncrTimeOut : SysTimer8;


const
  LevelArrOffs = @OutputMode - @EditArray;
  EditArrayLen = @LeveldB - @EditArray;

{ $NOSAVE}

{###########################################################################}

{DDS functions }
procedure myDDS;
begin
  disableints;
  case wave of
  0:   //Sinus
    asm;
      in          SoftDDS.m,PIND       ;  m:=PinD;
    ; set sinewave output as default
      ldi		_ACCCHI, SoftDDS.sinus SHRB 8	; setup Z pointer hi
      ldi		_ACCCLO, SoftDDS.sinus AND $FF	; setup Z pointer lo
      ldi 	_ACCDHI,$00		; clear accumulator
      ldi 	_ACCDLO,$00
      mov   _ACCA,SoftDDS.DDSL    ; setup adder registers
      mov 	_ACCFLO,_ACCA
      mov   _ACCA,SoftDDS.DDSM
      mov 	_ACCFHI,_ACCA
      mov   _ACCA,SoftDDS.DDSH
      mov 	_ACCBLO,_ACCA		;
  ; main loop
  ;	_ACCDLO,_ACCDHI,_ACCCLO is the phase accumulator
  ;  	_ACCFLO,_ACCFHI,_ACCBLO is the adder value determining frequency
  ; 	add value to accumulator
  ;	load byte from current table in ROM
  ;	output byte to port
  ;	repeat
  SoftDDS.LOOP1:
      add	_ACCDLO,_ACCFLO			; 1
      adc	_ACCDHI,_ACCFHI			; 1
      adc	_ACCCLO,_ACCBLO			; 1
      lpm					; 3, ROM access
      out	PORTB,_ACCGLO		        ; 1
      in        _ACCA,PIND                ; 1
      CPSE      _ACCA,SoftDDS.m  ; 2, bei Änderung an Taster-Port abbrechen
      rjmp	SoftDDS.ENDLOOP1     ; bei Abbruch
      rjmp	SoftDDS.LOOP1	     ; 2 => 12 cycles
  SoftDDS.ENDLOOP1:
      ldi 	_ACCA,32	; Ausgang auf 6-Bit-Mittelwert
      out	PORTB,_ACCA
    endasm;
      |
  1:  // Triangle
      |
  2:  // Square
      |
  3:  // externe Quelle
      |
  endcase;
  enableints;
end;

procedure SendSR;  //Sende MP3Port an den 4094

begin
  STROBE:=high;
  SCLK:=low;
  k:=MP3Port;
  for i := 0 to 7 do
    SDATA:=bit(k,7);
    SCLK:=high;
    k := k shl 1;
    SCLK:=low;
  endfor;
  STROBE:=low;
 // SCLK:=high;
end;

procedure SetFrequ;
var
  freq, fac, acc:LongInt;
  freqHi,freqLo:word;
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;
  freqHi:=hiword(freq);
  freqLo:=Loword(freq);
  DDSL:=Lo(freqLo);
  DDSM:=Hi(freqLo);
  DDSH:=Lo(freqHi);
end;

procedure SetLevel;
var temp:byte;
begin
  temp:= 7-LeveldB;
  MP3Port:=(MP3inh SHL 4) OR temp;
  SendSR;
end;

{###########################################################################}

{MP3-Routinen }

procedure MP3remote;
var temp:byte;
begin
  temp:= 7-LeveldB;
  MP3Port:=(MP3command SHL 4) OR temp;  // "Taste drücken"
  SendSR;
  mdelay(100);
  MP3Port:=(MP3inh SHL 4) OR temp;     // "Taste loslassen"
  SendSR;
  mdelay(300);
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:= 7;   // 8 Stufen
    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);
    |
  1:
    ROMStr16Out(@TriStr);
    |
  2:
    ROMStr16Out(@SquStr);
    |
  3: //externe Quelle, DSS abschalten
    ROMStr16Out(@ExtStr);
    |
  endcase;
//  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 WriteLeveldB;
var dBval: Integer;
begin
  //0...7 = +10 ... -60dB
  dBval:= integer(LeveldB*10)-60;
  write(lcdout_m, IntToStr(dBval:4));
  ROMStr16Out(@dBStr);
end;

function EditItem : boolean;
var altered: boolean;
{Wert in EditValue, Limit in ItemLimit}
begin
  altered:= false;
    myDDS;
    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;

{während auf eingabe gewartet wird, Ton generieren}
  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;
    WriteFrequency;
    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;
  i:=gettable(sinus,1);        //damit bekannt
  
  MP3port:=(MP3playstop SHL 4) or 7;  // gain mute, stopp gedrückt halten
  SendSR;

  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);
  LCD1x0y1;
  ROMStr16Out(@InitStr);
  PatchNum:=0;
  PatchCopyFromEE;       // Optionen-Defaults
  BtnInPort:=PinD;
  
  if (not _TasteAux) or (EEprime <> $aa55) then
{EEPROM-Table erstmals mit Patch 1 initialisieren, wenn Taste Aux gedrückt}
    for PatchNum:= 0 to 30 do
      PresetNum:=PatchNum;
      _LEDaux := not _LEDaux;
      PresetCopyFromROM;
      PatchCopyToEE;
    endfor;
    _LEDaux := high;
    EEprime:=$aa55;
    PatchNum:=0;
    EElastpatch:= PatchNum;
  else
    mdelay(2500);  // für MP3-Ausschaltzeit
  endif;

  LeveldB:=0;  // mute
  
  MP3port:=(MP3inh SHL 4) or 7;  // gain mute, stopp loslassen
  SendSR;
  mdelay(300);  // für MP3-Ausschaltzeit

// MP3-Player einschalten, max. Lautstärke, Equ off, Repeat on
  MP3command:= MP3playstop;  // Stop/On
  MP3remote;
  mdelay(1000);   // booten...
  MP3command:= MP3repeat;  // repeat einschalten
  MP3remote;
  MP3command:= MP3volplus;  // inc volume to max
  for m := 0 to 7 do
    MP3remote;
  endfor;
  MP3command:= MP3playstop;  // repeat einschalten
  MP3remote;

  PatchNum:= EElastpatch;
  PatchCopyFromEE;
  PresetNum:=0;
  PresetOn:=true;
  BurstPrime:=False;
  LCDclr_M(LCD_m1);
  Modified:= false;
  EnableInts;
end;

{###########################################################################}
{ Main Program }
{$IDATA}

begin
  Initall;
  prime:=false;
  loop
    SetLevel;
    SetFrequ;
    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 SoftDDS.

