program SoftDDS;
{DDS-Funktionsgenerator mit R2R-Netzwerk und MP3t-Spieler}
{(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de}
{LCDisMP3stat 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 mega8515_16}         {ATmega8515 mit 16 MHz, sonst 90S8515 mit 8 MHz}

{$DEFINE miniDDS}          {MINI-DDS Anpassungen in einigen Standard-Routinen}

{$IFDEF mega8515_16}
Device = mega8515, VCC = 5;
{$ELSE}
Device = 90S8515, VCC = 5;
{$ENDIF}

Import SysTick, LCDPort;   // I2Cexpand,
From System Import LongInt;

Define
  ProcClock      = 16000000;        {Hertz}
  SysTick        = 10;              {msec}
  StackSize      = $0080, iData;
  FrameSize      = $0060, iData;

  LCDPort        = PortC;
  LCDTYPE        = 44780;          // 66712;
  LCDrows        = 2;              {2 rows}
  LCDcolumns     = 16;             {16 chars per line}

Implementation

{--------------------------------------------------------------}
{ Type Declarations }
type
  TEditWhich     = (Patch, dB, Fr1, Fr2, Fr3, Fr4, Fr5, Fr6, Fr7, FrDec, Wv, MP3s, Aux1, Aux2, Frequ, mVdB);
  t_MP3Status    = (Off, Play);
//  t_wave         = (sinw, triw, squw, 3..99);

{--------------------------------------------------------------}
{ Const Declarations }
const
  sinw                       = 0;
  triw                       = 1;
  squw                       = 2;
  audio                      = 3;

  FirstMenu                  = Patch;
  LastMenu                   = MP3s;
  ItemOffset                 = 2; // Abstand Patch-Fr1
{ Keyboard-Interface, Taster und LEDs PortBits }

  DDRAinit                   = %11111111;            {PortA dir }
  PortAinit                  = %10000000;            {PortA, R2R-Netz}

  DDRBinit                   = %11111111;            {PortB dir }
  PortBinit                  = %10000111;            {PortB, MP3t-Steuerung}

  DDRCinit                   = %10000000;            {PortC dir }
  PortCinit                  = %10000000;            {PortC, LED PC7}

  DDRDinit                   = %00000000;            {PortD dir }
  PortDinit                  = %11111111;            {PortD, Taster}

  DDREinit                   = %00000111;            {PortE dir }
  PortEinit                  = %00000111;            {PortE, MP3 PowerCtrl}

  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  LEDOutPort                 = @PortC;   {LED-Port}
  R2ROutPort                 = @PortA;   {DDS-Port}
  MP3OutPort                 = @PortB;   {DDS-Port}
  PwrOutPort                 = @PortE;   {E0 = /MP3-Power}

{Konstanten für Frequenzen, addiert für DDS, 16 MHz/12 DDS-Zyklen}
{$IFDEF mega8515_16}
  fhz            : Table[0..7] of LongInt = (12582912, 1258291, 125829, 12583, 1258, 126, 13, 1);
{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);
{$ELSE}
{Konstanten für Frequenzen, addiert für DDS, 8 MHz/12 DDS-Zyklen}
  fhz            : Table[0..7] of LongInt = (25165824, 2516582, 251658, 25166, 2517, 252, 25, 3);
{Dezimalstelle gerundet, nur die ersten 10 benutzt}
  fdhz           : Table[0..15] of LongInt = (0, 3, 5, 8, 10, 13, 15, 18, 20, 23, 0, 0, 0, 0, 0, 0);
{$ENDIF}
  dBfac          : Table[0..31] of Word = (
{$I dB775mV.txt}
  );

{$IFDEF miniDDS}
{Sinus-Tabelle für Soft-DDS, *muss* auf Seiten-Anfang liegen}
  sinus[@$1f00]  : Table[0..255] of byte = (
  {$I sinus.txt}
  );
{$ENDIF}

// Patch, dB, Fr1,Fr2,Fr3,Fr4,Fr5,Fr6,Fr7,FrDec,Wv,MP3s,Aux1,Aux2,Frequ,dBmV;
  posx           : Table[0..15] of byte = (7, 9, 0, 2, 4, 6, 8, 10, 12, 14, 0, 5, 9, 12, 4, 4);
  cursx          : Table[0..15] of byte = (9, 13, 1, 3, 5, 7, 9, 11, 13, 15, 3, 7, 11, 15, 0, 0);

{Lineal     .----.----.----.----.----.----.----.----.----}
  Vers1Str                   = 'Audio-DDS V#1.07';    //Vers1
  Vers2Str                   = 'C. Meyer 11/05';    //Vers2

  HzStr                      = ' Hz';
  dBStr                      = ' dB';
  SinStr                     = 'Sin';
  SquStr                     = 'Sqr';
  TriStr                     = 'Swt';

  PlayStr                    = 'Play';
  WaitStr                    = 'Wait';

  InitStr                    = 'Initialising...';     //InitStr
  MemStr                     = 'Memory ';     //MemStr
  PresetStr                  = 'Preset ';     //MemStr
  FrequStr                   = 'Frequency Hz    ';
  OptStr                     = 'Wave Rt         ';

{
  PwrStr                     =                      'Pwr';
  NrmStr                     =                      'Nrm';
}
  high                       = true;
  low                        = false;

  MP3playstop                = $10;
  MP3rev                     = $20;
  MP3forw                    = $30;
  MP3repeat                  = $40;
  MP3volplus                 = $50;
  MP3equ                     = $60;
  MP3inh                     = $80;

//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;


  EEPatchArray   : Array[0..15] of Byte =
  (
  0, 0, 0, 1, 0, 0, 0, 0,    //Frequ
  0, 30, 0, 0, 6, 0, 0, 0     //Wav MP3s Aux1 Aux2 dB - - -
  );


{--------------------------------------------------------------}
{ Var Declarations }

var
{$EEPROM}

{$DATA} {Schnelle Register-Variablen}
  i, k           : Byte;
  m, DDSL, DDSM, DDSH  : Byte;

{$IDATA}  {Langsamere SRAM-Variablen}

  MP3Port        : Byte; //Zwischenspeicher für 4094 SR

  BtnInPort      : byte;  //Sample-Wert der Panel-Tasten
  _TasteAuf[@BtnInPort, 2]   : bit; {Taste Up}
  _TasteAb[@BtnInPort, 3]    : bit; {Taste Down}
  _TasteLinks[@BtnInPort, 4] : bit;
  _TasteRechts[@BtnInPort, 5]: bit;
  _TasteAux[@BtnInPort, 7]   : bit; {Bit 7 ist Taste Aux, separat}

  _LEDaux[LEDOutPort, 7]     : bit; {Bit 7 ist LED, separat}
  _MP3Power[PwrOutPort, 0]   : bit; {Bit 0 ist PNP-Schalttransistor}

  EditWhich, DispWhich       : TEditWhich;

  DisplayValue, EditValue,
  ItemUpperLimit, ItemLowerLimit, x, y, {x,y Koordinaten LCD}
  digits,
  BtnTime, BtnTime2    : Byte;
  prime, Modified, aBool, PresetOn, MP3isOn: boolean;
  MP3command, MP3currenttrack: Byte;
  MP3repeatBset  : Boolean;      // Repeat-Punkt B gesetzt?
{Edit-Item-Reihenfolge}

  PatchNum       : byte;
  PresetNum,
  MP3Track       : byte;
  MP3Status      : t_MP3Status;

  EditArray      : Table[0..15] of Byte;
  Edit1[@EditArray]    : 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

  Wave[@FrequDez+1]    : byte;
  MP3sec[@Wave+1]      : byte;
  Aux1val[@Wave+2]     : byte;
  Aux2val[@Wave+3]     : byte;
  LeveldB[@Wave+4]     : byte;        //0...7= -60 ... +10dB

  SaveArray      : Table[0..15] of Byte;
  Save1[@SaveArray]    : byte;

  LevelStr       : string[7];
  FreqStr        : String[8];
  MP3repeatTimer : SysTimer;
  MP3runningTimer,MP3runningSeconds: Byte;

const
  EditArrayLen               = @LeveldB - @EditArray;

procedure OnSysTick;
begin
  inc(MP3runningTimer);
  if MP3runningTimer = 100 then
    MP3runningTimer:=0;
    inc(MP3runningSeconds);
    if MP3runningSeconds = MP3sec then
      MP3runningSeconds :=0;
    endif;
  endif;
end;
{$NOSAVE}

{###########################################################################}
{DDS functions }

{$IFDEF miniDDS}
procedure miniDDS;
begin
  disableints;
  m:= byte(wave);
  asm;
    mov  _ACCA, SoftDDS.m
    ldi  _ACCDLO, $FF; kein Taster gedrückt-Vergleich
    mov  SoftDDS.m, _ACCDLO
   ; 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  _ACCFLO, SoftDDS.DDSL; setup adder registers
    mov  _ACCFHI, SoftDDS.DDSM
    mov  _ACCBLO, SoftDDS.DDSH;
    tst  _ACCA
    breq  SoftDDS.SinLoop
    dec  _ACCA
    breq  SoftDDS.TriLoop
    dec  _ACCA
    breq  SoftDDS.SquLoop
    rjmp  SoftDDS.myExit

   ; _ACCDLO, _ACCDHI, _ACCCLO is the phase accumulator
   ; _ACCFLO, _ACCFHI, _ACCBLO is the adder value determining frequency

   ; Sinus:
    SoftDDS.SinLoop:
    add _ACCDLO, _ACCFLO; 1  add value to accumulator
    adc _ACCDHI, _ACCFHI; 1
    adc _ACCCLO, _ACCBLO; 1
    lpm; 3, ROM Zugriff Sinustabelle
    out PORTA, _ACCGLO; 1  output byte to port
    in        _ACCA, PIND; 1
    CPSE      _ACCA, SoftDDS.m; 2, bei Änderung an Taster-Port abbrechen
    rjmp SoftDDS.myExit; bei Abbruch
    rjmp SoftDDS.SinLoop; 2 = > 12 cycles

   ; Rechteck:
    SoftDDS.SquLoop:
    add _ACCDLO, _ACCFLO; 1
    adc _ACCDHI, _ACCFHI; 1
    adc _ACCCLO, _ACCBLO; 1
    ldi  _ACCA, $00; 1
    brmi  SoftDDS.setsqu; 1 / 2
    ldi  _ACCA, $FF; 0 / 1
    SoftDDS.setsqu:
    out PORTA, _ACCA; 1
    in        _ACCA, PIND; 1
    CPSE      _ACCA, SoftDDS.m; 2, bei Änderung an Taster-Port abbrechen
    rjmp SoftDDS.myExit; bei Abbruch
    rjmp SoftDDS.SquLoop; 2 = > 12 cycles

    in          SoftDDS.m, PIND;  m:= PinD;
    ldi  _ACCDHI, $00; clear accumulator
    ldi  _ACCDLO, $00
    mov  _ACCFLO, SoftDDS.DDSL; setup adder registers
    mov  _ACCFHI, SoftDDS.DDSM
    mov  _ACCBLO, SoftDDS.DDSH;

   ; Sägezahn:
    SoftDDS.TriLoop:
    add _ACCDLO, _ACCFLO; 1
    adc _ACCDHI, _ACCFHI; 1
    adc _ACCCLO, _ACCBLO; 1

    mov _ACCA, _ACCCLO; Zählerstand LSB
    nop
    nop

    out PORTA, _ACCA; 1
    in        _ACCA, PIND; 1
    CPSE      _ACCA, SoftDDS.m; 2, bei Änderung an Taster-Port abbrechen
    rjmp SoftDDS.myExit; bei Abbruch
    rjmp SoftDDS.TriLoop; 2 = > 12 cycles

    SoftDDS.myExit:
    ldi  _ACCA, 128; Ausgang auf 8-Bit-Mittelwert
    out PORTA, _ACCA
  endasm;
  enableints;
end;
{$ENDIF}


procedure SendSR;  //Sende MP3Port an den 4094 oder Ausgangsport

begin
  PortB:= MP3Port;
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 OR temp;
  SendSR;
end;

{###########################################################################}
{EEPROM-Routinen}

procedure PresetCopyFromROM;
{Frequenz aus ROM holen}
begin
  //Wert in String zwecks Auswertung (raffiniert!)
  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 PresetSave;
{Sichert aktuelles EditArray}
begin
  CopyBlock(@Frequ1, @Save1, EditArrayLen + 1);
end;

procedure PresetRestore;
begin
  CopyBlock(@Save1, @Frequ1, EditArrayLen + 1);
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 SetPos;
begin
  i:= ord(DispWhich);
  x:= gettable(posx, i);
  y:=1;
  if DispWhich < Fr1 then
    y:=0;
  endif;
  LCDxy(x, y);
end;

procedure SetCursor;
begin
  i:= ord(EditWhich);
  x:= gettable(cursx, i);
  y:=1;
  if EditWhich < Fr1 then
    y:=0;
  endif;
  LCDxy(x, y);
  LCDout(#0);
end;

procedure LCD1xy;
begin
  LCDxy(x, y);
end;

procedure LCD1x0y0;
begin
  LCDhome;
end;

procedure LCD1x0y1;
begin
  x:= 0;
  y:= 1;
  LCD1xy;
end;

{$I buttons.pas}
{$I mp3control.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:= 2;
  case EditWhich of
    Patch      :
                 ItemUpperLimit:= 31;
               |
    dB         :
                 ItemUpperLimit:= 7;   // 8 Stufen
               |
    Fr1..FrDec :
                 if EditWhich = Fr1 then
                   ItemUpperLimit:= 1;
                 endif;
                 digits:= 1;
               |
    Wv         :
                 ItemUpperLimit:= 99;
               |
    MP3s       :
                 ItemLowerLimit:= 1;
                 ItemUpperLimit:= 99;
               |
  endcase;
end;

procedure WriteItem;
{einzelnen Wert DisplayValue darstellen}
begin
//Zahlenausgabe ein- bis zweistellig, VIEL platzsparender als ByteToStr:
//  write(lcdout, ByteToStr(DisplayValue:digits));
  if digits > 1 then
    k:= DisplayValue mod 100;
    k:= (k div 10);
    LCDout(char(k + 48));
  endif;
  k:= DisplayValue mod 10;
  LCDout(char(k + 48));
end;


procedure WriteWave;
begin
  DispWhich:= Wv;
  setPos;
  case Wave of
    sinw..squw :
                 if wave = sinw then
                   WriteLCD(@SinStr);
                 endif;
                 if wave = triw then
                   WriteLCD(@TriStr);
                 endif;
                 if wave = squw then
                   WriteLCD(@SquStr);
                 endif;
               |
    3..99      :
                 //Audio-File-Nummer -2
                 //bei externer Quelle wurde DDS abgeschaltet
                 LCDout('A'); // A wie Audio
                 k:= Wave - 2;
                 MP3Track:= k;
                 DisplayValue:= k;
                 digits:= 2;
                 WriteItem; // Track-Nummer
               |
  endcase;
//  SendDSS;
//  SendSR;
end;

procedure WriteFrequArrayData;
{Teil des EditArray darstellen}
begin
  LCD1x0y1;
  for i:= 0 to 7 do
    k:= gettable(EditArray, i);
    LCDout(char(k + 48));
    if i = 6 then
      LCDout(#46); //Dezimalpunkt nach 7. Stelle
    else
      LCDout(#32);
    endif;
  endfor;
end;

procedure WriteOptionsArrayData;
{Teil des EditArray darstellen}
begin
  WriteWave;
  LCDout(#32);

  DispWhich:= MP3s;
  setPos;
  DisplayValue:= MP3sec;
  WriteItem;
end;

procedure WriteFrequency;
var blank: boolean;
begin
  DispWhich:= Frequ;
  setpos;
  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(#32);
    else
      LCDout(char(DisplayValue + 48));
    endif;
  endfor;
  LCDout(#46);   //Dezimalpunkt nach 7. Stelle
  DisplayValue:= FrequDez;
  LCDout(char(DisplayValue + 48));
  WriteLCD(@HzStr);
end;

procedure WriteLevelmV;
var blank: boolean;
  dBtab          : byte;
  Prefix         : char;
{mV aus dB ausrechnen}
begin
  DispWhich:= mVdB;
  SetPos;

{$IFDEF miniDDS}
  dBtab:= Leveldb*10 + 20;   //Anpassung für Mini-DDS mit 10dB-Stufung
{$ELSE}
  dBtab := LeveldB + (10*GainOffset) + 10; // für AD9833-DDS mit LM1972
{$ENDIF}

  i:= dBtab mod 20;
  k:= ((dBtab + 10) div 20) + 2;  //Komma-Position ermitteln
  Prefix:= 'm';   // für mV
  LevelStr:= inttostr(gettable(dBfac, i) : 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(#32);   //Leerzeichen
  LCDout(#32);   //Leerzeichen
  write(lcdout, LevelStr);
  LCDout(#32);   //Leerzeichen
  LCDout(Prefix);
  LCDout(#86);
end;

procedure WriteLeveldB;
var dBval: Integer;
begin
  DispWhich:= dB;
  SetPos;
{$IFDEF miniDDS}
  //0...7 = +10 ... -60dB
  dBval:= integer(LeveldB*10) - 60;
  write(lcdout, IntToStr(dBval : 4));
  WriteLCD(@dBStr);
{$ELSE}
  //0...100 = -70 ... +30dB
  //eigener Regelbereich 0...80 = -70 ... +10dB
  dBval:= integer(LeveldB) - 70 + integer(GainOffset*10);
  write(lcdout_m, IntToStr(dBval : 4));
  WriteLCD(@dBStr);
{$ENDIF}
end;

procedure ShowDefaultUpper;
{Darstellung der PerfPatch-Daten}
begin
  LCD1x0y0;
  if PresetOn Then
    WriteLCD(@PresetStr);
    DisplayValue:= PresetNum;
  else
    WriteLCD(@MemStr);
    DisplayValue:= PatchNum;
  endif;
  DispWhich:= Patch;
  SetPos;
  WriteItem;
  LCDout(#32);

  WriteLeveldB;
  SetCursor;
end;

procedure ShowDefaultLower;
{Darstellung der unteren Zeile Hauptfenster}
begin
  WriteWave;
  LCDclrEOL;
  if EditWhich = dB then
    WriteLevelmV;
  else
    if MP3isON then
      DispWhich:= Aux2;    // nur für Position
      setpos;
      WriteLCD(@PlayStr);
    else
      if Wave < Audio then // für Up/Down-Durchlauf
        WriteFrequency;
      endif;
    endif;
  endif;
end;

function EditItem : boolean;
var altered: boolean;
{Wert in EditValue, Limit in ItemLimit}
begin
  altered:= false;
{$IFDEF miniDDS}
{während auf Tastatureingabe gewartet wird, Ton generieren}
  if prime then
    miniDDS;
  else
    prime:= true;   //erster Durchlauf erfolgt
  endif;
{$ENDIF}
  while UpDownButton do
    altered:= true;
    if not _TasteAuf then
      inc(EditValue);
    endif;
    if not _TasteAb then
      dec(EditValue);
    endif;

    CheckItemLimits;
    DisplayValue:= EditValue;
    DispWhich:= EditWhich;
    SetPos;
    case EditWhich of
      Patch :
              WriteItem;
              if PresetOn then
                PresetNum:= EditValue;
                PresetCopyFromROM;
              else
                PatchNum:= EditValue;
                PatchCopyFromEE;
              endif;
              ShowDefaultLower;
              SetFrequ;
              SetLevel;
            |
      Wv    :
              Wave:= EditValue;
              WriteWave;
            |
      MP3s  :
              MP3sec:= EditValue;
              DispWhich:= MP3s;
              WriteItem;
            |
      dB    :
              LeveldB:= EditValue;
              SetLevel;
              WriteLeveldB;
              SetCursor;   // wg. Leerzeichen
            |
    else
      WriteItem;
    endcase;
    FastRepeatRelease;
  endwhile;
  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;
//gleich hier setzen - kurze Reaktionszeit:
    SetFrequ;
    SetLevel;
  endif;
end;

procedure MP3trackTime;
// Spielzeit seit Repeat-Start
begin
  if MP3isOn then
      DispWhich:= MP3s;    // nur für Position
      setpos;
      DisplayValue:=MP3runningSeconds;
      WriteItem;
      LCDout(#58);
      digits:=1;
      DisplayValue:=MP3runningTimer div 10;
      WriteItem;
      LCDout(#48); // Hundertstel-"0"
      digits:=2;
  endif;
end;

procedure SetupMP3;
begin
  if not UpDownbutton then
    DispWhich:= Aux2;    // nur für Position
    if Wave > 2 then // Audio-File gewünscht
      PresetNum:= 0;
      if (not MP3isOn) then
        setpos;
        WriteLCD(@WaitStr);
        MP3on;
        MP3isOn:= true;
      endif;
      if MP3isOn then
        if MP3Track <> MP3currenttrack then
          setpos;
          WriteLCD(@WaitStr);
          MP3gotoTrack;
        endif;
        setpos;
        WriteLCD(@PlayStr);
      endif;
    else
      if MP3isOn then   // wieder Wave, MP3 ausschalten
//      setpos;
//      WriteLCD(@WaitStr); {dauert, falls Ausschalten nicht über PowerOff}
        MP3off;
        MP3isOn:= false;
        setpos;
        LCDclrEOL;
      endif;
    endif;
  endif;
end;

{###########################################################################}


procedure initall;
{nach Reset aufgerufen}
begin
  DDRA:=  DDRAinit;            {PortA dir}
  PortA:= PortAinit;           {PortA}
  DDRB:=  DDRBinit;            {PortB dir}
  PortB:= PortBinit;           {PortB}
  DDRC:=  DDRCinit;            {PortC dir}
  PortC:= PortCinit;           {PortC}
  DDRD:=  DDRDinit;            {PortD dir}
  PortD:= PortDinit;           {PortD}
  DDRE:=  DDREinit;            {PortE dir}
  PortE:= PortEinit;           {PortE}

  i:= gettable(sinus, 1);        //damit bekannt

  LCDclr;
  LCDcursor(false, false);
  LCDCharSet(#0, $02, $06, $0E, $1E, $0E, $06, $02, $00); {"<" Cursor}

  WriteLCD(@Vers1Str);
  LCD1x0y1;
  WriteLCD(@Vers2Str);
  mdelay(1000);

  PatchNum:= 0;
  PatchCopyFromEE;       // Optionen-Defaults

  BtnInPort:= PinD; // für _TasteAux
  if (not _TasteAux) or (EEprime <> $aa55) then
    LCD1x0y1;
    WriteLCD(@InitStr);
{EEPROM-Table erstmals mit Presets initialisieren, wenn Taste Aux gedrückt}
    for m:= 0 to 30 do
      PresetNum:= m;
      PatchNum:= m;
      _LEDaux := not _LEDaux;
      PresetCopyFromROM;
      PatchCopyToEE;
    endfor;
    _LEDaux := high;
    EEprime:= $aa55;
  endif;

  PatchNum:= 0;
  PresetNum:= 0;
  PresetCopyFromROM;

  BtnTime:= 40; // Initial timer value
  BtnTime2:= 10; // Anzahl Durchläufe bis fast forward


  PresetOn:= true;
  LCDclr;
  Modified:= false;
  MP3isOn:= false;
  EnableInts;
  prime:= false;
end;

{###########################################################################}
{ Main Program }
{$IDATA}

begin
  Initall;
  EditWhich:= Patch;
  loop
    SetLevel;
    SetFrequ;
    SetItemLimPos;
    case EditWhich of
      Patch        :
                     ShowDefaultUpper;
                     ShowDefaultLower;
                     SlowRepeatRelease;
                     if PresetOn then
                       EditValue:= PresetNum;
                     else
                       EditValue:= PatchNum;
                     endif;
                     SetItemLimPos;
                     repeat
                       if EditItem then
                         ShowDefaultUpper;
                         WriteWave;
                         LCDclrEOL;
                         SetupMP3;
                         ShowDefaultLower;
                       endif;
                       MP3checkRepeat;
                       MP3trackTime;
                     until NaviButton;
                   |
      dB           :
                     ShowDefaultUpper;
                     ShowDefaultLower;
                     SlowRepeatRelease;
                     EditValue:= LeveldB;
                     SetItemLimPos;
                     repeat
                       if EditItem then
                         if not PresetOn then
                           PatchCopyToEE;
                         endif;
                         ShowDefaultUpper;
                         ShowDefaultLower;
                       endif;
                       MP3checkRepeat;
                     until NaviButton;
                   |
      Fr1..FrDec   :
                     LCD1x0y0;
                     WriteLCD(@FrequStr);
                     WriteFrequArrayData;
                     SetCursor;
                     SlowRepeatRelease;
                     repeat
                       UpDownData;
                       MP3checkRepeat;
                     until NaviButton;
                   |
      Wv..LastMenu :
                     LCDclr;
                     WriteLCD(@OptStr);
                     WriteOptionsArrayData;
                     SetCursor;
                     SlowRepeatRelease;
                     repeat
                       UpDownData;
                       MP3checkRepeat;
                       SetupMP3;
                     until NaviButton;
                   |
    endcase;

    if not _TasteLinks then
      if EditWhich = FirstMenu then
        EditWhich:= LastMenu;
      else
        EditWhich:= pred(EditWhich);
      endif;
    endif;
    if not _TasteRechts then
      if EditWhich = LastMenu then
        EditWhich:= FirstMenu;
      else
        EditWhich:= succ(EditWhich);
      endif;
    endif;
    if not _TasteAux then
      if PresetOn then
        PresetOn:= false;
        _LEDaux := low;
        PresetSave;
        PatchCopyFromEE;
      else
        PresetOn:= true;
        _LEDaux := high;
        PresetRestore;
      endif;
{MP3 setzen, falls durch Wechsel nötig}
      WriteWave;
      LCDclrEOL;
      SetupMP3;
      ShowDefaultLower;
    endif;
  endloop;
end SoftDDS.

