{###########################################################################}

// je nach MainChInternFlag entweder auf OptoBus oder FPGA-CoreTx
{$NOFRAME}
procedure mySerOut(const b: char);
begin
  asm;
    lds  _ACCB, FPGA.MainChInternFlag
    cpi _ACCB,0
    brne FPGA.fpgaout
    call SYSTEM.SerOut  ; normale Ausgabe wenn MainChInternFlag=false
    ADIW      _FRAMEPTR, 1 ; FramePtr korrigieren
    ret

    FPGA.fpgaout:
    cpi  _ACCA, 0ah                ; LF ignorieren
    breq FPGA.mySeroutexit
    push _ACCA
    lds  _ACCA, FPGA.CoreTxSubCh
    sts  FPGA.FPGAreg, _ACCA   ;SPI-Register
    call FPGA.SendFPGAreg
    pop _ACCA
    sts  FPGA.FPGAsendByte, _ACCA  ;SPI-Daten, nur 1 Byte, MSB im SPI
    call FPGA.SendFPGA8;
    ldi _ACCA, 0ah                 ; 100 us Verzögerung für max. 10000 Z/s
    call SYSTEM.UDELAY
    FPGA.mySeroutexit:
  endasm;
end;

// SerOut immer auf FPGA-COM
{$NOFRAME}
procedure fpgaSerOut(const b: char);
begin
  asm;
    jmp FPGA.fpgaout
  endasm;
end;

interrupt Int2; // externer Trigger-Eingang, neg. Flanke
// Byte aus CoreRxSubCh im IRQbuf ablegen, ggf. XON/XOFF senden
begin
  asm;
    lds  _ACCA, FPGA.CoreRxSubCh
    sts  FPGA.FPGAreg, _ACCA       ; SPI-Register
    call FPGA.SendFPGAreg
    call FPGA.SendFPGA8;
    lds  _ACCA, FPGA.FPGAreceivebyte
    cpi  _ACCA, 0h                 ; Null-Byte ignorieren
    breq FPGA.IRQexit
    cpi  _ACCA, 0ah                ; LF ignorieren
    breq FPGA.IRQexit
    lds  _ACCCLO, FPGA.IRQrBufPtr
    inc  _ACCCLO
    sts  FPGA.IRQrBufPtr, _ACCCLO

    lds  _ACCCLO, FPGA.IRQrBufPtr
    ldi  _ACCCHI, 0Eh              ; absolute RAM-Bereichsadresse!!
    st   Z, _ACCA                  ; empfangenes Byte ablegen
    FPGA.IRQexit:
  endasm;
end;


procedure serinpCoreACK;
// wird an Core gesendet, wenn CR empfangen wurde
begin
  if MainChInternFlag then
  asm;
    lds  _ACCA, FPGA.CoreTxSubCh
    sts  FPGA.FPGAreg, _ACCA       ; SPI-Register
    call FPGA.SendFPGAreg
    ldi _ACCA, 06h                 ; ACK
    sts  FPGA.FPGAsendByte, _ACCA  ; SPI-Daten, nur 1 Byte, MSB im SPI
    call FPGA.SendFPGA8;
  endasm;
  endif;
end;


function serinpCore:char;
var myChar:char;
begin
  while (IRQrBufPtr=rBufPtr) do
  endwhile;
  inc(rBufPtr);
  myChar:=IRQrBuf[rBufPtr];
  return(myChar);
end;

function serStatCore:boolean;
begin
  if IRQrBufPtr=rBufPtr then
    return(false);
  else
    return(true);
  endif;
end;

{###########################################################################}

{FPGA functions }

procedure SendFPGA8;
//Sende und empfange ein Daten-Byte an den FPGA-Chip über SPI
begin
  asm;
    cli ; Disable interrupts
    lds  _ACCA, FPGA.FPGAsendByte
    cbi  FPGA.FPGAport, FPGA.b_DATASEL
    out SPDR, _ACCA    ; SPI wurde von FAT16-Treiber eingeschaltet!
  SPIwait8_1:
    in _ACCA, SPSR
    sbrs _ACCA,7
    rjmp SPIwait8_1
    in _ACCA, SPDR
    sts  FPGA.FPGAreceiveByte, _ACCA  ;Lesewert zurück ins Datenbyte
    sbi  FPGA.FPGAport, FPGA.b_DATASEL
    sei ; Enable interrupts
  endasm;
end;

procedure SendFPGA16;
//Sende und empfange ein Daten-Wort (16 Bit-Register) an den FPGA-Chip über SPI
begin
  asm;
    cli ; Disable interrupts
    lds  _ACCA, FPGA.FPGAsendWord+1
    cbi  FPGA.FPGAport, FPGA.b_DATASEL
    out SPDR, _ACCA    ; SPI wurde von FAT16-Treiber eingeschaltet!
  SPIwait16_3:
    in _ACCA, SPSR
    sbrs _ACCA,7
    rjmp SPIwait16_3
    in _ACCA, SPDR
    sts  FPGA.FPGAreceiveWord+1, _ACCA

    lds  _ACCA, FPGA.FPGAsendWord+0
    out SPDR, _ACCA
  SPIwait16_4:
    in _ACCA, SPSR
    sbrs _ACCA,7
    rjmp SPIwait16_4
    in _ACCA, SPDR
    sts  FPGA.FPGAreceiveWord+0, _ACCA

    sbi  FPGA.FPGAport, FPGA.b_DATASEL
    sei ; Enable interrupts
  endasm;
end;

procedure SendFPGA32;
//Sende und empfange ein Daten-Langwort (32 Bit-Register) an den FPGA-Chip über SPI
begin
  asm;
    cli ; Disable interrupts
    lds  _ACCA, FPGA.FPGAsendLong+3
    cbi  FPGA.FPGAport, FPGA.b_DATASEL
    out SPDR, _ACCA    ; SPI wurde von FAT16-Treiber eingeschaltet!
  SPIwait32_1:
    in _ACCA, SPSR
    sbrs _ACCA,7       ; SPIF gesetzt?
    rjmp SPIwait32_1   ; auf Ende des SPI-Transfer warten
    in _ACCA, SPDR     ; und empfangenes Byte wieder in FPGAsendLong ablegen
    sts  FPGA.FPGAreceiveLong+3, _ACCA

    lds  _ACCA, FPGA.FPGAsendLong+2
    out SPDR, _ACCA     ; SPI von FAT16-Treiber eingeschaltet!
  SPIwait32_2:
    in _ACCA, SPSR
    sbrs _ACCA,7
    rjmp SPIwait32_2
    in _ACCA, SPDR
    sts  FPGA.FPGAreceiveLong+2, _ACCA

    lds  _ACCA, FPGA.FPGAsendLong+1
    out SPDR, _ACCA
  SPIwait32_3:
    in _ACCA, SPSR
    sbrs _ACCA,7
    rjmp SPIwait32_3
    in _ACCA, SPDR
    sts  FPGA.FPGAreceiveLong+1, _ACCA

    lds  _ACCA, FPGA.FPGAsendLong+0
    out SPDR, _ACCA
  SPIwait32_4:
    in _ACCA, SPSR
    sbrs _ACCA,7
    rjmp SPIwait32_4
    in _ACCA, SPDR
    sts  FPGA.FPGAreceiveLong+0, _ACCA

    sbi  FPGA.FPGAport, FPGA.b_DATASEL
    sei ; Enable interrupts
  endasm;
end;


procedure SendFPGAreg;
//Sende ein Byte (Registeradresse) an den FPGA-Chip
begin
  asm;
    cli ; Disable interrupts
    lds  _ACCA, FPGA.FPGAreg
    cbi  FPGA.FPGAport, FPGA.b_REGSEL
    out SPDR, _ACCA     ; SPI von FAT16-Treiber eingeschaltet!
  SPIwaitReg:
    in _ACCA, SPSR
    sbrs _ACCA,7 ; SPIF?
    rjmp SPIwaitReg     ;  auf Ende des SPI-Transfer warten
    sbi  FPGA.FPGAport, FPGA.b_REGSEL
    sei ; Enable interrupts
  endasm;
end;

procedure ShiftFPGAconf;  //Sende ein Byte an FPGA-Configuration
begin
  asm;
    lds  _ACCA, FPGA.FPGAbyte
    ldi  _ACCB, 8
    FPGA.confloop1:    ; höherwertiges Byte rausschieben
    cbi  FPGA.ConFPGABitPort, FPGA.c_DATA
    sbrc _ACCA,7 // Bit high?
    sbi  FPGA.ConFPGABitPort, FPGA.c_DATA
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK
    LSL  _ACCA
    cbi  FPGA.ConFPGABitPort, FPGA.c_CCLK
    dec _ACCB
    brne  FPGA.confloop1
  endasm;
end;


procedure ShiftFPGAconfblock;  //Sende einen Block an FPGA-Configuration
begin
  asm;
    LDI       _ACCCLO, FPGA.BlockTable AND 0FFh  ;Z-Register
    LDI       _ACCCHI, FPGA.BlockTable SHRB 8

    FPGA.shiftFPGAbyte:
   ; Z-Pointer auf Array-Eintrag
    ld   _ACCA, Z
    cbi  FPGA.ConFPGABitPort, FPGA.c_CCLK
    in   _ACCB, FPGA.ConFPGABitPort
   ; schleifenloses, auf Schnelligkeit optimiertes Ausschieben eines Bytes
   ; DATA  __--__--__
   ; CCLK  _---_---_-
   
    bst   _ACCA,7  ; MSBit in T Flag
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high
    bst   _ACCA,6
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high
    bst   _ACCA,5
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high
    bst   _ACCA,4
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high
    bst   _ACCA,3
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high
    bst   _ACCA,2
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high
    bst   _ACCA,1
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high
    bst   _ACCA,0
    bld  _ACCB, FPGA.c_DATA  ; T Flag in Register speichern
    out  FPGA.ConFPGABitPort, _ACCB ; mit CCLK auf low
    sbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK high

    cbi  FPGA.ConFPGABitPort, FPGA.c_CCLK  ; CCLK wieder low
    inc   _ACCCLO
    brne  FPGA.shiftFPGAbyte  ; wenn 0 => 256 Durchläufe
  endasm;
end;

{###########################################################################}

//FPGA-Funktionen

procedure ExchangeFPGA(myreg:byte);
// Kompketter Datenaustauch von vier 32-Bit-Registern über SPI
begin
    FPGAreg:=myreg;     // Schreib-Register
    SendFPGAreg;
    FPGAsendLong:=ParamLongArray[myreg]; // ans FPGA
    SendFPGA32;
    InputLongArray[myreg]:=FPGAreceiveLong; // kommt ohnehin gleichzeitig an
end;


procedure ExchangeFPGA4;
// Kompletter Datenaustauch von vier 32-Bit-Registern über SPI
begin
  for i:=0 to 3 do
    ExchangeFPGA(i);
  endfor;
end;

procedure RTCtickSecond;      // CallBack from RTCclock
begin
  tickSeconds:=true;
  if INITfileIsOpen then
    LEDactivity:= not LEDactivity;
  endif;
end;

procedure RTCtickMinute;      // CallBack from RTCclock
begin
  tickMinutes:=true;
end;

procedure RTCtickHour;        // CallBack from RTCclock
begin
  tickHours:=true;
end;

{$IFDEF EXTRTC}
procedure ShiftRTCout;  // für externe RTC DS1302 oder HT1381 an PortB
var SPItemp:byte;
begin
  SPItemp:=SPCR;
  SPCR:=0; // SPI abklemmen
  asm;
    cbi  PortB, FPGA.p_RTCclk
    sbi  PortB, FPGA.p_RTCrst

    lds  _ACCA, FPGA.RTCcmd
    lsl  _ACCA        // wg. RW-Bit
    andi  _ACCA,$FE   // RW-Bit auf 0
    ori  _ACCA,$80    // MSB auf 1
    ldi  _ACCB, 8

    FPGA.cmdloop1:    ; Cmd-Byte rausschieben
    sbrs _ACCA,0 // Bit low?
    cbi  PortB, FPGA.p_RTCio
    sbrc _ACCA,0 // Bit high?
    sbi  PortB, FPGA.p_RTCio
    sbi  PortB, FPGA.p_RTCclk
    lsr  _ACCA
    nop
    nop
    cbi  PortB, FPGA.p_RTCclk
    dec _ACCB
    brne  FPGA.cmdloop1

    lds  _ACCA, FPGA.RTCdata
    ldi  _ACCB, 8

    FPGA.dataloop1:    ; Data-Byte rausschieben
    sbrs _ACCA,0 // Bit low?
    cbi  PortB, FPGA.p_RTCio
    sbrc _ACCA,0 // Bit high?
    sbi  PortB, FPGA.p_RTCio
    sbi  PortB, FPGA.p_RTCclk
    lsr  _ACCA
    nop
    nop
    cbi  PortB, FPGA.p_RTCclk
    dec _ACCB
    brne  FPGA.dataloop1

    cbi  PortB, FPGA.p_RTCrst
  endasm;
  SPCR:=SPItemp;       // SPI wiederherstellen
end;

procedure ShiftRTCin;  // für externe RTC DS1302 oder HT1381
var SPItemp:byte;
begin
  SPItemp:=SPCR;
  SPCR:=0; // SPI abklemmen
  asm;
    cbi  PortB, FPGA.p_RTCclk
    sbi  PortB, FPGA.p_RTCrst
    lds  _ACCA, FPGA.RTCcmd
    lsl  _ACCA        // wg. RW-Bit Adresse eins nach links
    ori  _ACCA,$81    // MSB auf 1
    ldi  _ACCB, 8
    FPGA.cmdloop2:    ; Cmd-Byte rausschieben
    sbrs _ACCA,0 // Bit low?
    cbi  PortB, FPGA.p_RTCio
    sbrc _ACCA,0 // Bit high?
    sbi  PortB, FPGA.p_RTCio
    sbi  PortB, FPGA.p_RTCclk
    lsr  _ACCA
    nop
    nop
    cbi  PortB, FPGA.p_RTCclk
    dec _ACCB
    brne  FPGA.cmdloop2
  endasm;
  DDRB:=  DDRBinitRdRTC;       {PortB dir}
  asm;
    ldi  _ACCB, 7
    FPGA.dataloop2:    ; Data-Byte reinschieben
    clc
    sbic PinB, FPGA.p_RTCio // Bit gesetzt?
    sec
    sbi  PortB, FPGA.p_RTCclk
    nop
    nop
    ror _ACCA  // Carry-Bit einschieben
    cbi  PortB, FPGA.p_RTCclk
    dec _ACCB
    brne  FPGA.dataloop2
    cbi  PortB, FPGA.p_RTCrst
    lsr _ACCA
    sts FPGA.RTCdata,_ACCA ; Daten-Byte
  endasm;
  DDRB:=  DDRBinit;       {PortB dir}
  PortB:= PortBinit;           {PortB}
  SPCR:=SPItemp;       // SPI wiederherstellen
end;

procedure GetExtRTC;
begin
  RTCcmd:=2;
  ShiftRTCin;
  Hour:=BCDtoByte(RTCdata);
  RTCcmd:=1;
  ShiftRTCin;
  Minute:=BCDtoByte(RTCdata);
  RTCcmd:=0;
  ShiftRTCin;
  Second:=BCDtoByte(RTCdata);
  RTCcmd:=3;
  ShiftRTCin;
  Day:=BCDtoByte(RTCdata);
  RTCcmd:=4;
  ShiftRTCin;
  Month:=BCDtoByte(RTCdata);
  RTCcmd:=6;
  ShiftRTCin;
  Year:=BCDtoByte(RTCdata);
end;

procedure SetExtRTC;
begin
  RTCcmd:=7;
  RTCdata:=0;
  ShiftRTCout;
  RTCcmd:=2;
  RTCdata:=ByteToBCD(Hour);
  ShiftRTCout;
  RTCcmd:=1;
  RTCdata:=ByteToBCD(Minute);
  ShiftRTCout;
  RTCcmd:=0;
  RTCdata:=ByteToBCD(Second);
  ShiftRTCout;
  RTCcmd:=3;
  RTCdata:=ByteToBCD(Day);
  ShiftRTCout;
  RTCcmd:=4;
  RTCdata:=ByteToBCD(Month);
  ShiftRTCout;
  RTCcmd:=6;
  RTCdata:=ByteToBCD(Year);
  ShiftRTCout;
  RTCcmd:=7;
  RTCdata:=128;
  ShiftRTCout;
end;
{$ENDIF}

procedure GetRTC;
begin
  disableints;
  Hour:=RTCgethour;
  Minute:=RTCgetminute;
  Second:=RTCgetsecond;
  Day:=RTCgetday;
  Month:=RTCgetmonth;
  Year:=RTCgetyear;
  enableints;
end;

procedure SetRTC;
begin
  disableints;
  RTCsethour(Hour);
  RTCsetminute(Minute);
  RTCsetsecond(Second);
  RTCsetday(Day);
  RTCsetmonth(Month);
  RTCsetyear(Year);
  enableints;
{$IFDEF EXTRTC}
  SetExtRTC;
{$ENDIF}
end;

procedure DateTimeToDOS;
{ DOS time format bits:
DOStime: 15..11=Hours (0-23), 10..5=Minutes (0-59), 4..0=Seconds/2 (0-29)
DOSdate: 15..9=Year (0=1980, 127=2107), 8..5=Month (1=January, 12=December),
4..0=Day (1-31) }
begin
  GetRTC;
  DOStime:=(word(Hour) shl 11) or (word(Minute) shl 5) or (word(Second) shr 1);
  DOSdate:=(word(Year+20) shl 9) or (word(Month) shl 5) or (word(Day));
end;

procedure TimeToParamStr;
begin
  ParamStr:=byteToStr(Hour:2:'0')+':'+byteToStr(Minute:2:'0')+':'+byteToStr(Second:2:'0');
end;

procedure DateToParamStr;
begin
  ParamStr:=byteToStr(Day:2:'0')+'.'+byteToStr(Month:2:'0')+'.'+byteToStr(Year:2:'0');
end;

