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

  • Da im Ergebnis die Assembler-Sources nicht mit dabei waren, hiermit nachgereicht. Ich würde alles Sources gerne gesammelt haben. Auch wenn's recht mühsam ist, das jetzt allle ihren Sourcecode einzeln hier posten müssen und man das so einsammeln muss ...
    Bei der Vielzahl der 4x-Lösungen hat meine 50er-Lösung zwar nur Rang 8 erreicht. Hier der Vollständigkeit halber auch der Sourcecode:

    Quellcode

    1. ; ### BASIC-Intro
    2. *= $0801
    3. start = entry
    4. basic_start
    5. ; ZN SYSnnnn
    6. !by <EOP,>EOP,<(src),>(src),$9E
    7. ; Zeilennummer entspricht Wert von scr
    8. !by '0' + start % 10000 DIV 1000
    9. !by '0' + start % 1000 DIV 100
    10. !by '0' + start % 100 DIV 10
    11. !by '0' + start % 10
    12. !by 0 ; End of Line
    13. EOP !by 0, 0 ; Basic-Programmende
    14. ; ### HAUPTPROGRAMM
    15. ; Quell-Sprite-Index Y startet bei 0
    16. newbytecolumn
    17. inc columnstart ; Low-Byte in "ror dst,x"!
    18. ; C=1 implizit, als Markierung in "Zähler" v:
    19. ; Byte im Ziel-Sprite 8 mal rotieren, v ist Zähler,
    20. ; Inhalt aber egal, Start-Bit ist gesetztes Bit 7,
    21. ; das nach 8 mal.
    22. pixelcolumn
    23. ; C=0 implizit
    24. ror v ; Höchstwertiges Bit muss 8 mal geschoben werden.
    25. ; -> C=1, das Markierungs-Bit.
    26. beq newbytecolumn ; Nach 8 mal ist es leer, d.h. Byte-Spalte ist fertig.
    27. entry
    28. ldx #60+3 ; Vertikale Startposition im Ziel-Sprite,
    29. ; +3 damit auf 0 endend.
    30. byte
    31. iny ; Quell-Sprite-Index: horizontal zum nächsten Byte.
    32. ; Spalte 22-24 des Ziel-Sprites wird abgeleitet
    33. ; aus Zeilenposition des Quell-Sprites (Zeile 22-24).
    34. cpy #sprlen+1 ; 1 - 63 tatsächliche Quell-Sprite-Daten
    35. ; < sprlen+1 ?
    36. bcs + ; Nein: A immer 0, als Quelle für Pseudozeilen 22-24.
    37. lda (zs),y ; Ja: Tatsächliches Byte aus Quell-Sprite (Zeile 1-21).
    38. sec ; Höchstwertiges Bit zuerst, Bit-Zähler (8x).
    39. + !by $24 ; Folgendes CLC ignorieren (BIT zp).
    40. getpixel
    41. clc ; 0-Bit nachschieben bis das Byte 0 ist.
    42. rol ; Quell-Sprite-Byte (horizontal) nächstes Bit.
    43. beq byte ; Alle 8 Bits durch?
    44. setpixel
    45. ; Position für Code-Modifikation,
    46. ; Low-Byte von dst wird inkrementiert.
    47. dst_1 rol dst,x ; Pixel aus Carry vertikal, erst nach 8 Durchläufen an
    48. ; richtiger Position.
    49. dex ; Ziel-Sprite vertikal eine Zeile nach oben bis Spaltenende.
    50. dex
    51. dex
    52. bne getpixel ; Spaltenende (ganz oben) erreicht?
    53. ; X endet immer auf 0, brauchen wir noch für A, da
    54. ; bei A Bit 7-6 unbestimmt, Bit 5 (Markierung)gesetzt ist,
    55. txa ; also damit sicherstellen, dass A wirklich immer 0 ist.
    56. nextpixelcolumn
    57. cpy #sprlen+9 ; Zeiger auf Quelle-Sprite<Ende eines fiktiven 24x24-Sprites
    58. bcc pixelcolumn ; wenn kleiner, dann gilt C=0, A=0.
    59. exit
    60. rts
    Alles anzeigen
    Neben der obligatorischen $39-Einsparung, ist der Spaltenzähler wie bei anderen auch als 8-Shift-Zähler ausgebildet. Dieser bekommt seine Grundinitialisierung von Exponent der letzten Floatingpoint-Zahl (Auswertung des Sys-Ausdrucks), welcher als positiver Wert immer >= $80 ist. Das gesetzte Bit 7 brauche ich als initialen Zustand (Rest egal).
    Für den Optimierungschritt in Zeile 38. bis 41. war der Wald vor lauter Bäumen nicht mehr zu sehen. :S
    Also statt
    sec
    !by $24
    loop
    clc
    rol ...
    beq byte
    dann
    sec
    loop
    rol ...
    clc
    beq byte
    scheint jetzt so offensichtlich.
    Acorns tolle Lösung schaut für mich jetzt so klar aus, als hätte nie eine andere geben können. :D
    Wie dort noch $B2 für X zu nehmen und Y in Ruhe zu lassen, damit damit A löschen kann (ich hab da X verwendet und damit die X-Ansätze von Acorn verbaut), ist das, was ja bereits im Thread erwähnt wurde: Wenn man stecken bleibt, noch mal von Null weg das Programm neu schreiben, statt das bestehende versuchen zu optimieren. ;)

    Also wirklich tollle Erkenntnisse, die die Compo hier geliefert hat. Danke an peiselulli als Ausrichter und an alle, die hier mitgemacht haben! :thanx:
  • Bin begeistert über die Kreativität :) .. vor allem die Basicversionen hauen mich um ;)
    Ich muss mir die einzelnen Beiträge demnächst mal in Ruhe anschauen.
    Der Vollständigkeit halber hier noch mein Quellcode. Ich hatte ein paar Probleme herauszufinden wie ich das mit Turbo Ass löse ...

    Quellcode

    1. ;*** STARTADRESSE BASIC-ZEILE
    2. *= $0801
    3. .BYTE $0A,$08,$36,$05,$9E
    4. .BYTE $32,$30,$35,$39,0
    5. ;*** BEGINN DES HAUPTPROGRAMMS
    6. LDA #0
    7. JSR RUNFIRST
    8. LDA #$80
    9. RUNFIRST
    10. LDX #60
    11. LX
    12. LDY #60
    13. LY
    14. PHA
    15. LAND
    16. AND $0340,Y
    17. CMP #1
    18. PLA
    19. ROR $0380,X
    20. ROR $0381,X
    21. ROR $0382,X
    22. DEY
    23. DEY
    24. DEY
    25. BPL LY
    26. LSR A
    27. BCC NC2
    28. ROR A
    29. INC LAND+1
    30. NC2
    31. DEX
    32. DEX
    33. DEX
    34. BPL LX
    35. RTS
    Alles anzeigen
  • Zur Basicnachbetrachtung kann ich nur sagen: Top, ein perfekter Abschluss für eine schöne Compo! :thumbsup:

    Ich werd' mir von Zeit zu Zeit nochmal die Lösungen anschauen und sacken lassen. Sind viele kleine Kniffe drin.
    Wissen ist das einzige Gut, das sich beim Teilen vermehrt. Also seid vorsichtig damit!
  • Ich würde alles Sources gerne gesammelt haben. Auch wenn's recht mühsam ist, das jetzt allle ihren Sourcecode einzeln hier posten müssen und man das so einsammeln muss ...
    Das habe ich von vorne rein abgelehnt. Es war so schon schwierig genug, die ganzen Einsendungen zu sammeln, zu testen und gegebenfalls alte Versioen rauszuschmeissen. Alle haben mir ihre Sourcen auch nicht gesendet. Eventuell war da ja auch der eine oder andere Monitor-Coder dabei ;)

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

  • Gratulation an Acorn, wann gibt es die nächste Compo? :D
    Vielen Dank auch an alle anderen Teilnehmer und besonders an peiselulli fürs Ausrichten und das interessante Thema.
    Hier mein Source:

    Quellcode

    1. ;ACME 0.96.1
    2. !to "mb7-64.prg", cbm
    3. !addr {
    4. source = 13 * 64
    5. target = 14 * 64
    6. basic = $0801
    7. }
    8. offset = 65
    9. ;read 63 bytes: offsets 0..62
    10. ;63 is illegal y, so must be mapped to 128 to be able to use N flag => +=65
    11. ; generate BASIC header
    12. * = basic
    13. !word line2, source - offset ; current line number is copied to $39/$3a
    14. !addr ptr = $39;/$3a
    15. !byte $9e
    16. !byte '0' + entry % 10000 / 1000
    17. !byte '0' + entry % 1000 / 100
    18. !byte '0' + entry % 100 / 10
    19. !byte '0' + entry % 10
    20. entry ldy #offset - 1 ; read offset
    21. .do_column ldx #62 + 1 ; write offset for a new bit column (bottom-up)
    22. .get_byte ; get a new byte
    23. iny ; increment read pointer
    24. bmi +
    25. lda (ptr), y ; get new source byte (or skip for three rightmost columns)
    26. + sec ; init for "marker C"
    27. .get_bit ;C is set if coming from above, C is clear if coming from below.
    28. rol ; get source bit (and put C in it: once set, then clear)
    29. beq .get_byte ; byte exhausted? (if so, C will be set)
    30. ; put bit
    31. rol target - 1, x ; put pixel
    32. dex ; go to center byte
    33. rol target - 1, x ; shift line
    34. dex ; go to left byte
    35. rol target - 1, x ; shift line
    36. dex ; go up in target lines
    37. clc ; init for "no marker"
    38. bne .get_bit ; if not at top of sprite yet, go on
    39. ; we did 21 pixels, we're done with a column
    40. txa ; clear A for next column (only actually needed for dummy columns)
    41. cpy #offset - 1 + 24 * 3 ; done with lines? (this includes dummy reads, so 24 instead of 21)
    42. bne .do_column
    43. ;rts
    44. !byte $00 ; end of line
    45. line2
    46. !byte $00, $00 ; end of basic
    47. !warn "Code is ", * - entry, " bytes (plus 12 for basic header)"
    Alles anzeigen
    Ich hatte auch kurz überlegt, die drei Nullen wegzulassen, aber auf Nullen im Speicher zu hoffen war mir dann doch zu dreckig. Ich hatte ja schon ein schlechtes Gewissen, das RTS einzusparen...
    Yes, I'm the guy responsible for the ACME cross assembler
  • Hier mal eine Zwischenversion mit den Sourcen zusammengetragen und in die Ordner eingefügt.

    Wenn ich nichts übersehen habe, fehlen noch die Sourcen von 5 Teilnehmern:

    ASM/BAS Programm Name
    ------------------------------------------
    ASM alx_rot-sprite-alx-v4.prg
    ASM Gold_Breaver_cssprrot.prg
    ASM Haubitze_asm-combo-rolror.prg
    ASM mafiosino_sprite2.prg
    ASM ssda_drehsprite-ssdsa-asm-20170205.prg

    DrehSprite_Solutions+Src.zip
    _________________________¨--O/\__~_________________________
    Meine Kreationen: Deviant Art | Toonsup | Flickr | Youtube
    | Twitter
  • Mac Bacon schrieb:

    Ich hatte auch kurz überlegt, die drei Nullen wegzulassen, aber auf Nullen im Speicher zu hoffen war mir dann doch zu dreckig. Ich hatte ja schon ein schlechtes Gewissen, das RTS einzusparen...
    Zwei Nullen reichen, wenn es sich dabei um das erste und dritte Nullbyte im Endmarker handelt

    Quellcode

    1. 10 x=rnd(-1963):fori=1to81:y=rnd(1):next
    2. 20 forj=1to5:printchr$(rnd(1)*16+70);:next
    3. 30 printint(rnd(1)*328)-217

    sd2iec Homepage
  • Ich habe ja nun den letzten Platz belegt.
    Zum Lernen für all die, die nicht durch die Zulassung gekommen sind, hat das aber vielleicht doch einen Vorteil, weil meine Lösung nichts tut außer auf dem simpelsten Weg zu funktionieren. Da ist nichts optimiert/gekürzt/getrickst, was einem Anfänger evtl. das Verständnis erschwert.

    Daher poste ich hier die erste Version die bei mir funktioniert hat noch bevor ich angefangen habe mit Doppelpunkten und Leerzeichen killen etc.

    Quellcode

    1. 399 dimz(9):c=1:fori=886to894:z(c)=peek(i):c=c+1:next
    2. 400 n=956:nb=956:nbb=956:m=832:mb=832:x=128:y=128
    3. 401 for i=896 to 896+63:poke i,0:next
    4. 450 for o8=1 to 3
    5. 490 for o9=1 to 8
    6. 501 for u=1 to 3
    7. 510 for i=1 to 8
    8. 520 k= peek(m) and x
    9. 521 if k=x then z=y:goto 540
    10. 522 z=0
    11. 540 poke n,peek(n) or z
    12. 550 m=m+3:y=y/2
    13. 560 next i
    14. 561 y=128:nb=nb+1:n=nb
    15. 562 next u
    16. 665 nb=nb-6:n=nb:m=mb:x=x/2:y=128
    17. 667 next o9
    18. 670 mb=mb+1:m=mb:x=128:y=128:nb=nbb:n=nb
    19. 680 next o8
    20. 699 d0=898
    21. 700 for nc=0 to 20
    22. 720 poke d0,peek(d0) and 248
    23. 730 d0=d0+3
    24. 740 next nc
    25. 745 c=1:fori=886to894:pokei,z(c):c=c+1:next
    Alles anzeigen
  • Neu

    syshack schrieb:

    Hier mal eine Zwischenversion mit den Sourcen zusammengetragen und in die Ordner eingefügt.

    Wenn ich nichts übersehen habe, fehlen noch die Sourcen von 5 Teilnehmern:

    ASM/BAS Programm Name
    ------------------------------------------
    ASM alx_rot-sprite-alx-v4.prg
    ASM Gold_Breaver_cssprrot.prg
    ASM Haubitze_asm-combo-rolror.prg
    ASM mafiosino_sprite2.prg
    ASM ssda_drehsprite-ssdsa-asm-20170205.prg
    Irgendwie irgendwo irgendwann scheint ein Buchstabe meines Usernamens "ssdsa" abhanden gekommen zu sein, hm. Die Buchstaben sind die Initialen meines Namens.
    Hier ist jedenfalls mein ASM-Source (zu "ssda_drehsprite-ssdsa-asm-20170205.prg"):

    Quellcode

    1. DEFINE file_start $0801
    2. PC file_start-2
    3. .wo file_start ; generate lo- and hi-byte of start address as first 2 bytes of file
    4. .wo basic_next_line
    5. ; Die Zeilennummer ist frei wählbar! Dies nutzen wir aus, denn die Zeropage ZP $39/$3A enthält die momentan ausgeführte BASIC-Zeilennummer.
    6. .wo sprite_src-65 ; landet in ZP $39 und ZP $3A
    7. .by $9E ; "SYS"
    8. .ascnum4 basic_sys_start
    9. .by 0
    10. basic_next_line:
    11. basic_sys_start:
    12. ldx $BB00 ; $BB00 enthält den Wert #$BC. Wichtig ist,dass das Low-Byte der Adresse null ist, damit das Basic-Programm dadurch terminiert wird.
    13. DEFINE zp_spr_read $39
    14. DEFINE sprite_src $0340
    15. DEFINE sprite_dest $0380
    16. DEFINE count8_zp2B $2B
    17. ; Wir nutzen aus, dass $2B initial den Wert #$01 enthält.
    18. DEFINE count8_zp37 $37
    19. ; Wir nutzen aus, dass $2B initial den Wert #$00 enthält.
    20. DEFINE count8_zpC3 $C3
    21. ; Wir nutzen aus, dass $C3 initial den Wert #$01 enthält.
    22. ; Erinnerung: ZP $B2/$B3 zeigt auf $033C, d.h. es wäre geeignet zum Lesen via (zp),Y,
    23. ; solange wir den Offset in Y um 4 größer wählen als beim normalen Lesen ab $0340.
    24. ; Hier verwenden wir folgende Strategie: Linear lesen, und zwar vorwärts.
    25. ; Wir beginnen das Lesen bei spr_X=0, spr_Y=0, d.h. bei Offset 0.
    26. ; Wir haben den Lese-Offset allerdings um 65 verschoben, und außerdem das INY nach oben gezogen. Daher:
    27. ldy #0+(65-1)
    28. ; Das Schreiben beginnen wir also bei spr_X=0, spr_Y=20, d.h. bei Offset 60.
    29. ; Wir haben den Schreib-Offset allerdings um 128 verschoben. Daher:
    30. ; ldx #60+128 ; 60+128 = 188 = #$BC
    31. ; Beim Lesen berücksichtigen wir, dass alle Bytes mit Offset >= 63 nicht echt gelesen werden müssen, sondern als Wert #0 angenommen werden müssen.
    32. ; Da wir den Lese-Offset allerdings um 65 verschoben haben, bedeutet dies, dass bei Offset >= 63+65 = 128 nicht gelesen werden darf.
    33. ; Beim Schreiben berücksichtigen wir, dass wir bei Offset < 0 nichts schreiben dürfen.
    34. ; Beim Start ist hier A=0.
    35. _read_write_loop:
    36. ; Wir wollen hier beim ersten Mal und danach jeweils immer beim achten Mal ein neues Byte lesen.
    37. asl count8_zp37 ; liefert Z=1 beim ersten Mal und danach jeweils immer beim achten Mal, ansonsten Z=0
    38. bne _skip_read
    39. ; hier A=0, weil: beim ersten Mal war bereits A=0, danach jeweils beim achten Mal: weil wir acht Mal ASL ausgeführt haben.
    40. _read_next_byte:
    41. inc count8_zp37 ; auf einen Wert mit gesetztem Bit 0 setzen, d.h. konkret auf Wert #$01 oder #$11
    42. ; Linear lesen - Offset modifizieren:
    43. iny
    44. ; Lesen überspringen, falls Lese-Offset zu groß ist:
    45. bmi _skip_read
    46. lda (zp_spr_read),Y
    47. _skip_read:
    48. ; Jetzt ein Bit schreiben:
    49. asl ; fetch next bit into C
    50. rol sprite_dest-128,X ; write C into dest
    51. dex ; Schreiboffset -= 3
    52. dex
    53. dex
    54. bmi _read_write_loop
    55. ; Hier ist X gleich #$FF, #$FE oder #$FD (d.h. -1, -2 oder -3)
    56. ; Schreiboffset += 63
    57. ; und bei jedem achten Mal: Schreiboffset += 1
    58. asl count8_zp2B ; liefert C=1 und Z=1 beim achten Mal, ansonsten C=0 und Z=0
    59. rol count8_zpC3 ; liefert also C=1 beim 8. und beim 16. Mal, ansonsten C=0
    60. txa
    61. adc #63
    62. tax
    63. ; Hier ist immer C=1
    64. ; Hier sind A und X gleich #60, #61 oder #62
    65. ; Wenn der Lese-Offset Y aus den regulären Quell-Sprite-Daten herausläuft, ist hier A = X = #62 = #$3E
    66. ; Hier ist in count8_zp37 immer der Wert #$10
    67. lda #0 ; A=0 setzen
    68. cpy #72+(65-1)
    69. bne _read_next_byte
    70. rts
    71. file_end:
    Alles anzeigen
  • Neu

    hatte die sourcen aber mit "eingereicht"... hier nun.

    Was die Rahmenbedinungen betrifft hätte ich mir soetwas gewünscht:
    - ein assembler-programm mit ladeadresse 0x1000 was per sys 4096 gestartet wird
    - der zugriff auf folgenden speicher ist erlaubt: 0x00fb-0x00ff, 0x0340-0x03ff, 0x1000-0x1fff
    - jeglicher speicher (aussder dem ausgangs-sprite und dem geladenen programm) kann beliebigen inhalt haben

    damit kommt es nicht darauf an wie gut man mit basic trickst bzw. ausnutzt dass irgendwo bereits passende bytes liegen
    Dateien
  • Neu

    Das Leben ist kein Wunschkonzert... ich fand einige Beschränkungen auch fies, aber hey - gerade das macht die Aufgabenstellung interessant. Sonst hätte ich was mit Spritekollision probiert :D

    Das Einzige, was mich grämt - so viele Basiceinzeiler und kein Lebenszeichen vom König der Einzeiler... :bgdev naja, vermutlich hat er's nicht über's Herz gebracht, geliebte Doppelpunkte für nen möglichen Sieg zu opfern :D
    Wissen ist das einzige Gut, das sich beim Teilen vermehrt. Also seid vorsichtig damit!
  • Neu

    Hoogo schrieb:

    Dann wären Dir aber diese Tricksereien entgangen, und das wäre doch Schade
    Das ist schon richtig, hat auch etwas für sich - um den Master of Dirtyness zu küren - klar. Aber ich hätt' mir auch etwas restriktivere, maschinenneutralere Vorgaben gemäß alx gewünscht, zumindest Hinblick auf die Vergleichbarkeit zu anderen CPUs (wo man jetzt für deren Lösung sozusagen den Memory-Snap-Shot des C64 als Grundzustand vorausetzen muss - das verzerrt etwas). ;)