program SoftDDS;
(*
DDS-Funktionsgenerator mit AD98833 oder R2R-Netzwerk und MP3-Spieler
als universelle Audio-Quelle
(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de

Zwei Alternativen:
Entweder mit
-- MIDIvice-Platine mit ATmega168 (16KByte Code!) und
   AD9833-DDS-Platine an ManualScan-Port, Pegeleinstellung
   in 1-m_db-Schritten per LM1972, Ausgangspegel asym./sym. mit
   entsprechender Korrektur
oder
-- eigenständige Simpel-Version mit AT90S8515, R2R-D/A-Wandler,
   m_db-Einstellung nur in 10-m_db-Schritten

LCD mit 2x16 Zeichen, für Preset- und Default-Anzeige
5 Taster zur Bedienung, Up/Down, Left/Right, Aux
Steuert MP3-Spieler, die per Widerstands-Netz fernbedienbar sind
Vorliegende Version angepasst für Billigst-MP3-Spieler mit
Myson Century MTM809/CS8822 Chipsatz von EFB/ETT, Braunschweig
*)

{$NOSHADOW}
{ $W+ Warnings}            {Warnings off}
{$TYPEDCONST OFF}
{$DEBDELAY}

//Defines aktivieren durch Entfernen des 1. Leerzeichens!

//miniDDS-Anpassungen in einigen I/O-Routinen, Software-DDS-Prozedur
//sonst AD9833-DDS Anpassungen und mega168 auf MIDIvice-Platine}
{ $DEFINE miniDDS}           //sozusagen HAUPTSCHALTER

//Wenn miniDDS, dann einen der folgen Prozessoren auswählen:
{ $DEFINE atmega8515}        //ATmega8515 mit 16 MHz
{ $DEFINE at90s8515}         //AT90S8515 mit 8 MHz

{$DEFINE minEEPROM}          //TEST: minimale EEPROM-Presets während Entwicklung
{$DEFINE MenuTest}          //TEST: Keine HW-IO-Routinen, zum Testen der Menüs
{ $DEFINE IncrEnc}           //Incremental-Encoder statt Up/Down?

{$IFDEF atmega8515}
Device = mega8515, VCC = 5;
{$ENDIF}
{$IFDEF at90s8515}
Device = 90S8515, VCC = 5;
{$ENDIF}
{$IFDEF miniDDS}
Import SysTick, LCDPort;
{$ELSE}
Device = mega168, VCC = 5;
Import SysTick, TWImaster, LCDmultiPort, IncrPort4;
{$ENDIF}

From System Import LongInt;

Define
{$IFDEF at90s8515}
  ProcClock      = 8000000;        {Hertz}
{$ELSE}
  ProcClock      = 16000000;        {Hertz}
{$ENDIF}

{$IFDEF miniDDS}
  LCDPort        = PortC;
  LCDTYPE        = 44780;
  LCDrows        = 2;              //2 rows
  LCDcolumns     = 16;             //16 chars per line
{$ELSE}
  TWIpresc       = TWI_BR400;
  LCDmultiPort   = I2C_TWI;
  LCDTYPE_M      = 44780;          // 66712;
  LCDrows_M      = 2;              //2 rows
  LCDcolumns_M   = 16;             //16 chars per line
  IncrPort4      = PinC, 1, 16; // pin-reg used, channels, 16 or 32bit integer
  IncrScan4      = Timer1, 4; // timer used, scan rate 1kHz (1..100)

{$ENDIF}

  SysTick        = 10;              //msec
  StackSize      = $0080, iData;
  FrameSize      = $0060, iData;

Implementation

{--------------------------------------------------------------}
type
  TEditWhich     = (m_fr1, m_fr2, m_fr3, m_fr4, m_fr5, m_fr6, m_fr7, m_frdec, m_patch, m_db, m_wav, m_rpt, m_out, m_brst, m_frequ, m_mv);

{--------------------------------------------------------------}
const
//Wave = Sinus, Dreieck/Sägezahn, Rechteck, Audio 1..99
  c_sinw                     = 0;
  c_triw                     = 1;
  c_squw                     = 2;
  c_audio                    = 3;

//OutputMode Normal,
  c_nrmo                     = 0;
  c_symo                     = 1;
  c_pwro                     = 2;
  c_psymo                    = 3;

{ Keyboard-Interface, Taster und LEDs PortBits }

{$IFDEF miniDDS}
  m_lastmenu                 = m_rpt;

  DDRAinit                   = %11111111;            {PortA dir }
  PortAinit                  = %10000000;            {PortA, R2R-Netz}

  DDRBinit                   = %11111111;            {PortB dir }
  PortBinit                  = %10001111;            {PortB, MP3-Steuerung}

  DDRCinit                   = %10000000;            {PortC dir }
  PortCinit                  = %10000000;            {PortC, LED PC7}

  DDRDinit                   = %00000000;            {PortD dir }
  PortDinit                  = %11111111;            {PortD, Taster}

  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  LEDOutPort                 = @PortC;   {LED-Port}

  R2ROutPort                 = @PortA;   {DA-Wandler-Port}
  DDSOutPort                 = @PortB;   {Multiplexer-Port}
{$ELSE}
  m_lastmenu                 = m_brst;

  DDRBinit                   = %00111111;            {PortB dir }
  PortBinit                  = %11101111;            {PortB, b_STROBE=0 }

  DDRCinit                   = %00001100;            {PortC dir }
  PortCinit                  = %11110011;            {PortC mit IncrEncoder}

  DDRDinit                   = %00000100;            {PortD dir }
  PortDinit                  = %11111100;            {PortD }

  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  LEDOutPort                 = @PortD;   {LED-Port}
  DDSOutPort                 = @PortB;   {DDS-Port}

  b_SCLK                     = 0; //Takt für alle
  b_SDATA                    = 1; //Daten für alle
  b_FSYNC                    = 2; //DSS-Frame
  b_LOADPOT                  = 3; //LM1972 LOADS
  b_STROBE                   = 4; //4094 SR Strobe

//für AD9833-DDS
//                         Bit    5432109876543210
  c_ddsresetcmd              = %0010000100000000;
//                         Bit              O D M
  c_ddssinecmd               = %0010000000000000;
  c_ddstrianglecmd           = %0010000000000010;
  c_ddssquarecmd             = %0010000000101000;
{$ENDIF}

{miniDDS-Konstanten für Frequenzen, addiert für DDS, 16 MHz/12 DDS-Zyklen}
{$IFDEF atmega8515}
  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);
{$ENDIF}
{$IFDEF at90s8515}
{miniDDS-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}
{$IFNDEF miniDDS}
{AD9833-DDS-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);
  dBadj          : Table[0..63] of byte = (
{$I dBadj.txt}
  );
{$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}

//LCDXY-Positionen der Menüs und der zugehörigen Cursor-Position auf dem Display
//Reihenfolge wie EditWhich:
//m_fr1, m_fr2, m_fr3, m_fr4, m_fr5, m_fr6, m_fr7, m_frdec, m_patch, m_db, m_wav, m_rpt, m_out, m_brst, m_frequ, m_mv
  posx           : Table[0..15] of byte = (0, 2, 4, 6, 8, 10, 12, 14, 7, 9, 0, 5, 8, 13, 4, 4);
  cursx          : Table[0..15] of byte = (1, 3, 5, 7, 9, 11, 13, 15, 9, 13, 3, 7, 11, 15, 0, 0);
  posy           : Table[0..15] of byte = (1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1);

//Lineal     .----.----.----.----.----.----.----.----.----
  Vers1Str                   = 'Audio-DDS V#1.10';    //Vers1
  Vers2Str                   = 'C. Meyer 11/05';    //Vers2

  HzStr                      = ' Hz';
  dBStr                      = ' dB';
  SinStr                     = 'Sin';
  SquStr                     = 'Sqr';
{$IFDEF miniDDS}
  TriStr                     = 'Swt';
{$ELSE}
  TriStr                     = 'Tri';
{$ENDIF}

  NrmStr                     = 'Nrm';
  SymStr                     = 'Sym';
  PwrStr                     = 'Pwr';
  SymPwrStr                  = 'PSy';

  PlayStr                    = 'Play   ';
  WaitStr                    = 'Wait';

  InitStr                    = 'Initialising...';     //InitStr
  MemStr                     = 'Memory ';     //MemStr
  PresetStr                  = 'Preset ';     //MemStr
  FrequStr                   = 'Frequency Hz    ';
{$IFDEF miniDDS}
  OptStr                     = 'Wav Rpt         ';
{$ELSE}
  OptStr                     = 'Wav Rpt Out Brst';
{$ENDIF}

  high                       = true;
  low                        = false;

{$IFDEF miniDDS}
  c_MP3playstop              = $10;
  c_MP3rev                   = $20;
  c_MP3forw                  = $30;
  c_MP3repeat                = $40;
  c_MP3volplus               = $50;
  c_MP3equ                   = $60;
  c_MP3inh                   = $80;
//  c_MP3power                 = $08;  //Bit 03
{$ELSE}
  c_MP3playstop              = $20;   //Bit 04--07
  c_MP3rev                   = $40;
  c_MP3forw                  = $60;
  c_MP3repeat                = $80;
  c_MP3volplus               = $A0;
  c_MP3equ                   = $C0;
  c_MP3inh                   = $00;
//  c_MP3power                 = $08;  //Bit 03 gesteuert durch MP3isOn
{$ENDIF}

//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
  );

//Default-EEPROM-Werte:
{$EEPROM}
structconst

{$IFDEF minEEPROM}
{$IFDEF miniDDS}
// m_wav, m_rpt, m_out, m_brst, m_db, -,-,-, m_fr1, m_fr2, m_fr3, m_fr4, m_fr5, m_fr6, m_fr7, m_frdec
  EEPatchArray   : array[0..63] of Byte =
  (
  0, 0, 0, 1, 0, 0, 0, 0,    //m_frequ
  0, 30, 0, 0, 6, 0, 0, 0,     //Wav m_rpt m_out m_brst m_db - - -
  0, 0, 0, 1, 0, 0, 0, 1,    //m_frequ
  0, 30, 0, 0, 6, 0, 0, 0,     //Wav m_rpt m_out m_brst m_db - - -
  0, 0, 0, 1, 0, 0, 0, 2,    //m_frequ
  0, 30, 0, 0, 6, 0, 0, 0,     //Wav m_rpt m_out m_brst m_db - - -
  0, 0, 0, 1, 0, 0, 0, 3,    //m_frequ
  0, 30, 0, 0, 6, 0, 0, 0      //Wav m_rpt m_out m_brst m_db - - -
  );
{$ELSE}
  EEPatchArray   : array[0..63] of Byte =
  (
  0, 0, 0, 1, 0, 0, 0, 0,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20,     //Wav m_rpt m_out m_brst m_db - - GainOffs
  0, 0, 0, 1, 0, 0, 0, 1,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20,     //Wav m_rpt m_out m_brst m_db - - -
  0, 0, 0, 1, 0, 0, 0, 2,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20,     //Wav m_rpt m_out m_brst m_db - - -
  0, 0, 0, 1, 0, 0, 0, 3,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20      //Wav m_rpt m_out m_brst m_db - - -
  );
{$ENDIF}
{$ELSE}
{$IFDEF miniDDS}
{$I presets-tl6.txt}
{$ELSE}
{$I presets-tl70.txt}
{$ENDIF}
{$ENDIF}

{--------------------------------------------------------------}

var
{$EEPROM}

{$DATA} {Schnelle Register-Variablen}
  i, k           : Byte;
  m, DDSL, DDSM, DDSH  : Byte;

{$IDATA}  {Langsamere SRAM-Variablen}

  dss_cmd        : word;
  wave_cmd       : word;
  IncrValue      : integer;
  MP3switchTemp, MP3State, SwitchState: Byte; //Zwischenspeicher für 4094 SR oder PortB miniDDS
  BtnInState     : Byte;  //Sample-Wert der Panel-Tasten

{$IFDEF miniDDS}
  b_TasteAuf[@BtnInState, 2] : bit; {Taste Up}
  b_TasteAb[@BtnInState, 3]  : bit; {Taste Down}
  b_TasteLinks[@BtnInState, 4]: bit;
  b_TasteRechts[@BtnInState, 5]: bit;
  b_TasteAux[@BtnInState, 7] : bit; {Bit 7 ist Taste Aux, separat}

  b_LEDaux[LEDOutPort, 7]    : bit; {Bit 7 ist LED, separat}
  b_ExtOnMP3Power[@SwitchState, 3]: bit; {Bit 0 ist PNP-Schalttransistor}
{$ELSE}
  b_TasteRechts[@BtnInState, 4]: bit; {Bit 0 ist Taste Enter, ganz rechts}
  b_TasteAb[@BtnInState, 6]  : bit; {Bit 6 ist Taste Down}
  b_TasteAuf[@BtnInState, 5] : bit; {Bit 5 ist Taste Up}
  b_TasteLinks[@BtnInState, 3]: bit; {Bit 3 ist Taste Esc, ganz links}
  b_TasteAux[@BtnInState, 7] : bit; {Bit 7 ist Taste Aux, separat}

  b_LEDaux[LEDOutPort, 2]    : bit; {Bit 2 ist LED, separat}

  b_SquareSw[@SwitchState, 0]: bit;
  b_AttnSw[@SwitchState, 1]  : bit;
  b_SymSw[@SwitchState, 2]   : bit;
  b_ExtOnMP3Power[@SwitchState, 3]: bit;
  b_PwrSw[@SwitchState, 4]   : bit;
{$ENDIF}
  EditValue, ItemUpperLimit, ItemLowerLimit: Integer;
  EditWhich, DispWhich, LastMenu: TEditWhich;
  ItemToEdit,
  DisplayValue,
  x, y, {x,y Koordinaten LCD}
  GainOffset,                    //0 oder 20 für NormOut/SymOut oder PowerOut/PowerSym
  digits,
  BtnTime, BtnTime2,
  BurstCount     : Byte;
  prime, BurstPrime, Modified, aBool, PresetOn, MP3isOn: boolean;
  MP3command, MP3currenttrack: Byte;
  MP3repeatBset  : Boolean;      // Repeat-Punkt B gesetzt?


  PatchNum       : byte;
  PresetNum,
  MP3Track       : byte;

//Edit-Item-Reihenfolge
  EditArray      : Table[0..15] of Byte;
  Edit1[@EditArray]    : byte;

  Frequ1[@Edit1] : 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[@Edit1+8] : byte;
  MP3sec[@Wave+1]      : byte;
  OutputMode[@Wave+2]  : byte;
  BurstMode[@Wave+3]   : byte;
  LeveldB[@Wave+4]     : byte; //0...7= -60 ... +10dB
  PwrGain[@Wave+7]     : byte; //Nachverstärkung extern Default (kein Edit-Wert!)
  SaveArray      : Table[0..15] of Byte;
  Save1[@SaveArray]    : byte;

  LevelStr       : string[7];
  FreqStr        : String[8];
  MP3repeatTimer : SysTimer;
  BurstTimer     : SysTimer8;
  MP3runningTimer, MP3runningSeconds: Byte;

const
//Anpassen bei Änderungen der Reihenfolge: Letzter - Erster Eintrag
  EditArrayLen               = @PwrGain - @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-Funktionen

{$IFNDEF MenuTest}
{$I MP3source-HW.pas}
{$ELSE}
procedure SetupDSS;  //Freqenz und Pegel einstellen
begin
end;
procedure SetLevelDDS;  //Sende alle Daten
begin
end;
procedure SendDDS;  //Sende ein Wort an den DDS-Chip
begin
end;
procedure SendSR;  //Sende MP3- und SwitchState an den 4094 oder Ausgangsport
begin
end;
procedure miniDDS;
begin
end;
{$ENDIF}

{###########################################################################}
//EEPROM-Routinen

procedure PresetCopyFromROM;
//Frequenz aus ROM-Terz-Tabelle (Integer!) 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 bei Wechsel auf Memory
begin
  CopyBlock(@Frequ1, @Save1, EditArrayLen + 1);
end;

procedure PresetRestore;
//Stellt EditArray nach Wechsel von Memory auf Presets wieder her
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 LCD1xy;
begin
{$IFDEF miniDDS}
  LCDxy(x, y);
{$ELSE}
  LCDxy_M(LCD_m1, x, y);
{$ENDIF}
end;

procedure SetPos;
begin
  i:= ord(DispWhich);
  x:= gettable(posx, i);
  y:= gettable(posy, i);
  LCD1xy;
end;

procedure SetCursor;
begin
  i:= ord(EditWhich);
  x:= gettable(cursx, i);
  y:= gettable(posy, i);
  LCD1xy;
//Cursor-Zeichen "<" ist #0
{$IFDEF miniDDS}
  LCDout(#0);
{$ELSE}
  LCDout_M(#0);
{$ENDIF}
end;


procedure LCD1x0y0;
begin
{$IFDEF miniDDS}
  LCDhome;
{$ELSE}
  LCDhome_M(LCD_m1);
{$ENDIF}
  mdelay(1); //sonst manchmal Fehlfunktionen
end;

procedure LCD1x0y1;
begin
  x:= 0;
  y:= 1;
  LCD1xy;
end;

{$I buttons.pas}
{$I mp3control.pas}

procedure CheckItemLimits;
//EditValue eingrenzen
begin

  if EditValue < 0  then
    EditValue := 0;
  endif;
  if EditValue > ItemUpperLimit then
    EditValue := ItemUpperLimit;
  endif;
  if EditValue < ItemlowerLimit then
    EditValue := ItemlowerLimit;
  endif;
end;

procedure SetItemLimits;
//Item-Grenzen je nach EditWhich setzen
begin
  ItemLowerLimit:= 0;
  ItemUpperLimit:= 9;
  digits:= 2;
  case EditWhich of
    m_patch        :
                     ItemUpperLimit:= 31;
                   |
    m_db           :
{$IFDEF miniDDS}
                     ItemUpperLimit:= 7;   // 8 Stufen
{$ELSE}
                     ItemUpperLimit:= 80;   // 80dB eigener Regelbereich
{$ENDIF}
                   |
    m_fr1..m_frdec :
                     if EditWhich = m_fr1 then
                       ItemUpperLimit:= 1;
                     endif;
                     digits:= 1;
                   |
    m_wav          :
                     ItemUpperLimit:= 99;
                   |
    m_rpt          :
                     ItemLowerLimit:= 1;
                     ItemUpperLimit:= 99;
                   |
    m_out          :
                     ItemUpperLimit:= 3;
                   |
  endcase;
  IncrValue:= integer(EditValue) shl 1;
  SetIncrVal4(0, IncrValue);
end;

procedure WriteItem;
//einzelnen Wert DisplayValue darstellen
begin
//Zahlenausgabe ein- bis zweistellig, ggf. mit führender Null:
//Äquivalent zu write(lcdout, ByteToStr(DisplayValue:digits));
  if digits > 1 then
    k:= DisplayValue mod 100;
    k:= (k div 10);
{$IFDEF miniDDS}
    LCDout(char(k + 48));
{$ELSE}
    LCDout_M(char(k + 48));
{$ENDIF}
  endif;
  k:= DisplayValue mod 10;
{$IFDEF miniDDS}
  LCDout(char(k + 48));
{$ELSE}
  LCDout_M(char(k + 48));
{$ENDIF}
end;


procedure WriteWave;
//Wellenform ausschreiben
begin
  DispWhich:= m_wav;
  setPos;
  case Wave of
    c_sinw..c_squw :
                     if wave = c_sinw then
                       WriteLCD(@SinStr);
                     endif;
                     if wave = c_triw then
                       WriteLCD(@TriStr);
                     endif;
                     if wave = c_squw then
                       WriteLCD(@SquStr);
                     endif;
                   |
    3..99          :
//Audio-File-Nummer -2
//bei externer Quelle wurde DDS abgeschaltet
{$IFDEF miniDDS}
                     LCDout('A'); // A wie Audio
{$ELSE}
                     LCDout_M('A'); // A wie Audio
{$ENDIF}
                     k:= Wave - 2;
                     MP3Track:= k;
                     DisplayValue:= k;
                     digits:= 2;
                     WriteItem; // Track-Nummer
                   |
  endcase;
end;

{$IFNDEF miniDDS}
procedure WriteOutputMode;
//OutputMode ausschreiben: Normal, Symmetrisch, PowerAmp, SymPowerAmp
begin
  DispWhich:= m_out;
  setPos;
  case OutputMode of
    c_nrmo  :
              WriteLCD(@NrmStr);
            |
    c_symo  :
              WriteLCD(@SymStr);
            |
    c_pwro  :
              WriteLCD(@PwrStr);
            |
    c_psymo :
              WriteLCD(@SymPwrStr);
            |
  endcase;
end;
{$ENDIF}

procedure WriteFrequSpaced;
//Frequenz-Array (Teil von EditArray) mit Leerzeichen
//zwischen einzelnen Ziffern darstellen
begin
  LCD1x0y1;
  for i:= 0 to 7 do
    k:= gettable(EditArray, i);
{$IFDEF miniDDS}
    LCDout(char(k + 48));
{$ELSE}
    LCDout_M(char(k + 48));
{$ENDIF}
    if i = 6 then
{$IFDEF miniDDS}
      LCDout(#46); //Dezimalpunkt nach 7. Stelle
{$ELSE}
      LCDout_M(#46); //Dezimalpunkt nach 7. Stelle
{$ENDIF}
    else
{$IFDEF miniDDS}
      LCDout(#32);
{$ELSE}
      LCDout_M(#32);
{$ENDIF}
    endif;
  endfor;
end;


procedure WriteFrequency;
//Frequenz-Array (Teil von EditArray) darstellen
var blank: boolean;
begin
  DispWhich:= m_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
{$IFDEF miniDDS}
      LCDout(#32);
{$ELSE}
      LCDout_M(#32);
{$ENDIF}
    else
{$IFDEF miniDDS}
      LCDout(char(DisplayValue + 48));
{$ELSE}
      LCDout_M(char(DisplayValue + 48));
{$ENDIF}
    endif;
  endfor;
{$IFDEF miniDDS}
  LCDout(#46); //Dezimalpunkt nach 7. Stelle
{$ELSE}
  LCDout_M(#46); //Dezimalpunkt nach 7. Stelle
{$ENDIF}
  DisplayValue:= FrequDez;
{$IFDEF miniDDS}
  LCDout(char(DisplayValue + 48));
{$ELSE}
  LCDout_M(char(DisplayValue + 48));
{$ENDIF}
  WriteLCD(@HzStr);
end;

procedure WriteLevelmV;
//m_db-Wert in LeveldB in mV umrechnen (nach mV-Tabelle) und darstellen
var blank: boolean;
  dBtab          : byte;
  Prefix         : char;
{mV aus m_db ausrechnen}
begin
  DispWhich:= m_mv;
  SetPos;

{$IFDEF miniDDS}
  dBtab:= Leveldb*10 + 20;   //Anpassung für Mini-DDS mit 10dB-Stufung
{$ELSE}
  dBtab := LeveldB + 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;
{$IFDEF miniDDS}
  LCDout(#32);   //Leerzeichen
  LCDout(#32);   //Leerzeichen
  write(lcdout, LevelStr);
  LCDout(#32);   //Leerzeichen
  LCDout(Prefix);
  LCDout(#86);
{$ELSE}
  LCDout_M(#32);   //Leerzeichen
  LCDout_M(#32);   //Leerzeichen
  write(lcdout_m, LevelStr);
  LCDout_M(#32);   //Leerzeichen
  LCDout_M(Prefix);
  LCDout_M(#86);
{$ENDIF}
end;

procedure WriteLeveldB;
//m_db-Wert LeveldB ausschreiben
var dBval: Integer;
begin
  DispWhich:= m_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);
  write(lcdout_m, IntToStr(dBval : 4));
  WriteLCD(@dBStr);
{$ENDIF}
end;


procedure WriteWaveMP3trackTime;
//Wave sowie Message, Spielzeit seit Repeat-Start ausschreiben,
//sofern MP3-Player läuft
begin
  WriteWave;
  if MP3isOn then
    y:= 1;
    x:= 4;    // nur für Position
    LCD1xy;
    WriteLCD(@PlayStr);
    DisplayValue:= MP3runningSeconds;
    WriteItem;
{$IFDEF miniDDS}
    LCDout(#58);   //Doppelpunkt
{$ELSE}
    LCDout_M(#58);
{$ENDIF}
    digits:= 1;
    DisplayValue:= MP3runningTimer div 10;
    WriteItem;
{$IFDEF miniDDS}
    LCDout(#48);  //Null anhängen
{$ELSE}
    LCDout_M(#48);
{$ENDIF}
    digits:= 2;
  else
{$IFDEF miniDDS}
    LCDclrEOL;
{$ELSE}
    LCDclrEOL_M(LCD_m1);
{$ENDIF}
  endif;
end;

procedure MP3waitMessage;
//Warte-Meldung bei Trackwechsel und Einschalten/Initialisieren des MP3-Players
begin
  y:= 1;
  x:= 4;    // nur für Position
  LCD1xy;
  WriteLCD(@WaitStr);
{$IFDEF miniDDS}
  LCDclrEOL;
{$ELSE}
  LCDclrEOL_M(LCD_m1);
{$ENDIF}
end;

procedure ShowOptionsLower;
//Alle Optionen (Teil des EditArray, vier Werte) darstellen
begin
  WriteWaveMP3trackTime;
  if not MP3isOn then
    DispWhich:= m_rpt;
    setPos;
    DisplayValue:= MP3sec;
    WriteItem;
{$IFNDEF miniDDS}
    WriteOutputMode;
    DispWhich:= m_brst;
    setPos;
    DisplayValue:= BurstMode;
    WriteItem;
{$ENDIF}
  endif;
end;

procedure ShowDefaultUpper;
//m_patch/Preset-Nummer und Pegel ausschreiben
begin
  LCD1x0y0;
  if PresetOn Then
    WriteLCD(@PresetStr);
    DisplayValue:= PresetNum;
  else
    WriteLCD(@MemStr);
    DisplayValue:= PatchNum;
  endif;
  DispWhich:= m_patch;
  SetPos;
  WriteItem;
{$IFDEF miniDDS}
  LCDout(#32);
{$ELSE}
  LCDout_M(#32);
{$ENDIF}

  WriteLeveldB;
  SetCursor;
  y:= 1;
  x:= 0;    // untere Zeile löschen
  LCD1xy;
{$IFDEF miniDDS}
  LCDclrEOL;
{$ELSE}
  LCDclrEOL_M(LCD_m1);
{$ENDIF}
end;

procedure ShowDefaultLower;
//Darstellung der unteren Zeile Hauptmenü: Frequenz, Pegel in mV oder Spielzeit
begin
  WriteWaveMP3trackTime;
  if EditWhich = m_db then
    WriteLevelmV;
  else
    if not MP3isOn then
      if Wave < c_audio then // für Up/Down-Durchlauf
        WriteFrequency;
      endif;
    endif;
  endif;
end;

procedure ShowDefault;
//komplettes Hauptmenü anzeigen
begin
  ShowDefaultUpper;
  ShowDefaultLower;
end;

procedure SetupMP3;
//MP3-Spieler initialisieren und Track anfahren
begin
  if not UpDownbutton then
    if Wave > 2 then // Audio-File gewünscht
      PresetNum:= 0;
      if (not MP3isOn) then
        MP3waitMessage;
        MP3on;
      endif;
      if MP3isOn then
        if MP3Track <> MP3currenttrack then
          setpos;
          MP3waitMessage;
          MP3gotoTrack;
        endif;
        WriteWaveMP3trackTime;
      endif;
    else
      if MP3isOn then   // wieder Wave, MP3 ausschalten
        MP3off;
        ShowOptionsLower;
        SetCursor;
      endif;
    endif;
  endif;
end;


function EditSelectedItem : boolean;
//universelle Edit-Funktion für einzelnen Wert in EditValue
//Wert in EditValue, Limit in ItemLimit
var altered: boolean;
begin
{$IFDEF miniDDS}
//während auf Tastatureingabe gewartet wird, Ton generieren
//Abbruch der DDS-Routine erfolgt bei Änderung des Button-Ports
  if prime then
    miniDDS;
  else
    prime:= true;   //erster Durchlauf erfolgt
  endif;
  delta:= 0;
{$ENDIF}
  while UpDownButton do
    altered:= true;
    if not b_TasteAuf then
      inc(EditValue);
    endif;
    if not b_TasteAb then
      dec(EditValue);
    endif;
    CheckItemLimits;
    DisplayValue:= byte(EditValue);
    DispWhich:= EditWhich;
    SetPos;
    case EditWhich of
      m_patch :
                WriteItem;
                if PresetOn then
                  PresetNum:= byte(EditValue);
                  PresetCopyFromROM;
                else
                  PatchNum:= byte(EditValue);
                  PatchCopyFromEE;
                endif;
                ShowDefaultLower;
              |
      m_wav   :
                Wave:= byte(EditValue);
                WriteWave;
              |
{$IFNDEF miniDDS}
      m_out   :
                OutputMode:= byte(EditValue);
                WriteOutputMode;
              |
{$ENDIF}
      m_rpt   :
                MP3sec:= byte(EditValue);
                DispWhich:= m_rpt;
                WriteItem;
              |
      m_db    :
                LeveldB:= byte(EditValue);
                WriteLeveldB;
                SetCursor;   // wg. Leerzeichen
                WriteLevelmv;
{$IFNDEF miniDDS}
                if (LeveldB mod 10) = 0 then
                  BtnTime:= 25; //kurz stehenbleiben bei Autorepeat
                  BtnTime2:= 5;
                endif;
{$ENDIF}
              |
    else
      settable(EditArray, ItemToEdit, byte(EditValue));
      WriteItem;
    endcase;
//gleich hier setzen - kurze Reaktionszeit:
    SetLevelDDS;
    FastRepeatRelease;
  endwhile;
{$IFNDEF miniDDS}
//Burst-Generierung per Software hier, während auf Tastatureingabe gewartet wird
  if BurstMode <> 0 then
    if isSysTimerZero(BurstTimer) then
      setSystimer(BurstTimer, 1);
      dec(BurstCount);
      if BurstCount = 0 then
        dss_cmd:= c_ddsresetcmd;
        SendDDS;
        BurstCount:= BurstMode + 1;
        BurstPrime:= true;
      endif;
    endif;
    if BurstCount = 1 then
      if BurstPrime then
        dss_cmd:= wave_cmd;
        SendDDS;
        BurstPrime:= false;
      endif;
    endif;
  endif;
{$ENDIF}
  return (altered);
end;


procedure UpDownData;
//Generelle Edit-Funktion für reine Zahlenwerte
//Zu editierender Wert bestimmt durch EditWhich
begin
  Modified:= EditSelectedItem; // wird noch gebraucht
  if Modified then
{neuen Patch senden}
    if not PresetOn then
      PatchCopyToEE;
    endif;
  endif;
  MP3checkRepeat;
end;

{###########################################################################}

procedure initall;
//nach Reset aufgerufen
begin
{$IFDEF miniDDS}
  DDRA:=  DDRAinit;            {PortA dir}
  PortA:= PortAinit;           {PortA}
{$ENDIF}
  DDRB:=  DDRBinit;            {PortB dir}
  PortB:= PortBinit;           {PortB}
  DDRC:=  DDRCinit;            {PortC dir}
  PortC:= PortCinit;           {PortC}
  DDRD:=  DDRDinit;            {PortD dir}
  PortD:= PortDinit;           {PortD}

{$IFDEF miniDDS}
  i:= gettable(sinus, 1);        //damit bekannt
{$ENDIF}

{$IFDEF miniDDS}
  LCDclr;
  LCDcursor(false, false);
  LCDCharSet(#0, $02, $06, $0E, $1E, $0E, $06, $02, $00); {"<" Cursor}
{$ELSE}
  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}
  endif;
  LCDclr_M(LCD_m1);
{$ENDIF}

  WriteLCD(@Vers1Str);
  LCD1x0y1;
  WriteLCD(@Vers2Str);
  mdelay(1000);

  PatchNum:= 0;
  PatchCopyFromEE;       // Optionen-Defaults
  PresetNum:= 0;

  BtnTime:= 40; // Initial timer value
  BtnTime2:= 10; // Anzahl Durchläufe bis fast forward

  GainOffset:= 0;
  PresetOn:= true;
{$IFDEF miniDDS}
  b_ExtOnMP3Power:= high;
  sendSR;
  LCDclr;
{$ELSE}
  LCDclr_M(LCD_m1);
{$ENDIF}
  Modified:= false;
  MP3isOn:= false;
  EnableInts;
  prime:= false;
  BurstCount:= 1;
  setSystimer(BurstTimer, 1);
  SetLevelDDS;
  IncrCount4start;
  ClearIncrAll4;
end;

{###########################################################################}
//Hauptschleife, wird bei jedem Druck eines Aux- oder Navi-Buttons durchlaufen
//sonst Schleifendurchlauf innerhalb der EditWhich-CASEs bei jedem Up/Down-Button

begin
  Initall;
  EditWhich:= m_patch;
  loop
    SlowRepeatRelease; // warten bis tasten oben oder Autorepeat
    SetLevelDDS;
    LastMenu:= m_lastmenu;
    ItemToEdit:= ord(EditWhich);
    if EditWhich > m_patch then    //m_patch, m_db überspringen
      ItemToEdit:= ItemToEdit - 2;
    endif;
    EditValue:= integer(gettable(EditArray, ItemToEdit));
    if EditWhich = m_patch then
      if PresetOn then
        EditValue:= integer(PresetNum);
      else
        EditValue:= integer(PatchNum);
      endif;
    endif;
    if EditWhich = m_patch then
       EditValue:= integer(LeveldB);
       SetItemLimits;
    endif;
    SetItemLimits;
    SetPos;
    case EditWhich of

    case EditWhich of
      m_patch :
                WriteItem;
                if PresetOn then
                  PresetNum:= byte(EditValue);
                  PresetCopyFromROM;
                else
                  PatchNum:= byte(EditValue);
                  PatchCopyFromEE;
                endif;
                ShowDefaultLower;
              |
      m_wav   :
                Wave:= byte(EditValue);
                WriteWave;
              |
{$IFNDEF miniDDS}
      m_out   :
                OutputMode:= byte(EditValue);
                WriteOutputMode;
              |
{$ENDIF}
      m_rpt   :
                MP3sec:= byte(EditValue);
                DispWhich:= m_rpt;
                WriteItem;
              |
      m_db    :
                LeveldB:= byte(EditValue);
                WriteLeveldB;
                SetCursor;   // wg. Leerzeichen
                WriteLevelmv;
{$IFNDEF miniDDS}
                if (LeveldB mod 10) = 0 then
                  BtnTime:= 25; //kurz stehenbleiben bei Autorepeat
                  BtnTime2:= 5;
                endif;
{$ENDIF}
              |
    else
      settable(EditArray, ItemToEdit, byte(EditValue));
      WriteItem;
    endcase;

      m_patch           :
                          ShowDefault;
                          BtnTime:= 40; //kurz stehenbleiben bei Autorepeat
                          SlowRepeatRelease;
                          repeat
                            if EditSelectedItem then
                              ShowDefaultUpper;
                              SetupMP3;
                              ShowDefaultLower;
                            endif;
                            MP3checkRepeat;
                            if MP3isOn then
                              WriteWaveMP3trackTime;
                            endif;
                          until NaviButton;
                        |
      m_db              :
                          ShowDefault;
                          SlowRepeatRelease;
                          repeat
                            if EditSelectedItem then
                              if not PresetOn then
                                PatchCopyToEE;
                              endif;
                              ShowDefault;
                            endif;
                            MP3checkRepeat;
                          until NaviButton;
                        |
      m_fr1..m_frdec    :
                          LCD1x0y0;
                          WriteLCD(@FrequStr);
                          WriteFrequSpaced;
                          SetCursor;
                          SlowRepeatRelease;
                          repeat
                            UpDownData;
                          until NaviButton;
                        |
      m_wav..m_lastmenu :
                          LCD1x0y0;
                          WriteLCD(@OptStr);
                          ShowOptionsLower;
                          SetCursor;
                          SlowRepeatRelease;
                          repeat
                            UpDownData;
                            SetupMP3;
                          until NaviButton;
                        |
    endcase;

    if not b_TasteLinks then
      if EditWhich = m_patch then
        EditWhich:= m_frdec;
      else
        EditWhich:= pred(EditWhich);
      endif;
    endif;
    if not b_TasteRechts then
      inc(EditWhich);
      if MP3isOn then
        LastMenu:= m_wav;
      endif;
      if EditWhich > LastMenu then
        EditWhich:= LastMenu;
      endif;
    endif;
    if not b_TasteAux then
      if PresetOn then
        PresetOn:= false;
        b_LEDaux := low;
        PresetSave;
        PatchCopyFromEE;
      else
        PresetOn:= true;
        b_LEDaux := high;
        PresetRestore;
      endif;
      SetupMP3;
      ShowDefaultLower;
    endif;
  endloop;
end SoftDDS.

