DEC Attack: Die kleine Problem-Ecke

Es gibt 88 Antworten in diesem Thema, welches 11.674 mal aufgerufen wurde. Der letzte Beitrag (5. Mai 2024 um 22:32) ist von goloMAK.

  • Sofern bit 1 stets 0 ist:

    Code
      lsr zeropageByte [6]    ; shift to the right, carry flag contains now former bit 0
      bcc nottin       [2/3]  ; carry is clear: do nothing 
      inc zeropageByte [5]    ; carry is set: increase zeropageByte => current bit 0 becomes 1
    .nottin
      rol zeropageByte [5]    ; rotate to the left, carry flag is shifted into bit 0

    insgesamt 18/19 Taktzyklen und 8 bytes.

    Andernfalls mit bit 1 "initialisieren":

    Code
      lda zeropageByte [3]    ; clear bit 1
      and #%00000010   [2]
      sta zeropageByte [3]
      lsr zeropageByte [6]    ; shift to the right, carry flag contains now former bit 0
      bcc nottin       [2/3]  ; carry is clear: do nothing 
      inc zeropageByte [5]    ; carry is set: increase zeropageByte => current bit 0 becomes 1
    .nottin
      rol zeropageByte [5]    ; rotate to the left, carry flag is shifted into bit 0

    insgesamt 26/27 Taktzyklen und 14 bytes.

    Freitags-brain-fart und ungestestet.

  • Andernfalls mit bit 1 "initialisieren":

    Code
      lda zeropageByte [3]    ; clear bit 1
      and #%00000010   [2]
      sta zeropageByte [3]
      lsr zeropageByte [6]    ; shift to the right, carry flag contains now former bit 0
      bcc nottin       [2/3]  ; carry is clear: do nothing 
      inc zeropageByte [5]    ; carry is set: increase zeropageByte => current bit 0 becomes 1
    .nottin
      rol zeropageByte [5]    ; rotate to the left, carry flag is shifted into bit 0

    insgesamt 26/27 Taktzyklen und 14 bytes.

    Freitags-brain-fart und ungestestet.

    Ich glaube, du meinst and #%11111101, sonst hast du dein zeropageByte fast komplett zerstört.

  • Ich habe noch einen Vorschlag, inspiriert durch bernies erste Version und ayvazhzfs Version. Der ist aber auch ungetestet:

    Code
    ldx byte
    inx
    txa
    and #%00000010
    eor byte
    sta byte

    Die Idee ist, dass man sich das shiften spart und damit weniger Bytes braucht.

    Gefällt mir. 13 Byte (bzw. 10 Byte, wenn die Speicherzelle in der Zeropage liegt), 18 Taktzyklen (bzw. 15).

  • Aber vielleicht denke ich auch schon zu sehr in x86, denn wenn man Branches vermeiden kann ist das da in der Regel ein Vorteil. :)

    Beim 6510 können die schon auch doof sein: Wenn man exaktes Timing braucht, benötigen die Branches mal 2, mal 3 Taktzyklen. Das ist dann schon auch blöd, weil man es wieder durch einen weiteren Branch ausgleichen muss und damit Zeit verliert.

  • Beim 6510 können die schon auch doof sein: Wenn man exaktes Timing braucht, benötigen die Branches mal 2, mal 3 Taktzyklen. Das ist dann schon auch blöd, weil man es wieder durch einen weiteren Branch ausgleichen muss und damit Zeit verliert.

    Betrifft das Pageboundaries, oder woran liegt das?

  • Noch eine Idee war, den BIT-Befehl zu benutzen, um einen Takt in einem Fall zu sparen:
    12 Bytes und 14 bzw. 15 Zyklen

  • Beim 6510 können die schon auch doof sein: Wenn man exaktes Timing braucht, benötigen die Branches mal 2, mal 3 Taktzyklen. Das ist dann schon auch blöd, weil man es wieder durch einen weiteren Branch ausgleichen muss und damit Zeit verliert.

    Betrifft das Pageboundaries, oder woran liegt das?

    Du hast recht: Bei Page-Boundaries kommt auch noch ein weiterer Extra-Zyklus hinzu. Das hatte ich vergessen. Was ich meinte: Wenn ein Sprung ausgeführt wird, wird ein Zyklus mehr benötigt, wie wenn er nicht ausgeführt wird. Das liegt tatsächlich an sowas, wie primitivem Pipelining.

    Siehe Bitte melde dich an, um diesen Link zu sehen.: ab Zeile 1242 sind die Branch-Befehle erklärt.

  • Und noch eine Idee:

    15 Bytes und je nach Endung des Eingabewertes:


    "00" = 3+2+3 = 8 Zyklen

    "11" = 3+2+2+ 2+3 = 12 Zyklen

    "10" oder "01" = 3+2+2+ 2+2+ 2+2+3 = 18 Zyklen

    , d.h. wenn alle Endungen gleich häufig drankommen würden:
    8 * 0.25 + 12 * 0.25 + 18 * 0.5 = 2 + 3 + 9 = 14 Zyklen im Durchschnitt.


    Edit: Korrektur 18 statt 20 Zyklen im schlechtesten Fall bei "10" oder "01", d.h. 14 Zyklen im Durchschnitt.

  • Noch ein Byte kürzer:

    Code
      lda byte
      and #%11111101
      lsr
      bcc +
      ora #1
    + rol
      sta byte

    Ich habe gerade gesehen, dass es noch einen illegalen Befehl gibt, der AND und LSR zusammenfasst, nämlich ALR mit 2 Taktzyklen, d.h. es ergäben sich:
    11 Bytes und 13 bzw. 14 Zyklen.

    Code
      lda byte        ; 3
      alr #%11111101  ; 2
      bcc +           ; 3/2
      ora #1          ; 2
    + rol             ; 2
      sta byte        ; 3
  • Noch eine Variante, diesmal mit selbstmodifizierendem Code :D

    Code
    lda byte
    and #%0000 0001
    asl
    sta T+1
    lda byte
    T: ora #$00
    sta byte

    13 Bytes und
    3+2+2+3+3+2+3 = 18 Zyklen

    Hier könnte man wieder LAX einfügen, falls illegale Befehle erlaubt wären.
    Ich würde dann wieder in Richtung Tabelle gehen, wie in Bitte melde dich an, um diesen Link zu sehen..

    Durch Deine Idee könnte man also die Tabelle im verlinkten Post nochmal verkürzen.

    12 + 2 = 14 Bytes und

    3+2+2+2+2+4+3 = 18 Zyklen

    Edit: Ich denke, dass durch die Verwendung von ORA ein Fehler entsteht, d.h. auch bei Dir. Ich habe gerade wieder EOR eingesetzt.
    Edit2: Ich denke, dass das 2te Bit nicht gelöscht wird, ... d.h. es müsste noch ein AND hinzu. Deshalb ist auch meine Lösung wieder 2 Takte langamer und 2 Byte länger. Das Ora kann dann wieder zurück. Im Vergleich zum Post hast sich also nichts geändert.

  • Ja, stimmt. Wenn das bit gesetzt war, aber gelöscht werden muss, dann passiert das nicht durch ORA. Aber IMO ist das gleiche Problem mit EOR.

    Code
    lda byte
    and #%0000 0001
    asl
    sta T+1
    lda byte
    and #%1111 1101
    T: ora #$00
    sta byte

    So sollte es richtig sein. Ich hatte sogar dran gedacht dass ich das ANDen muss, aber vergessen hinzuschreiben. :)

  • Noch ein Byte kürzer:

    Code
      lda byte
      and #%11111101
      lsr
      bcc +
      ora #1
    + rol
      sta byte

    Ich habe gerade gesehen, dass es noch einen illegalen Befehl gibt, der AND und LSR zusammenfasst, nämlich ALR mit 2 Taktzyklen, d.h. es ergäben sich:
    11 Bytes und 13 bzw. 14 Zyklen.

    Code
      lda byte        ; 3
      alr #%11111101  ; 2
      bcc +           ; 3/2
      ora #1          ; 2
    + rol             ; 2
      sta byte        ; 3

    Noch ein Gedanke hierzu:
    Falls es funktioniert (??? und ich sehe jetzt keinen Fehler, weil Bit 0 gelöscht ist), wären das:
    9 Bytes und 12 Zyklen

    Code
      lda byte        ; 3
      alr #%11111101  ; 2
      adc #0          ; 2    statt bcc +; ora #1; +
      rol             ; 2
      sta byte        ; 3
  • Noch ein Gedanke hierzu:
    Falls es funktioniert (??? und ich sehe jetzt keinen Fehler, weil Bit 0 gelöscht ist), wären das:
    9 Bytes und 12 Zyklen

    Code
      lda byte        ; 3
      alr #%11111101  ; 2
      adc #0          ; 2    statt bcc +; ora #1; +
      rol             ; 2
      sta byte        ; 3

    Ist das carry nach dem ADC nicht gelöscht? Es sollte ja kein Überlauf stattfinden, also müsste es dann immer gelöscht sein. Dann würde beim ROL immer eine 0 reingerollt in Bit 0.

    Edit: Yep, habs gerade getestet. Genau das passiert. :) Das Carry muss gesichert werden, damit das Bit 0 erhalten bleibt.

  • Noch ein Gedanke hierzu:
    Falls es funktioniert (??? und ich sehe jetzt keinen Fehler, weil Bit 0 gelöscht ist), wären das:
    9 Bytes und 12 Zyklen

    Code
      lda byte        ; 3
      alr #%11111101  ; 2
      adc #0          ; 2    statt bcc +; ora #1; +
      rol             ; 2
      sta byte        ; 3

    Ist das carry nach dem ADC nicht gelöscht? Es sollte ja kein Überlauf stattfinden, also müsste es dann immer gelöscht sein. Dann würde beim ROL immer eine 0 reingerollt in Bit 0.

    Edit: Yep, habs gerade getestet. Genau das passiert. :) Das Carry muss gesichert werden, damit das Bit 0 erhalten bleibt.

    Ja, das stimmt leider, d.h. es müssten wieder PHP und PLP dazu und das wären dann wieder +2 Bytes und +7 Takte.
    Schade, aber evtl. gibt es ja noch einen anderen Lösungsweg.

  • Ich hab' mal eine Tabelle mit allen bisherigen Versuchen zusammengestellt:

    NrAutorkorrekt?Länge (Zero)Dauer (Zero)Anmerkung
    1goloMAKok16 (13)19,5 (16,5)Branch
    2berniok14 (12)17,5 (15,5)Branch
    3ayvazhzf2->2, korrekt: 0
    4sparhawkok14 (12)30 (28)
    5berniok9+256 (7+256)12 (10)Extra Daten
    6Mafiosinook13 (10)18 (15)
    75aceok13+4 (11+4)20 (18)Extra Daten
    85aceok12+4 (10+4)18 (16)Extra Daten
    9raydenok19 (14)27,5 (23)Branch
    105aceok14+1 (12+1)16,5 (14,5)Branch, extra Daten
    115aceok14+1 (12+1)16,5 (14,5)Branch, extra Daten
    125aceok17 (15)15,5 (14)Illegal Opcode, 2x Branch
    13sparhawk2=>2, korrekt: 0
    145aceok13 (11)15,5 (13,5)Illegal Opcode, Branch
    155ace1=>2, korrekt: 3
    16sparhawkok19-1 (16-1)24-1 (21-1)Selbstmodifizierender Code
    -1, falls Code in Zeropage
    175ace1=>2, korrekt: 3

    Die Nummern beziehen sich auf das Test-Programm im Anhang (sollte eigentlich test.a sein, aber dann akzeptiert es die Foren-Software nicht), ein Testprogramm im ACME-Format.

    Ist gar nicht so einfach zu entscheiden, was da jetzt das beste ist. Nr. 14 sieht bislang ziemlich gut aus. Ohne illegale Opcodes ist Nr. 6 ein guter Favorit. Und Nr. 5, wenn Geschwindigkeit so viel wichtiger ist, als 256 Byte Zusatzdaten.

  • Mir ist noch ein anderer Ansatz in den Sinn gekommen:

    Bits 0 und 1 sollen umgeformt werden:
    00 nach 00, dezimal: 0 nach 0
    01 nach 11, 1 nach 3
    10 nach 00, 2 nach 0
    11 nach 11, 3 nach 3

    , d.h. wenn BIT 0 gesetzt ist, sollen zwei addiert werden und wenn BIT 1 gesetzt ist, sollen wieder 2 abgezogen werden.
    Die Idee ist dann folgende:

    14 Bytes und je nach Endung des Eingabewertes:

    "00" = 3 + 2+3 + 2+3 + 3 = 16 Zyklen
    "01" oder "10" = 3 + 2+3 + 2+2+2+2 + 3 = 16 Zyklen
    "11" = 3 + 2+2+2+2 + 2+2+2+2 + 3 = 22 Zyklen

    Im Durchschnitt: 16*0.25 + 16*0.5 + 22*0.25 = 4 + 8 + 5.5 = 17.5 Zyklen

    -----
    Von der Geschwindigkeit gab es bessere Lösungen, aber evtl. kann man diese Idee/Umsetzung noch verbessern.

    -----
    Edit:
    Ich bin selbst nochmal auf eine Antwort gekommen, d.h. der Sprung muss nur einmal vorkommen; wobei die Lösung irgendwie bekannt aussieht:
    12 Bytes und 15 bzw. 18 Zyklen, d.h. im Schnitt 16.5 Zyklen.

  • Ein verrückter Ansatz, der für das konkrete 1 Bit aber nicht lohnt, aber vielleicht was für mehr Bits taugt

    Code
    lda byte
    lsr
    eor byte
    and#$fe
    eor byte
    rol
    sta byte