Was tut dieser Assembly Code? (Anfängerfrage)



  • ps: Aufpassen falls du mit single-precision rechnest: cos hat nen double als Parameter. Die Variante mit single-precision heisst cosf.


  • Gesperrt

    Danke, hab alles nach double umgestellt - und divss oder divsd endlich "richtigherum" verwendet:

    getx:
    .LFB0:
    # main.c:7: 	float w2 = w * (M_PI / 180.0);
    # main.c:8: 	x = (int) (r * cos(w2));
    # main.c:9: 	return x;
    	pushq		%rcx
    	cvtsi2sd	%eax, %xmm3
    	movq		$314, %rcx
    	movq		$100, %rdx
    	cvtsi2sdq	%rcx, %xmm0
    	cvtsi2sdq	%rdx, %xmm1
    	divsd		%xmm1, %xmm0
    	movq		$180, %rdx
    	cvtsi2sdq	%rdx, %xmm1
    	divsd		%xmm1, %xmm0
    	cvtsi2sd	%ebx, %xmm1
    	mulsd		%xmm1, %xmm0
    	call		cos@PLT
    	mulsd		%xmm3, %xmm0
    	cvttsd2si	%xmm0, %eax
    	popq		%rcx
    	ret
    

    Jetzt wird genau einmal das richtige Ergebnis ausgegeben.
    Außerdem hab ich noch herausgefunden, dass cos eax ändert...

    Also ich hab bestimmt mit pushq %rcx usw. etwas falsch gemacht. Aber ich kann mich auch nicht mehr lange damit aufhalten.


  • Gesperrt

    Jetzt gehts (Aufrufkonventionen eingehalten), aber nicht alle Ausgaben sind richtig:

    	pushq	%rbp
    	movq	%rsp, %rbp
    	
    	movq		$314, %rcx
    	movq		$100, %rdx
    	cvtsi2sdq	%rcx, %xmm0
    	cvtsi2sdq	%rdx, %xmm1
    	divsd		%xmm1, %xmm0
    	movq		$180, %rdx
    	cvtsi2sdq	%rdx, %xmm1
    	divsd		%xmm1, %xmm0
    	cvtsi2sd	%esi, %xmm1
    	mulsd		%xmm1, %xmm0
    	call		cos@PLT
    	cvtsi2sd	%edi, %xmm1
    	mulsd		%xmm1, %xmm0
    	cvttsd2si	%xmm0, %eax
    	#movl		%eax, -8(%rbp)
    	#movl		-8(%rbp), %eax
    	
    	leave
    	ret
    
    $ ./a.out 
    i=000 x=025 y=000
    i=001 x=024 y=000
    i=003 x=024 y=001
    i=005 x=024 y=002
    i=007 x=024 y=003
    i=010 x=024 y=004
    i=012 x=024 y=005
    i=014 x=024 y=006
    i=017 x=023 y=007
    i=019 x=023 y=008
    i=022 x=023 y=009
    i=024 x=022 y=010
    i=027 x=022 y=011
    i=029 x=021 y=012
    i=032 x=021 y=013
    i=033 x=020 y=013
    i=035 x=020 y=014
    i=037 x=019 y=015
    i=040 x=019 y=016
    i=041 x=018 y=016
    i=043 x=018 y=017
    i=044 x=017 y=017
    i=047 x=017 y=018
    i=048 x=016 y=018
    i=050 x=016 y=019
    i=051 x=015 y=019
    i=054 x=014 y=020
    i=056 x=013 y=020
    i=058 x=013 y=021
    i=059 x=012 y=021
    i=062 x=011 y=022
    i=064 x=010 y=022
    i=067 x=009 y=023
    i=069 x=008 y=023
    i=072 x=007 y=023
    i=074 x=006 y=024
    i=077 x=005 y=024
    i=079 x=004 y=024
    i=081 x=003 y=024
    i=084 x=002 y=024
    i=086 x=001 y=024
    i=088 x=000 y=024
    i=093 x=-01 y=024
    i=095 x=-02 y=024
    i=097 x=-03 y=024
    i=100 x=-04 y=024
    i=102 x=-05 y=024
    i=104 x=-06 y=024
    i=107 x=-07 y=023
    i=109 x=-08 y=023
    i=112 x=-09 y=023
    i=114 x=-10 y=022
    i=117 x=-11 y=022
    i=119 x=-12 y=021
    i=122 x=-13 y=021
    i=123 x=-13 y=020
    i=125 x=-14 y=020
    i=127 x=-15 y=019
    i=130 x=-16 y=019
    i=131 x=-16 y=018
    i=133 x=-17 y=018
    i=134 x=-17 y=017
    i=137 x=-18 y=017
    i=138 x=-18 y=016
    i=140 x=-276 y=016
    i=141 x=-270 y=015
    ...
    

    Woran liegt das nun wieder?


  • Gesperrt

    Ok, noch schnell abschließend...
    Da tritt ein integer overflow auf:
    cvtsi2sd %esi, %xmm1
    in esi steht der Winkel w (0 bis 720).
    esi ist ein 32-bit Register.
    cvtsi2sd interpretiert ein 32-bit Register als Vorzeichen Byte (-128 bis 127).
    man kann es mit movl umgehen:

    movl		%esi, 16(%rcx)
    cvtsi2sd	16(%rcx), %xmm1
    mulsd		%xmm1, %xmm0
    

    etwas unschön wegen der relativen Adressierung. Vielleicht andere Ideeen?

    Edit: Außerdem, da pushq und movq muss es dann leaveq heißen.



  • @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    cvtsi2sd interpretiert ein 32-bit Register als Vorzeichen Byte (-128 bis 127).

    Bitte was?



  • @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    call cos@PLT
    cvtsi2sd %edi, %xmm1

    https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI

    If the callee wishes to use registers RBX, RBP, and R12–R15, it must restore their original values before returning control to the caller. All other registers must be saved by the caller if it wishes to preserve their values.

    =>

    	push		%rdi
    	call		cos@PLT
    	pop		%rdi
    	cvtsi2sd	%edi, %xmm1
    

    Wobei...
    Das is vermutlich auch falsch, wegen

    Stack aligned on 16 bytes boundary. 128 bytes red zone below stack.

    Die red zone ist jetzt kein Problem, aber das 16 Byte Alignment halten wir jetzt nicht ein. Andrerseits scheinen sich GCC, Clang, ICC auch nicht so wirklich darum zu scheren und generieren Code mit bloss 1x push gefolgt von call. Ich bin verwirrt.

    Falls mir das jmd. erklären kann (also was das "Stack aligned on 16 bytes boundary" bedeutet/warum es dann doch kein Problem ist mit um 8 verschobenem Stack-Pointer eine andere Funktion aufzurufen...).


  • Gesperrt

    Dieser Beitrag wurde gelöscht!

  • Gesperrt

    Also an Pi könnte man auch so herankommen:

    #include <stdio.h>
    #include <math.h>
    
    double getpi()
    {
    	int i = 0;
    	double d = 1;
    	while (i <= 100000)
    	{
    		if ((i % 2) == 0)
    		{
    			d -= (1.0 / ((i * 2) + 3));
    		}
    		else
    		{
    			d += (1.0 / ((i * 2) + 3));
    		}
    		i++;
    	}
    	return d / 45.0;
    }
    
    int getxy(int b, int w)
    {
    	int x;
    	float w2 = w * getpi();
    	if (b)
    		x = (int)(25.0 * cos(w2));
    	else
    		x = (int)(25.0 * sin(w2));
    	return x;
    }
    
    void get_kreis(int to)
    {
    	int i = 0, x, y, x2, y2;
    	while (i <= to)
    	{
    		x = getxy(1, i);
    		y = getxy(0, i);
    		if (x != x2 || y != y2)
    		{
    			printf("i=%03d x=%03d y=%03d\n", i, x, y);
    		}
    		x2 = x;
    		y2 = y;
    		i++;
    	}
    	printf("ready.\n");
    }
    
    int main()
    {
    	get_kreis(720);
    }
    

    Das fände ich sogar noch besser, da man nicht durch 180 teilen muss. Aber das in Assembler macht keinen Spaß...


  • Mod

    ??? Was ist dein Ziel? Pi brauchst du gar nicht selber zu berechnen, außer das soll eine Übungsaufgabe für Assembler sein, Pi zu berechnen. Und dann wäre es geschummelt, den Cosinus einzusetzen.



  • @SeppJ Er berechnet Pi doch eh über so ne lustige unendliche Summenformel ganz ohne Cosinus.

    Wo du natürlich Recht hast: man braucht das nicht selbst zu implementieren, man kann auch einfach nen Taschenrechner/Wikipedia nehmen, die Zahl dann in die erstbeste IEEE float converter Webseite reinpasten und die Hex-Konstante aus dieser rauskopieren.

    Bzw. evtl. kann der Assembler der Wahl auch float/double Konstanten. Keine Ahnung, ich kenn ich mit Computern nicht aus 😄


  • Gesperrt

    @hustbaer sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    Wikipedia nehmen, die Zahl dann in die erstbeste IEEE float converter Webseite reinpasten und die Hex-Konstante aus dieser rauskopieren.
    Bzw. evtl. kann der Assembler der Wahl auch float/double Konstanten. Keine Ahnung, ich kenn ich mit Computern nicht aus

    Das habe ich ja getan. Also die float Konstante/Literal bestimmt; aber das Problem ist, x86_64 erlaubt glaube ich keine float Konstanten.

    @SeppJ sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    Was ist dein Ziel? Pi brauchst du gar nicht selber zu berechnen, außer das soll eine Übungsaufgabe für Assembler sein

    Das ist eine Übungsaufgabe für Assembler. Ich hatte auch drüber nachgedacht, nicht alles in Assembler zu schreiben, sondern nur die Methodenrümpfe in Inline-Assembler... um sich die Aufruf-Konventionen zu sparen. Aber es ist quasi genau so schwer, wie alles in Asm zu schreiben.


    Kann man denn sagen, die Assembler-Syntax ist immer von der konkreten Architektur des Prozessors abhängig?
    Und was war zuerst da, Asm oder C?



  • @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    , x86_64 erlaubt glaube ich keine float Konstanten.

    Das hat nichts mit der Architektur zu tun, sondern mit dem Assembler (das Programm)

    Kann man denn sagen, die Assembler-Syntax ist immer von der konkreten Architektur des Prozessors abhängig?

    Ja. Und vom verwendeten Assembler (Programm).

    Und was war zuerst da, Asm oder C?

    Seit wann gibt es Computer?
    Die allerersten hatten sicher kein Assembler

    Seit wann gibt es C?
    Vor C gab es schon Fortran.



  • Zitat Wikipedia:

    C ist eine imperative und prozedurale Programmiersprache, die der Informatiker Dennis Ritchie in den frühen 1970er Jahren an den Bell Laboratories entwickelte. Seitdem ist sie eine der am weitesten verbreiteten Programmiersprachen.

    Der erste Assembler wurde zwischen 1948 und 1950 von Nathaniel Rochester für eine IBM 701 geschrieben.


  • Gesperrt

    Hier ist getpi in einer für mich etwas besser lesbaren Form,

    	.globl	getpi
    	.def	getpi;	.scl	2;	.type	32;	.endef
    	.seh_proc	getpi
    getpi:
    	pushq		%rbp
    	movq		%rsp, %rbp
    	pushq		%r12
    	pushq		%r13
    	pushq		%r14
    	subq		$32, %rsp
    
    	movq		$0, %r12
    	movq		$1, %r14
    	cvtsi2sd	%r14, %xmm3
    	jmp	.L2
    .L5:
    	movq		%r12, %r13
    	andq		$1, %r13
    	testq		%r13, %r13
    	jne	.L3
    	movq		%r12, %r13
    	addq		%r13, %r13
    	addq		$3, %r13
    	cvtsi2sd	%r13, %xmm0
    	movq		$1, %r14
    	cvtsi2sd	%r14, %xmm1
    	divsd		%xmm0, %xmm1
    	subsd		%xmm1, %xmm3
    	jmp	.L4
    .L3:
    	movq		%r12, %r13
    	addq		%r13, %r13
    	addq		$3, %r13
    	cvtsi2sd	%r13, %xmm0
    	movq		$1, %r14
    	cvtsi2sd	%r14, %xmm1
    	divsd		%xmm0, %xmm1
    	addsd		%xmm1, %xmm3
    .L4:
    	addq		$1, %r12
    .L2:
    	cmpq		$100000, %r12
    	jle	.L5
    	movq		$45, %r14
    	cvtsi2sd	%r14, %xmm1
    	divsd		%xmm1, %xmm3
    	movsd		%xmm3, %xmm0
    
    	addq		$32, %rsp
    	popq		%r14
    	popq		%r13
    	popq		%r12
    	popq		%rbp
    	ret
    	.seh_endproc
    

    aber was bezweckt das allozieren mit subq $32, %rsp?


    Und was gab es vor Assembler?


  • Mod

    @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    Und was gab es vor Assembler?

    Da hat man halt direkt in der Maschinensprache geschrieben, also dem Binary das jetzt raus kommt, wenn du dein Assemblerprogramm übersetzt.

    https://en.wikipedia.org/wiki/First-generation_programming_language



  • assemblersprache ist ja grob gesagt auch nur die ersetzung der zahlenwerte der opcodes durch buchstabenkombinationen, die man sich besser merken kann.



  • @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    aber was bezweckt das allozieren mit subq $32, %rsp?

    Es macht Platz für 32 Bytes auf dem Stack. Das ist genau das was ich die ganze Zeit meine, beschäftige Dich lieber erst mit den Basics.



  • @Wade1234 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    assemblersprache ist ja grob gesagt auch nur die ersetzung der zahlenwerte der opcodes durch buchstabenkombinationen, die man sich besser merken kann.

    Die ganze Adressberechnung für Sprungziele, Konstanten use. werden auch gemacht.


  • Gesperrt

    @Swordfish sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    aber was bezweckt das allozieren mit subq $32, %rsp?

    Es macht Platz für 32 Bytes auf dem Stack. Das ist genau das was ich die ganze Zeit meine, Beschäftige dich lieber erst mit den Basics.

    Das ist genau das was ich die ganze Zeit mache. 🙄 Aber Danke dennoch für den hilfreichen Kommentar. 🤭



  • @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    Das ist genau das was ich die ganze Zeit mache. 🙄

    Na dann. 👍🏻

    @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    Aber Danke dennoch für den hilfreichen Kommentar. 🤭

    @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    aber was bezweckt das allozieren mit subq $32, %rsp?

    Ach so, Du willst wissen warum?

    @EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):

    	pushq		%rbp
    	movq		%rsp, %rbp
    	pushq		%r12
    	pushq		%r13
    	pushq		%r14
    

    Wie viele Bytes werden hier gepusht? (Es sind 4 QWORDs ...)

    // edit:

    WORD = 16 bit (2 bytes)
    DWORD = 32 bit (4 bytes)
    QWORD = 64 bit (8 bytes)

    auf heutzutage gängigen desktop-archtekturen. Und das letzte mal als ich nachgesehen habe war 8 x 4 = 32.


Anmelden zum Antworten