Array als Referenz direkt per Register übergeben

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Array als Referenz direkt per Register übergeben

      Hallo,

      ich möchte ein Byte-Array an eine Sub übergeben, in der aber per Assembler auf das Array zugegriffen werden soll.
      Damit ich in der Sub nicht lange die Adresse des Arrays ermitteln muss, wollte ich die Adresse direkt per X- oder Z-Register übergeben.

      In den Bascom-Libs wird das oft so gemacht, dass die Zeiger schon bei Einsprung in ein Label bereits in den Register stehen.

      In Basic würde man folgendes schreiben, um einen Zeiger zu übergeben:

      BASCOM Source Code

      1. Dim Buffer(128) as Byte
      2. Sub DoSomething(ByRef src() as Byte)
      3. End Sub
      4. Call DoSomething(Buffer())

      Der Zeiger steht aber dann nicht in einem Register, sondern liegt auf dem Frame und muss man erst per Y-Register einlesen.

      Ich dachte jetzt, wenn ich die Sub wie folgt deklariere, dass das Array dann direkt per Register übergeben werden könnte. Der Aufruf der Sub bleibt gleich.

      BASCOM Source Code

      1. Sub DoSomething(ByReg r26 as Word) ' per X-Register
      2. End Sub
      Leider kommt im X-Register nur der Wert aus dem Array an.

      Weil Bascom ja manchmal eigen ist, dachte ich, es könnte vielleicht mit Byref anstatt ByReg klappen. Also so:

      BASCOM Source Code

      1. Sub DoSomething(ByRef r26 as Word) ' Zeiger über Y-Register
      2. End Sub
      3. Call DoSomething(Buffer())
      Da kommt aber leider auch nur Müll an.

      Ich habe dann gedacht, ok, dann berechne ich eben den Pointer und gebe diesen dann per Register rüber:

      BASCOM Source Code

      1. Sub DoSomething(ByReg r26 as Word) per Register X
      2. End Sub
      3. Call DoSomething(VarPtr(Buffer())) ' Zeiger auf Buffer übergeben
      Leider hat diese Methode auch Tücken. Denn wenn ich noch einen Parameter vor dem eigentlichen Zeiger übergeben möchte, zerschießt es mir den 1. Parameter. Also diese Methode fällt dann auch weg.

      Die beste Lösung, die ich jetzt finden konnte und auch zuverlässig funktioniert, war folgende:

      Source Code

      1. Sub DoSomething(ByRef src() as Byte)
      2. !LDI xl,Y+0 ' Zeiger auf Buffer in X laden
      3. !LDI xh,Y+1
      4. End Sub
      5. ' Der Aufruf like Bascom
      6. Call DoSomething(Buffer())
      Über 2 Assembler-Befehle habe ich dann die Adresse im gewünschten Register.

      So, jetzt zurück zur Eingangsfrage.
      Gibt es eine Möglichkeit, die Array-Adresse ohne Umwege (aus Geschwindigkeitsgründen) direkt in einem Register (X oder Z) zu übergeben?

      Also dass in der Sub-Routine dann direkt die Adresse in r27:r26 steht oder wahlweise in einem anderen Register?

      Hat jemand eine Idee?

      Gruß Mitch64
    • ...und die Übergabe der vorher ermittelten Adresse ist wahrscheinlich zu umständlich? BYREF übermittelt die Adresse der Originalvariable, ist das Laden des Z-Registers jetzt wirklich das KO-Kriterium?
      Aus datenschutzrechtlichen Gründen befindet sich die Kontaktdaten auf der Rückseite dieses Beitrages.
    • Hallo Monkey

      Du meinst vermutlich so:

      BASCOM Source Code

      1. Dim Adresse as Word ' Variable um Adresse auf Buffer abzuspeichern
      2. ' Deklaration der Routine
      3. Sub DoSomething(ByReg r26 as Word)
      4. Nop ' bis hier her 17 Takte
      5. End Sub
      6. ' Aufruf der Routine
      7. Adresse = VarPtr(Buffer()) ' Adresse ermitteln
      8. Call DoSomething(Adresse) ' Adresse übergeben


      Ich finde das umständlich und ist außerdem recht langsam.
      Bis ich am NOP in der Routine ankomme, sind bereits 17 Takte vergangen, gerechnet mit Adresse-Ermittung.

      Da ist diese Variante aus dem 1. Post bereits schneller und eleganter.

      BASCOM Source Code

      1. Sub DoSomething(ByRef src() as Byte) ' Zeiger auf src() nach X
      2. ' bis hier 9 Takte
      3. !LDD xl,y+0 ' 2 Takte
      4. !LDD xh,y+1 ' 2 Takte
      5. ' nach 13 Takte ist Zeiger in X verfügbar (incl. Call)
      6. End Sub
      7. ' Aufruf
      8. Call DoSomething(Buffer())
      Einschließlich Call, bis die Adresse im X-Register steht nur 13 Takte.

      Ich wollte hat vermeiden, dass die Adresse erst auf dem Soft-Stack gespeichert wird und ich die dann dort wieder holen muss.

      Daher ja die eingangs gestellte Frage, ob es eine Möglichkeit gibt, ein Zeifer auf ein Array (Adresse) direkt über ein Registerpaar zu übergeben.

      Also Aufruf wie oben, aber in der Routine steht dann ohne zutun bereits die Adresse in gewünschten Register.

      Ziel soll sein, die Routine mit möglichst wenig Overhead aufzurufen, damit sie maximal schnell ist.

      Mal im Vergleich:

      BASCOM Source Code

      1. Sub DoSomething()
      2. ' bis hier 3 Takte
      3. LoadAdr Buffer() , X ' 2 Takte
      4. ' nach 5 Takten ist Zeiger in X verfügbar (incl. Call)
      5. End Sub
      6. ' Aufruf
      7. ' Call DoSomething()
      Nach 3 Takten wäre man in der Routine, zusätzlich 2 Takte für die Initialisierung des Registers mit der Buffer-Adresse.
      Also nach 5 Takten (einschl. Call) bereit.

      Leider kann ich das so nicht machen. Der Buffer muss als Zeiger übergeben werden, denn es können verschiedene Buffer sein.

      Hat noch jemand eine Idee?
    • @stefanhamburg - den Ansatz teile ich (Post #4).

      @Mitch64, in meinem aktuellen Projekt habe ich ein ähnliches Thema: Schnelles Kopieren von DATA-Blöcken in den RAM. Das mache ich auch mit dem Z- und X-Register. Ist sicher noch zu optimieren, aber das Projektziel schaffe ich (aus Sicht der Laufzeit).
      Für die letzten 5-10% ist mir der Aufwand zu hoch. Wenn es aber vielleicht um „passt noch in das Zeitfenster“ geht, dann verstehe ich Deine Suche nur zu gut...
      Aus datenschutzrechtlichen Gründen befindet sich die Kontaktdaten auf der Rückseite dieses Beitrages.
    • Es geht hierbei nicht darum, ob man 4 Takte mehr oder weniger verschmerzen kann.

      Wobei sich 4 Takte bei vielen schnellen Zugriffen extrem aufsummieren können.
      Man denke da an einen Videospeicher, der im Interruptverfahren auszulesen ist.

      Deswegen war die Grundsatzfrage, ob es eine Möglichkeit gibt, die Sub so aufzurufen, dass in der Sub in einem Register bereits die Adresse des Arrays liegt. Ich also nicht takt-raubend noch die Adresse ermitteln oder kopieren muss.

      Bascom legt die Adresse wohl immer im Softstack ab, anstelle diese in ein Register zu übernehmen.

      Aber vielleicht gibts ja einen Trick?

      Rein rechnerisch wäre es möglich innerhalb von 5 Takten (einschließlich Call) in der Sub anzukommen mit Adresse im Register. Und bei fixem Buffer ist es sogar möglich, selbst wenn ich die Adresse selber noch in der Sub ermittle (siehe Post 5).

      Leider gibts keinen fixen BufferName.

      Vielleicht ist unter 13 Takten nichts machbar, was ja auch schon recht gut ist.
      Aber 5 Takte zu 13 Takte ist schon ne Hausnummer.

      Bleibt die Frage, gehts schneller als 13 Takte wie im Post 5?
    • Mitch64 wrote:

      Bleibt die Frage, gehts schneller als 13 Takte wie im Post 5?
      " ...und bleibt es auch bei höchstens 13 Takten?".


      Ich finde solche Frage ja spannend wenn es um Speed geht und habe selbst ein paar Versuche unternommen. Von okay bis krank war alles dabei.


      Die direkte Parameterübergabe in das Zeigerregister X per byReg geht in aller Regel schief, wenn noch mehr Parameter hinzukommen. D.h. aber, der einfachste Fall würde in FÜNF Takten klappen (Vorsicht, nicht nachmachen!)

      BASCOM Source Code

      1. Sub DoSomethingBad(byreg r0 as word )
      2. 'hier dein Code
      3. End Sub
      4. Call DoSomethingBad(list(_base) )
      In X steht wie durch Zauberhand die Adresse vom ersten Element von List() und in R0 landet dessen Inhalt - ByReg ist halt nicht ByRev.


      Aus eben dargesteltem schlechten Code kann man jedoch zwei Dinge ableiten:
      1. Das Empfangsregisterpaar darf nicht X sein, weil es bei der Parameterübergabe eine Rolle spielt. Von Y ganz zu schweigen.
      2. Wenn die Ermittlung der Adresse zur Laufzeit zur Last fällt, kann man diese zum nachträglichem Boost vom Compiler erzeugen lassen.


      Mein Vorschlag:

      BASCOM Source Code

      1. dim List(15) as byte
      2. const List_ptr = varptr("List(_base)")
      3. sub doSomethingNew (byreg r16 as word )
      4. !Movw Xl,R16
      5. 'hier könnte dein Code stehen
      6. end sub
      7. call doSomethingNew(list_ptr )
      8. call doSomethingNew(list_ptr +1)
      9. dim n as byte
      10. n = 2
      11. call DoSomethingNew(list_ptr +n)
      12. end
      Display All

      Du musst also eine Pointerkonstante bilden, geht sonst nicht anders, wenn du mehr Tempo möchtest.

      Außerdem wirst Du festellen, daß meine Methode beim dritten Aufruf ebenfalls 13 Takte benötigt, wie in Post#5, jedoch hier bereits schon ein variabler Index mitgeführt wird. Also eigentlich garnicht mal so schlecht. Die vorhergehenden beiden Calls liegen bei 6 Takte, die funktional mit Post#5 vergleichbar sind.


      Viel Erfolg!
    • Hallo @Galahat

      Vielen Dank für deine Ausführungen.
      Insgeheim hatte ich schon an dich gedacht, dass du etwas zu dem Thema beitragen kannst (Rainbow-Lib).

      Nun. Mir war klar, dass ich das Y-Register für die Übergabe nicht verwenden kann. Es wird ja von Bascom als Base-Pointer benutzt.
      Bleibt also nur X und Z sowie die anderen Register übrig. Das reicht aber auch.

      Ich habe das mal ausprobiert mit den Pointern, diese in einer Konstante zu definieren.
      Das funktioniert prima! Auch mit 2 Übergabeparametern.

      BASCOM Source Code

      1. Dim Dummy as Byte
      2. Dim Dest(25) as Byte
      3. Dim Buffer(256) as Byte
      4. ' Pointer auf Arrays definieren
      5. Const ptrBuffer = VarPtr( "Buffer(_base)")
      6. Const ptrDest = VarPtr( "Dest(_base)")
      7. ' Übergabe von 2 Pointern in X und Z
      8. Sub DoSomething(ByReg r26 as Word , ByReg r30 as Word)
      9. Nop ' bis hier her 7 Takte
      10. End Sub
      11. ' Aufruf
      12. Call DoSomething(ptrBuffer , ptrDest)
      Display All
      Ich denke - nein ich bin überzeugt - schneller geht's nicht mehr!
      Bei Übergabe von einem Pointer in 5 Takte und mit 2 Pointer in 7 Takte.
      Das ist doch jetzt mal ne Hausnummer!

      Das ist die Lösung! Danke für den Tip @Galahat!

      Die andere Variante mit R0 überzeugt mich nicht wirklich und verwirrt auch. Ich hab's jetzt nicht ausprobiert, aber das wird nur mit einem Parameter funktionieren.

      Für mich sind die Pointer als Konstante die Wahl.

      Aber noch ne Frage.

      Woher weißt du, dass man den Arrayname in Anführungszeichen setzen muss? In der Bascomhilfe ist darüber was angegeben, aber verstehen tu ich es nicht.

      Also diese schreibweise: Const ptrBuffer = VarPtr( "Buffer(_base)")

      Ich habe das jetzt nur etwas fetter gemacht, dass man es besser erkennt!

      Gruß Mitch64

      The post was edited 1 time, last by Mitch64 ().

    • Galahat wrote:

      Hierbei ist darauf zu achten, dass bei der Zuweisung an eine Konstante der Variablenname in Anführungszeichen steht, wohingegen bei der Variablenzuweisung nur der Variablenname in den Klammern steht.
      Die so gewonnenen Adressen könnten als Parameter in Funktionsaufrufen dienen oder in einer Tabelle angelegt werden.
      das findet man hier im Lexikon
      Das beantwortet zwar nicht die Frage nach dem 'woher', wie ich @Galahat einschätze, er nimmt ab und zu einen Löffel aus einem Gläschen, das mit 'Wissen instant' beschriftet ist.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------