Forum: Mikrocontroller und Digitale Elektronik 6502 Assembler - Zweck einer Funktion


von Marco W. (watz)


Lesenswert?

Hi!

Ich bin über ein Stück 6502 Maschinencode von einer Art Bytecode 
Interpreter gestolpert und erfasse den Sinn einer Funktion nicht. 
Vielleicht hat jemand von euch sowas schoneinmal gesehen?

Hier ist, was ich bis jetzt glaube verstanden zu haben:
- Die Funktion greift auf einen Stack von 16 Bit Werten zu. Die Lobytes 
liegen ab $A408, die Hibytes ab $A42C. X enthält den 0-basierten Index 
des letzten gültigen Stackeintrags.
- Das X Register ist beim Aufruf der Funktion = 1 und beim Verlassen = 
0. Sie bekommt also zwei 16 Bit Werte über den Stack rein und gibt einen 
16 Bit Wert über den Stack zurück.
- Sie rollt die Bits von stack[2] und stack[1] 16 mal nach rechts. Sieht 
mir wie das Rollen eines 32 Bit Wertes um 16 Bit nach rechts aus.
- Jedes mal, wenn Bit 0 von stack[1] (also quasi Bit0 des 32 Bit Wertes) 
gesetzt ist, wird der Wert an stack[2] (also quasi das Hiword des 32 Bit 
Wertes) um den Wert an stack[0] erhöht. (??)


1
A53B   A0 10      LDY #$10
2
A53D   A9 00      LDA #$00
3
A53F   9D 2D A4   STA $A42D,X        // stack[2] nullen (temp variable)
4
A542   9D 09 A4   STA $A409,X        // "
5
  A545   BD 08 A4   LDA $A408,X      // A = stack[1] lobyte
6
  A548   4A         LSR A            // A /= 2
7
  A549   90 13      BCC $A55E        // A % 2 == 0 ? goto A55E
8
    A54B   18         CLC
9
    A54C   BD 09 A4   LDA $A409,X    // stack[2] += stack[0]
10
    A54F   7D 07 A4   ADC $A407,X    // "
11
    A552   9D 09 A4   STA $A409,X    // "
12
    A555   BD 2D A4   LDA $A42D,X    // "
13
    A558   7D 2B A4   ADC $A42B,X    // "
14
    A55B   9D 2D A4   STA $A42D,X    // "
15
  A55E   7E 2D A4   ROR $A42D,X      // stack[2] nach rechts rotieren
16
  A561   7E 09 A4   ROR $A409,X      // "
17
  A564   7E 2C A4   ROR $A42C,X      // stack[1] nach rechts rotieren
18
  A567   7E 08 A4   ROR $A408,X      // "
19
  A56A   88         DEY              // Y--
20
  A56B   D0 D8      BNE $A545        // Y > 0 ? goto A545
21
A56D   BD 08 A4   LDA $A408,X        // pop stack[1], speichere in stack[0]
22
A570   9D 07 A4   STA $A407,X        // "
23
A573   BD 2C A4   LDA $A42C,X        // "
24
A576   9D 2B A4   STA $A42B,X        // "
25
A579   CA         DEX                // "
26
A57A   60         RTS

Gruß,
Marco

: Bearbeitet durch User
von Markus (Gast)


Lesenswert?


von foobar (Gast)


Lesenswert?

Sieht nach einer Multiplikation aus ...

von Marco W. (watz)


Lesenswert?

foobar schrieb:
> Sieht nach einer Multiplikation aus ...

Darüber muss ich mal nachdenken...

Der Code ist übrigens aus der C64 Version von Sid Meiers Pirates! und 
ist eine der vielen Operationen dieses Bytecode Interpreters. Aber 
SWEET16 ist es nicht, denn die Opcodes passen nicht.

von Wolfgang (Gast)


Lesenswert?

Marco W. schrieb:
> Hier ist, was ich bis jetzt glaube verstanden zu haben:

Das Verständnis würde steigen, wenn du den Code nicht willkürlich 
einrücken würdest

> A54C   BD 09 A4   LDA $A409,X    // stack[2] += stack[0]

Das ist eine ganz gewöhnliche absolut x-indizierte Adressierung. Damit 
da etwas sinnvolles passiert, muss das x-Register vor Einsprung in die 
Unterroutine vernünftig belegt werden.

Der Prozessorstack des 6502 liegt zwischen 0x0100 und 0x01FF, nicht bei 
0xA4..

von Apple Asi (Gast)


Lesenswert?

Wolfgang schrieb:
> Der Prozessorstack des 6502 liegt zwischen 0x0100 und 0x01FF, nicht bei
> 0xA4..

Naja, das ist eben nicht der Stack des Prozessors sondern
(ich nenne es mal) eine Arbeits-Page des Programms in der
verschiedene Objekte im Index-Raum 0...255 bearbeitet werden
können.

von Marco W. (watz)


Angehängte Dateien:

Lesenswert?

foobar schrieb:
> Sieht nach einer Multiplikation aus ...

Der Kandidat hat 100 Punkte! Danke ;-)

Habs mit nem 6502 Simulator laufen lassen:
1
#include <cstdint>
2
#include <fstream>
3
#include "mos6502.h"
4
5
uint8_t ram[0x10000];
6
7
void BusWrite(uint16_t addr, uint8_t val)
8
{
9
    ram[addr] = val;
10
}
11
12
uint8_t BusRead(uint16_t addr)
13
{
14
    return ram[addr];
15
}
16
17
void ExecUnknownFunc(uint16_t arg1, uint16_t arg2)
18
{
19
    ram[0xA408] = arg1 & 0xff;
20
    ram[0xA42C] = (arg1 >> 8) & 0xFF;
21
    ram[0xA409] = arg2 & 0xFF;
22
    ram[0xA42D] = (arg2 >> 8) & 0xFF;
23
24
    mos6502 cpu(BusRead, BusWrite);
25
    cpu.Reset(0xA53B);
26
    cpu.X = 1;
27
    uint64_t cycleCount = 0;
28
    while(cpu.pc != 0xA57A)
29
    {
30
        cpu.Run(1, cycleCount);
31
    }
32
33
    uint16_t res = ram[0xA42c] << 8 | ram[0xA408];
34
    std::cout << "ExecUnknownFunc(" << arg1 << ", " << arg2 << ") = " << res << "\n";
35
}
36
37
int main()
38
{
39
    std::ifstream ramfile("ramimage.bin");
40
    if(ramfile.is_open())
41
    {
42
        ramfile.read((char*)ram, sizeof(ram));
43
        ramfile.close();
44
    }
45
46
    ExecUnknownFunc(0, 0);
47
    ExecUnknownFunc(1, 1);
48
    ExecUnknownFunc(11, 55);
49
    ExecUnknownFunc(20, 2200);
50
    ExecUnknownFunc(111, 8);
51
52
}

Vollständiger Code ist im Anhang. Ergebnis:

ExecUnknownFunc(0, 0) = 0
ExecUnknownFunc(1, 1) = 1
ExecUnknownFunc(11, 55) = 605
ExecUnknownFunc(20, 2200) = 44000
ExecUnknownFunc(111, 8) = 888

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

der Ursprungs-Beitrag ist übrigens ein schönes Beispiel dafür, wie 
kniffelig es ist, "reverse engineering" zu betreiben, und aus 
disassemblierten Code wieder sogar nur einzelne Codefragmente 
"einfacher" Funktionen in einer Hochsprache herzuleiten.

einen vernünftigen komplexen Algorithmus (z.B. Fourier Transformation, 
trigonometrische Funktionen, Bresenham, Sortieralgorithmen etc) aus 
"einer handvoll arithmetischer oder Zeiger-Operationen" heraus 
herzuleiten, ist dann die nächste Weihe-Stufe.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.