program LED;
(*
(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de

04.09.2008 #0.1 Übernahme aus ADA-C EDL

*)

{ $NOSHADOW}
{$W- Warnings}            {Warnings off}
{$TYPEDCONST OFF}
{$DEBDELAY}
{$OPTI_QUICK}
{$DEFINE EXTRTC}          // externe RTC DS1302 an PortD
{$DEFINE LCDGRAPH}        // LCD-Grafik statt LED
{$DEFINE PARSERESULT}     // Results entgegennehmen, für Parser

Device = mega644, VCC = 5;

Define_Fuses
Override_Fuses; // optional, always replaces fuses in ISPE
  LockBits0 = [];

//  FuseBits0 = [BODEN,BODLEVEL];     // ATmega16 4,5V Brown-Out
//  FuseBits1 = [SPIEN,CKOPT,EESAVE]; // ATmega16

// mcb --> für ATMega 644
  FuseBits0      = [SUT0,CKSEL3];
  FuseBits1      = [SPIEN, EESAVE];
  FuseBits2      = [BODLEVEL2];
// <-- mcb
ProgMode = SPI;

{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
Import SysTick, FAT16, SerPort, IncrPort4, DCFclock, RTclock, LCDGraphic;
{$ELSE}
Import SysTick, FAT16, SerPort, Disp7sPort, IncrPort4, DCFclock, RTclock;
{$ENDIF}

from System import float, LongWord, LongInt;
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
From LCDGraphic Import CharSet;     {block CharSet, pixels}
{$ENDIF}

Define
  ProcClock      = 16000000;        {Hertz}

  SysTick        = 2;              //msec, hoch wg. Multplexer
  SerPort        = 38400, Stop1, timeout;    {Baud, StopBits|Parity}
  RxBuffer       = 128, iData;
  TxBuffer       = 64, iData;
  StackSize      = $0080, iData;
  FrameSize      = $00C0, iData;

  IncrPort4      = PinB, 1, 16; // pin-reg used, channels, 16 or 32bit integer
  IncrScan4      = Timer1, 1; // timer used, scan rate 1kHz (1..100)

  DCFclock    = iData;
  DCFport     = PinD, 4, negative;        {Port, Pin#}
  RTclock     = iData, DateTime;{Time, DateTime}
  RTCsource   = SysTick;

  FAT16          = MMC_SPI, iData;
  F16_MMCspeed   = fast;
  F16_FileHandles   = 2;
  F16_Dirlevels     = 1;

{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  LCDGraphic     = 128, 64, 8;      {x-pix, y-pix, accesswidth}
  // use the Column mode controller 61202 with 2 chipselects
  LCDgraphMode   = column, iData;   // linear
  DefCharSet     = 'Graphchars.pchr';  {FileName}
  GViewports     = 1, iData;        {logical ViewPorts, scalings}
  TGraphStr      = 20;              {Graphic Text String Length}
{$ELSE}
  Disp7sPort = UserPort; {User defined IO-driver, multiplexed}
  DispMode    = ShiftLeft;       // wrap
  DispDigits  = 8, iData;
{$ENDIF}

USES
  UFAT16;

Implementation

type
//  tCmdWhich = (str,idn,
//                 val,dsp,opt,skn,gto,rpt,lbl,reg,dly,cfg,lst,fnm,hex,wen,erc, sbd,nop, err);
  tError = (NoErr, UserReq, BusyErr, FileErr, SyntaxErr,
                   ParamErr, LockedErr,ChecksumErr);
  tCompare = (gleich, groesser, kleiner);
{--------------------------------------------------------------}
const



  DDRBinit                   = %00000000;            {PortB dir }
  PortBinit                  = %11111111;            {PortB }

{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  ctbitmap      : array[1..(32*32 div 8) + 2] of byte = 'ct.pbmp';
  sdcardbitmap  : array[1..(32*32 div 8) + 2] of byte = 'sdcard.pbmp';
{$ELSE}
  DDRAinit                   = %11111111;            {PortA dir }
  PortAinit                  = %00000000;            {PortA }
  DDRCinit                   = %11111111;            {PortC dir }
  PortCinit                  = %11111111;            {PortC }
{$ENDIF}


{ Jumper und LEDs PortBits }
  DDRDinitRdRTC              = %00010100;            {PortD dir, 0..1=Serial }
  DDRDinitWrRTC              = %00011100;            {PortD dir, 0..1=Serial }
  PortDinit                  = %11100011;            {PortD }
  
  p_RTCclk                     = 2;
  p_RTCio                      = 3;
  p_RTCrst                     = 4;

//Lineal     .----.----.----.----.----.----.----.----.----
{ mcb }  Vers1Str                   :string = '0.6 [LOG by CM/c''t 08/2008]';    //Vers1
{ mcb }  Vers3Str                   :string = 'LOG 0.6';    // Kurzform von Vers1

  AdrStr                     = 'Adr ';
  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]',
    '[]');

  HeaderStr = 'VAL'+#9+'REG'+#9+'CH'+#9+'SUBCH'+#9+'TIME'+#9+'DATE';

  ErrSubCh    = 255;

  CmdStrArr: array[0..49] of String[3] = (
  'STR', // 255
  'IDN', // 254
  'VAL', // 0..255
  'DBG', // 0..9 Display-Background, 0=keiner, AlphaStr=BMP-Name, 1=Zeigerinstr.
  'DSP', // 80
  'OPT', // 150..159  Init-Dateien, Init-Werte 0..3
{--------------------------------------------------------------}
// 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)

  '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)
  
  '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
  'DPL', // 298  Display-Register (9) auf Anzeige
  'DLY', // 299  Delay ms

  'GET', // 400..409  Warte auf Ergebnis, speichere in Registerinhalt 0..9
  'PUT', // 500..509  Sende Wert in Akku A auf MCM/SCM
  'FWR', // 900..999 FileWrite, Register 0..9 in Datei DATA0..DATA99 schreiben

  '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

  'RIO', // 2000..2255 Register I/O, Output (=) oder Input (?),
         // Ein-/Ausgabe in/vom moduleigenen SubCh 0..255

  'CLK', // 90 = RTC-Register und Befehle
{--------------------------------------------------------------}
  'CFG', // 240 = File Auswahl
  'LST', // 241 = Dir Listing
  'DIR', // 241 = Dir Listing
  'FNM', // 242 = FileNumber, Anzahl der Dateien
  'HEX', // 249 = 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..49] of integer = (
  255, 254,
  0, 70, 80, 150,
  300,300,
  310,320,330,340,350,
  600,610,620,630,640,650,660,
  270,271,
  290,291,292,
  298,299,
  400,500,
  900,
  1000,1100,1100,1200,1300,1400,1500,1600,
  2000,
  90,
  240, 241, 241, 242, 249, 250, 251, 252, 253, 253);

  cmdAnzahl=49; // letzter Eintrag, statt tCmdwhich
  cmdErr=50; // Error, Statt tCmdwhich

  high                     = true;
  low                      = false;

{ Default-Segmentzuordnung für Disp7Out:
  segA=1;
  segB=2;
  segC=4;
  segD=8;
  segE=16;
  segF=32;
  segG=64;
  segDP=128;}

//Default-EEPROM-Werte:
{$EEPROM}
structconst
  dummy:LongInt=0;        // diese Speicherstelle wird oft korrumpiert

  EESerBaudReg:byte=51;
  EETrigMask:byte = 0;
  InitIncRast:Integer =4;
  EEinitialised:word=$AA55;

// Options 150..159: diverse Optionen
  OptionArray:Array[0..9] of Integer =  (
  255,0,0,0,
  0,  // Result MainCh
  10, // Result SubCh
  7,  // InitDigits
  255,  // InitNachkomma, 255=auto
  9,  // EEMainCh
  500); //EEgetTimeOut
{
// Options 160..169: Ziffern-Segment-Umsetztabelle, nicht mehr benutzt
  Digit2LEDArr:Array[0..9] of byte =  (
  segA+segB+segC+segD+segE+segF,
  segB+segC,
  segA+segB+segD+segE+segG,
  segA+segB+segC+segD+segG,
  segB+segC+segF+segG,
  segA+segC+segD+segF+segG,
  segA+segC+segD+segE+segF+segG,
  segA+segB+segC,
  segA+segB+segC+segD+segE+segF+segG,
  segA+segB+segC+segD+segF+segG);
}
{--------------------------------------------------------------}

var
{$EEPROM}
  EEbinFileNum[@OptionArray+0]:integer;      // FileNum 0
  EEiniFileNum[@OptionArray+2]:integer;      // FileNum -1 (kein INI-File)
  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, j   : byte;

{$IDATA}  {Langsamere SRAM-Variablen}
  MainCh: byte;
  SD_CDSW[@PinB,  3]: bit;       // Card Insertion Switch, low = inserted
  IncrButton[@PinB,  2]: bit;    // Taster vom Incrementalgeben

//Parameter
  Options:byte;  //

  CmdWhich: byte; // tcmdwhich nicht mehr benutzt
  
  SubCh: Integer;
  CurrentCh       : byte;
  OmniFlag, verbose: boolean;
  
  Param          : float;
  ParamAlpha     : boolean; // Flag für Zeichenfolge hinter "="
  ParamInt       : Integer;
  ParamByte      : Byte;
  SerInpStr      : String[63];
  SerInpPtr:byte;
  Prefix:char;

//  ActivityTimer:Systimer8;
//  IncrTimer:Systimer8;
  digits, nachkomma:byte;
  ChangedFlag:Boolean;

  IncrValue,OldIncrValue: Integer;
  IncrDiff, IncRast:Integer;
  IncrDiffByte:Byte;

//für Parser
  ParamStr:string[40]; // auch für Display

  Status:byte;  // 0..3 Fehler/Button-Nummer
  BusyFlag[@Status, 7]  : bit;
  UserSRQFlag[@Status, 6]  : bit;
  FileErrorFlag[@Status, 5]  : bit;  //
  EEUnlocked[@Status, 4]  : bit; // EEPROM-unlocked-Flag

  FaultFlags, ButtonNumber:byte;
  NotFound[@FaultFlags, 0]  : bit;
  NoCard[@FaultFlags, 1]  : bit;
  ConfErr[@FaultFlags, 2]  : bit;
  ReadErr[@FaultFlags, 3]  : bit;
  ErrCount:integer;
  ErrFlag:boolean;
  FaultTimerByte: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, CardOK, FlashOK:Boolean;
  BinFile : file of byte;
  TextFile,DataFile : file of text;
  INITfileIsOpen, LabelSeek:Boolean;
  CurrentGotoLabel:byte;
  LastBinFileNum, FileNum, FileAnzahl,FileNumTemp : byte;

// 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;

  aInt                    : Integer;

{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  LCD_DatRD[@PINC]        : byte;
  LCD_DatWR[@PORTC]       : byte;
  LCD_DDR[@DDRC]          : byte;
  LCD_EN[@PORTA, 0]       : Bit;                  {Enabled line}
  LCD_RW[@PORTA, 1]       : Bit;                  {R/W line}
  LCD_DI[@PORTA, 2]       : Bit;                  {D/I line}
  LCD_CS1[@PORTA, 3]      : Bit;                  {LCD_CS1 line}
  LCD_CS2[@PORTA, 4]      : Bit;                  {LCD_CS2 line}
  LCD_RST[@PORTA, 5]      : Bit;                  {LCD_CS2 line}
  LCD_busy[@LCD_DatRD, 7] : Bit;                  {busy bit}
  col                     : Byte;                 {column counter}
  CurRow                  : Byte;                 {storage for 2. controller}
{$ENDIF}

  ParamLine,ParamCol: Integer;       // LCD-Grafik-Zeile
  ParamFontScaleX, ParamFontScaleY: byte;       // LCD-Font-Skalierungen
  BarScale,NeedleScale:Float;
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  BMPArray[@GraphColArr]:array[0..1023] of byte;
  BMPline:array[0..15] of byte;
{$ENDIF}

{###########################################################################}


{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED

{$I LED-LCDG.pas}

{$ELSE}
UserDevice Disp7sIOS(digit, segment : byte);
begin
  // this is a callback from SysTick
  // bits for active segments and digits are "1"
  // dependend on the hardware these values must be inverted
  PortC:= $ff;
  PortA:= 0;
  Nop;
  // update display
  if (segment or $80) =%10110001 then
    PortC:= (not (segment and $80)) and %10000111; // remap "t"
  else
    PortC:= not segment;
  endif;
  PortA:= digit;
end;
{$ENDIF}

{$I LED-RTC.pas}


{###########################################################################}
{$NOSAVE}

procedure ClearDirectory;
begin
  FaultFlags:=0;
  FileErrorFlag:=false;
  for i:=0 to 63 do
    DirectoryArray[i]:='<empty>';
  endfor;
  for i:=0 to 9 do
    LabelValidArray[i]:=false;
  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;
  CardOK:= false;
  FileAnzahl:=0;
  FaultFlags:=0;
  if F16_DiskInit then
    CardOK:= F16_DiskReset;
  endif;
  if not CardOK then
    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;
  NoCard:= not CardOK;
  FileErrorFlag:=(FaultFlags<>0);
end;

{###########################################################################}

procedure InitVars;
begin
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  ParamLine:=24;        // LCD-Grafik-Zeile in Pixel
  ParamCol:= 0;         // LCD-Grafik-Spalte in Pixel
  ParamFontScaleX:=2;       // LCD-Font-Skalierung
  ParamFontScaleY:=2;       // LCD-Font-Skalierung
  BarScale:=1;
  NeedleScale:=1;
{$ENDIF}
  IncRast:=InitIncRast;
  MainCh:= lo(EEMainCh);
  digits:=byte(InitDigits);
  nachkomma:=byte(InitNachkomma);
  TransferCh:=byte(InitTransferCh);
  TransferSubCh:=InitTransferSubCh;
  ParamStr:='0.0';
  ClearDirectory;
  GetTimeout:=EEgetTimeOut;
  DestinationRegister:=9; // Default immer auf Anzeige

end;

// auch für DEBUG-mode gebraucht

procedure SerCRLF;
begin
  Serout(#$0D);
  Serout(#$0A);
end;

procedure WriteChPrefix;
begin
  Serout('#');
  Serout(char(MainCh+48));
  Serout(':');
  write(Serout, IntToStr(SubCh));
  Serout('=');
end;

procedure WriteTransferChPrefix;
begin
  Serout(char(TransferCh+48));
  Serout(':');
  write(Serout, IntToStr(TransferSubCh));
end;

procedure WriteSerInp;
begin
  write(Serout, SerInpStr);   // Befehl weiterreichen
  SerCRLF;
end;


{###########################################################################}

//allg. Menü-Prozeduren

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;
  if myErr = UserReq then
    myStatus:=Status or ButtonNumber or 64;
  else
    if (FaultFlags<>0) or (myErr=NoErr) then
      myStatus:=Status or FaultFlags;
    else
      myStatus:=Status or ord(myErr);
      if myErr <> noErr then
        inc(ErrCount);
      endif;
    endif;
  endif;
  write(Serout, ByteToStr(myStatus));
  if (FaultFlags = 0) then
    Serout(#32);
    write(serout,ErrStrArr[ord(myErr)]);
  else
    myFault:=FaultFlags;
{ mcb }    for i := 0 to 4 do
      if (myFault and 1) = 1 then
        Serout(#32);
        write(serout,FaultStrArr[i]);
      endif;
      myFault:=myFault shr 1;
    endfor;
  endif;
  SerCRLF;
end;

{###########################################################################}

procedure WriteParamStrSer;
begin
  WriteChPrefix;
  write(Serout, ParamStr);
  SerCRLF;
end;


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 WriteParamSer;
begin
  ParamToStr;
  WriteChPrefix;
  write(serout,ParamStr);
  SerCRLF;
end;

procedure WriteTransferParamSer;
begin
  ParamToStr;
  WriteTransferChPrefix;
  Serout('=');
  write(serout,ParamStr);
  SerCRLF;
end;

procedure WriteParamIntSer;
begin
  ParamStr:= IntToStr(ParamInt);
  ParamStr:= trim(ParamStr);
  WriteChPrefix;
  write(Serout, ParamStr);
  SerCRLF;
end;

procedure WriteParamByteSer;
begin
  ParamStr:= ByteToStr(ParamByte);
  WriteParamStrSer;
end;

{###########################################################################}


procedure ParamAlphaStr2LED;
//ParamStr auf LED-Siebensegmentanzeige bringen,
//LEDsegmentArr wird von SysTick ausgegeben
begin
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  gDrawString(ParamCol, ParamLine, ParamFontScaleX, ParamFontScaleY, TxtRot0, ParamStr);
  gdisprefresh;
{$ELSE}
  DispClear;
  write(DispOut,#10 +ParamStr);
{$ENDIF}
end;

procedure ParamStr2LED;
//ParamStr auf LED-Siebensegmentanzeige bringen,
//LEDsegmentArr wird von SysTick ausgegeben
begin
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  ParamStr:=padright(ParamStr,digits);
  setlength(ParamStr,digits);
{$ENDIF}
  ParamAlphaStr2LED;
end;

{###########################################################################}

procedure WriteDirEntry;
//Directory-Eintrag FileNum ausgeben
begin
   WriteChPrefix;
   ParamStr:= ByteToStr(FileNum);
   write(Serout, ParamStr);
   serout(#32);
   serout('[');
   ParamStr:= DirectoryArray[FileNum];
   write(Serout, ParamStr);
   serout(']');
   SerCRLF;
end;


procedure WriteDirEntryLCD;
//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;
  gdisprefresh;
{$ELSE}
  ParamStr2LED;
{$ENDIF}
end;

procedure LCDnew;
//Directory-Eintrag FileNum ausgeben
begin
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  gclrscr(0);
  WriteDirEntryLCD;
  gdisprefresh;
{$ELSE}
  DispClear;
{$ENDIF}
end;

{###########################################################################}

procedure FileLoad;  forward;
procedure CheckSer;  forward;

procedure DataWrite(myFileNum,myIndex:byte);
// schreibt Messwert aus Reg 0..9, Transfer(Sub)Ch und Zeitpunkt in Datei
var myfileName:String[12];
begin
  if CardOK then
    myfileName:='DATA'+ byteToStr(myFileNum)+'.XLS';
    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
      Param:=RegisterArray[myIndex];
      ParamToStr;
      Write(Datafile,ParamStr);
      ParamStr:=#9+ByteToStr(myIndex)+#9+ByteToStr(TransferCh)+#9+IntToStr(TransferSubCh);
      Write(Datafile,ParamStr);
      GetRTC;
      TimeToParamStr;
      Write(Datafile,#9+ParamStr);
      DateToParamStr;
      WriteLn(Datafile,#9+ParamStr);
    endif;
    F16_FileClose (Datafile);
  endif;
end;

procedure BMPLoad(myBMPfileName: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 myResult, aWord: Word;
    mySource, myDest, myBottomPtr,myTopPtr, myPointer: Pointer;
    myFilePos: longWord;
begin
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  LCDnew;
  Notfound:=true;
  if CardOK then
   if F16_FileExist ('\', myBMPfileName, faFilesOnly) then
      F16_FileAssign (BinFile, '',myBMPfileName);
      F16_FileReset (BinFile); // Datei zum Lesen öffnen
      F16_FileSeek (BinFile,10); // BMP-Startadresse holen
      F16_BlockRead (BinFile, @myFilePos,4,myResult); // BMP-Startadresse holen
      F16_FileSeek (BinFile,myFilePos);      //
      F16_BlockRead (BinFile, @GraphColArr,1024,myResult); // Bitmap lesen
      F16_FileClose (BinFile);
      Notfound:=false;
    endif;
  endif;
  if NotFound then
    gclrscr(0);  // Kein Bild anzeigen, nur löschen
    gDrawString(16, 40, 2, 2, TxtRot0, 'NotFound');
  else
// Bild kontern (oben/unten vertauschen)
    myBottomPtr:=@BMPArray;
    myTopPtr:=@BMPArray+1008;
    for i:=0 to 31 do
      CopyBlock (myBottomPtr, @BMPline, 16);
      CopyBlock (myTopPtr, myBottomPtr, 16);
      CopyBlock (@BMPline, myTopPtr, 16);
      myBottomPtr:=myBottomPtr+16;
      myTopPtr:=myTopPtr-16;
    endfor;
  endif;
  WriteDirEntryLCD;
//  gdisprefresh;  // Buffer ins Display kopieren
{$ENDIF}
end;

{$I LED-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;
begin
  LCDnew;
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  gdrawbitmapN(112, 0, @sdcardbitmap, wmsetpix);
  gdisprefresh;
{$ENDIF}
  Notfound:=false;
  LabelSeek:=false;
  GetWaitFlag:=false;
  if CardOK then
   BusyFlag:=true;
   if F16_FileExist ('\', myINITfileName, faFilesOnly) then
      F16_FileAssign (TextFile, '',myINITfileName);
      F16_FileReset (TextFile); // Datei zum Lesen öffnen
      INITfileIsOpen:=true;
      while IncrButton and (not F16_EndOfFile(TextFile)) do // read the entire file
        ReadLn(TextFile,SerInpStr);
        if SerInpStr[1]<>'/' then
          ParseSubCh;
        endif;
        SerInpStr:='';
        if serstat then
          CheckSer;
        endif;
        Waitloops:=GetTimeout;
        while (IncrButton and GetWaitFlag and (Waitloops>0)) do // auf Antwort warten
          CheckSer;
          dec(Waitloops);
        endwhile;
        GetWaitFlag:=false; // nach Timeout
      endwhile; // until end of file
      F16_FileClose (TextFile);
      SerInpStr:='';
    else
      Notfound:=true;
    endif;
  endif;
  if FileNum>FileAnzahl then
    Notfound:=true;
  endif;
  INITfileIsOpen:=false;
  GetWaitFlag:=false; // nach Timeout
  LabelSeek:=false;
  DestinationRegister:=9; // sonst immer auf Anzeige
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  gdrawbitmapN(112, 0, @sdcardbitmap, wmsetpix);
  gdrawbitmap(112, 0, @sdcardbitmap, wmxorpix);
  gdisprefresh;
{$ENDIF}
  BusyFlag:=false;
end;

procedure FileLoad;
// INI-Datei laden und ausführen
var myFileExt:String[3]; myFileName:String[8]; myNotFoundSave:Boolean;
begin
  if FileNum<=FileAnzahl then // gültigen Eintrag angewählt
    for i:=0 to 9 do
      LabelValidArray[i]:=false;
    endfor;
    myFileExt:=ExtractFileExt(DirectoryArray[FileNum]);
    myFileName:=ExtractFileName(DirectoryArray[FileNum]);
    if myFileExt='INI' then
      INITload(DirectoryArray[FileNum]);
    endif;
    if myFileExt='BMP' then
      BMPload(DirectoryArray[FileNum]);
    endif;
  endif;
end;


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
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
    gclrscr(0);
    gDrawString(16, 40, 2, 2, TxtRot0, 'CdInsert');
    gdisprefresh;
{$ELSE}
    DispClear;
    write(DispOut,#10 +'CdInsert');
{$ENDIF}
    mdelay(500);
    GetDirectory;
    if CardOK and (EEiniFileNum<64) then
      FileNum:=byte(EEiniFileNum);
      if FaultFlags<>0 then
        FileNum:=0;
        serprompt(FileErr);
      else
        SubCh:=240;
        WriteDirEntry;
        WriteDirEntryLCD;
      endif;
    endif;
  endif;
end;

procedure CheckSer;
var
myChar: char;
begin
  while SerInp_TO(myChar,20) do // 20 ms Timeout
//    serout(mychar); // Echo Character
    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
      Chores;
      ParseSubCh;           //Befehl interpretieren, wenn für uns
      SerInpStr:='';
    endif;
  endwhile;
  Chores;
end;

procedure CheckDelay(myDelay:byte);
// Delay mit serieller Abfrage in 20-ms-Schritten
var mycount:byte;
begin
  for mycount:=1 to mydelay do
    CheckSer;
  endfor;
end;

{###########################################################################}

procedure initall;
//nach Reset aufgerufen
begin
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  LCD_DDR:= 0;                             // all input
  DDRA:= $3F;
  PORTA:= $FF;
  LCD_RST:=false;
  mdelay(100);
  LCD_RST:=true;
  mdelay(100);
  GraphInit;
{$ELSE}
  DDRA:=  DDRAinit;            {PortB dir}
  PortA:= PortAinit;           {PortB}
  DDRC:=  DDRCinit;            {PortC dir}
  PortC:= PortCinit;           {PortC}
{$ENDIF}

  DDRD:=  DDRDinitRdRTC;       {PortD dir}
  PortD:= PortDinit;           {PortD}
  

  i:=EESerBaudReg;
  if not i in [9..239] then
    EESerBaudReg:=51;
    i:=51;
  endif;
// mcb --> für ATmega644
//  serBaud(EESerbaud);        // nur mit 644
//  ubrr1:=i;           // für ATMega32
// mcb --> für ATMega644
//  UCSR1A := UCSR1A or 2;
//  UCSRA:=UCSRA or 2; // Set U2X bit  für ATMega32


//  ADMUX:= ADMUX OR %11000000;  {Internal ADC Reference}

  EnableInts;
  MainCh:= lo(EEMainCh);

  Status:=0;
  while serstat do
    i:=SerInp;
  endwhile;
  SubCh:=254;
  WriteChPrefix;
  write(serout,Vers1Str);
  SerCRLF;
  CurrentCh:=255; // erstmal keine Reaktion
  mdelay(10);
  ErrCount:=0;
  InitVars;

  IncrCount4start;
  SetIncrVal4(0,0);
  IncrValue:=GetIncrVal4(0);
  OldIncrValue:= IncrValue;
  IncrDiff:=0;
{$IFDEF EXTRTC}
  GetExtRTC;   // Uhren synchronisieren
  SetRTC;
{$ENDIF}

// Default-Dateien laden
  Chores; // Files ggf. laden
{$IFDEF LCDGRAPH}        // LCD-Grafik statt LED
  GraphSplashScreen;
  gclrscr(0);
  WriteDirEntryLCD;
  gdisprefresh;
{$ELSE}
  write(DispOut,#10 +Vers3Str);
  mdelay(500);
  write(DispOut,#10 +AdrStr);
  DispOut(char(MainCh+48));
  mdelay(1000);
{$ENDIF}
end;

{###########################################################################}

begin
  Initall;
//Hauptschleife

  loop
    IncrValue:=GetIncrVal4(0);
// Inkrementalgeber löst in Zweierschritten auf, deshalb der Umstand
    if (IncrValue<>OldIncrValue) then
      IncrDiff:=IncrDiff + IncrValue-OldIncrValue;
      OldIncrValue:= IncrValue;
      if abs(IncrDiff)>= IncRast then // erst wenn n Impulse gezählt
        ChangedFlag:=true;
        IncrDiff:=IncrDiff div IncRast;
        IncrDiffByte:=byte(IncrDiff);    // Byte ohne Beschleunigung
        FileNum:=FileNum-IncrDiffByte;
        if FileNum>128 then  // negativ geworden
          FileNum:=0;
        endif;
        if (FileNum>=FileAnzahl) and (FileAnzahl>0) then
          FileNum:=FileAnzahl-1;
        endif;
        SubCh:=240;
        WriteDirEntry;
        WriteDirEntryLCD;
        IncrDiff:=0; // für nächsten Durchlauf
      endif;
    endif;
    if (not IncrButton) then
      ButtonNumber:=3; // Button Enter/Fine
      serprompt(UserReq);
      while not IncrButton do
        CheckSer;
      endwhile;
      FileLoad;
      while not IncrButton do
        CheckSer;
      endwhile;
      ButtonNumber:=0; // Button Enter/Fine
      serprompt(UserReq);
    endif;
    Checkdelay(2); // wichtig für Entprellung
  endloop;
end LED.

