Neue C64 ASM/Basic Compo : Dreh' das Sprite.

  • Man das ist ja wie eine Sucht, jetzt bastel ich doch an einer dritten Version. Im Kopf schiebe ich die ganze Zeit Bits und Bytes hin und her, da ich ja weiß dass es eine viel einfachere Lösung als die meine gibt. Na mal schauen ob ich vor Abgabetermin noch fertig werde.

    C64 hat echtes Suchpotential, is ja schlimm.
  • Phasengleich schrieb:

    Man das ist ja wie eine Sucht, jetzt bastel ich doch an einer dritten Version. Im Kopf schiebe ich die ganze Zeit Bits und Bytes hin und her, da ich ja weiß dass es eine viel einfachere Lösung als die meine gibt. Na mal schauen ob ich vor Abgabetermin noch fertig werde.

    C64 hat echtes Suchpotential, is ja schlimm.
    Kenne ich zu gut, ich habe hier irgendwann angefangen bei meinen Texten zu überlegen, ob ich das nicht kürzer hinbekomme.

    Cool, dass es Dich gepackt hat!

    Finde die Compo auch echt klasse. ASM war bei mir dieses mal nix, aber vielleicht nächstes mal.
    carrier lost...
  • Chagizzz schrieb:

    Finde die Compo auch echt klasse. ASM war bei mir dieses mal nix, aber vielleicht nächstes mal.
    Du hast noch >24 Stunden Zeit dafür. Ich wollte auch schon ein paar mal hinwerfen. Auch wenn ich "nur" ne Z80 Version versuche, komme ich nicht los davon. :smoke:
    Mitwäre das nicht passiert! :prof:
    :syshack: Meine 3D-Drucker Teile auf thingiverse.com/oobdoo/designs :strom:
  • oobdoo schrieb:

    Du hast noch >24 Stunden Zeit dafür. Ich wollte auch schon ein paar mal hinwerfen. Auch wenn ich "nur" ne Z80 Version versuche, komme ich nicht los davon.
    Ach, so lange noch... na dann mach ich mich mal dran.

    Und wie weit ist Deine Version? Ich sehe in Deinem Footer ja c64, aber der ist für Dich nur Ausstellungsstück oder wie? :bgdev
    carrier lost...
  • Chagizzz schrieb:


    Und wie weit ist Deine Version? Ich sehe in Deinem Footer ja c64, aber der ist für Dich nur Ausstellungsstück oder wie? :bgdev
    Ich mach bei der 6502 Version nicht mit. Habe schon genug mit Z80 zu tun. :puhh:
    Sollte ich fertig werden, dann steht das im ausgelagerten Thread zur Compo.
    Mitwäre das nicht passiert! :prof:
    :syshack: Meine 3D-Drucker Teile auf thingiverse.com/oobdoo/designs :strom:
  • So, ich mach jetzt auch Schluss. Ich habe noch eine Version probiert in dem ich den Stack als Umsortierungshilfe verwenden wollte, aber habe mich dann doch total verhaspelt. Morgen habe ich leider nicht so viel Zeit. Ich bin sehr auf die anderen Lösungen gespannt.

    Das war auf alle Fälle ein schöner Einstieg in 6502 Assembler für mich.
  • Phasengleich schrieb:

    So, ich mach jetzt auch Schluss. Ich habe noch eine Version probiert in dem ich den Stack als Umsortierungshilfe verwenden wollte, aber habe mich dann doch total verhaspelt. Morgen habe ich leider nicht so viel Zeit. Ich bin sehr auf die anderen Lösungen gespannt.

    Das war auf alle Fälle ein schöner Einstieg in 6502 Assembler für mich.
    Du wirst dich wundern was da so alles geht wenn man nur den richtigen Grundgedanken hat.
    Ich habe nach über 25 Jahren mich wieder mal mit dem C64 und Assembler versucht und bin auch bei meiner ersten Lösung geblieben. Egal ob ich Gewinne, es war geil wieder mal an die Zeit damals zurückzudenken.

    Und wer weiss, ich habe mich VICE + TurboAss besorgt und es hat richtig Spaß gemacht. Mal sehen was die nächste Compo bringt :D
  • So, hier die eingereichten Beiträge der Größe nach ...

    43 /Acorn/68k-coder.prg
    45 /m.j./Compo.prg
    47 /thomasjentsch/thomasjentzsch3.prg
    47 /MacBacon/mb7-64.prg
    47 /Merlin/rota-spr(brk)mln.prg
    49 /yps/drehdassprite_yps.prg
    49 /mafiosino/sprite2.prg
    50 /JeeK/sda-1.prg
    52 /ssda/drehsprite-ssdsa-asm-20170205.prg
    53 /Hoogo/Sprite_drehen.prg
    54 /Gold_Breaver/cssprrot.prg
    56 /TheRealWanderer/SpriteRot8.prg
    57 /td1334/compo44.prg
    58 /kbr/sprite13.prg
    63 /alx/rot-sprite-alx-v4.prg
    63 /lvr/spriterot-lvr.prg
    73 /Haubitze/asm-combo-rolror.prg
    89 /Endurion/rotate.prg
    91 /ssda/drehsprite-ssdsa-basic-20170203.prg
    95 /JeeK/sd-basic-3.prg
    97 /Chagizz/dds-chagizzz.prg
    99 /Tale-X/drehsprite5.prg
    165 /phasengleich/phasengleich2.prg
    366 /ByteBreaker/bbdreh2.prg

    Und hier die Programme dazu, alle zusammengepackt:
    drehsprite.zip

    Damit :

    1. Platz Acorn (tätä !!!) mit 43 Bytes
    2. Platz M.J. mit 45 Bytes
    3. Platz Thomas Jentsch (aka thrust2600), da das Programm im Gegensatz zu den beiden anderen 47er Lösungen mit RTS statt BRK beeendet wird.

    Beste reine Basic Lösung : ssda mit 91 Bytes
  • Herzlichen Glückwunsch an Acorn und vielen Dank an Peiselulli für die Ausrichtung dieser ASM-Compo!

    Hätte ich eine Sprite-Dreh Routine für ein Demo oder Spiel benötigt, hätte ich wahrscheinlich die erste funktionierende Lösung verwendet und keine weitere Zeit in eine Optimierung in Laufzeit oder Code-Größe vorgenommen (es sei denn, es wäre irgendwie nötig gewesen).
    Aber es hat mir viel Spaß gemacht, verschiedene Ansätze auszuprobieren und die Code-Größe immer weiter zu reduzieren.
    Ich freue mich auch schon auf die Lösungen der anderen Teilnehmer - mal schauen, welche Wege Ihr so beschritten habt und was ich daraus lernen kann.

    Hier nun mein Beitrag mit Erläuterung, extra ausführlich und hoffentlich für alle verständlich:

    drehdassprite_yps_49b.asm

    Grundsätzliche Herangehensweise: Sprite 1 wird Zeilenweise von oben links nach unten rechts auf die Spalten von Sprite 2 übertragen (von links unten nach rechts oben).
    Die gelesenen Bits einer Zeile werden dabei als Spalte grundsätzlich vom rechten(!) Rand in das Zielsprite reinge-"shiftet" - und zwar über die gesamte Sprite-Breite, also über 3 Bytes. Wenn dann 24 (Spalten) mal 21 (Zeilen) Bits reingeschoben wurden, liegt die zuerst reingeschobene Spalte dann am linken Rand und auch alle anderen Bits da, wo sie hin sollen.

    Meine Lösung nutzt keine Selbstmodifikation, keine Illegalen Opcodes - und es kann auch (auf Kosten von 1 Byte) auf Zeropageadressen verzichtet werden. Das Programm ist beliebig im Speicher verschiebbar und immer wieder erneut aufrufbar, (ev. müssen A und Y initialisiert sein).

    Sprite 1 wird während des Drehens nicht verändert. Da das Auslesen zeilenweise erfolgt, reicht ein LDA für 8 Bits. Mittels ROL gelangen die Bits dann ins Carry-Flag und von da aus über drei ROLs in die jeweilige Zielzeile von Sprite 2. Nach 8 Bits muss zum nächsten Byte von Sprite 1 gewechselt werden. Um den Zeitpunkt abzupassen, verwende ich keinen zusätzlichen Zähler, sondern nutze den Akku selbst dafür: Vor dem ersten ROL wird das Carry-Flag gesetzt, so dass danach Bit 0 gesetzt ist. Vor allen weiteren ROLs wird das Carry-Flag gelöscht, so dass nach genau 8 Durchgängen A=0, und damit das Zero-Flag gesetzt ist. Dann wird das nächste Byte geholt.

    Damit im Zielsprite nur 21 Zeilen beschrieben werden (und nicht noch wohlmöglich Sprite 1 beschädigt wird), prüfe ich den X-Offset, der von 62 in Dreierschritten runterzählt, auf sein Vorzeichen. Bei X<0 wird für die nächste Spalte X wieder auf 62 gesetzt und das nächste Byte von Sprite 1 gelesen.

    Endbedingung ist erfüllt, wenn nach Sprite 1 noch 3x3 Bytes (drei zusätzliche zeilen a drei Bytes für die drei Leerspalten am rechten Rand) gelesen wurden. Konkret hier also 254+9 das entspricht 7 (Low-Byte von 263).

    Um einen benötigten Pointer ohne zusätzliche Initialisierung in der Zeropage vorliegen zu haben, nutze ich die Adressen $39/$3A, in der die aktuelle BASIC-Zeilennummer steht. Die Zeilennummer wird dann einfach so gewählt, dass in $39/$3A die gewünschte Adresse ($0340-192) zum Lesen von Sprite 1 steht.

    Für den Lesezugriff auf Sprite 1 benutze ich einen Offset von 192 (also $340-192=$280), damit das Sprite für 192<=Y<=254 ausgelesen wird. So wird automatisch das Carry Flag im relevanten Bereich (wo ich es brauche) mit der Endbedingungsprüfung CPY #07 gesetzt und man spart sich ein SEC. Zusätzlich ist beim erreichen der Leerspalten das Vorzeichen-Flag positiv (statt vorher negativ), so spart man sich einen Vergleichsbefehl und kann gleich BPL verwenden.

    Allerdings wird hierbei das ('unsichtbare') Byte nach dem auszulesenden Sprite (quasi Byte 64) für den Anfang der Leerspalten mit ausgelesen und muss deshalb zwingend 0 sein. Das ist es meistens sowieso, aber man kann (speziell hier bei der Compo) nicht davon ausgehen.
    Daher wird bei Programmstart das betreffende Byte ($037F) auf 0 gesetzt.
    Wenn das Byte sowieo garantiert 0 ist, kann man sich diese 3 Bytes sparen und hätte eine 46 Byte Lösung - aber wie gesagt, für die Compo kann ich leider nicht davon ausgehen, dass in $037F garantiert eine 0 steht.

    Das Verschieben des Offsets um eins, um das Lesen von Byte $037F zu umgehen, funktioniert übrigens leider nicht so ohne Weiteres, da sonst bei Y=0 sowohl Carry=1, als auch A=1 gesetzt wird, was in einem einzelnen Stör-Pixel in den Leerspalten resultiert. Aber vielleicht findet ja jemand anderes hierfür noch eine elegante Lösung.

    Je nach Startwert von Y wird vor dem eigentlichen Sprite noch mehr oder weniger Datenmüll durch das Zielsprite geshiftet. Das macht aber ja nichts, solange am Ende das richtige Ergebnis vorliegt.
  • Ich hoffe es lag nicht nur an der Basic-Zeile das es 43 Bytes geworden sind.

    Quellcode

    1. ; Basiczeile 832 sys2058
    2. !BYTE $2b,$08,$40,$03,$9e,$32,$30,$35,$38

    Und hier das Programm.

    Quellcode

    1. Len:
    2. ldx $b2 ; Länge von Sprite 14 (Zeilen*3 plus Spalte) ($b2=$3c)
    3. sec ; Carry für Byteende von Sprite 13 setzen
    4. Byte:
    5. lda ($39),y ; Spritedaten von Sprite 13 ($340)
    6. inc $39 ; Nächstes Byte von Sprite 13
    7. bpl Bits ; Noch Spritedaten da
    8. tya ; Keine Spritedaten mehr
    9. Bits:
    10. rol ; Bit -> Carry von Sprite 13
    11. beq Byte ; Neue Spritedaten holen
    12. rol $0380,x ; Carry -> Bit für Sprite 14
    13. clc ; Carry löschen
    14. dex ; Skip Spalte Sprite 14
    15. dex ; Skip Spalte Sprite 14
    16. dex ; Neue Zeile Sprite 14
    17. bpl Bits ; Bits von Sprite 13
    18. Carry:
    19. rol $c3 ; Bitzähler für Sprite 14 ($c3=$01)
    20. bcc Len ; Noch Bits frei
    21. inc $b2 ; Neue Spalte für Sprite 14
    22. inx ; Anzahl Spalten prüfen
    23. bmi Carry ; Neue Schleife starten
    24. rts ; Fertig
    Alles anzeigen
  • Herzlichen Glückwunsch auch von mir, Acorn! :bia :respect:

    Acorn schrieb:

    Ich hoffe es lag nicht nur an der Basic-Zeile das es 43 Bytes geworden sind.
    Definitiv nicht.
    Hab' mal kurz 'drüber geguckt und das is bei den anderen vorne platzierten Lösungen auch so. (Meine startet sogar mit SYS 2051)

    Interessant auch, daß die vordersten 3 Lösungen recht ähnlich sind (zeilenweise lesen und Spalten schreiben).
    Ich machs genau umgekehrt und kam so nich unter 47 Bytes. (ist aber auch 'ne schöne Lösung, denke ich.) Poste den Quellcode später noch.
  • 43 Bytes? Wow 8o :ilikeit:

    Meine Version hat 56 Bytes. Mehr war nicht drin.

    XML-Quellcode: SpriteRot8.asm

    1. SPRITEORG = $0342
    2. SPRITENEU = $0380
    3. BITSPALTE = $30 ; Standardwert = 8 = Bit 3 gesetzt
    4. *=$0801
    5. !byte $0B,$08,$E1,$07,$9E,$32,$30,$36,$31,$00,$00,$00 ; 12 Bytes + 2 Bytes Header (PRG) = 14 Bytes
    6. ;----------------------Spriteinitalisierung---------------------------------------
    7. ; !source "SPRINIT.asm" ; DEBUG CODE->Spriteinitialisierung 98 Bytes -
    8. ;---------------------------------------------------------------------------------
    9. L: ldy #0 ;Scheife für 21/24 Zeilen (Quellsprite)
    10. L0: lda SPRITEORG:,y
    11. bit BITSPALTE
    12. beq VL1:
    13. sec
    14. VL1: ldx #63 ;Schleife für 63 x ROL (Zielsprite)
    15. txa
    16. L1: rol SPRITENEU:-1,x
    17. dex
    18. bne L1:
    19. iny
    20. iny
    21. iny
    22. cpy #63 ;Letzte (21. = 63/3) Zeile erreicht?
    23. bcc L0: ;Nein, nächste Zeile abarbeiten
    24. cpy #72 ;3 Leerspalten einfügen ;)
    25. bcc VL1:
    26. asl BITSPALTE ;Alle Bits raus?
    27. bcc L: ;Carry nicht gesetzt, dann zum Anfang
    28. rol BITSPALTE ;Carry gesetzt, dann Bit 0 (in Bitspalte) setzen
    29. dcp L0:+1 ;kombiniertes dec und cmp (63)
    30. bne L:
    31. rts
    32. ;---------------------------------------------------------------------------------
    33. ; !source "sprites.asm" ;Sprites für DEBUG Code 64 Bytes -
    34. ;---------------------------------------------------------------------------------
    35. ;56 Byte Version
    Alles anzeigen
    Der Code sollte (hoffentlich) relativ selbsterklärend sein. Einen Illegalen Opcode habe ich verwendet (DCP), mit diesem wird ein CMP gespart.

    Kurzerklärung zum Code:
    Im Prinzip Frage ich Spalteweise die Bits des Ursprungssprites ab und schiebe die Bits dann in das neue (Ziel-)Sprite. Das ganze beginnt in Spalte 21 und geht rückwärts bis Spalte 1, so lasse ich die 3 rechten Spalten (Pixel) von Anfang an raus. Sobald 21 Zeilen abgearbeitet wurden, werden 3 0-Bits ins Zielsprite geschoben. Damit sind später im Zielsprite die rechten 3 Pixel leer (0).

    Herzlichen Glückwunsch an den Gewinner und alle die mitgemacht haben.
    Und vielen Dank an @peiselulli für die tolle Compo. Das Thema hat mich total gefesselt und etliche Stunden meiner Freizeit gekostet :D
    ______________________________________________
    Wenn ich posten will, werde ich Briefträger...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von TheRealWanderer ()

  • Klasse! Ich bin es irgendwie zu kompliziert angegangen, aber es funktioniert :)

    Zum Spaß hier meine Variante im sauberen Code:

    Quellcode

    1. !to "rotate.prg",cbm
    2. TARGET_BIT_COUNT = $39
    3. TARGET_BYTE_VALUE = $f1
    4. COLUMN_BIT = $f3
    5. COLUMN_MASK = $3a
    6. * = $0801
    7. ;pointer, line number, SYS
    8. ;line number is $0808
    9. !byte $0B,$08,$08,$08,$9E,$32,$30,$30 + ( ( START_LOCATION / 10 ) % 10 ),$30 + ( START_LOCATION % 10 )
    10. ;comment out for hacky solution
    11. !byte $00,$00,$00
    12. START_LOCATION
    13. !ifndef RELEASE_ONLY {
    14. jsr CopySprite
    15. lda #0
    16. ldx #0
    17. ldy #0
    18. jsr RotateSprite
    19. lda #100
    20. sta $d000
    21. sta $d001
    22. sta $d003
    23. lda #160
    24. sta $d002
    25. lda #13
    26. sta 2040
    27. lda #14
    28. sta 2041
    29. lda #3
    30. sta $d015
    31. sta $d017
    32. sta $d01d
    33. lda #1
    34. sta $d027
    35. sta $d028
    36. rts
    37. !zone CopySprite
    38. CopySprite
    39. ldx #0
    40. -
    41. lda SPRITE_DATA,x
    42. sta 832,x
    43. inx
    44. cpx #63
    45. bne -
    46. rts
    47. SPRITE_DATA
    48. !media "sprite.spriteproject",SPRITE,0,1
    49. }
    50. ;x = target offset in bytes
    51. !zone RotateSprite
    52. RotateSprite
    53. lda #$80
    54. sta COLUMN_BIT
    55. tay
    56. ;0 = target byte index
    57. stx TARGET_BYTE_VALUE
    58. --
    59. lda 832 - $80 + 2,y ;$80 - start offset, +2, start at right most byte
    60. and COLUMN_MASK
    61. beq +
    62. lda COLUMN_BIT
    63. ora TARGET_BYTE_VALUE
    64. sta TARGET_BYTE_VALUE
    65. +
    66. ;source pointer + 3
    67. iny
    68. iny
    69. iny
    70. ;clc - not needed, never set by addition above
    71. ror COLUMN_BIT
    72. bcc +
    73. ror COLUMN_BIT
    74. +
    75. ;reached 21 target pixel? (offset from start is 20 * 3)
    76. cpy #60 + 3 + $80 - 2 ;$80 start offset, 2 start byte offset
    77. bcs .ColumnComplete
    78. dec TARGET_BIT_COUNT
    79. bne --
    80. beq .ByteComplete
    81. .ColumnComplete
    82. ;back 21 lines
    83. tya
    84. sec
    85. sbc #21 * 3
    86. tay
    87. clc
    88. rol COLUMN_MASK
    89. bcc +
    90. rol COLUMN_MASK
    91. ;masked wrapped over, need to get one byte back
    92. dey
    93. +
    94. lda #$80
    95. sta COLUMN_BIT
    96. .ByteComplete
    97. lda TARGET_BYTE_VALUE
    98. sta 896,x
    99. lda #0
    100. sta TARGET_BYTE_VALUE
    101. lda #8
    102. sta TARGET_BIT_COUNT
    103. inx
    104. cpx #63
    105. bne --
    106. rts
    107. ;COLUMN_MASK = $0802
    108. ;!byte $08
    109. END_OF_FILE
    110. !message "Size is ", END_OF_FILE - 2049 - 137 + 2, " bytes"
    Alles anzeigen
  • Acorn schrieb:

    Ich hoffe es lag nicht nur an der Basic-Zeile das es 43 Bytes geworden sind.
    Öh... Doch. :D Meine Lösung ist nahezu identisch, aber sie enthält zusätzlich drei (46) bzw. zwei (45) Bytes für ein korrektes, definiertes Ende des Basicprogranms:

    Quellcode

    1. sprite_0: .equ $340
    2. sprite_1: .equ $380
    3. .org $7ff
    4. .obj $7ff
    5. prg:
    6. ; BASIC header
    7. .da $801 ; Ladeadresse
    8. .da zeile ; Zeiger auf nächste Zeile
    9. .word sprite_0 ; Zeilennummer enthält Zeiger auf Sprite
    10. .byte $9e ; SYS
    11. .byte 48 + ((starten MOD 10000) DIV 1000)
    12. .byte 48 + ((starten MOD 1000) DIV 100)
    13. .byte 48 + ((starten MOD 100) DIV 10)
    14. .byte 48 + ((starten MOD 10) DIV 1)
    15. .byte $00 ; Zeilenende
    16. zeile:
    17. .byte $00, $00 ; Basicende
    18. ;========================================
    19. starten:
    20. ?0: ldx #63 ; letzte Zeile + 3
    21. sec ; Carry setzen für den ROL-Befehl, damit dieser gleichzeitig als Bitzähler fungiert.
    22. ?1: lda ($39), y ; Originaldaten holen
    23. inc $39 ; Zeiger erhöhen
    24. bpl ?2 ; Zeiger >= $80?
    25. tya ; dann nur 0 laden
    26. ?2: rol ; schiebe Daten nach links und Carry rein (beim ersten Mal 1, dann 0)
    27. beq ?1 ; alle Daten ausgelesen ==> neue Daten holen (Carry = 1!)
    28. ?3: rol sprite_1 - 3, x ; ansonsten Bit in Zieldaten reinrotieren
    29. clc ; Carry löschen für den nächsten ROL-Befehl oben wie auch unten
    30. dex ; Zeilenindex um drei runterzählen
    31. dex
    32. dex
    33. bne ?3 ; Solange Index > 0 ==> Spalte noch nicht zuende
    34. ?4: rol $c3 ; $c3 enthält das Lowbyte der Ladeadresse. Ist daher vor dem Start mit $01 initialisiert.
    35. bcc ?0 ; Das Bit $01 wird 8 mal nach links rotiert => Zähler von 1..8
    36. inc ?3 + 1 ; Zeiger auf nächste Spalte setzen
    37. bpl ?4 ; Zeiger noch nicht >= $80? Dann weitermachen, aber Carry = 1 ==> $c3 := 1
    38. rts
    39. prgende:
    Alles anzeigen
    Zur Strafe darf Acorn jetzt die nächste Compo abhalten. :bgdev
    An dieser Stelle nochmal vielen Dank an peiselulli für das Ausrichten der Compo und die interessante Aufgabe. :thnks: Und an alle, die auf diesem Wege zum C64 oder Assembler gefunden haben: Bitte bleibt dabei. Es macht viel Spaß. ^^