program FPGA;
(*

(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de

29.04.2009 #2.61 Checkser bereinigt, PanelTimer eingeführt
                 Panel-LED ist jetzt nur Seriell-Indikator (Panel abgeschaltet)
27.04.2009 #2.60 Latenzzeiten FPGA-Core <-> AVR stark verringert
                 Interrupt-Handling verbessert
                 INI/CFG-Default-Filename jetzt auch in Klartext möglich
24.04.2009 #2.51 COM AVR-Core auf SubCh/SPI-Reg. 64 ff gelegt
17.04.2009 #2.50 einige Filesystem-SubCh neu verteilt,  MainCh 9 für interne
                 Kommunikation FPGA-AVR eingeführt, gelangt nicht auf OptoBus
                 CLK zurück auf 90..98 wie bis #2.34
                 Neue Befehle:
                 XMR XMODEM-Receive
                 TTY Teletype Text
                 TTF Type File to Register
                 TSF Text Save to File
                 Options erweitert auf Core Rx/Tx
                 RTC Echtzeituhr DS1302 an PortB/Debug unterstützt
12.04.2009 #2.41 beta FWV Parameter Write neu, FNA DataFileName
27.02.2009 #2.40 beta Graph-Unit aus Platzgründen entfernt, F_Int-Unterstützung für
                 FPGA-Terminal-Betrieb
                 CLK auf SubCh 190 verlegt
13.02.2009 #2.34 AutoIncrement-Reset jetzt auch nach DAT-Files
10.02.2009 #2.33 AIW auf Default 4 Byte (LongWord-DATs) gesetzt
10.02.2009 #2.32 AIW (Byte-Breite AI-Register) eingeführt,
                 SubCh für AIR und AIS neu vergeben
09.02.2009 #2.31 Support für DEC-Files, Dezimalstrings für AIR,
                 buggy RIO-Befehl ersetzt durch INP und OUT
06.02.2009 #2.30 Label-Bug behoben, INI Exec Message wenn Script läuft
06.02.2009 #2.21 AutoInc-Reset auch für DAT-Files
06.02.2009 #2.20 Bug in Parser bez. Alpha-Strings als Parameter behoben,
                 Error-Handling verbessert
05.02.2009 #2.13 AIS Select-Register für multiple BlockRAMs bzw. PicoBlaze Cores,
                 mit Defaultwert auf OPT 2
03.02.2009 #2.12 AIR mit Defaultwert auf OPT 3,
                 autom. Datenaustausch nur noch auf
                 dem gerade angezeigten PARAM/VALUE 0 bis 3
20.01.2009 #2.10 Unterstützung für DAT- und MEM-Files sowie Auto-Inkrement AIR
10.12.2008 #2.01 Bug in Parser behoben (akzeptierte keine Fließkommazahlen)
12.10.2008 #2.00 LabScript-Implementation, QVGA-Treiber
18.09.2008 #1.02 DLY-Pause, GTO-Goto-Befehl, RPT-Repeat-Zähler in Scripts
17.09.2008 #1.00 Parser-Bug behoben, Script-Fähigkeit über INI-Files eingeführt,
                 Default-Dateinummern jetzt in OPT-Parametern,
                 File-Menu übersichtlicher
17.07.2008 #0.90 begrenzt auf 63 FPGA-Register wg. RAM-Zwischenspeicherung;
                 SPI schreibt beim Lesen immer auch gleichnamige Register neu
20.03.2008 #0.10 Übernahme aus SQG-Version

*)

//Defines aktivieren durch Entfernen des 1. Leerzeichens!

{$OPTI SMARTLINK_ONLY}

{$NOSHADOW}
{$W- Warnings}            {Warnings off}
{$TYPEDCONST OFF}
{$DEBDELAY}
{$OPTI_QUICK}
{ $DEFINE debug}         // Fehler-Anzeige auf LCD
{$DEFINE Panel}         // WICHTIG: Bedien-Panel vorhanden, bindet LCD mit ein
{$DEFINE Parser}         // für Testkompilierung auskommentieren

Device = mega644, VCC = 5;


Import SysTick, FAT16, TWImaster, SerPort;  // ADCport,
{$IFDEF Panel}
Import IncrPort4, LCDmultiPort; //, LCDGraphic;
{$ENDIF}

From System Import float, LongWord, LongInt;
// From LCDGraphic Import CharSet;     {block CharSet, pixels}

Define
  ProcClock      = 16000000;        {Hertz}
  TWIpresc       = TWI_BR400;

{$IFDEF Panel}
  LCDmultiPort   = I2C_TWI;
  LCDTYPE_M      = 44780; // 44780 oder 66712;
  LCDrows_M      = 2;              //rows
  LCDcolumns_M   = 16;             //chars per line
  IncrPort4      = PinA, 1, 32; // pin-reg used, channels, 16 or 32bit integer
  IncrScan4      = Timer1, 4; // timer used, scan rate 1kHz (1..100)
{$ENDIF}


  SysTick        = 10;              //msec
  SerPort        = 38400, Stop1, timeout;    {Baud, StopBits|Parity}
  RxBuffer       = 255, iData;
  TxBuffer       = 32, iData;

  StackSize      = $0100, iData;
  FrameSize      = $0180, iData;

//  ADCchans = [3,4], iData, int2;
//  ADCpresc       = 16;
  FAT16          = MMC_SPI, iData;
  F16_MMCspeed   = fast;
  F16_FileHandles   = 2;
  F16_Dirlevels     = 1;


USES
  UFAT16;


Implementation

type
  tModify   = (pulse_low, pulse_high, freq_hz, freq_PWM, filesel);
  tError = (NoErr, UserReq, BusyErr, FileErr, SyntaxErr, ParamErr, LockedErr,ChecksumErr);

{--------------------------------------------------------------}
const


//                              ----SFDC             Strobe,DATASEL,Data,Clk
  DDRBinit:byte                   = %10111011;            {PortB dir }
  DDRBinitRdRTC:byte               = %10011011;            {PortB dir wenn RTC gelesen wird}
  PortBinit:byte                   = %10111010;            {PortB }

  DDRCinit:byte                    = %01110000;            {PortC dir, 0..1=Incr4 }
  PortCinit:byte                   = %11001111;            {PortC }

{ Jumper und LEDs PortBits }
  DDRDinit:byte                    = %00001100;            {PortD dir, 0..1=Serial }
  PortDinit:byte                   = %11111100;            {PortD }


  BtnInPortReg:byte                = @PinD;   {Taster-Eingangsport}
{$TYPEDCONST OFF}
  LEDOutPort                 = @PortD;   {LED-Port}
  FPGAport                   = @PortB;   {FPGA-SPI-Port}
  FPGApin                    = @PinB;    {FPGA-SPI-Port}
  ConFPGABitPort             = @PortC;   {FPGA-Config-Port}
  ConFPGABitPin              = @PinC;   {FPGA-Config-Port}


  b_SCK                      = 7; //Takt für alle, SPI-Belegung!
  b_MISO                     = 6; //SPI Daten von allen
  b_MOSI                     = 5; //Daten an alle
  b_SS                       = 4; //für MMC-Karte benutzt, HW-SPI!
  b_DATASEL                  = 3; //FPGA 32-Bit-Register
  b_INT2                     = 2; //INT2 ausgelöst vom FPGA
  b_REGSEL                   = 1; //FPGA Registerauswahl
  b_AUX                      = 0; //Testpin Trigger LA

  c_CDSW                     = 3; //Card insert switch, low = inserted
  c_DATA                     = 4; //Daten FPGA Configuration
  c_CCLK                     = 5; //Takt FPGA Configuration
  c_PROG                     = 6; //Prog-Leitung FPGA Configuration
  c_DONE                     = 7; //Done-Leitung FPGA Configuration


//Lineal     .----.----.----.----.----.----.----.----.----
  Vers1Str                   = '2.61 [PULSEGEN by CM/c''t 06/2008]';    //Vers1
  Vers3Str                   = 'PULSEGEN';    // Kurzform von Vers1

  ErrStrArr      : array[0..7] of String[8] = (
    '[OK]','[SRQUSR]','[BUSY]','[FILERR]','[CMDERR]','[PARERR]',
    '[LOCKED]','[CHKSUM]');

  FaultStrArr : array[0..3] of String[10] = (
    '[NOTFOUND]',
    '[NOCARD]',
    '[CONFERR]',
    '[WRITERR]');

  ParamNameArr : array[0..3] of String[8] = (
    'Low (us)',
    'Hi  (us)',
    'Freq Hz ',
    'Freq PWM');

  EEnotProgrammedStr         = 'EEPROM EMPTY! ';
  AdrStr                     = 'Adr ';
  ParamHintStr               = 'Param  ';
  InputHintStr               = 'Value  ';
  EvalHintStr                = 'DataAdr';
  RunStr1                    = 'BTN:STOP';
  RunStr2                    = 'INI exec';
  FileNumStr                 = ' file';
  ErrSubCh:Integer           = 255;

  CmdStrArr: array[0..15] of String[3] = (
  'STR', // 255
  'IDN', // 254
  'VAL', // 0..9999

  'CFG', // 240 File Auswahl
  'LST', // 241 Dir Listing
  'DIR', // 241 Dir Listing
  'FNM', // 242 FileNumber, Anzahl der Dateien
  'FNA', // 243 Data FileName (alpha)
  'FDL', // 244 Delete DataFileName (alpha)
  'FQU', // 249 File Query, OK/Exists
  'HEX', // 88  Hex-Mode bei Zahlenausgeben seriell [$12345678]
  'WEN', // 250 Write enable
  'ERC', // 251 ErrCount seit letztem Reset
  'SBD', // 252 SerBaud UBRR-Register mit U2X=1
  'REM', // 253
  'NOP');

  Cmd2SubChArr: array[0..15] of integer = (
  255, 254,
  0,
  240, 241, 241, 242, 243, 244, 249,
  88, 250, 251, 252, 253, 253);

  cmdAnzahl=15; // letzter Eintrag, statt tCmdwhich
  cmdErr=cmdAnzahl+1; // Error, Statt tCmdwhich

  high: boolean                       = true;
  low: boolean                        = false;

//Inkrementalgeber-Beschleunigungstabelle
  IncrAccArray: array[0..15] of integer = (0,1,2,5,10,25,50,100,250,
  500,1000,2500,5000,10000,10000,10000);
  
  IncrDigitArray: array[0..7] of LongInt = (
  $10000000,$01000000,$00100000,$00010000,
  $00001000,$00000100,$00000010,$00000001);

structconst

//Default-EEPROM-Werte:
{$EEPROM}
  dummy:LongInt=0;
  EEinitialised:word=$AA55;
  InitIncRast:Integer =4;
  EESerBaudReg:byte=51;
  InitHexMode:boolean=false;

  OptionArray:Array[0..19] of Integer =  (
  255,  // 0 BIN-File Default, 255=keine
  255,  // 1 INI-File Default
  0,    // 2 AutoIncSel, default 0
  128,  // 3 AutoIncReg, default 128
  0,    // 4 Result MainCh
  10,   // 5 Result SubCh
  7,    // 6 InitDigits
  255,  // 7 InitNachkomma, 255=auto
  9,    // 8 EEMainCh
  500,  // 9 EEgetTimeOut
  64,    // 10 Core Rx FPGA to AVR intern
  65,    // 11 Core Tx AVR to FPGA intern
  0,    // 12
  0,
  0,
  0,
  0,
  0,
  0,
  0);

  InitParamLongArray: array[0..3] of LongInt = (
  50000,
  50000,
  100000,
  50);

{--------------------------------------------------------------}

var
{$EEPROM}
  EEbinFileNum[@OptionArray+0]:integer;      // BIN-FileNum
  EEiniFileNum[@OptionArray+2]:integer;      // INI-FileNum -1 (kein INI-File)
  EEAutoIncSel[@OptionArray+4]:integer;      // Default AutoIncSel
  EEAutoIncReg[@OptionArray+6]:integer;      // Default AutoIncReg
  InitDigits[@OptionArray+12]:integer;
  InitNachkomma[@OptionArray+14]:integer;
  EEMainCh[@OptionArray+16]:integer;
  EEgetTimeOut[@OptionArray+18]:integer;

{$DATA} {Schnelle Register-Variablen}
  i,
  k           : Byte;  // k in HW-Prozess benutzt
  m: Byte;

{$PDATA}  {Langsamere SRAM-Variablen}
  LEDactivity[LEDOutPort, 2]    : bit; {Bit 2 LED Remote-Activity}
  LEDswitch[LEDOutPort, 3]      : bit; {Bit 3 LED über dem Display}
  FPGA_PROG[ConFPGABitPort, c_PROG]: bit;  // Prog-Leitung FPGA Configuration
  FPGA_DONE[ConFPGABitPin,  c_DONE]: bit;  // Done-Leitung FPGA Configuration
  SD_CDSW[ConFPGABitPin,  c_CDSW]: bit;    // Card Insertion Switch, low = inserted
  F_Aux[FPGAport, b_AUX]:bit;    // Testpin fü LA-Trigger
  F_Int[FPGAport, b_INT2]:bit;   // Daten-abholen-Interrupt

{$IDATA}  {Langsamere SRAM-Variablen}
  aChar:char;
  MainCh: byte;
  SwitchState: Byte; //Zwischenspeicher für 4094 SR

  ButtonTemp,RangeTemp: Byte; // invertiert - low=on!
  ButtonDown[@ButtonTemp, 5]  : bit;
  ButtonUp[@ButtonTemp, 4]  : bit;
  ButtonEnter[@ButtonTemp, 3]  : bit;

  PulseLowTime, PulseHighTime: LongInt;

  FPGAreg: byte;   // FPGA-Registerauswahl
  FPGAsendLong,FPGAreceiveLong : LongInt;  // FPGA-Registerwert Langwort
  FPGAsendLong0[@FPGAsendLong+0]:byte;
  FPGAsendLong1[@FPGAsendLong+1]:byte;
  FPGAsendLong2[@FPGAsendLong+2]:byte;
  FPGAsendLong3[@FPGAsendLong+3]:byte;
  FPGAsendWord, FPGAreceiveWord:Word;
  FPGAsendWord0[@FPGAsendWord+0]:byte;
  FPGAsendWord1[@FPGAsendWord+1]:byte;
  FPGAsendByte,FPGAreceiveByte: byte;  // FPGA-Registerwert Byte
  DisplayTimer:Systimer8;
  IncrTimer, PanelTimer, ACKTimer:Systimer8;

  CmdWhich: byte; // tcmdwhich nicht mehr benutzt
  CmdStr: string[3];
  SubCh: Integer;
  CurrentCh       : byte;
  OmniFlag,verbose: boolean;
  Param          : float;
  ParamInt:Integer;
  ParamAlpha     : boolean; // Flag für Zeichenfolge hinter "="
  ParamLong:LongInt;
  ParamByte :Byte;

  DispChangeDigit, DispChangeDigitCol: Int8;
  DispDigitArray: array[0..7] of Int8;
  
  ParamLongArray: array[0..7] of LongInt;
  ParamLongArray0[@ParamLongArray+0]:LongInt;
  ParamLongArray1[@ParamLongArray+4]:LongInt;
  ParamLongArray2[@ParamLongArray+8]:LongInt;
  ParamLongArray3[@ParamLongArray+12]:LongInt;

  InputLongArray: array[0..7] of LongInt;
  SerInpStr : String[63];
  SerInpPtr:byte;
  
  LCDpresent: boolean;
  Modify: tModify;
  IncrValue,OldIncrValue: LongInt;
  SelectDigit,FirstTurn:Boolean;
  IncrDiff,IncrAccInt10, IncRast:Integer;
  IncrDiffShort: Int8;
  digits, nachkomma:byte;
  ChangedFlag:Boolean;


//für Parser
  ParamStr:string[40]; // auch für Display
  isInteger,Request,hexmode:boolean;
  Status:byte;  // 0..3 Fehlernummer
  BusyFlag[@Status, 7]  : bit;
  UserSRQFlag[@Status, 6]  : bit;
  FileErrorFlag[@Status, 5]  : bit;  //
  EEUnlocked[@Status, 4]  : bit; // EEPROM-unlocked-Flag

{  FaultStrArr : array[0..3] of String[10] = (
    '[READERR]',
    '[NOCARD]',
    '[CONFERR]',
    '[WRITERR]');  }

  FaultFlags, ButtonNumber:byte;
  ReadErr[@FaultFlags, 0]  : bit;
  NoCard[@FaultFlags, 1]  : bit;
  ConfErr[@FaultFlags, 2]  : bit;
  WriteErr[@FaultFlags, 3]  : bit;
  CardOK:boolean;
  ErrCount:integer;
  ErrFlag:boolean;
  FaultTimerByte:Byte;
  NoINIfound:Boolean;  // Flag für INI-Dateien
  byteCount: LongInt;
  byteCount3[@byteCount+3]:byte;
  byteCount2[@byteCount+2]:byte;
  byteCount1[@byteCount+1]:byte;
  byteCount0[@byteCount]:byte;

  ParseResult: Boolean;
  TransferCh: byte;
  TransferSubCh: Integer;
  Pause:word;

  GetTimeout: Integer;
  CompareParam:Float;
  DirectoryArray:array[0..63] of string[12];
  DirectoryDone, FlashOK:Boolean;
  BinFile : file of byte;
  InitFile,MemFile,DataFile : file of text;
  DatFileName: string[12];
  INITfileIsOpen, LabelSeek:Boolean;
  CurrentGotoLabel:byte;
  LastBinFileNum, FileNum, FileAnzahl,FileNumTemp : byte;
  AutoIncReg:byte;         // Auto-Increment-Register, hierhin werden .DAT-Datenblöcke geschrieben
  AutoIncSel:byte;         // Auto-Increment-Select
  AutoIncWidth:byte;       // AI Breite in Bytes, 1, 2 oder 4

  DOStime, DOSdate:Word;
  FPGAbyte, FlashByte: byte;
  pDummy:byte;

  MainChInternFlag:Boolean; // interner Aufruf vom FPGA, serout geht an FPGA
  SPIsema:Boolean;
  CoreRxSubCh: byte;  // Daten vom FPGA
  CoreTxSubCh: byte;  // Daten zum FPGA
  IRQrBufPtr,rBufPtr, IRQtemp:byte;
  AutoIncBlockEnd,AutoIncBlockStart: LongInt;
  IRQrBuf[$e00]:Array[0..255] of char;
  BlockTable[$f00]:Table[0..255] of byte;
  BlockArray[@BlockTable]:Array[0..255] of byte; // für FAT16
  BlockArray2[@BlockTable]:Array[0..127] of Word; // für FAT16
  BlockArray4[@BlockTable]:Array[0..63] of LongInt; // für FAT16
  BlockStr[@BlockTable]:String[255];



{###########################################################################}
{$I FPGA-HW.pas}
{$IFDEF LCDGRAPH}
{$I FPGA-LCDG.pas}
{$ENDIF}
procedure serDummy;
begin
  serout('#');
end;

procedure CheckCard;
begin
  if F16_DiskInit then
    F16_DiskReset;
    CardOK:=true;
    NoCard:=false;
  else
    CardOK:=false;
    NoCard:=true;
  endif;
end;

procedure ClearDirectory;
begin
  for i:=0 to 63 do
    DirectoryArray[i]:='<empty>';
  endfor;
  FileNum:=0;
  LastBinFileNum:=0;
  DirectoryDone:=false; // einlesen des Directory beim nächsten FileSel
end;

procedure GetDirectory;
var
SR : TSearchRec;
st : TfileName;
begin
// search for filename/directory entries, accept wildcards
  i:=0;
  CheckCard;
  FileAnzahl:=0;
  if NoCard then
    if LCDpresent then
      LCDxy_M(LCD_m1, 0, 0);
      Write(LCDOut_M, 'CdInsert');
      setsystimer(DisplayTimer,100);
    endif;
    FileNum:=0;
    FileAnzahl:=0;
  else
    if (F16_FindFirst ('\', '*.*', [faArchive], SR)) then
      repeat
        DirectoryArray[i]:= SR.Name;
        inc(i);
      until (i=64) or (not F16_FindNext (SR));
    endif;
    FileAnzahl:=i;
    DirectoryDone:=true;
  endif;
end;

procedure FlashActivity;
// Aktivitätsanzeige 1. Zeile, starten mit ParamDigit=8!
begin
  if isSystimerzero(DisplayTimer) and LCDpresent then
    setsystimer(DisplayTimer,20);
    LCDxy_M(LCD_m1, 0, 0);
    Write(LCDOut_M, longtostr(byteCount shl 11));
  endif;
end;

procedure FPGAload(myBinFileName:String[12]);
// liest Datei "FPGAn.bin" von SD- oder MMC-Karte und programmiert
// Spartan-2-FPGA über SPI damit

var firstBlock, FPGAresponse:Boolean;
  BytesRead:word;
begin
  FPGAresponse:=false;
  byteCount:=0;
  ReadErr:=false;
  ConfErr:=false;
  if CardOK then
    if F16_FileExist ('\', myBinFileName, faFilesOnly) then
      LastBinFileNum:=FileNum; // Binärdateinummer merken
      F16_FileAssign (BinFile, '',myBinFileName);
      F16_FileReset (BinFile); // Datei zum Lesen öffnen
      firstBlock:=true;
      while not F16_EndOfFile (BinFile) do // read the entire file
        F16_BlockRead (BinFile, @BlockArray, 256, BytesRead);
        if firstBlock then
          FPGA_PROG:=low;
          udelay(1);
          FPGA_PROG:=high;
{$D-}
          mdelay(10);     // Setup-Zeit FPGA Spartan-3
{$D+}
          firstBlock:=false;
          if not FPGA_Done then
            FPGAresponse:=true;
          endif;
          if LCDpresent then
            LCDxy_M(LCD_m1, 0, 1);
            Write(LCDOut_M, 'BitCount');
            LCDxy_M(LCD_m1, 0, 0);
            LCDclrEOL_m(LCD_m1);
            setsystimer(DisplayTimer,10);
          endif;
        endif;
        ShiftFPGAconfblock;
        inc(byteCount);
        FlashActivity;
        if FPGA_Done then break; endif;
      endwhile; // until end of file
      setsystimer(DisplayTimer,100);
      FPGAbyte:= 255;  // Wichtig: noch ein paar CCLK-Impulse nachschieben
      ShiftFPGAconf;
      F16_FileClose (BinFile);
      if (not FPGAresponse) or (not FPGA_Done) then
        if LCDpresent then
          LCDxy_M(LCD_m1, 0, 0);
          Write(LCDOut_M, 'ConfFail');
          setsystimer(DisplayTimer,200);
        endif;
        ConfErr:=true;
      endif;
    else
      if LCDpresent then
        LCDxy_M(LCD_m1, 0, 0);
        Write(LCDOut_M, 'NotFound');
        setsystimer(DisplayTimer,200);
      endif;
      ReadErr:=true;
    endif;
  endif;
  NoCard:=not CardOK;
end;


{###########################################################################}

//EEPROM-Routinen

procedure InitVars;
//Frequenz und Settings aus EEPROM holen
begin
  hexmode:=InitHexmode;
  for i :=0 to 3 do
    ParamLongArray[i]:=InitParamLongArray[i];
  endfor;
  IncRast:=InitIncRast;
  digits:=byte(InitDigits);
  nachkomma:=byte(InitNachkomma);
  ParamStr:='0.0';
  ClearDirectory;
  GetTimeout:=EEgetTimeOut;
  AutoIncReg:=byte(EEAutoIncReg);
  AutoIncSel:=byte(EEAutoIncSel);
  AutoIncWidth:=4;
  IRQrBufPtr:=0;
  rBufPtr:=0;
end;

{###########################################################################}

//allg. Menü-Prozeduren

procedure SerCRLF;
begin
  mySerOut(#$0D);
  mySerOut(#$0A);
end;

procedure WriteChPrefix;
begin
  serinpCoreACK;           // angenommen, kann weitergehen
  mySerOut('#');
  mySerOut(char(MainCh+48));
  mySerOut(':');
  write(mySerOut, IntToStr(SubCh));
  mySerOut('=');
end;

procedure WriteSerInp;
begin
  serinpCoreACK;           // angenommen, kann weitergehen
  write(mySerOut, SerInpStr);   // Befehl weiterreichen
  SerCRLF;
end;

procedure SerPrompt(myErr:tError);
var myFault, myStatus:Byte;
//Error-Meldung und Status-Request-Antwort,
//Status Bit 7=Busy, 6=UserSRQ, 5=OverLoad, 4=WriteEnable, 3..0=Fault/Error
begin
  SubCh:=ErrSubCh; // Fehler-Kanal
  WriteChPrefix;
  FileErrorFlag:=(FaultFlags<>0);
  if myErr = UserReq then
    myStatus:=Status or ButtonNumber or 64;
  else
    if FileErrorFlag then
      myStatus:=Status or FaultFlags;
    else
      myStatus:=Status or ord(myErr);
      if myErr <> noErr then
        inc(ErrCount);
      endif;
    endif;
  endif;
  write(mySerOut, ByteToStr(myStatus));
  if FileErrorFlag then
    myFault:=FaultFlags;
    for i := 0 to 3 do
      if (myFault and 1) = 1 then
        mySerOut(#32);
        write(mySerOut,FaultStrArr[i]);
      endif;
      myFault:=myFault shr 1;
    endfor;
  endif;
  mySerOut(#32);
  write(mySerOut,ErrStrArr[ord(myErr)]);
  SerCRLF;
end;


{###########################################################################}
//Ausgabe-Prozeduren

procedure ParamToStr;
var myNachkomma:byte;
begin
  myNachkomma:=Nachkomma;
  if Nachkomma>7 then
    myNachkomma:=5;
    if abs(Param)>=10 then
      myNachkomma:=4;
    endif;
    if abs(Param)>=100 then
      myNachkomma:=3;
    endif;
    if abs(Param)>=1000 then
      myNachkomma:=2;
    endif;
  endif;
  if Param<>0 then
    ParamStr:=floatToStr(Param:1:myNachkomma);
  else
    ParamStr:='0.00000';
    setLength(ParamStr,myNachkomma+2);
  endif;
end;

procedure ParamLongToStr;
//mit einer Nachkommastelle in Strin wandeln
begin
  ParamStr:=LongToStr(ParamLong);
end;

procedure ParamLongToHex;
//Longin in Hex-String wandeln
begin
  ParamStr:=LongToHex(ParamLong);
end;

procedure ParamIntToHex;
//Longin in Hex-String wandeln
begin
  ParamStr:=IntToHex(ParamInt);
end;

procedure WriteDirEntry;
//Directory-Eintrag FileNum ausgeben
begin
   SubCh:=240;
   WriteChPrefix;
   ParamStr:= ByteToStr(FileNum);
   write(mySerOut, ParamStr);
   mySerOut(#32);
   mySerOut('[');
   ParamStr:= DirectoryArray[FileNum];
   write(mySerOut, ParamStr);
   mySerOut(']');
   SerCRLF;
   mdelay(10);
end;

procedure WriteParamStrSer;
begin
  WriteChPrefix;
  write(mySerOut, ParamStr);
  SerCRLF;
end;

procedure WriteTransferChPrefix;
begin
  mySerOut(char(TransferCh+48));
  mySerOut(':');
  write(mySerOut, IntToStr(TransferSubCh));
end;


procedure WriteTransferParamSer;
begin
  ParamToStr;
  WriteTransferChPrefix;
  mySerOut('=');
  write(mySerOut,ParamStr);
  SerCRLF;
end;

procedure WriteParamSer;
begin
  ParamToStr;
  WriteParamStrSer;
end;

procedure WriteParamIntSer;
begin
  ParamStr:= IntToStr(ParamInt);
  WriteParamStrSer;
end;

procedure WriteParamByteSer;
begin
  ParamStr:= ByteToStr(ParamByte);
  WriteParamStrSer;
end;

procedure WriteParamHexSer;    // für verbose-Anfrage
begin
  WriteChPrefix;
  write(mySerOut, ParamStr);
  mySerOut(#32);
  mySerOut('[');
  mySerOut('$');
  ParamStr:= LongToHex(ParamLong);
  write(mySerOut, ParamStr);
  mySerOut(']');
  SerCRLF;
end;

procedure WriteParamLongSer;
begin
  ParamStr:= LongToStr(ParamLong);
  if hexmode then
    WriteParamHexSer;
  else
    WriteParamStrSer;
  endif;
end;

{###########################################################################}

procedure ParamStrOnLCD;
begin
  LCDxy_M(LCD_m1, 0, 0);
  write(lcdout_m,ParamStr);
  LCDclrEOL_m(LCD_m1);
  LCDxy_M(LCD_m1, 7, 0);
  if SelectDigit then
    lcdout_m(#2);
  else
    lcdout_m(#0);
  endif;
  LCDxy_M(LCD_m1, 0, 1);
end;

procedure RunningMsg;
begin
  if (not LCDpresent) then
    return;
  endif;
  if INITfileIsOpen and (modify=filesel) then
    LCDxy_M(LCD_m1, 0, 0);
    write(lcdout_m,RunStr1);
    LCDxy_M(LCD_m1, 0, 1);
    write(lcdout_m,RunStr2);
  endif;
end;


function Checklimits:boolean;
// liefert TRUE wenn "Out of Range"
var myBool: Boolean;
begin
  myBool:=false;
  if FileNum>127 then  // Byte!
    FileNum:=0;
    myBool:=true;
  endif;
  if FileNum>63 then  // Byte!
    FileNum:=63;
    myBool:=true;
  endif;
  return(myBool);
end;

function ButtonPressed:Boolean;
begin
  if LCDpresent then
    ButtonTemp := (LCDportInp_M(LCD_m1) or %11000111);
  else
    return(false);
  endif;
  if ButtonTemp=$ff then
    return(false);
  else
    return(true);
  endif;
end;

{###########################################################################}

procedure WriteDirEntryVGA;
//Directory-Eintrag FileNum ausgeben
begin
  ParamStr:= DirectoryArray[FileNum];
end;


{###########################################################################}


procedure AutoIncSetup(for_read:boolean);
// AutoInc vorbereiten: Länge, Start an SPI übermitteln
begin
  FPGAreg:=AutoIncReg+1;  // Schreib-Register Core-Select
  SendFPGAreg;
  FPGAsendbyte:=AutoIncSel; // AutoIncSel-Ziel ans FPGA, resettet Auto-Increment
  SendFPGA8;
  if for_read then
    FPGAreg:=AutoIncReg+3;  // Lesen triggern, SubCh 131
    SendFPGAreg;
    FPGAsendLong:=AutoIncBlockStart; // AutoIncBlockStart ans FPGA, resettet Auto-Increment
    SendFPGA32;
  else
    FPGAreg:=AutoIncReg+2;  // Schreib-Register Startadresse, SubCh 130
    SendFPGAreg;
    FPGAsendLong:=AutoIncBlockStart; // AutoIncBlockStart ans FPGA, resettet Auto-Increment
    SendFPGA32;
  endif;
  FPGAreg:=AutoIncReg;  // Schreib-Register Daten für DAT-Files
  SendFPGAreg;
end;

procedure AutoIncReset;
// AutoInc zurücksetzen, Core freigeben
begin
  FPGAreg:=AutoIncReg+1;  // Schreib-Register Adress-Reset
  SendFPGAreg;
  FPGAsendByte:=0; // resettet Core Select
  SendFPGA8;
end;

procedure DataFileType(myTypefileName:String[12];toRegister:Boolean;myIndex:Byte);
// Anzeige der Datei seriell
var myChar:char;
begin
  ReadErr:=true;
  if CardOK then
    if F16_FileExist ('\', myTypefileName, faFilesOnly) then
      F16_FileAssign (MemFile, '',myTypefileName);
      F16_FileReset (MemFile); // Datei zum lesen öffnen
      if toRegister then
        FPGAreg:=myIndex;  // Schreib-Register Daten für DAT-Files
        SendFPGAreg;
      endif;
      while (not F16_EndOfFile(MemFile)) do // read the entire file
        Read(MemFile,myChar);
        if toRegister then
          FPGAsendByte:=byte(myChar);
          SendFPGA8;  // nur Byte-Breite
          udelay(10);
          if mychar=#13 then
            mdelay(50);  // Pause zur Verarbeitung nach CR
          endif;
        else
          myserout(mychar);
          if mychar=#13 then
            myserout(#10);
            mdelay(10);  // Pause zur Verarbeitung nach CR
          endif;
        endif;
      endwhile;
      F16_FileClose (MemFile);
      ReadErr:=false;
    endif;
  endif;
  NoCard:=not CardOK;
end;


procedure FileLoad(myLoadfileName:String[12]);  forward;
procedure CheckSer;  forward;
procedure InitLoad(myINITfileName:String[12]); forward;

procedure MEMload(myMEMfileName:String[12]; myHEXmode:Boolean);
// liest MEM-Datei von SD- oder MMC-Karte und überträgt in SPI-Register AutoIncReg
begin
  ReadErr:=true;
  repeat
  until not ButtonPressed;
  if CardOK and F16_FileExist ('\', myMEMfileName, faFilesOnly) then
    F16_FileAssign (MemFile, '',myMEMfileName);
    F16_FileReset (MemFile); // Datei zum Lesen öffnen
    AutoIncSetup(false);
    while (not ButtonPressed) and (not F16_EndOfFile(MemFile)) do // read the entire file
      ReadLn(MemFile,SerInpStr);
      if (SerInpStr[1]<>'@') and (length(SerInpStr)>1) then
      // Hex- ode Dezimal-String lesen, wandeln und senden
        if myHEXmode then  // Hex-Zahlen
          FPGAsendLong:=hexToInt(SerInpStr);
        else
          FPGAsendLong:=StrToInt(SerInpStr);
        endif;
        SendFPGA32;
      endif;
    endwhile; // until end of file
    AutoIncReset;
    F16_FileClose (MemFile);
    ReadErr:=false;
  endif;
  NoCard:=not CardOK;
end;

procedure XModemRx;
// Empfang von Daten über vereinfachtes XMODEM-Chksum-Protokoll
var hdrstart, blk, nblk, chk, chkx: byte; mytimeout, mytry, bytesrx: word;
    gotSOH, serok: boolean;
begin
  AutoIncSetup(false);
  FlushBuffer(RxBuffer);
  gotSOH:=false;
  hdrstart:=0;
  chk:=0;
  chkx:=255;
  bytesrx:=0;
  mytry:=0;
  repeat
    if not gotSOH then
      mytry:=0;
      repeat
        gotSOH:=serinp_to(hdrstart,50); // auf Block-SOH oder EOT warten
        if gotSOH then
          if (hdrstart=1) or (hdrstart=4) then
            mytry:=0;
            break;
          endif;
        else
          serout($15);  // NAK senden
          inc(mytry);
        endif;
      until mytry>20;
    endif;
    if (hdrstart=1) then // SOH Start of Header
       serok:=serinp_to(blk,10);
       serok:=serinp_to(nblk,10);
       if (255-blk = nblk) then
         chk:=0;
         for i:=0 to 127 do
           serok:=serinp_to(FPGAsendByte,10);
           chk:=chk+FPGAsendByte;
           inc(bytesrx);
           SendFPGA8;
         endfor;
         serok:=serinp_to(chkx,100);
         if chk = chkx then
           mdelay(20);
           serout(#$06); // ACK
         else
           inc(mytry);
           serout(#$15); // NAK
         endif;
       endif;
       hdrstart:=0;
    endif;
    gotSOH:=false;
  until (hdrstart=$04) or (mytry>10);
  serout(#$0D);  // CR
  serok:=serinp_to(hdrstart,10);
  serout(#$06);  // ACK
  if  chkx <> chk then
    ParamLong:=-1;
  else
    ParamLong:=LongInt(bytesrx);
  endif;
  AutoIncReset;
end;


{$I FPGA-Parser.pas}

procedure InitLoad(myINITfileName:String[12]);
// liest Datei von SD- oder MMC-Karte und interpretiert Befehle nach
// c't-Lab-Syntax
var
  WaitLoops:Integer; verboseSave:Boolean;
begin
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  drawLogoSD;
{$ENDIF}
  repeat
  until not ButtonPressed;
  verboseSave:=verbose;
  ReadErr:=true;
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  gBlockColor:=$3f;
  GColor:=$3f;
{$ENDIF}
  if CardOK then
   BusyFlag:=true;
   if F16_FileExist ('\', myINITfileName, faFilesOnly) then
      F16_FileAssign (InitFile, '',myINITfileName);
      F16_FileReset (InitFile); // Datei zum Lesen öffnen
      INITfileIsOpen:=true;
      RunningMsg;
      while (not ButtonPressed) and (not F16_EndOfFile(InitFile)) do // read the entire file
        ReadLn(InitFile,SerInpStr);
        if SerInpStr[1]<>'/' then
          ParseSubCh;
        endif;
        SerInpStr:='';
        if serstat then
          CheckSer;
        endif;
        Waitloops:=GetTimeout;
        while ((not ButtonPressed) and (Waitloops>0)) do // auf Antwort warten
          CheckSer;
          dec(Waitloops);
        endwhile;
      endwhile; // until end of file
      F16_FileClose (InitFile);
      SerInpStr:='';
      ReadErr:=false;
    endif;
  endif;
  INITfileIsOpen:=false;
  BusyFlag:=false;
  NoCard:=not CardOK;
  verbose:=verboseSave;
end;

{###########################################################################}


procedure FileLoad(myLoadfileName:String[12]);
// lädt File je nach Extension in das FPGA oder ins AutoInc-RAM

var myFileExt:String[3]; myFileName:String[8]; myReadErr:Boolean;
begin
  myFileExt:=ExtractFileExt(myLoadfileName);
  myFileName:=ExtractFileName(myLoadfileName);
  LEDactivity:=low;
  myReadErr:=true;
  if (myFileExt='BIN') or (myFileExt='BIT') then
    FPGAload(myLoadfileName);
    myReadErr:=ReadErr;
  endif;
  if myFileExt='INI' then
    InitLoad(myLoadfileName);
    myReadErr:=ReadErr;
  endif;
  if myFileExt='MEM' then
    // AutoIncReg muss manuell gesetzt sein, sonst Default Register/SubCh 128
    MEMload(myLoadfileName,true);
    myReadErr:=ReadErr;
  endif;
  if myFileExt='DEC' then
    // AutoIncReg muss manuell gesetzt sein, sonst Default Register/SubCh 128
    MEMload(myLoadfileName,false);
    myReadErr:=ReadErr;
  endif;
  RunningMsg;
  ReadErr:=myReadErr;
end;


{###########################################################################}
// Regelmäßig aufgerufen, aber nicht im Interrupt

procedure Chores;
// Regelmäßig außerhalb des Interrupts aus CheckSer heraus aufgerufen:
begin
  if SD_CDSW and DirectoryDone then
    ClearDirectory;
  endif;
  if (not SD_CDSW) and (not DirectoryDone) then
{$D-}
    mdelay(200);
{$D+}
    GetDirectory;
  endif;
end;

procedure CheckSer;
var
myChar: char;
begin
  if serStat then //
    myChar:=serInp;
    if myChar in [#32..#127] then // nur 7-Bit-ASCII ohne Controls
      append(myChar,SerInpStr);
    endif;
    if mychar=#8 then // Backspace
      setlength(SerInpStr, length(SerInpStr)-1);
    endif;
    if myChar=#13 then
      LEDactivity:=low; // LED aus
      FaultFlags:=0;
      MainChInternFlag:=false;  // mySerout-Antwort an OptoBus
      ParseSubCh;               // Befehl interpretieren, wenn für uns
      SerInpStr:='';
      setSystimer(PanelTimer,10);    // keine Panel-Abfrage während der nächsten 100 ms
      SetSystimer(DisplayTimer, 25); // kein Display-Refresh während der nächsten 250 ms
    endif;
  endif;
  if serStatCore then
    myChar:=serInpCore;
    // serout(mychar);          // TEST
    if myChar in [#32..#127] then // nur 7-Bit-ASCII ohne Controls
      append(myChar,BlockStr);
    endif;
    if (mychar=#8) and (length(BlockStr) > 0) then // Backspace
      setlength(BlockStr, length(BlockStr)-1);
    endif;
    if myChar=#13 then
      LEDactivity:=low; // LED ein
      FaultFlags:=0;
      SerInpStr:=BlockStr;
      MainChInternFlag:=true;  // mySerout-Antwort an FPGA
      ParseSubCh;              // Befehl interpretieren, wenn für uns
      SerInpStr:='';
      BlockStr:='';
      setSystimer(ACKTimer,10);
      setSystimer(PanelTimer,10);    // keine Panel-Abfrage während der nächsten 100 ms
      SetSystimer(DisplayTimer, 25); // kein Display-Refresh während der nächsten 250 ms
    endif;
  endif;
  if isSystimerZero(PanelTimer) then
    Chores;
    mdelay(1); // hier treffen max. 10 Zeichen seriell ein
    LEDactivity:=high; // LED aus
  endif;
  if isSystimerZero(ACKTimer) and MainChInternFlag  then
    serinpCoreACK; // Bereit-Signal nach 100ms wiederholen
    setSystimer(ACKTimer,10);
  endif;
end;

procedure CheckDelay(myDelay:byte);
// Delay mit serieller Abfrage in 1-ms-Schritten
var mycount:byte;
begin
  for mycount:=1 to mydelay do
    CheckSer;
  endfor;
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}

//  serBaud(EESerbaud);        // nur mit 644

  ADMUX := ADMUX OR %11000000;  {Internal ADC Reference}
  EIMSK := EIMSK or %00000100;  // external IRQ F_INT auf INT2
  EICRA := EICRA or %00100000;  // Bit 4 und 5 = INT2 falling Edge

  InitVars;       // Optionen-Defaults
  
  udelay(1);
  MainCh:=(not PinD) shr 5;
  LEDactivity:= low;

{$IFDEF Panel}
  if LCDsetup_M(LCD_m1) then
    LCDcursor_M(LCD_m1, false, false);
    LCDCharSet_M(LCD_m1, #0, $01, $03, $07, $0F, $07, $03, $01, $00); {"<" Cursor}
    LCDCharSet_M(LCD_m1, #1, $01, $03, $05, $09, $05, $03, $01, $00); {"<" Cursor hohl}
    LCDCharSet_M(LCD_m1, #2, $01, $02, $05, $0A, $05, $02, $01, $00); {"<" Cursor IncrValeinstellung}
    LCDCharSet_M(LCD_m1, #3, $15, $00, $11, $00, $11, $00, $15, $00); {Kästchen hohl}

    LCDpresent:=true;
    write(lcdout_m,Vers3Str);
    LCDxy_M(LCD_m1, 0, 1);
    if EEinitialised <> $AA55 then
      write(lcdout_m, EEnotProgrammedStr);
    else
      write(lcdout_m,AdrStr);
      lcdout_m(char(MainCh+48));
    endif;
  endif;
{$ENDIF}  //Panel

{$D-}
  mdelay(1000);
  if MainCh>0 then
    for i := 0 to MainCh-1 do
      LEDactivity:= not LEDactivity;
      mdelay(150);
      LEDactivity:= not LEDactivity;
      mdelay(150);
    endfor;
  endif;
{$D+}
  LEDactivity:= high;
  Status:=0;
  EnableInts;
  while serstat do
    i:=SerInp;
  endwhile;
{$IFDEF Panel}
  IncrCount4start;
  SetIncrVal4(0,0);
  IncrValue:=GetIncrVal4(0);
  OldIncrValue:= IncrValue;
  IncrDiff:=0;
  SelectDigit:=false;
{$ENDIF}  //Panel
  Modify:=filesel;
  FirstTurn:=true;
  SubCh:=254;
  WriteChPrefix;
  write(mySerOut,Vers1Str);
  if EEinitialised <> $AA55 then
    write(mySerOut, EEnotProgrammedStr);
  endif;
  SerCRLF;

  ClearDirectory;
  Chores; // Files ggf. laden

  CurrentCh:=255; // erstmal keine Reaktion
  errcount:=0;
  ChangedFlag:=true;
  ButtonTemp := $FF;
  GetDirectory;
  if CardOK then
    FPGAload('MAIN.BIT');
    if FaultFlags<>0 then
      FileNum:=0;
      serprompt(FileErr);
    else
      WriteDirEntry;
    endif;
  endif;
  setSystimer(ACKTimer,1);
  setSystimer(PanelTimer,1);
  SetSystimer(DisplayTimer, 1);
  SetSystimer(IncrTimer, 1);
  DispChangeDigit:=4; // 10us-Stelle

  FPGAsendLong:= ParamLongArray1 * 2 - 1;
  FPGAreg:=23; // Pulse Default High
  SendFPGAreg;
  SendFPGA32;

  FPGAsendLong:= 33; // Clock Sel
  FPGAreg:=25;
  SendFPGAreg;
  SendFPGA32;

  FPGAsendLong:= 0;
  FPGAreg:=21; // FrequGen Range
  SendFPGAreg;
  SendFPGA32;
  FPGAsendLong:= ParamLongArray0 * 2 - 1;
  FPGAreg:=22; // Pulse Default Low
  SendFPGAreg;
  SendFPGA32;
end;

{###########################################################################}


// Anzeigewert besteht aus 8 Stellen, davon werden 7 angezeigt.
// Zur einfacheren Bearbeitung werden die einzelnen BCD-STellen gespeichert.

{
function DispArr2Long: LongInt;
var my_val, my_fac, my_dgv: LongInt;
begin
  my_val:= 0;
  my_fac:= 1;
  for i:= 0 to 7 do
    my_dgv:= LongInt(DispDigitArray[7-i]);
    my_val:= my_val + (my_dgv * my_fac);
    my_fac:= my_fac * 10;
  endfor;
  return(my_val);
end;
}

procedure Long2DispArr(my_long: LongInt);
begin
  for i:= 0 to 7 do
    DispDigitArray[7-i]:= Int8(my_long mod 10);
    my_long := my_long div 10;
  endfor;
end;

procedure HandleDigits;
var my_digit: Int8;
begin
  Long2DispArr(ParamLongArray[ord(Modify)]);
  LCDxy_M(LCD_m1, 0, 0);
  for i:= 0 to 5 do
    lcdout_m(char(48 + DispDigitArray[i]));  // 0 ist MSBCD
  endfor;
  lcdout_m('.');
  lcdout_m(char(48 + DispDigitArray[6]));
  LCDxy_M(LCD_m1, 0, 1);
  write(lcdout_m,ParamNameArr[ord(modify)]);
  my_digit:= DispChangeDigit;
  if DispChangeDigit > 5 then // Dezimalpunkt übersrpingen
    inc(my_digit);
  endif;
  if SelectDigit then
    LCDxy_M(LCD_m1, byte(my_digit), 0);
    LCDcursor_m(LCD_m1, false, false);
    lcdout_m(#3);
  else
    LCDcursor_m(LCD_m1, true, false);
  endif;
  LCDxy_M(LCD_m1, byte(my_digit), 0);
end;

procedure SendFrequPWM;
var my_long: LongInt;
begin
  my_long:= 200000000 div ParamLongArray2;
  FPGAsendLong:= my_long * (100 - ParamLongArray3) - 1;
  if FPGAsendLong <= 0 then
    FPGAsendLong:= 1;
  endif;
  FPGAreg:=22;
  SendFPGAreg;
  SendFPGA32;
  FPGAsendLong:= my_long * ParamLongArray3 - 1;
  if FPGAsendLong <= 0 then
    FPGAsendLong:= 1;
  endif;
  FPGAreg:=23;
  SendFPGAreg;
  SendFPGA32;
end;

procedure SollWerteOnLCD;
var my_digit: Int8; my_long: LongInt;
begin
  if (not LCDpresent) then
    return;
  endif;
  if (not ChangedFlag) then
    return; // unverändert, nichts zu tun
  endif;
  case Modify of
    pulse_low:
      HandleDigits;
      FPGAsendLong:= ParamLongArray0 * 2 - 1; // in 5-ns-Schritten
      if FPGAsendLong <= 0 then
        FPGAsendLong:= 1;
      endif;
      FPGAreg:=22;
      SendFPGAreg;
      SendFPGA32;
      |
    pulse_high:
      HandleDigits;
      FPGAsendLong:= ParamLongArray1 * 2 - 1;
      if FPGAsendLong <= 0 then
        FPGAsendLong:= 1;
      endif;
      FPGAreg:=23;
      SendFPGAreg;
      SendFPGA32;
      |
    freq_Hz:
      HandleDigits;
      SendFrequPWM;
      |
    freq_PWM:
      LCDxy_M(LCD_m1, 0, 0);
      if lo(ParamLongArray3) < 10 then
        lcdout_m(#32);
      endif;
      write(lcdout_m,ByteToSTr(lo(ParamLongArray3)));
      lcdout_m('%');
      LCDclrEOL_M(LCD_m1);
      LCDxy_M(LCD_m1, 0, 1);
      write(lcdout_m,ParamNameArr[3]);
      SendFrequPWM;
      |
    filesel:
      SelectDigit:=false;
      LCDcursor_m(LCD_m1, false, false);
      ParamStr:=ExtractFileName(DirectoryArray[FileNum]);
      ParamStrOnLCD;
      lcdout_m(#0);
      LCDxy_M(LCD_m1, 0, 1);
      ParamStr:=ExtractFileExt(DirectoryArray[FileNum]);
      if length(ParamStr)=0 then
        ParamStr:='---';
      endif;
      write(lcdout_m,ParamStr+FileNumStr);
    |
  endcase;
  ChangedFlag:=false;
end;

procedure ChangeParam(index:byte);
// ändert ParamLongArray[index] mit IncrDiff-Werten an Stelle ParamDigit
// oder ParamDigit selbst, wenn SelectDigit=true
var my_val, my_fac: LongInt; my_short: Int8;
begin
  if SelectDigit then
    DispChangeDigit:= DispChangeDigit + IncrDiffShort;
    if DispChangeDigit < 0 then
      DispChangeDigit:=0;
    endif;
    if DispChangeDigit > 6 then
      DispChangeDigit:=6;
    endif;
  else
    my_fac:= 100000000;
    for i:= 0 to byte(DispChangeDigit) do
      my_fac:= my_fac div 10;
    endfor;
    my_val:= ParamLongArray[index] + my_fac * LongInt(IncrDiffShort);
    if my_val < 0 then
      ParamLongArray[index]:= 0;
    else
      ParamLongArray[index]:= my_val;
    endif;
  endif;
end;

begin
  Initall;
//Hauptschleife
  loop
    CheckSer;  // Serinp parsen
{$IFDEF Panel}
    if LCDpresent and isSystimerZero(PanelTimer) then
      IncrValue:=GetIncrVal4(0);
// Inkrementalgeber löst in Zweierschritten auf, deshalb der Umstand
      if (IncrValue<>OldIncrValue) then
        MainChInternFlag:=false;  // mySerout-Antwort an OptoBus
        LEDactivity:=low;
        IncrDiff:=IncrDiff + integer(IncrValue-OldIncrValue);
        OldIncrValue:= IncrValue;
        SetSystimer(IncrTimer, 20);
        if abs(IncrDiff)>= IncRast then // erst wenn n Impulse gezählt
          ChangedFlag:=true;
          BusyFlag:=true;
          IncrDiff:=IncrDiff div IncRast;
// verbesserte Beschleunigungsfunktion, hier für 20 ms Schleifenzeit
          IncrAccInt10:=sgn(IncrDiff);     // hier als Sign benutzt
          IncrDiffShort:=Int8(IncrDiff);    // Byte ohne Beschleunigung
          IncrDiff:=IncrAccInt10 * IncrAccArray[byte(abs(IncrDiff))];
          IncrAccInt10:=IncrDiff*10; // 40x Differenz mit Beschleunigung
          if SelectDigit then
            SetSystimer(DisplayTimer, 100); // Auswahl-Kästchen stehenlassen
          endif;
          case Modify of
            pulse_low..freq_Hz:
              ChangeParam(ord(Modify));
              |
            freq_PWM:
              ParamLongArray3:= ParamLongArray3 + LongInt(IncrDiffShort);
              if ParamLongArray3 > 99 then
                ParamLongArray3:= 99;
              endif;
              if ParamLongArray3 < 1 then
                ParamLongArray3:= 1;
              endif;
              |
            filesel:
              SelectDigit:=false;
              FileNum:=FileNum+IncrDiffShort;
              CheckLimits;
              SubCh:=240;
              ParseGetParam;
              |
          endcase;
          IncrDiff:=0; // für nächsten Durchlauf
          SollwerteOnLCD;
          FirstTurn:=false;
        endif;
      endif;
      Checkdelay(40); // wichtig für Beschleunigungsfunktion
      if ButtonPressed then
        MainChInternFlag:=false;  // mySerout-Antwort an OptoBus
        Checkdelay(10);
        if ButtonPressed then
          ChangedFlag:=true;
          BusyFlag:=true;
          if (not ButtonEnter) then
            ButtonNumber:=3; // Button Enter/Fine
            serprompt(UserReq);
            if Modify=filesel then
              LEDactivity:=low;
              if CardOK then
                FileLoad(DirectoryArray[FileNum]);
                ExchangeFPGA4;
              endif;
            else
              SelectDigit:=not SelectDigit;
              SetSystimer(DisplayTimer, 75);
            endif;
            SollWerteOnLCD;
          endif;
          if not ButtonUp then
            ButtonNumber:=2; // Button Up
            serprompt(UserReq);
            inc(Modify);
            SollwerteOnLCD;
          endif;
          if not ButtonDown then
            ButtonNumber:=1; // Button Down
            serprompt(UserReq);
            dec(Modify);
            SollwerteOnLCD;
          endif;
          repeat
            Checkdelay(10);
          until not ButtonPressed;
          FirstTurn:=false;
        endif;
      endif;
    endif;
    if isSystimerZero(IncrTimer) and LCDpresent then
      SetSystimer(IncrTimer, 20);
      if not FirstTurn then
        ButtonNumber:=0; // kein Button, freigegeben
        serprompt(UserReq);
      endif;
      FirstTurn:=true;
    endif;
{$ENDIF}  //Panel

    if isSystimerZero(DisplayTimer) and LCDpresent then
      SetSystimer(DisplayTimer, 25);
      SelectDigit:=false;
      BusyFlag:=false;
      SollwerteOnLCD;
//      ChangedFlag:=false;
    endif;
  endloop;
end FPGA.

