Przekazywanie tablicy dynamicznej z C++ do assembelra

0

Problem podany jak powyżej. Moduł główny stworzony jest w C++, a moduł zewnętrzny w assemblerze, ma on za zadanie zwrócić średnią z tablicy. Za nic nie potrafie dobrze odnieść się do poszczególnych parametrów. Kod się kompiluje.

Kod z C++ (borland C++):

#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>


extern "C" double srednia(double *,int);
extern "C" double il_znakow();

int main()
{
    int r;

    cout << "\t Obliczanie sredniej wartosci tablicy. \nPodaj rozmiar tablicy: ";
    cin >> r;
    double *tab = new double [r];

     for( int i=0;i<r;i++)
    {
        cout << "Element " << i+1 << ": ";
        cin >> tab[i];
    }

    float srednia1 = srednia(tab,r);
    printf("%e", srednia1, "\n");

return 0;
}

a tutaj assembler (Turbo Assembler w Dosie):

.MODEL SMALL, C
	.386
	PUBLIC SREDNIA
	
	.DATA
suma      		 	 DD 0.0
dzielnik			 DD ? 
pom					 DD ?
srednia				 DD ?
		
	.CODE
START:	
		
SREDNIA PROC 
	finit            ; definicja koprocesora
		push ebp
		mov ebp,esp
		xor ecx, ecx
		xor eax, eax
		mov ecx, [ebp+4]        ; pobranie wielkosci tablicy
		mov dzielnik, ecx          ; zapisanie wielkosci tablicy jako dzielnik
		fld suma                        ; zapisanie sumy na szycie koprocesora
	
DODAWAJ:
		mov eax,[ebp+6]       ; wyraz tablicy
		mov pom, eax
		fadd pom                    ; dodawanie elementow tablicy do sumy
		inc ebp                       ; dostanie sie do kolejnego wyrazu tablicy ???
		loop DODAWAJ
		 
		fdiv dzielnik               ; podzielenie st0 przez ilosc elementow
		fst srednia 
		mov eax, srednia
		
		mov esp,ebp
		pop ebp     	
	ret
SREDNIA ENDP

.STACK 100h
END START
0

Dawno nie pisałem w czystym asemblerze, ale:

mov ecx, [ebp+4] ; pobranie wielkosci tablicy


Tu chyba powinieneś "przeskoczyć" zachowany EBP i adres powrotu, więc po winno być +8. Zresztą prawdopodobnie pomyliłeś też kolejność argumentów. Przy konwencji `__cdecl` w  `[ebp  + 8]` będziesz miał wskaźnik na tablicę (czyli pierwszy argument funkcji; argumenty odkładane są stos od końca). 

>```asm
mov eax,[ebp+6]       ; wyraz tablicy

jw. Zastanawia mnie jedna rzecz. Kod asm jest 32-bitowy, a z offsetów wynika, że int jest 16-bitowy (choć czytasz go jak 32-bitowy) - trochę to dziwne.

fst srednia
mov eax, srednia


To chyba jest zbędne. Z tego, co pamiętam, wartości zmiennoprzecinkowe zwracane są w rejestrze `st0`.

>```asm
mov eax,[ebp+6]       ; wyraz tablicy
...
inc ebp                       ; dostanie sie do kolejnego wyrazu tablicy ???

Ojojoj, kolejny wyraz tablicy jest sizeof(double) bajtów dalej, a nie jeden bajt.

0

dzięki za wskazówki. Udało mi się przekazać do ecx rozmiar tablicy, ale nie mam pojęcia jak to dalej ugryźć. W debugrze na stosie znajduje się podany rozmiar tablicy, a wartości stablicowanych nie moge za nic odnaleźć. Teraz kod wyglada tak:

...

.CODE
START:	
		
srednia:
	finit
		push ebp
		mov ebp,esp
	
		mov ecx, [ebp+6]    ; 2 bajty na powrot i 4 na ebp
		mov dzielnik, ecx
		add ebp, 10            ; nie wiem ile tu powinienem dodać żeby dostać się do pierwszej wartości tablicy ???
		fld suma
	
DODAWAJ:
		mov eax,[ebp]
		mov pom, eax
		fadd pom
		add ebp, 4         ; zwiekszam o 4 bajty zeby przejsc do kolejnej wartosci tablicy
		loop DODAWAJ
		 
		fdiv dzielnik

		mov esp,ebp
		pop ebp     	
	ret

END START

I tak jak mówiles, zmieniłem w C++

float srednia1 = srednia(r,tab);
0

Ja tu widzę dziwną hybrydę 16 i 32 bitów. Wyjaśnij może:

  • co to za wersja Borland C++ i Turbo Assemblera
  • jak to kompilujesz i linkujesz (jakie polecenia)
0

mov ecx, [ebp+6] ; 2 bajty na powrot i 4 na ebp


To jak, adres powrotu jest 16-bitowy, a wskaźnik na ramkę stosu 32-bitowy?

>```asm
add ebp, 10            ; nie wiem ile tu powinienem dodać żeby dostać się do pierwszej wartości tablicy ???

W 32-bitowym asemblerze przy 32-bitowym typie int IMO drugi argument, czyli wskaźnik do tablicy, powinien być [EBP + 12]. Zamiast dodawać coś do rejestru EBP, użyj innego, np. ESI:

        mov esi, [ebp + 12]
DODAWAJ:
        fadd dword ptr [esi] ; zakładam, że tablica jest float[]
        add esi, 4
        loop DODAWAJ

Zdecyduj się w jakim asemblerze piszesz: 16- czy 32-bitowym. Tak jak @Azarien wspomniał, mieszasz te dwie "rzeczywistości", co prawdopodobnie jest powodem Twoich problemów.

0

extern "C" double srednia(double *,int);

takie coś będzie mieć nazwę: _srednia, w asm.
i taką funkcję musisz tam zdefiniować - nie żadne SREDNIA - tak byłoby w wersji stdcall, a nie w C.

0

ponadto: wyniki z funkcji w C są zwracane w rejestrze ax/eax, ale floaty poprzez stos FPU.

w przypadku:
double/float fun(....)

wynik ma być pozostawiony na stosie - w ST0!

1 użytkowników online, w tym zalogowanych: 0, gości: 1