#
#    Mecrisp-Quintus - A native code Forth implementation for RISC-V
#    Copyright (C) 2018  Matthias Koch
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# Initialisiert die Pointer und Flash-Variablen nach dem Neustart.
# Wird direkt eingefügt und nur einmal beim Start benutzt,
# deshalb werden Register hier nicht gesichert. 

# Initialises pointers and variables for flash dictionary after Reset.
# This runs one time after Reset, no registers are saved here.


   # Hardware sets return stack pointer on startup from vector table.
   # Set Return stack pointer here (again) just in case this might be a software re-entry.

   # Return stack pointer already set up. Time to set data stack pointer !
   # Normaler Stackpointer bereits gesetzt. Setze den Datenstackpointer:

   # TOS setzen, um Pufferunterläufe gut erkennen zu können
   # TOS magic number to see spurious stack underflows in .s

  # -----------------------------------------------------------------------------    
  # Initialize register file
  addi x1, zero, 0           # Return address register, holds link back and is also used to compose long calls with auipc and jalr
  laf  x2, returnstackanfang # Stack pointer x2 (sp) is initialized by reset to end of RAM... We may choose another location.
  addi x3, zero, 42          # TOS
  laf  x4, datenstackanfang  # PSP Set data stack pointer
  addi x5, zero, 0           # Free scratch register, not saved across calls.
  addi x6, zero, 0           # Free scratch register, not saved across calls.
  addi x7, zero, 0           # Scratch register, needs to be saved.
  addi x8, zero, 0           # Loop index
  addi x9, zero, 0           # Loop limit
  addi x10, zero, 0          # Scratch register, needs to be saved.
  addi x11, zero, 0          # Scratch register, needs to be saved.
  addi x12, zero, 0          # Scratch register, needs to be saved.
  addi x13, zero, 0          # Scratch register, needs to be saved.
  addi x14, zero, 0          # Scratch register, needs to be saved.
  addi x15, zero, 0          # Scratch register, needs to be saved.
  # -----------------------------------------------------------------------------
#  addi x16, zero, 0         # x16-x31: Unused.
#  addi x17, zero, 0
#  addi x18, zero, 0
#  addi x19, zero, 0
#  addi x20, zero, 0
#  addi x21, zero, 0
#  addi x22, zero, 0
#  addi x23, zero, 0
#  addi x24, zero, 0
#  addi x25, zero, 0
#  addi x26, zero, 0
#  addi x27, zero, 0
#  addi x28, zero, 0
#  addi x29, zero, 0
#  addi x30, zero, 0
#  addi x31, zero, 0
  # ----------------------------------------------------------------------------- 
 
  # For debug only: Initialise whole memory with a known value.
  
#   li x31, 0x42434445
#   li x30, RamEnde
#   # Zero initialize scratchpad memory from 0 to x2 = sp.
# setmemloop:
#   sw x31, 0(x1)
#   addi x1, x1, 4
#   blt x1, x30, setmemloop   
#          
   
  # Suche nun im Flash nach Anfang und Ende.
  # Short: Search for begin and end in Flash.

   # Dictionarypointer ins RAM setzen
   # Set dictionary pointer into RAM first
   laf x6, Dictionarypointer
   laf x5, RamDictionaryAnfang
   sw x5, 0(x6)

   # Fadenende fürs RAM vorbereiten
   # Set latest for RAM
   laf x6, Fadenende
   la x5, CoreDictionaryAnfang
   sw x5, 0(x6)
   
 #  write "Setze Fadenende:"
 #  pushdaconst Fadenende
 #  lw x3, 0(x3)
 #  # laf x3, CoreDictionaryAnfang
 #  call hexdot
 #  writeln ""
  
  # Registerbelegung:  Register allocation here:
  
  #    x10 Für dies und das    Temporary this and that
  #    x11 Aktuelle Flags      Current Flags
  #    x13 Für dies und das    Temporary this and that
  #    x15 Belegtes Ram.       Keeps track of allocated RAM
  # TOS=x3 Adresshangelzeiger  Pointer that crawls through dictionary

  
  pushdatos
  la x3,  CoreDictionaryAnfang  # Hier fängt es an.  Start at the beginning
  laf x15, RamDictionaryEnde     # Fürs Abzählen des Variablenplatzes  Variables start at the end of RAM dictionary

SucheFlashPointer_Hangelschleife:
#  pushda x3
#  call hexdot
#  writeln "Hangelschleife"
  lw x11, 4(x3)  # Aktuelle Flags lesen  Fetch current Flags

  li x13, Flag_invisible # Flag_invisible ? Überspringen !  Skip invisible definitions
  beq x11, x13, Sucheflashpointer_Speicherbelegung_fertig
  
    # Dies Wort ist sichtbar. Prüfe, ob es Ram-Speicher anfordert und belegt.
    # This definition is visible. Check if it allocates RAM.

    andi x13, x11, Flag_buffer & ~Flag_visible
    beq x13, zero, 1f # No buffer requested.

      # Search for end of code of current definition.
      push x3
      
      addi x3, x3, 8
      call skipstring  # x3 zeigt nun an den Codebeginn des aktuellen Wortes.  x3 points to start of code of current definition
      call suchedefinitionsende # Advance pointer to end of code. This is detected by "bx lr" or "pop {pc}" opcodes.
    
      # x3 ist nun an der Stelle, wo die Initialisierungsdaten liegen. x3 now points to the location of the initialisation at the end of code of current definition.
      lw x13, 0(x3) # Fetch required length of buffer

      
 #     pushda x13
 #     call hexdot
 #     writeln " buffer:"      
      
      # Ramvariablenpointer wandern lassen  Subtract from the pointer that points to the next free location
      sub x15, x15, x13
      pop x3
      j Sucheflashpointer_Speicherbelegung_fertig # Finished

1:  andi x13, x11, Flag_ramallot & ~Flag_visible    
    beq x13, zero, Sucheflashpointer_Speicherbelegung_fertig # Benötigt doch kein RAM.
      # writeln "Speicher gewünscht !"
      # Die Flags werden später nicht mehr gebraucht.
      # This one allocates RAM, Flags are not needed anymore.

      andi x11, x11, 0x0F # Das unterste Nibble maskieren  Mask lower 4 bits that contains amount of 32 bit locations requested.

 #     pushda x11
 #     call hexdot
 #     writeln " initram"
      
        # Bei Null Bytes brauche ich nichts zu kopieren, den Fall erkennt move.
        # Zero byte requests are handled by move itself, no need to catch this special case. Sounds strange, but is useful to have two handles for one variable.
        slli x11, x11, 2  # Mit vier malnehmen                  Multiply by 4
        sub x15, x15, x11 # Ramvariablenpointer wandern lassen  Subtract from the pointer that points to the next free location

        # Den neu geschaffenen Platz initialisieren !
        # Initialise the freshly allocated RAM locations !
        # r0: zeigt gerade auf das Namensfeld und wird danach nicht mehr benötigt.  Points to Name and is not needed afterwards
        # r1: enthält die Zahl der noch zu verschiebenden Bytes                     Contains number of bytes to copy/initialise
        # r3: ist frei für uns.                                                     Free for use !
        # r5: die Startadresse des Ram-Bereiches, der geschrieben werden soll.      Contains address in RAM the initialisation values have to be written in.

        # Muss zuerst schaffen, das Ende der aktuellen Definition zu finden.
        # Search for end of code of current definition.
        pushdatos      
        addi x3, x3, 8
        call skipstring  # x3 zeigt nun an den Codebeginn des aktuellen Wortes.  x3 points to start of code of current definition
        call suchedefinitionsende # Advance pointer to end of code. This is detected by "bx lr" or "pop {pc}" opcodes.
        
        # x3 ist nun an der Stelle, wo die Initialisierungsdaten liegen. x3 now points to the location of the initialisation at the end of code of current definition.
        # Kopiere die gewünschte Zahl von r1 Bytes von [r0] an [r5]  Copy desired amount of r1 bytes from [r0] to [r5].
                   # Quelle  Source
        pushdadouble x15, x11
        #pushda x15 # Ziel    Target
        #pushda x11 # Anzahl an Bytes  Amount
        call move

Sucheflashpointer_Speicherbelegung_fertig:
  # Speicherbelegung und -initialisierung abgeschlossen.
  # Finished RAM allocation and initialisation.

  # Weiterhangeln  Continue crawl.
  call dictionarynext
  popda x10
  beq x10, zero, SucheFlashPointer_Hangelschleife
 
  laf x10, ZweitFadenende
  sw x3, 0(x10) # Das Fadenende für den Flash setzen.  Set pointer to latest definition.
  drop

  laf x10, VariablenPointer # Set pointer to current end-of-ram-dictionary for later permanent RAM allocations by variables defined in Flash.
  sw x15, 0(x10)

  
  #    writeln "Hangelschleife durchgelaufen"
  
  # Mache mich auf die Suche nach dem Dictionarypointer im Flash:
  # Suche jetzt gleich noch den DictionaryPointer.
  # Time to search the Dictionarypointer !

  laf x10, FlashDictionaryEnde
  laf x11, FlashDictionaryAnfang
  li x12, erasedword

  # Gehe Rückwärts, bis ich aus dem $FFFF-Freigebiet in Daten komme.
  # Run backwards through whole Flash memory to find DictionaryPointer.
1:beq x10, x11, 2f #  Wenn ich am Anfang angelangt bin, ist das der DictionaryPointer.
                   #  Finished if beginning of Flash is hit.

  addi x10, x10, -4
    
  lw x13, 0(x10)
  
  beq x13, x12, 1b # Wenn es nicht gleich ist, habe ich eine Füllung gefunden.
                   # If there is not $FFFF on that location I have found "end of free space".

  addi x10, x10, 4

2:# Dictionarypointer gefunden. Found DictionaryPointer.
  laf x11, ZweitDictionaryPointer # We start to compile into RAM - the pointer found goes to the second set of pointers that are swapped with compiletoflash/compiletoram.
  sw x10, 0(x11)

#  writeln "Dictionarypointer gefunden"
  
  .ifdef initflash
  call initflash
  .endif
