Hallo Besucher, der Thread wurde 12k mal aufgerufen und enthält 35 Antworten

letzter Beitrag von spacer am

Code für bitweises Verschieben von Character a -> b optimieren

  • Moin,


    ich schreibe gerade ein Spiel für den VC20. Da es hier keine Sprites gibt, werden alle Spielergrafiken als Characterzeichen dargestellt und bei Bewegung bitweise auf ihr Ziel verschoben.


    Jede Figur hat ein fest zugewiesenes Characterzeichen (hier A) und für die Bewegungsanimation jeweils ein festes Zeichen, dass für das Verschieben benutzt wird (hier B).

    Bewegt sich die Figur nicht, wird sie immer nur mit dem Zeichen A abgebildet.

    B ist immer %00000000 und dient nur als temporäres Zeichen um die bittweise Verschiebung abzubilden.

    Jede Figur bindet also immer ein Characterpärchen (AB, CD, EF,...).


    Der Bewegungsablauf findet über diese 5 Schritte statt:

    • B wird auf die Zielposition geschrieben
    • Die Bewegung wird durchgeführt (siehe Code)
    • Auf dem BS wird B durch A ersetzt
    • Die ursprünglichen Bits werden von B auf A zurückkopiert (unsichtbar)
    • Alle Bits auf B werden gelöscht (unsichtbar).

    Das ist nötig weil der Character, wenn er sich nicht bewegt, immer das Zeichen A abgebildet wird.


    Hier mein Codeschnipsel, mit dem ich die Bewegungen Hoch, Runter, Rechts, Links abbilde.


    Es funktioniert so, dass ich das aktuelle Characterzeichen über die Zeropage (zp) auf ein Zielzeichen (zp2) bitreise verschiebe.

    Also, Zeichen A wandert Bit für Bit auf Zeichen B.


    Nicht wundern, die Funktion verschiebt das Zeichen immer nur um einen Pixel je Richtung, wird aber 8 mal durchlaufen.


    Der Code funktioniert, fühlt sich aber umständlich an.

    Aktuell benötige ich hierfür 145 Bytes.

    Wie lässt sich diese Funktion optimieren und verkürzen?


    Hoffentlich konnte ich mein "Konzept" hier verständlich beschreiben.


    aitsch

  • androSID

    Hat den Titel des Themas von „Code für bitweises Verschieben von Caracter a -> b optimieren“ zu „Code für bitweises Verschieben von Character a -> b optimieren“ geändert.
  • Hallo, guten Tag.


    Ist ja interessant was du da so programmierst.


    Ich Spiele mit dem Vice-VIc20 und dem Vforth und dem MOSpeed.

    Als den VIC20 habe ich den MiSter mit dem Vic20-Core.


    Ich fand für den VIC20 keine so richtige Programmiersprache. Das Basic ist in der Grafik nicht so schnell , weil die Darstellung ja nicht in drei Schritten gemacht werden kann beim Vic20. Ist da schon komplizierter. Das MOSpeed stellt zb aus dem normalen Basic des Vic20 eine ASM her. Ist schon angenehm schnell.

    Und das VForth hat mich mal interessiert (die Grafik im Bild ist vom Vforth) , ich wollte mal Grafik mit Text mischen und ist mir da nicht schwer gefallen.

    Ich kannte Forth von vor über 30 Jahren.


    Reines ASM wie bei dir ist nicht so mein Ding.


    Ich bin gerade dabei über die RS232 mit dem Vice und dem Vic20 Daten nach Draussen zu schicken und von Draussen wieder zu dem Vic20 mit dem Netzwerk. Klappt wunderbar. Das Vforth macht das auch mit , war sehr erstaunt.


    Mich würde mal interessieren wie dein ASM-Code aussieht.


    Mein Programm mit Vforth :




    Danke.

    GRuss

  • Jede Figur bindet also immer ein Characterpärchen (AB, CD, EF,...).

    Sprites hatte ich auch mal von Hand programmiert, alleine um

    mal zu sehen, was der VIC-II so alles mit wenigen

    Registerzugriffen leistet. Von daher habe ich eine Vorstellung,

    wie das in Assembler programmiert werden kann. Was ich bei

    deinem Ansatz (AB,CD,etc. und Bewegung in alle Richtungen)

    nicht verstehe, wie du mit nur 2 Zeichen je 8x8-Sprite

    auskommst. Ich würde 4 Zeichen AB-CD nehmen und darin

    das Sprite ablegen:

    - (1) In 16 Bytes (z.B.ZeroPage) wird zuerst das Sprite in die

    gewünschte Richtung geshiftet (x-Position Modulo x08)

    - (2) in die 4 Zeichen AB-CD werden die 2x2-Zeichen

    (X00,X10,X01,X11) reinkopiert (X00,.. sind die 4 Zeichen, die

    von dem Sprite abgedeckt werden)

    - (3) in die 4 Zeichen AB-CD werden dann die geshifteten

    Spritebytes reingeodert

    - (4) die 4 Zeichen AB-CD überschreiben die 2x2-Zeichen Xij


    Der Ansatz hat den Vorteil, dass auch Sprite-Background

    Kollisitonen als auch Sprite-Sprite-Kollisionen ergänzt werden

    können. Aufwand:

    - (1) 16+x Ops (x: Overhead für ZP-Init, vlt. 4-8 Ops)

    - (2) 32+x Ops (x: evtl. für Schleife, ZP-Init, vlt. 10 Ops)

    - (3) 16+x Ops (x: Schleife, ZP-Init, vlt. 5-10 Ops)

    - (4) 4+x Ops (x: Position von Xij bestimmen, ZP-basiert,vlt 10 Ops)

    ergibt ca. 60+y ~= 60+40 <= 100 Operations (ohne Collision-Detection).


    Falls mehrere Sprites gewünscht sind, dann würde ich je

    angezeigtem Sprite 4 Zeichen reservieren: Dein Background

    hat z.B. 240 Zeichen, bleiben 16=4*4 Zeichen für insgesamt

    4 Sprites. Die Sprites werden nacheinander gezeichnet,

    wobei für Sprite2 Xij dann auch AB-CD von Sprite1 umfassen

    kann.


    Im Extremfall muss für ein Sprite sieben mal geshiftet werden,

    hier würde ich ein Sprite in 2 Ausprägungen abspeichern:

    einmal ungeshiftet, einmal um 4 Bits geshiftet (in x-Richtung).

    Damit ist die max Anzahl Shiftoperationen je Byte auf 3 Shifts

    begrenzt.

  • Für "down" und "up" würde ich vorschlagen den Zwischenspeicher in der Schleife mit Register X zu machen, statt am Stack und das jeweilige Rand-Byte auf den Stack zu legen. PHA/PLA braucht 7 Zyklen, während TXA/TAX nur 4 braucht. Da spart man schon mal 3x6 Takte.

    Warum wird eigentlich (zp),y für y=0 von y=7 übernomen, bei zp2 aber nicht?

  • Ja. Das macht Sinn.

  • Der Ansatz als selbstmodifizierender Code sollte aber auch den scroll_l-Teil berücksichtigen, also


    lda #$18

    sta adr1+2

    sta adr2+2

    ldy character_no

    lda lowbyte_tmp,y

    sta adr2+1

    sta adr4+1

    lda lowbyte_char,y

    sta adr1+1

    sta adr3+1

    ldx #7

    ...

    .scroll_l

    adr3 lsl $ffff,x

    adr4 rol $ffff,x


    Für down/up wären dann, wenn man das auch so macht auch noch weitere Adressen nötig, die zu manipulieren wären.

    Das könnte man aber auch bereits selektiv abhängig von "dir" machen, damit für eine Bewegung nicht auf Verdacht alle 4 Code-Teile modifiziert werden.


    ldy #7

    ldx #6

    adr5_c lda $ffff,y

    pha

    .scroll_d

    adr6_c lda $ffff,x

    adr7_c sta $ffff,y

    adr8_t lda $ffff,x

    adr9_t sta $ffff,y

    dey

    dex

    bpl .scoll_d

    tya ; 0

    adr10_t sta $ffff,y

    pla

    adr11_c sta $ffff,y


    Da muss man halt an recht vielen Stellen patchen ... da wäre wie oben schon erwähnt ein selektives Vorgehen abhängig von der Richtung schon ganz praktisch.

  • Warum macht ihr euch das Leben schwer.

    Ich habe oben meine Demo vom Vforth als Grundlage gezeigt. Mindestens 8KB erweiterung beim Vice einstellen.


    Ihr geht in den Grafikmodus vom Vic20. Die Startadresse dort ist dann oben links 4352.

    Jedes Zeichen im Grafikmodus hat eine Höhe von 16 Byte dann geht es weiter nach rechts.


    Ich habe oben die Buchstaben ab Adresse 4352 gesetzt , nach 8 Byte habe ich 8 dazu gezählt damit der 2 Buchstabe daneben ist ansonsten wären es 16 Byte untereinander. Der Farbram beginnt bei 37888 und geht untereinander über 16 Byte.


    Jetzt braucht ihr nur noch mit ROL, ROR, ASL und LSR spielen.


    Ist doch einfacher als sich wie oben mit dem Characterzeichen rumzuquälen.

    Außerdem könnt ihr noch Grafik reinbringen für den Augenschmaus.


    Dieses Bild zeigt , wenn ich 16 Byte hintereinander setze , AB CD.


    Gruss


  • Das ist genial. Eigentlich total einfach zu verstehen aber ich bin von selbst nicht drauf gekommen.

    Ich dachte, das Potential zum Optimieren liegt in der Auf- und Abwärtsbewegung.

    Mit rechts/links war ich schon recht zufrieden.


    Vielen Dank!!!

    Danke für die umfangreiche Beschreibung. Allerdings bilde ich keine Sprites nach (2x2 Characters) sondern eine Figur besteht nur aus 1 Character.

    Aber zum Schluss soll es einen Boss geben. Der besteht aus 2x2 Characters.

    Dann muss ich nochmal neu nachdenken aber ich will (noch) nicht an meinem Grundkonzept rütteln.

    Ich müsste dann fast den ganzen Code ändern.


    Trotzdem Vielen Dank

    Für "down" und "up" würde ich vorschlagen den Zwischenspeicher in der Schleife mit Register X zu machen, statt am Stack und das jeweilige Rand-Byte auf den Stack zu legen. PHA/PLA braucht 7 Zyklen, während TXA/TAX nur 4 braucht. Da spart man schon mal 3x6 Takte.

    Warum wird eigentlich (zp),y für y=0 von y=7 übernomen, bei zp2 aber nicht?

    PHA/PLA war gar nicht notwendig und sind schon aus dem Code entfernt worden. Aber Vielen Dank für Deine Antwort.





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

    Nun zum aktuellen Stand der Dinge

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


    Die aktuelle Codeversion sieht jetzt so aus:


    Ich bin jetzt runter auf 116 Bytes.


    Das ist erstmal nur eine Übergangsversion, weil ich bei hoch/runter noch keine Änderungen vorgenommen habe und die Ursprungslösung (h/r)und die neue Lösung (r/l) noch nicht aufeinander abgestimmt sind. Habe bis zum WE nicht die Zeit dafür.


    Aber die rechts/links-Bewegung laufen jetzt in einer einzelnen Routine ab und ist mir nur noch 34 Bytes wahrscheinlich unschlagbar kompakt.


    Hat noch jemand eine gute Idee für hoch/runter ?????

    ( JeeK: Deinen Vorschlag muss ich am WE noch etwas genauer studieren)


    Danke an alle Beteiligten


    aitsch

  • Da die Zeichen im Speicher hintereinander liegen, kann man hoch/runter auch mit Selfmod-Code kürzen.


  • Hat noch jemand eine gute Idee für hoch/runter ?????

    ( JeeK : Deinen Vorschlag muss ich am WE noch etwas genauer studieren)

    Der selbstmodifizierende Code ist eigentlich auch nicht wirklich meines. Ich hab's versucht in Anlehnung an die Rechts/Links-Bewegung.

    Ich selbst würde es eher so machen. Für die Bewegung rauf runter sind die beiden Zeichen übereinander angeordnet, oder?

    D.h. du hast die eine Schleife über 7 Schritte gemacht, die in beiden Zeichen gleichzeitig nach oben (bzw. unten) kopiert.

    Nur das eine Byte das von einem Block in den nächsten wandert muss gesondert behandelt werden. Ganz unten (oben) kommt eine 0 rein.

    (das erklärt auch meine Frage zuvor)

    Das PHA/PLA braucht man dann nur, um eben den "Übertrag" von einem Block zum nächsten zu machen.

    Der Ansatz ist der, dass man nicht mit 2 ZP-Pointer, sondern mit 4 arbeitet, wobei 2 schon entsprechend versetzt sind.

    Da muss man dann nicht mehr mit den Indizes herum tun.

    Schaut auch eleganter aus, ist fast so schnell wie der selbstmodifizierende Code, wobei beim letzteren mehr Initialisierungsarbeit nötig ist (das Patchen an 7 Stellen, wenn man von meinem Vorschlag ausgeht).

  • Danke für die umfangreiche Beschreibung. Allerdings bilde ich keine Sprites nach (2x2 Characters) sondern eine Figur besteht nur aus 1 Character.

    Nein, meine Figur hat auch nur 8x8 Pixel. Für das Verschieben

    nach Links/Rechts brauchst du nunmal 16x8 Pixel, d.h. wie

    bei deinem Ansatz ganz Oben 2 Zeichen. Und da du bei deinem

    ersten Programm auch noch Hoch/Runter erwähnt hast, habe

    ich daraus 16x16 Pixel, d.h. 2x2 Zeichen für beliebige Richtungen.

    (nur für Hoch/Runter brauchst du natürlich auch nur 2 Zeichen)


    Und mit


    Hat noch jemand eine gute Idee für hoch/runter ?????

    kommst du in etwa auf meinen Ansatz. (ist dein Endgegner

    dann z.B. 16x16 Pixel, dann hast du 3x3 Zeichen etc.)

    Ob du wie ich Oben beschrieben dann noch den Hintergrund

    (der Zeichen z.B.) noch reinkopierst musst du entscheiden,

    ich kenne dein Anwendungsszenario nicht.