program FPGA;
(*

(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de
Spezialversion für Hammond on Chip

18.05.2010 #2.63 Directory-SubCh-Bug behoben (240, 241)
09.06.2009 #2.62 XMR jetzt auch mit 2 und 4 Bytes Breite bei AIW
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!


{$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
{ $DEFINE EXTRTC}          // externe RTC DS1302 an Port B
{ $DEFINE EXTDCF}          // externer DCF-Empfänger an Port D4
{ $DEFINE LCDGRAPH}        // LCD-Grafik im FPGA
{$DEFINE PARSERESULT}     // Results entgegennehmen, für Parser

Device = mega644, VCC = 5;


Import SysTick, FAT16, TWImaster, SerPort,RTclock;  // ADCport,
{$IFDEF Panel}
Import IncrPort4, LCDmultiPort; //, LCDGraphic;
{$ENDIF}
{$IFDEF LCDGRAPH}
Import LCDGraphic;
{$ENDIF}
{$IFDEF EXTDCF}
Import DCFclock;
{$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   = 8;             //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;

{$IFDEF LCDGRAPH}
  LCDGraphic     = 320, 240, 8;      {x-pix, y-pix, accesswidth}
  // use the Column mode controller 61202 with 2 chipselects
  DefCharSet     = 'Graphchars.pchr';  {FileName}
  GViewports     = 1, iData;        {logical ViewPorts, scalings}
  TGraphStr      = 20;              {Graphic Text String Length}
{$ENDIF}
{$IFDEF EXTDCF}
  DCFclock    = iData;
  DCFport     = PinD, 4, negative;        {Port, Pin#}
{$ENDIF}
  RTclock     = iData, DateTime;{Time, DateTime}
  RTCsource   = SysTick;

USES
  UFAT16;


Implementation

type
  tModify   = (param0, param1, param2, param3, input0, input1, input2, input3, filesel);
  tError = (NoErr, UserReq, BusyErr, FileErr, SyntaxErr, ParamErr, LockedErr,ChecksumErr);

{--------------------------------------------------------------}
const


//                              ----SFDC             Strobe,DATASEL,Data,Clk
  DDRBinit                   = %10111011;            {PortB dir }
  DDRBinitRdRTC              = %10011011;            {PortB dir wenn RTC gelesen wird}
  PortBinit                  = %10111010;            {PortB }

  DDRCinit                   = %01110000;            {PortC dir, 0..1=Incr4 }
  PortCinit                  = %11001111;            {PortC }

{ Jumper und LEDs PortBits }
  DDRDinit                   = %00001100;            {PortD dir, 0..1=Serial }
  PortDinit                  = %11111100;            {PortD }


  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  LEDOutPort                 = @PortD;   {LED-Port}
  FPGAport                   = @PortB;   {FPGA-SPI-Port}
  FPGApin                    = @PinB;    {FPGA-SPI-Port}
  ConFPGABitPort             = @PortC;   {FPGA-Config-Port}
  ConFPGABitPin              = @PinC;   {FPGA-Config-Port}

  p_RTCclk                   = 7;      // RTC an Port B, RST = F_AUX!
  p_RTCio                    = 5;
  p_RTCrst                   = 0;
  
  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.63 [FPGA by CM/c''t 06/2008]';    //Vers1
  Vers3Str                   = 'FPGA 2.6';    // 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]');

  HeaderStr = 'VAL'+#9+'REG'+#9+'TIME'+#9+'DATE';

  EEnotProgrammedStr         = 'EEPROM EMPTY! ';
  AdrStr                     = 'Adr ';
  ParamHintStr               = 'Param  ';
  InputHintStr               = 'Value  ';
  EvalHintStr                = 'DataAdr';
  RunStr1                    = 'BTN:STOP';
  RunStr2                    = 'INI exec';
  FileNumStr                 = ' file';
  ErrSubCh                   = 255;

  CmdStrArr: array[0..65] of String[3] = (
  'STR', // 255
  'IDN', // 254
  'VAL', // 0..9999
{--------------------------------------------------------------}
// Script-Befehle
  'REG', // 300..309  Load Register 0..9 immediately mit Wert, auch Abfrage
  'ACC', // 300  Load Akku A (Register 0) immediately mit Wert, auch Abfrage
  'MOV', // 310..319  Move Register 0..9 := 0..9
  'DEC', // 320  Decrement Register (0..9), Ergebnis für Branches merken
  'INC', // 330  Increment Register (0..9), Ergebnis für Branches merken
  'CPZ', // 340  Compare Register (0..9) mit "0", Inhalt für Branches merken
  'XCH', // 350  Exchange 0..9 :=: 0..9, ohne Argument: Akku A mit Register (0..9)


  'GET', // 400..409  Warte auf Ergebnis, speichere in Registerinhalt 0..9
  'PUT', // 500..509  Sende Wert in Akku A auf MCM/SCM

  'MUL', // 600  Multiplikation Akku A = Akku A * Register (0..9)
  'DIV', // 610  Division Akku A = Akku A / Register (0..9)
  'ADD', // 620  Addition Akku A = Akku A + Register (0..9)
  'SUB', // 630  Subtraktion Akku A = Akku A - Register (0..9)
  'SQR', // 640  Quadratwurzel Register (0..9) = sqr(Register (0..9))
  'SQU', // 650  Quadrat Register (0..9) = Register (0..9) * Register (0..9)
  'NEG', // 660  Negiere Register (*-1)

  'LBL', // 1000  Label 0..99 setzen (hier begrenzt auf 0..31)
  'GTO', // 1100  Goto <labelnr>
  'BRA', // 1100  Branch always to <labelnr>, wie GTO
  'BRG', // 1200  Branch if greater 0 to Label <labelnr>, wenn Akku A größer als B
  'BGE', // 1300  Branch if greater or equal 0 to Label <labelnr>, wenn Akku A größer oder gleich B
  'BEQ', // 1400  Branch if equal 0 to Label <labelnr>, wenn Akku A gleich B
  'BLE', // 1500  Branch if lower or equal 0 to Label <labelnr>, wenn Akku A kleiner oder gleich B
  'BRL', // 1600  Branch if lower 0 to Label <labelnr>, wenn Akku A kleiner als B

  'INP', // 2000..2063 Input Akku mit Wert von moduleigener SubCh-Abfrage (SPI-Reg.)
  'OUT', // 2000..2063 Register Output,
         // Ausgabe von Akku oder Reg. 0..9 in moduleigenen SubCh (SPI-Reg.) 0..63
(*
  'DPV', // 700 Display Value, Wert     DPV=1.2345  @ DPX, DPY
  'DPR', // 710..719 Display Inhalt Register 0..9  DPR 0 oder DPR=0  @ DPX, DPY
  'DPS', // 720 Display String DPS=HALLO_FPGA  @ DPX, DPY
  'DPC', // 730 Display RTC, DPC=0 Zeit, DPC=1 Datum  @ DPX, DPY
  'DRW', // 740 DrawRect 741 FillRect 742 Drawline mit XY-Param  @ DPX, DPY
  'DRI', // 750 Draw Instrument, Bargraph usw.
  'DBG', // 770 Display-Background, DBG=0 keiner, DBG=AlphaStr=BMP-Name

  'DPX', // 780 Display-Parameter, EndX, vorheriger Wert wird StartX
  'DPY', // 781 Display-Parameter, EndY, vorheriger Wert wird StartY
  'DFX', // 782 Font-Skalierung X
  'DFY', // 783 Font-Skalierung Y
  'DCO', // 790 Block-Farbe 6 Bit für nächste Anzeige-Operation
*)
  'TTF', // 800 Type File an SPI, TTF <SPI-Register>="<filename>"
  'TTY', // 880 Type File an serielle Schnittstelle, TTY ="<filename>"
  'TSF', // 881 Text Save (append) to File, TSF="<filename>"
         //     <CR> <Textdaten>, Ende mit ETX ($03) = Ctrl-C
         
  'XMR', // 890 XModem-Receive AutoInc-Block vom Rechner an AIR

  'TSR', // 900 Type String an SPI, TSR <SPI-Register>="<string>"
  'TSS', // 980 Type String an serielle Schnittstelle, TSS ="<string>"

  'COM', // 990 CoreRxSubCh-SPI-Register
         // 991 CoreTxSubCh-SPI-Register
         // 992 Default-Kommunikationskanal für INI-Files, 0=OptoBus, 1=intern COM an FPGA

  'AIR', // 280 Auto-Increment-Register für DAT-File-Loads
  'AIS', // 281 Auto-Increment-Select
  'AIW', // 282 Auto-Increment-Breite
  'BLD', // 283 AutoInc-Block von SD-Card lesen und auf AIR ausgeben
  'BSV', // 284 AutoInc-Block von AIR lesen und auf SD-Card speichen
  'AIM', // 285 AutoInc-Blockstart in LongWords
  'AIE', // 286 AutoInc-Blockende in LongWords


{--------------------------------------------------------------}
  'CLK', // 190..199 RTC-Register und Befehle
  'OPT', // 150..179  Init-Dateien, Init-Werte 0..3
         // 180  IniFileName (INI oder CFG)
  'MCH', // 270  Main-Channel für GET/PUT, Script-gesendete/empfangene Daten, Moduladresse
  'SCH', // 271  Sub-Channel  für GET/PUT, Script-gesendete/empfangene Daten

  'WTH', // 290  Wait Tick Hour
  'WTM', // 291  Wait Tick Minute
  'WTS', // 292  Wait Tick Second
  'DLY', // 299  Delay ms

  'FWR', // 220 FileWriteRegister, FWR=<register0..9> in Datei DatFileName schreiben
  'FWV', // 230 FileWriteValue, FWV <index>=<wert> in Datei DatFileName schreiben
  '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..65] of integer = (
  255, 254,
  0,
  300,300,
  310,320,330,340,350,
  400,500,
  600,610,620,630,640,650,660,
  1000,1100,1100,1200,1300,1400,1500,1600,
  2000, 2000,
//  700,710,720,730,740,750,770,
  800,880,881,890,
  900,980,
  990,
  280,281,282,                      // AutoInc-Befehle
  283,284,285,286,
  90,
  150,
  270,271,
  290,291,292,299,
  220,
  230,
  240, 241, 241, 242, 243, 244,249,
  88, 250, 251, 252, 253, 253);

  cmdAnzahl=65; // letzter Eintrag, statt tCmdwhich
  cmdErr=cmdAnzahl+1; // Error, Statt tCmdwhich

  high                       = true;
  low                        = 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);

  EEInitFileName:String[15] = 'BASIC.INI';
  EEDatFileName:String[15] = 'DATAFILE.XLS';

  InitParamLongArray: array[0..3] of LongInt = (
  0,
  0,
  0,
  0);

{--------------------------------------------------------------}

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
  InitTransferCh[@OptionArray+8]:integer;
  InitTransferSubCh[@OptionArray+10]:integer;
  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;

{$IDATA}  {Langsamere SRAM-Variablen}
  aChar:char;
  MainCh: byte;
  SwitchState: Byte; //Zwischenspeicher für 4094 SR

  LEDactivity[LEDOutPort, 2]    : bit; {Bit 2 LED Remote-Activity}
  LEDswitch[LEDOutPort, 3]      : bit; {Bit 3 LED über dem Display}

  ButtonTemp,RangeTemp: Byte; // invertiert - low=on!
  ButtonDown[@ButtonTemp, 5]  : bit;
  ButtonUp[@ButtonTemp, 4]  : bit;
  ButtonEnter[@ButtonTemp, 3]  : bit;


  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

  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, ParamDigit :Byte;

  ParamLongArray: array[0..7] of 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;
  IncrDiffByte:Byte;
  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;

// Script-Register 0..9
  RegisterArray:array[0..9] of Float; // REG-Werte
  AccA[@RegisterArray+0]: Float;      // Akku A (0)
  Reg1[@RegisterArray+4]: Float;      // Akku B (1)
  Reg2[@RegisterArray+8]: Float;
  Reg3[@RegisterArray+12]: Float;
  Reg4[@RegisterArray+16]: Float;
  Reg5[@RegisterArray+20]: Float;
  Reg6[@RegisterArray+24]: Float;
  Reg7[@RegisterArray+28]: Float;
  Reg8[@RegisterArray+32]: Float;
  DispReg[@RegisterArray+36]: Float;      // Display-Register
  DestinationRegister:byte;
  GetTimeout: Integer;
  CompareParam:Float;
  GETwaitFlag:Boolean;
  LabelArray:array[0..31] of Word;
  LabelValidArray:array[0..31] of boolean;
  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
// Real Time Clock RTC
  tickSeconds, tickMinutes, tickHours: Boolean;
  RTCarray:array[0..5] of byte;
  Hour[@RTCarray+0]:byte;
  Minute[@RTCarray+1]:byte;
  Second[@RTCarray+2]:byte;
  Day[@RTCarray+3]:byte;
  Month[@RTCarray+4]:byte;
  Year[@RTCarray+5]:byte;
  RTCcmd, RTCdata:Byte;    // für externe RTC DS1302 oder HT1381

  DOStime, DOSdate:Word;
  LineNum,RepeatCount:Integer;
  FPGAbyte, FlashByte: byte;
  pDummy:byte;
{$IFDEF LCDGRAPH}        // LCD-Grafik
  gMask[@FPGAsendLong+0]:byte;
  gPixel[@FPGAsendLong+1]:byte;
  gColor[@FPGAsendLong+2]:byte;
  gCmd[@FPGAsendLong+3]:byte;
  gBlockColor:byte;
  qvga:Boolean; // QVGA-Ausgabe ins FPGA geladen?
  ParamLineStart,ParamLineEnd,ParamColStart,ParamColEnd: Integer;       // LCD-Grafik-Zeile
  ParamFontScaleX, ParamFontScaleY: byte;       // LCD-Font-Skalierungen
  BarScale:Float;
{$ENDIF}
  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;
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  ParamLineEnd:=24;        // LCD-Grafik-Zeile in Pixel
  ParamColEnd:= 0;         // LCD-Grafik-Spalte in Pixel
  ParamFontScaleX:=2;       // LCD-Font-Skalierung
  ParamFontScaleY:=2;       // LCD-Font-Skalierung
  BarScale:=1;
{$ENDIF}
  IncRast:=InitIncRast;
  digits:=byte(InitDigits);
  nachkomma:=byte(InitNachkomma);
  TransferCh:=byte(InitTransferCh);
  TransferSubCh:=InitTransferSubCh;
  ParamStr:='0.0';
  ClearDirectory;
  GetTimeout:=EEgetTimeOut;
  DestinationRegister:=9; // Default immer auf Anzeige
  AutoIncReg:=byte(EEAutoIncReg);
  AutoIncSel:=byte(EEAutoIncSel);
  AutoIncWidth:=4;
  CoreRxSubCh:=byte(OptionArray[10]);  // FPGA to AVR
  CoreTxSubCh:=byte(OptionArray[11]);  // AVR to FPGA
  IRQrBufPtr:=0;
  rBufPtr:=0;
  DatFileName:=EEDatFileName;
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
   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;

{###########################################################################}

{$IFDEF Panel}
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;

procedure SollWerteOnLCD;
begin
  if (not LCDpresent) then
    return;
  endif;
  if (not ChangedFlag) then
    return; // unverändert, nichts zu tun
  endif;
  case Modify of
    param0..param3:
      ParamLong:=ParamLongArray[ord(Modify)];
      ParamLongToHex;
      LCDxy_M(LCD_m1, 0, 0);
      write(lcdout_m,ParamStr);
      LCDxy_M(LCD_m1, 0, 1);
      write(lcdout_m,ParamHintStr);
      lcdout_m(char(ord(modify)+48));
      lcdout_m(#32);
      if SelectDigit then
        LCDxy_M(LCD_m1, ParamDigit, 0);
        LCDcursor_m(LCD_m1, false, false);
        lcdout_m(#3);
      else
        LCDcursor_m(LCD_m1, true, true);
      endif;
      LCDxy_M(LCD_m1, ParamDigit, 0);
      |
    input0..input3:
      LCDcursor_m(LCD_m1, false, false);
      ParamLong:=InputLongArray[ord(Modify)-ord(input0)];
      ParamLongToHex;
      LCDxy_M(LCD_m1, 0, 0);
      write(lcdout_m,ParamStr);
      LCDxy_M(LCD_m1, 0, 1);
      write(lcdout_m,InputHintStr);
      lcdout_m(char(ord(modify)+44));
      lcdout_m(#32);
      |
    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;
end;
{$ENDIF} //Panel

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];
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  ParamStr:=padright(ParamStr,12);
  gDrawString(0, 8, 1, 1, TxtRot0, 'File:'+ParamStr);
  if InitFileIsOpen then
    gdrawbitmap(112, 0, @sdcardbitmap, wmsetpix);
  endif;
{$ENDIF}
end;


{###########################################################################}

procedure DataWrite(myIndex:byte;isReg:boolean);
// schreibt Messwert aus Reg 0..9, Transfer(Sub)Ch und Zeitpunkt in Datei
var myfileName:String[12];
begin
  WriteErr:=true;
  if CardOK then
    myfileName:=DatFileName;
    if not F16_FileExist ('\', myfileName, faFilesOnly) then
      DateTimeToDOS;
      F16_FileAssign (DataFile, '',myfileName);
      F16_FileReWrite (DataFile,[faArchive],DOStime,DOSdate); // Datei erstellen
      if F16_CheckHandle(Datafile)<>faNone then
        Writeln(Datafile,HeaderStr);
      endif;
    else
       F16_FileAssign (DataFile, '',myfileName);
       F16_FileAppend (DataFile); // Datei zum Schreiben/Anhängen öffnen
    endif;
    if F16_CheckHandle(Datafile)<>faNone then
      if isReg then
        Param:=RegisterArray[myIndex];
      endif;
      ParamToStr;
      Write(Datafile,ParamStr);
      ParamStr:=#9+ByteToStr(myIndex);
      Write(Datafile,ParamStr);
      GetRTC;
      TimeToParamStr;
      Write(Datafile,#9+ParamStr);
      DateToParamStr;
      WriteLn(Datafile,#9+ParamStr);
      WriteErr:=false;
    endif;
    F16_FileClose (Datafile);
  endif;
  NoCard:=not CardOK;
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 DataFileReceive(myReceivefileName:String[12]);
var myChar:char;
  myfileName:String[12];
  started:boolean;
begin
  serinpCoreACK;   // Befehl angenommen
  WriteErr:=true;
  BlockStr:='';
  if CardOK then
    if not F16_FileExist ('\', myReceivefileName, faFilesOnly) then
      DateTimeToDOS;
      F16_FileAssign (MemFile, '',myReceivefileName);
      F16_FileReWrite (MemFile,[faArchive],DOStime,DOSdate); // Datei erstellen
    else
       F16_FileAssign (MemFile, '',myReceivefileName);
       F16_FileAppend (MemFile); // Datei zum Schreiben/Anhängen öffnen
    endif;
    started:=false;
    if F16_CheckHandle(MemFile)<>faNone then
      repeat
        if MainChInternFlag then
          if serstatCore then
            myChar:=serinpCore;
            // serout(myChar);  // zum Test
            if (myChar>=#10) then  // Nur Text und wichtige Steuerzeichen
              Append(myChar,BlockStr);
              if myChar=#13 then
                write(MemFile,BlockStr);
                BlockStr:='';
                serinpCoreACK; // Zeile angenommen
              endif;
            endif;
          endif;
        else
          myChar:=SerInp;
          if myChar>=#10 then
            write(MemFile,myChar);
          endif;
        endif;
      until (myChar=#3); // ETX, Ctrl-C
      if MainChInternFlag then
        write(MemFile,BlockStr);  // Rest wegschreiben
        serinpCoreACK;
      endif;
      WriteErr:=false;
    endif;
    F16_FileClose (MemFile);
  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); // for Write
  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;
         case AutoIncWidth of
         2: // DAT aus Words, Little-Endian-Folge
           for i:=0 to 63 do
             serok:=serinp_to(FPGAsendByte,10);
             chk:=chk+FPGAsendByte;
             FPGAsendWord0:=FPGAsendByte;
             serok:=serinp_to(FPGAsendByte,2);
             chk:=chk+FPGAsendByte;
             FPGAsendWord1:=FPGAsendByte;
             bytesrx:=bytesrx+2;
             SendFPGA16;
           endfor;
         |
         4: // DAT aus LongWords, Little-Endian-Folge
           for i:=0 to 31 do
             serok:=serinp_to(FPGAsendByte,10);
             chk:=chk+FPGAsendByte;
             FPGAsendLong0:=FPGAsendByte;
             serok:=serinp_to(FPGAsendByte,2);
             chk:=chk+FPGAsendByte;
             FPGAsendLong1:=FPGAsendByte;
             serok:=serinp_to(FPGAsendByte,2);
             chk:=chk+FPGAsendByte;
             FPGAsendLong2:=FPGAsendByte;
             serok:=serinp_to(FPGAsendByte,2);
             chk:=chk+FPGAsendByte;
             FPGAsendLong3:=FPGAsendByte;
             bytesrx:=bytesrx+4;
             SendFPGA32;
           endfor;
         |
         else
           for i:=0 to 127 do
             serok:=serinp_to(FPGAsendByte,10);
             chk:=chk+FPGAsendByte;
             inc(bytesrx);
             SendFPGA8;
           endfor;
         endcase;
         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;

procedure DATsave(myDATfileName:String[12]);
var myBlocks, myBlockCount, BytesWritten : Word;
    myByteCount : byte;
begin
  WriteErr:=true;
  if CardOK then
    myBlocks:=word((AutoIncBlockEnd-AutoIncBlockStart) div 256)+1;
    if myBlocks>0 then
      DateTimeToDOS;
      F16_FileAssign (BinFile, '',myDATfileName);
      F16_FileReWrite (BinFile,[faArchive],DOStime,DOSdate); // Datei erstellen
      AutoIncSetup(true);  // Register Daten für DAT-Files auf LESEN
      if (F16_CheckHandle(BinFile)<>faNone) then
        for myBlockCount:=0 to myBlocks do // write the entire file
          for myByteCount:=0 to 255 do
            SendFPGA8;  // nur Byte-Breite, soll Auto-Inkrement der Adresse ausführen!
            BlockArray[myByteCount]:=FPGAreceiveByte;
          endfor;
          F16_BlockWrite (BinFile, @BlockArray, 256, BytesWritten);
        endfor; // until end of file
      endif;
      AutoIncReset;
      F16_FileClose (BinFile);
      GetDirectory; // hat sich geändert
      WriteErr:=false;
    endif;
  endif;
  NoCard:=not CardOK;
end;


procedure DATload(myDATfileName:String[12]);
// liest Pixel-Background Datei von SD- oder MMC-Karte
// in den Grafik-Pufferspeicher GraphColArr
// Bitmap muss 128 Pixel breit und 64 Pixel hoch sein
// Achtung: mit Photoshop angelegte BMPs sind invertiert
var BytesRead, aWord: Word;
    myFilePos: longWord;
begin
  ReadErr:=true;
  repeat
  until not ButtonPressed;
  if CardOK and F16_FileExist ('\', myDATfileName, faFilesOnly) then
    F16_FileAssign (BinFile, '',myDATfileName);
    F16_FileReset (BinFile); // Datei zum Lesen öffnen
    AutoIncSetup(false);  // Schreib-Register Daten für DAT-Files
    while not F16_EndOfFile (BinFile) do // read the entire file
      F16_BlockRead (BinFile, @BlockArray, 256, BytesRead);
      if BytesRead>0 then
        case AutoIncWidth of
        2: // DAT aus Words, Little-Endian-Folge
          FPGAsendLong:=0;
          for aWord:=0 to ((BytesRead-2) shr 1) do
            FPGAsendWord:=BlockArray2[byte(aWord)];
            SendFPGA16;  // nur Word-Breite, soll Auto-Inkrement der Adresse ausführen!
          endfor;
        |
        4: // DAT aus LongWords, Little-Endian-Folge
          for aWord:=0 to ((BytesRead-4) shr 2) do
            FPGAsendLong:=BlockArray4[byte(aWord)];
            SendFPGA32;  // volle Breite, soll Auto-Inkrement der Adresse ausführen!
          endfor;
        |
        else
          for aWord:=0 to BytesRead-1 do
            FPGAsendByte:=BlockArray[byte(aWord)];
            SendFPGA8;  // nur Byte-Breite, soll Auto-Inkrement der Adresse ausführen!
          endfor;
        endcase;
      endif;
    endwhile; // until end of file
    F16_FileClose (BinFile);
    ReadErr:=false;
    AutoIncReset;
    mdelay(20); // Erholzeit für T65-Core
  endif;
  NoCard:=not CardOK;
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;
  for i:=0 to 31 do
    LabelValidArray[i]:=false;
  endfor;
  verboseSave:=verbose;
  ReadErr:=true;
  LabelSeek:=false;
  GetWaitFlag:=false;
{$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 GetWaitFlag and (Waitloops>0)) do // auf Antwort warten
          CheckSer;
          dec(Waitloops);
        endwhile;
        GetWaitFlag:=false; // nach Timeout
      endwhile; // until end of file
      F16_FileClose (InitFile);
      SerInpStr:='';
      ReadErr:=false;
    endif;
  endif;
  INITfileIsOpen:=false;
  GetWaitFlag:=false; // nach Timeout
  LabelSeek:=false;
  DestinationRegister:=9; // sonst immer auf Anzeige
  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='DAT' then
    // AutoIncReg muss manuell gesetzt sein, sonst Default Register/SubCh 128
    DATload(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 modify in [param0..input3] then
    ExchangeFPGA(byte(modify) AND 3);
  endif;
  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;
{$IFDEF EXTRTC}
  GetExtRTC;
  SetRTC;
{$ENDIF}
  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
{$IFDEF LCDGRAPH}
  TestQVGA;
{$ENDIF}

  CurrentCh:=255; // erstmal keine Reaktion
  errcount:=0;
  ChangedFlag:=true;
  ParamDigit:=7;
  ButtonTemp := $FF;
  fpgaSerOut(#0); //
  GetDirectory;
  if CardOK and (EEinitFileName<>'') then
    FileLoad(EEinitFileName);
    if FaultFlags<>0 then
      FileNum:=0;
      serprompt(FileErr);
    else
      SubCh:=240;
      WriteDirEntry;
    endif;
  endif;
  setSystimer(ACKTimer,1);
  setSystimer(PanelTimer,1);
  SetSystimer(DisplayTimer, 1);
  SetSystimer(IncrTimer, 1);
end;

{###########################################################################}

procedure ChangeParam(index:byte);
// ändert ParamLongArray[index] mit IncrDiff-Werten an Stelle ParamDigit
// oder ParamDigit selbst, wenn SelectDigit=true
begin
  if SelectDigit then
    ParamDigit:=ParamDigit+IncrDiffByte;
    if ParamDigit>127 then
      ParamDigit:=0;
    endif;
    if ParamDigit>7 then
      ParamDigit:=7;
    endif;
  else
    FPGAsendLong:=ParamLongArray[index];
    FPGAsendLong:=FPGAsendLong+(LongInt(IncrDiff)*IncrDigitArray[ParamDigit]);
    ParamLongArray[index]:=FPGAsendLong;
// wird auch laufend in Chores oder SollwerteOnLCD ausgetauscht, hier sofort
    FPGAreg:=index;
    SendFPGAreg;
    SendFPGA32;
  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
          IncrDiffByte:=byte(IncrDiff);    // Byte ohne Beschleunigung
          IncrDiff:=IncrAccInt10 * IncrAccArray[byte(abs(IncrDiff))];
{$IFDEF DEBUG}
   write(mySerOut, 'IncDiff=');
   write(mySerOut, IntToStr(IncrDiff));
   SerCRLF;
{$ENDIF}
          IncrAccInt10:=IncrDiff*10; // 40x Differenz mit Beschleunigung
          if SelectDigit then
            SetSystimer(DisplayTimer, 75); // Auswahl-Kästchen stehenlassen
          endif;
          case Modify of
            param0..param3:
              ChangeParam(ord(Modify));
              |
            input0..input3:
              ChangeParam(ord(Modify)-ord(input0));
              ChangedFlag:=true;   // Anzeige erzwingen
              |
            filesel:
              SelectDigit:=false;
              FileNum:=FileNum+IncrDiffByte;
              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;
          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.

