C und Assembler



  • Hallo zusammen,

    ich schreibe gerade ein kleines Programm(es gibt keinen Sinn,doch zum üben für ne Klausur ganz gut 🙂 ) und habe leider ein paar Probleme.

    Das Programm soll im C - Modul zwei Summanden einlesen, diese dann an Assembler übergeben damit das Assembler Modul die Summe bilden kann. Ich bin noch NEWBIE und habe es deshalb noch nicht wirklich drauf. Deswegen sind mir so einige Dinge auch noch unklar...

    Also, die Zahlen weden ganz normal eingelesen, doch nach dem Aufruf der Assembler Prozedur kackt das Programm ab.

    Hier mal der Code...

    #include <stdio.h>
    
    int main(void){
    
    	extern signed int A_ASM;
    	extern signed int B_ASM;
    	extern int SUMME;
    	extern unsigned int ADDIER(signed int,signed int); // Prototyp
    
    	printf("Bitte ersten Wert eingeben:\n");
    	scanf("%d",&A_ASM);
    
    	printf("\nBitte zweiten Wert eingeben:");
    	scanf("%d",&B_ASM);
    
    	SUMME = ADDIER(A_ASM,B_ASM);
    
    	printf("\nDie Summe lautet: %d",SUMME);
    
    	return(0);
    
    } // Ende "main()"
    

    Und nun das ASM Modul

    IDEAL
    	DOSSEG
    	MODEL SMALL
    	STACK 100h
    	DATASEG
    
    public  _A_ASM
    public  _B_ASM
    public  _SUMME
    
    _A_ASM DB ?
    _B_ASM DB ?
    _SUMME DB ?
    
    	CODESEG
    
    PUBLIC _ADDIER
    
    PROC _ADDIER NEAR
    
    	push bp
    	mov bp,sp	; Stackrahmen wurde erzeugt
    
    	push ax
    	push bx
    
    	mov al,[_A_ASM]	; B_ASM >> ax
    	mov bl,[_B_ASM]   ; A_ASM >> bx
    	add ax,bx       ; B_ASM + A_ASM
    	mov [_SUMME],al
    
    	pop bx
    	pop ax
    
    	pop bp
    	mov sp,bp
    
    	ret
    
    ENDP _ADDIER
    
    	END
    

    Ihr könnt mir sicherlich nen Tip geben... CIAO



  • AX musst du nicht sichern, nur BX, SI und DI.

    Wieso sind die Variablen alle innerhalb von main() deklariert? Hab gar nicht gewusst, dass das geht.

    Du greifst in C auf die Variablen als int zu (nachdem's 16bit-Code ist, ist ein int vermutlich 16 Bit groß), im asm aber als Byte, rechnest aber dann trotzdem mit AX und BX.

    Das mov sp, bp vor dem ret ist unnötig. Wenn schon, dann macht man es vor dem pop bp, nicht danach.

    Das STACK am Anfang gehört weg, das macht eh die C Library.

    Wenn du eine Funktion hast, dann muss das Funktionsergebnis in AX zurückkommen. Das tust du nicht. Du kannst dir dann auch das Schreiben nach _SUMME sparen.



  • Hallo,

    in deinem C-Programm liesst du zwei integer Werten ( 32bit) ein, und versuchst an Assembler-Procedur zu übergeben. In deinem Assembler-Modul deklarierst du aber zwei 16bit-Variablen. db = Define Byte, was nur einem Byte
    entspricht.
    Also du versuchst zwei 32bit Werten in 8bit Variablen zu packen, und das geht natürlich nicht.

    Deklarriere die einfach als DWORD und zwar so:

    ZAHL1 dw ? ; Define WORD
    ZAHL2 dw ? ; Define WORD

    Grüße
    Peter



  • ausserdem musst du den erweiterten RET befehl benutzen, da du parameter auf den stack pushst, diese müssen wieder runter vom stack sonst wird mit ret die falsche rücksprung adresse geladen, dein programm springt also ins nirgendwo und stürzt ab. wenn du also 2 word parameter hast dann musst du RET 4 benutzen. die 4 steht für 4 byte die vom stack gepopt werden (2 worte = 4 byte).



  • Nein, nur wenn er es mit pascal convetion (__stdcall) deklariert, was er aber nicht hat.



  • Moin, Moin...

    @PeterIII:

    Das Programm ist wohl für DOS geschrieben. Unter DOS sind int-Werte 16 Bit breit und nicht 32 Bit!!!!

    @waxalot:

    Du möchtest C und Assembler gemeinsam nutzen, dann musst Du auch die Konventionen einhalten. Die Variablen in dem Assemblermodul zu definieren, ist schlechter Stil. Die Parameter für die Funktion werden über den Stack übergeben und nicht nach Deiner Methode.

    Meine Lösung:

    #include <stdio.h>
    
    unsigned int addier(unsigned int, unsigned int);
    
    int main(void)
    {
      unsigned int var_a, var_b, summe;
    
      printf("Bitte ersten Wert eingeben:\n");
      scanf("%d",&var_a);
    
      printf("\nBitte zweiten Wert eingeben:");
      scanf("%d",&var_b);
    
      summe = addier(var_a, var_b);
    
      printf("\nDie Summe lautet: %d", summe);
    
      return 0;
    }
    

    Das Assemblermodul:

    ;
    ; tasm /zn /ml modul.asm
    ;
    
    IDEAL
    DOSSEG
    MODEL SMALL
    ; STACK 100h -> unnötig
    
    CODESEG
    
    PUBLIC _addier
    
    PROC _addier NEAR
    
        push bp
        mov bp,sp    ; Stackrahmen wurde erzeugt
    
        push bx
    
        ; Der Aufbau des Stacks:
        ; bp + 0 -> bp (gesichert durch obiges push bp)
        ; bp + 2 -> Rücksprungadresse (nur Offset, wg. Speichermodell SMALL)
        ; bp + 4 -> 1. Parameter der Funktion addier
        ; bp + 6 -> 2. Parameter der Funktion addier
        ;
        ; C legt die Parameter einer Funktion von rechts nach links auf
        ; den Stack.
        ; Die RUFENDE Funktion muss den Stack wieder aufräumen!!!!
        ; Nicht, wie wasily vorschlägt, ret 4 benutzen!!!!
    
        mov ax, [bp + 4]    ; 1. Parameter vom Stack holen
        mov bx, [bp + 6]    ; 2. Parameter vom Stack holen
        add ax, bx
    
        pop bx
    
        mov sp, bp
        pop bp
    
        ret
    
    ENDP _addier
    
    END
    

    Die Namen der Variablen und der Funktion habe ich klein geschrieben. Daher muss das Assemblermodul auch mit der Option /ml(case sensitiv) assembliert werden.

    Getestet habe ich alles mit Borland Turbo C++ 2.0. Lief ohne Probleme!

    Ciao...



  • Danke erstmal alle zusammen,

    @Kal El

    Sehe ich es dann richtig, dass der Wert, der zuletzt in AX steckt als Rückgabewert der Funktion gilt?



  • Wie übergebe ich denn dann z.B. einen "long int" Wert?

    Den muss ich ja über zwei Register verteilen...
    Z.B. DX:AX oder so...



  • Genau so ist es auch.

    Solche Sachen probier ich am liebsten einfach mit dem C Compiler aus. Ich schreib mir eine Funktion

    long a() {
        return 0x12345678L;
    }
    

    oder so und schau mir den erzeugten Code an. Daraus wird meistens recht schnell klar, wie man's machen muss.



  • @waxalot:

    Noch ein Nachtrag:

    Du kannst auf den Unterstrich vor dem Funktionsnamen verzichten, wenn Du in der MODEL-Direktive die Sprache angibst(also C) für die das Modul gedacht ist.

    MODEL SMALL, C

    Der Assembler stellt die Unterstriche dann automatisch voran.

    Ciao...



  • Hallo mal wieder =),

    ich komme einfach nicht dahinter wie ich in Assembler zwei "long int" Werte addieren kann. Zuerst dachte ich, dass ich die 4 Byte Werte nacheinander vom Stack hole und in Variablen(double word) packe und diese anschließend addiere. Leider ist mir dann durch ein Buch aufgefallen, dass "add" mit zwei Speicherstellen nicht gehen soll. Wie macht man das denn nun am besten?

    Und noch eine Frage: Falls ich mir einen Teil des "long int" Wertes (2 Byte) vom Stack hole und diesen nun in eine "variable (4 Byte)" Zwischenspeichern möchte, wie stelle ich dass an?
    Mit "mov variable,[word ptr bp+4]" geht es nicht. Da meckert mein Compiler nur 😡

    CIAO



  • Addieren, alles am Stack. Die richtigen offsets musst du dir dann schon selber anschauen.

    mov ax,[bp+4]
    mov dx,[bp+6]
    add ax,[bp+8]
    adc dx,[bp+10]
    mov [bp+12],ax
    mov [bp+14],dx
    

    word ptr [bp+4] ist die richtige Syntax. Aber ein Befehl kann immer nur einen Speicheroperand verwenden.

    EDIT:

    Wenn du eine Funktion

    long addlong(long a, long b) {
        return a+b;
    }
    

    geschrieben und den Code angeschaut hättest, wärest du da wahrscheinlich auch selber draufgekommen.


Anmelden zum Antworten