Autor Thema: Erste Assembler-Gehversuche  (Gelesen 6137 mal)

0 Mitglieder und 1 Gast betrachten dieses Thema.

Offline Mado

  • Benutzer
  • Beiträge: 196
Erste Assembler-Gehversuche
« am: Mo 27.06.2022, 07:35:44 »
Ich hatte gestern ein kleines erstes Programm geschrieben, wo ich eine Assembler-Datei asmmul.s habe, die ich in ein C-Programm einbinden möchte. Die Assembler-Unterroutine soll nichts anderes machen, als zwei WORD-Werte zu einem LONG multiplizieren und dann über D0 wieder an das C-Programm übergeben. Ich erhalte aber ein komisches Ergebnis:

a x b = 24240
Vielleicht kann mir ein alter Hase bei meinen Gehversuchen helfen? Hier mein Code:

Makefile:

# For Linux
CC = m68k-atari-mint-gcc
CFLAGS = -mshort -O2 -Wall

OBJS = main.o asmmul.o

all: asmmul.prg
asmmul.prg: $(OBJS)
$(CC) $(CFLAGS) -o $@ $(OBJS)

main.o: main.c
$(CC) $(CFLAGS) -c $<

asmmul.o: asmmul.s
$(CC) $(CFLAGS) -c $<

C-Programm main.c:

#include <stdio.h>

extern long asmmul(int factor, int value);

int main(void) {
    int f = 5;
    int v = 10;
    long ret = 0;

    ret = asmmul(f, v);

    printf("a x b = %d\n", ret);

    return 0;
}

Und das Assembler-Stückchen assmul.s:

       .text
        .global _asmmul
_asmmul:
        clr.l   d1
        clr.l   d0
        move.w  2(sp),d1
        move.w  4(sp),d0
        muls    d1,d0

        rts
        .end

Da ich nur die Scratch-Register D0 und D1 an Registern verwende, save ich keine Register auf dem Stack.
« Letzte Änderung: Mo 27.06.2022, 07:38:18 von Mado »
... ich bin ST-HIGH und eher so der Monochrom-Typ

Offline czietz

  • Benutzer
  • Beiträge: 3.589
Re: Erste Assembler-Gehversuche
« Antwort #1 am: Mo 27.06.2022, 08:24:09 »
Der erste Parameter liegt bei 4(SP) auf dem Stack.

PS: Vorsicht mit -mshort. Die Standard-MiNTLib ist nicht damit compiliert. Eventuell weiß @Thorsten Otto, ob es eine MiNTLib dafür gibt. (libcmini kann man als "mshort"-Version bauen.)
« Letzte Änderung: Mo 27.06.2022, 08:29:47 von czietz »

Offline Mado

  • Benutzer
  • Beiträge: 196
Re: Erste Assembler-Gehversuche
« Antwort #2 am: Mo 27.06.2022, 09:18:52 »
Ah, jetzt funktioniert es. So habe ich jetzt den Code angepasst:

       .text
        .global _asmmul
_asmmul:
        clr.l   d1
        clr.l   d0
        move.w  4(sp),d1
        move.w  6(sp),d0
        muls    d1,d0

        rts
        .end

Und beim printf im C-Programm muss es natürlich ein long bei der Ausgabe sein:

    printf("a x b = %ld\n", ret);
Beim Programmieren bin ich momentan komplett nur in TOS unterwegs. Soweit ich es sehen kann, ist im TOS jeder int und auch alle Übergaberegister etc 16 Bit weit. Wenn ich also auf 32 Bit aufbauen würde, müsste ich immer explizit short int (oder WORD???) angeben. Ebenfalls ist es ja so, dass bestimmte Assember-Direktiven in short schneller sind, als in long. Warum wurde dieser Weg gewählt?
... ich bin ST-HIGH und eher so der Monochrom-Typ

Offline czietz

  • Benutzer
  • Beiträge: 3.589
Re: Erste Assembler-Gehversuche
« Antwort #3 am: Mo 27.06.2022, 09:57:37 »
Die TOS-Bindings sind natürlich in MiNTLib korrekt, d.h. es werden 16-Bit-Worte übergeben, wo dies nötig ist, ohne dass Du explizit "short" dran schreiben müsstest. Generell gilt beim Portieren von Unix-Software (was ja die Stärke der MiNTLib ist), dass das besser funktioniert, wenn ein "int" eben 32 Bit lang ist. (Nicht jede Software ist mit intNN_t und uintNN_t geschrieben.)


Offline Mado

  • Benutzer
  • Beiträge: 196
Re: Erste Assembler-Gehversuche
« Antwort #4 am: Mo 27.06.2022, 10:08:56 »
Ah, ok, verstanden. Ich wurschtel gerade im EmuTOS rum, also nicht im Mint. Das EmuTOS ist ja mit -mshort kompiliert. Ich möchte herausfinden, welche Grafikroutinen sich ggf. durch schnellere Assembler-Routinen ersetzen lassen. Ich komm irgendwie nicht drüber hinweg, dass ich NVDI installiere und teilweise um Faktoren schneller bin.

Ich habe mir mal einige Routinen als Assembly angeschaut und nur die Hände über dem Kopf zusammen geschlagen. Was der C-Compiler da baut, scheint mir nicht die vollen CISC-Features bzw. besonders leistungsfähige Kommandos des Prozessors auszunutzen.

Da ich m68k-Assembler immer nur im ganz kleinen Bereich genutzt habe und das auch schon 25 Jahre her ist, werd ich wohl ein ziemlich langes Weilchen brauchen, bis ich da überhaupt was zustande bringe. Siehst ja. ;-)
... ich bin ST-HIGH und eher so der Monochrom-Typ

Offline czietz

  • Benutzer
  • Beiträge: 3.589
Re: Erste Assembler-Gehversuche
« Antwort #5 am: Mo 27.06.2022, 10:16:59 »
Natürlich ist es kein Problem für gcc etwas als "-mshort" zu compilieren. Ich schrieb ja bloß: Vorsicht beim Linken gegen MiNTLib.

Und was EmuTOS und Assembler-Routinen angeht: Beachte, dass EmuTOS auch auf ColdFire funktionieren muss. Somit hast Du in Assembler eigentlich immer #ifdef-Strecken, die die Wartbartkeit des Codes reduzieren.

Online Thorsten Otto

  • Benutzer
  • Beiträge: 1.250
Re: Erste Assembler-Gehversuche
« Antwort #6 am: Mo 27.06.2022, 13:31:28 »
PS: Vorsicht mit -mshort. Die Standard-MiNTLib ist nicht damit compiliert. Eventuell weiß @Thorsten Otto, ob es eine MiNTLib dafür gibt. (libcmini kann man als "mshort"-Version bauen.)

Nein, mintlib gibt es nicht (mehr) für -mshort. Theoretisch könnte man die wohl bauen, aber das würde vermutlich nur zu noch mehr  Verwirrung führen, und auch wenig bringen, weil sie ja hauptsächlich zum port von unix-tools verwendet wird, und die würden mit 16bit-ints sowieso i.d.R nicht klar kommen. Auch müsste man dann alle anderen verwendeten Bibliotheken auch mit -mshort kompilieren.

Gemlib gibt es allerdings noch für -mshort.

Online Thorsten Otto

  • Benutzer
  • Beiträge: 1.250
Re: Erste Assembler-Gehversuche
« Antwort #7 am: Mo 27.06.2022, 13:43:27 »
Ah, jetzt funktioniert es. So habe ich jetzt den Code angepasst:

       .text
        .global _asmmul
_asmmul:
        clr.l   d1
        clr.l   d0
        move.w  4(sp),d1
        move.w  6(sp),d0
        muls    d1,d0

        rts
        .end

Das wäre immer noch falsch. Der erste Parameter is bei 4(sp), aber das low-word davon is bei 6(SP). Der zweite Parameter ist bei 8(sp), das low-word davon bei 10(sp).

Im Zweifelsfall einmal kurz anschauen was GCC damit macht:

int asmmul(short x, short y)
{
return x * y;
}
$ m68k-atari-mint-gcc -fomit-frame-pointer -S -o - -O2 bla.c
       .text
        .even
        .globl  _asmmul
_asmmul:
        move.w 6(%sp),%d0
        muls.w 10(%sp),%d0
        rts

Wie man daran auch schön sieht, nutzt auch ein explizites "short" nichts, der Compiler pusht immer ein int auf den Stack.

Das ist auch der Grund für die ganzen komischen macros in osbind.h von GCC. Ohne die wäre GCC (ohne -mshort) nicht in der Lage, das für GEMDOS-Calls nötige Stack-layout zu erzeugen.
« Letzte Änderung: Mo 27.06.2022, 13:48:34 von Thorsten Otto »

Online Thorsten Otto

  • Benutzer
  • Beiträge: 1.250
Re: Erste Assembler-Gehversuche
« Antwort #8 am: Mo 27.06.2022, 13:51:48 »
Ich habe mir mal einige Routinen als Assembly angeschaut und nur die Hände über dem Kopf zusammen geschlagen. Was der C-Compiler da baut, scheint mir nicht die vollen CISC-Features bzw. besonders leistungsfähige Kommandos des Prozessors auszunutzen.

Ja, manchmal sieht der Code schon komisch aus. Aber insgesamt macht er schon einen ganz guten Job. Kannst ja mal probeweise ein paar benchmarks einmal mit Pure-C und einmal mit gcc übersetzen. I.d.R. ist da nicht viel Unterschied.

Offline czietz

  • Benutzer
  • Beiträge: 3.589
Re: Erste Assembler-Gehversuche
« Antwort #9 am: Mo 27.06.2022, 15:42:20 »
Das wäre immer noch falsch. Der erste Parameter is bei 4(sp), aber das low-word davon is bei 6(SP). Der zweite Parameter ist bei 8(sp), das low-word davon bei 10(sp).

Siehe oben: Martin baut mit "-mshort". Da sind die Parameter auf dem Stack tatsächlich 16-bit aligned.

Kannst ja mal probeweise ein paar benchmarks einmal mit Pure-C und einmal mit gcc übersetzen. I.d.R. ist da nicht viel Unterschied.

??? Im Vergleich zwischen Pure-C und gcc gewinnt gcc meilenweit.

Offline Mado

  • Benutzer
  • Beiträge: 196
Re: Erste Assembler-Gehversuche
« Antwort #10 am: Mo 27.06.2022, 18:34:52 »
Kann mir jemand im Detail erklären, was die Assembler-Direktive lea macht? Ich habe hier ein Stückchen Beispiel-Code:

#ifdef __mcoldfire__
        lea     -12(sp),sp
        movem.l d1/a0-a1,(sp)
#else
        movem.l d1/a0-a1,-(sp)
#endif

Im Falle von Nicht-Coldfire werden die angegebenen Register auf den Stack gesichert, der Stackpointer wird jeweils vorher dekrementiert.

Ich verstehe das lea Kommando oben nicht, der Rest ist klar. Warum ist das nicht einfach ein sub #12,sp? lea lädt doch eine Adresse, aber warum sind im linken Teil dann Klammern um sp, das würde doch bedeuten, dass der Inhalt der Speicherzelle referenziert wird, auf die sp zeigt?

Und, was ist der Unterschied zwischen dem Laden einer Adresse und einer effektiven Adresse? Bei allen Büchern, die ich gelesen habe, habe ich es nicht verstanden.
« Letzte Änderung: Mo 27.06.2022, 19:03:28 von Mado »
... ich bin ST-HIGH und eher so der Monochrom-Typ

Online mfro

  • Benutzer
  • Beiträge: 1.640
Re: Erste Assembler-Gehversuche
« Antwort #11 am: Mo 27.06.2022, 19:12:48 »
ein
suba.w   #12,spgibt's beim ColdFire nicht (mehr).
suba.l    #12,sp gibt's noch, der Befehl braucht aber (weil #-12 da ein Langwort ist) 3 Worte.

lea        -12(sp),sp macht effektiv dasselbe mit zwei Words.
« Letzte Änderung: Di 28.06.2022, 10:15:35 von mfro »
And remember: Beethoven wrote his first symphony in C

Online Thorsten Otto

  • Benutzer
  • Beiträge: 1.250
Re: Erste Assembler-Gehversuche
« Antwort #12 am: Di 28.06.2022, 06:16:56 »
lea kann man im Grunde mit dem Adress-operator (&) in C vergleichen. Es wird lediglich die berechnete Adresse ins Adress-Register geladen, aber nicht dereferenziert.

lea ist auf eigentlich auf allen Prozessoren schneller als ein suba (u.a. glaube ich weil dafür eine andere ALU benutzt wird als für Arithmetik-Operationen).

Offline Mado

  • Benutzer
  • Beiträge: 196
Re: Erste Assembler-Gehversuche
« Antwort #13 am: Di 28.06.2022, 07:08:39 »
lea kann man im Grunde mit dem Adress-operator (&) in C vergleichen. Es wird lediglich die berechnete Adresse ins Adress-Register geladen, aber nicht dereferenziert.
Ah, danke, das war die entscheidende Erklärung, jetzt habe ich es verstanden. Es wird also in der Adressierungsart auf einen Inhalt verwiesen und die effektive Adresse ist dann die "Hausnummer".
... ich bin ST-HIGH und eher so der Monochrom-Typ

Offline Mado

  • Benutzer
  • Beiträge: 196
Re: Erste Assembler-Gehversuche
« Antwort #14 am: Di 28.06.2022, 07:10:32 »
lea        -12(sp),sp macht effektiv dasselbe mit zwei Words.
Das ist dann der nächste Schritt: Zu wissen, welcher Code schnell läuft und welcher langsam. Danke.
... ich bin ST-HIGH und eher so der Monochrom-Typ

Online mfro

  • Benutzer
  • Beiträge: 1.640
Re: Erste Assembler-Gehversuche
« Antwort #15 am: Di 28.06.2022, 12:00:06 »
lea        -12(sp),sp macht effektiv dasselbe mit zwei Words.
Das ist dann der nächste Schritt: Zu wissen, welcher Code schnell läuft und welcher langsam. Danke.

Was Schnelligkeit angeht, ist es für ColdFire (fast) wurscht (beim 060 dürfte das genauso sein). lea und sub brauchen beide genau einen Taktzyklus (vorausgesetzt, der Instruction-Cache ist gefüllt und es tritt kein Pipeline-Stall auf).
Allerdings ist natürlich bei 3-Wort-Befehlen der Cache "schneller leer" als bei zweien und die Wahrscheinlichkeit eines unaligned access ist höher (dann gibt's u.U. "Straftakte").
 
« Letzte Änderung: Di 28.06.2022, 12:01:53 von mfro »
And remember: Beethoven wrote his first symphony in C

Offline Mado

  • Benutzer
  • Beiträge: 196
Re: Erste Assembler-Gehversuche
« Antwort #16 am: Fr 01.07.2022, 08:13:48 »
Oje. Ich habe eine komplette Routine aus dem EmuTOS-ROM in Assembler implementiert, vom Algorithmus der C-Implementierung folgend. Aber ich bin nicht schneller, als der C-Compiler mit Optimierung und einer kleinen Loop, die auch im C-Original als Inline-Assembler eingebettet ist. Frust - und viel gelernt.

Ich glaube, ich habe so ungefähr jeden Fehler gemacht, den man als Assembler-Anfänger machen kann. Aber immerhin habe ich meine Routine komplett zum Laufen gebracht. :-)

Ich habe:
- WORD-Arrays nur BYTEweise indiziert und damit nette kleine Address Errors provoziert,
- Ich habe mit MOVEM weniger Register gesichert, als ich brauchte und mich über komische Abstürze gewundert,
- Ich habe Register aus versehen doppelt verwendet
- Ich habe Schleifen von C nach Assembler falsch umgesetzt, C-For-Schleifen prüfen am Anfang!
- Ich habe vorzeichenbehaftete Zahlen so behandelt, wie welche ohne
- ... und vermutlich noch viele Sachen mehr, die ich schon wieder vergessen habe

Ich habe aber auch massiv viel gelernt. Letzte Woche hatte ich eine berufliche Fahrt nach Köln und habe mir während der Fahrt komplett "ATARI ST – Programmieren in Maschinensprache", das schwarze Buch vom SYBEX-Verlag, reingetan. Dafür, dass ich gerade erst anfange, bin ich eigentlich ganz zufrieden mit mir.

Was ich auch gelernt habe: Bis auf einige Mikro-Optimierungen, die man auch in C einbetten kann, lohnt sich bei den heutigen Compilern eine Programmierung in Assembler offenbar kaum noch, außer, man will hardware-nahe Interfaces machen, oder sowas.
« Letzte Änderung: Fr 01.07.2022, 11:49:24 von Mado »
... ich bin ST-HIGH und eher so der Monochrom-Typ

Offline gh-baden

  • Benutzer
  • Beiträge: 1.976
Re: Erste Assembler-Gehversuche
« Antwort #17 am: Fr 01.07.2022, 19:56:59 »
Was ich auch gelernt habe: Bis auf einige Mikro-Optimierungen, die man auch in C einbetten kann, lohnt sich bei den heutigen Compilern eine Programmierung in Assembler offenbar kaum noch,

Ganz so weit würde ich nicht gehen, aber es ist definitiv ein ganz anderer Unterschied zwischen heutigen Compilern und denen von 1986, und handgestricktem Assembler.
Wider dem Signaturspam!

Offline czietz

  • Benutzer
  • Beiträge: 3.589
Re: Erste Assembler-Gehversuche
« Antwort #18 am: Fr 01.07.2022, 20:42:58 »
Ich bin auch schon reingefallen mit Optimierungsversuchen, die das Gegenteil bewirkt haben. Was ich in einem Vierteljahrhundert Programmierung gelernt habe:

Ganz schlecht: C-Code angucken, denken "das kann keinen guten Code ergeben" und umständlicher hinschreiben. Compiler ‒ jedenfalls moderne ‒ sind gut im Optimieren, insbesondere, wenn man den Code eben nicht vorab händisch versucht zu verbessern.

Schon besser: Compiler-Output angucken und denken "was soll denn das?". Ja, vielleicht ist eine spezifische Stelle suboptimal übersetzt. Aber wenn diese Stelle nicht in einem "heißen" Teil des Codes ist, lohnt es die Mühe dennoch nicht, sie in (Inline-)Assembler umzuschreiben.

Tipp: Hatari hat auch einen Profiler. Zumindest für die ST/68000-Emulation ist er zyklusexakt. Damit kannst Du Dir z.B. eine VDI-Funktion vornehmen und sehen, welche Codestellen "heiß" sind.

Offline Mado

  • Benutzer
  • Beiträge: 196
Re: Erste Assembler-Gehversuche
« Antwort #19 am: Sa 02.07.2022, 11:28:19 »
Ich hatte gedacht, wenn ich soweit es geht alles in Registern unterbringe, anstatt immer auf Speicherstellen im Stack-Frame zuzugreifen, dann müsste es richtig schnell werden. Aber offensichtlich macht der Compiler genau das gleiche bei entsprechenden Optimierungsoptionen, -fomit-frame-pointer etc ...

Und das mit dem "heißen Teil" ist wohl ganz wichtig.

Wenn das NVDI Dinge irgendwo doppet so schnell macht, wie EmuTOS, dann liegt es auf jeden Fall nicht an der Routine in vdi_line.c, die ich mir da gegriffen habe.

Was ich auch gespürt habe ist, dass C-Code wirklich wesentlich übersichtlicher ist, als Assembler. Ich glaube nicht, dass es nur Gewohnheit ist.
... ich bin ST-HIGH und eher so der Monochrom-Typ