Wywołanie w C++ funkcji zaimplementowanej w Asemblerze

0

Mój program (tylko taki testowy, nic konkretnego nie robi) korzysta z funkcji asemblerowej. Wygląda to w ten sposób:
main.h:

 
#pragma once
#include <Windows.h>
extern "C" int Dodaj( int* tab, int a1, int a2);

main.cpp:

 
#include <iostream>
#include "naglowek.h"

using namespace std;

int main()
{
    int result = 0;
    int tab[2] = {2,3};
    result = Dodaj(tab, 2, 2);
    std::cout << result;
    getchar();
    return 0;
}

Natomiast procedure.asm:

.386 

.MODEL FLAT, C

.DATA

a1 dd 0
a2 dd 0

.CODE

Dodaj proc
    push ebp
    mov ebp, esp

    xor eax, eax
    mov ebx, [ebp+8]
    mov edx, [ebp+12]
    mov ecx, [ebp+16]
    mov [a1], edx
    mov [a2], ecx
    xor ecx, ecx
petla:
    cmp ecx, a2
    jae koniec_petli
       
    add eax, [ebx+4*ecx]
  
    inc ecx
    jmp petla

    koniec_petli:
    pop ebp
    ret
Dodaj endp

END Dodaj

Jeszcze jest plik .def ale nie wrzucam. Funkcja ma dodawać elementy tablicy. Testuje to tylko po to, żeby potem w przypadku trudniejszej procedury nie wyłożyć się na podstawach. Tak jak tutaj właśnie się wyłożyłem.

No i problem jest taki:

  1. Jeśli w pętli porównuje cmp ecx, a1 to niby dobrze.
    EDIT: W registers sprawdziłem to w EDX jest 00000001 niezależnie czy wprowadzę jako parametr 1 ,2 czy 10. Przez debugger sprawdzam i pętla zawsze wykonuje się tylko raz. A wynik jest taki jakby wykonała się tą zadaną ilość razy o.0 nie rozumiem.
  2. Jeśli w pętli porównuje cmp ecx, a2 to wywala access violation przy dodawaniu do EAX.

Nie umiem stwierdzić, czy błąd jest w kwestii przekazania argumentów do procedury czy dopiero potem w kodzie.

0

Powinno być:

petla:
    cmp ecx, [a2]

Bo zapewne chodziło ci o zawartość a2 a nie o adres.

PS. Kod da się znacznie uprościć:

.386 
 
.MODEL FLAT, C
 
.CODE
Dodaj proc
   push ebp
   mov ebp, esp
    
   mov ebx, [ebp+8]
   mov ecx, [ebp+12]
   xor eax, eax
    
   petla:
      add eax, [ebx-4+4*ecx]
   loop petla
   pop ebp
   ret
Dodaj endp
 
END Dodaj
0

Dzięki za uproszczenie :) Ale problem dalej jest z pobraniem 3 parametru. Gdybym tą część:

....
   mov ebx, [ebp+8]
   mov ecx, [ebp+12]
   xor eax, eax
    
   petla:
      add eax, [ebx-4+4*ecx]
...

zamienił tą:

....
   mov ebx, [ebp+8]
   mov edx, [ebp+12]
   mov ecx, [ebp+16]
   xor eax, eax
    
   petla:
      add eax, [ebx-4+4*ecx]
...

To wyskakuje access violation na

 add eax, [ebx-4+4*ecx]

.
Wczytywanie parametrów raczej dobrze, bo właśnie sprawdziłem wartości ecx i edx po pobraniu ich i maja dokładnie takie jakie były przesłane. Ale z jakiegoś powodu jak odwołuje się do ecx zapełnionego drugim parametrem, a nie pierwszym to wyskakuje błąd ;/

W ogóle to chyba temat się nadaje do działu "inne języki programowania".

0

Ciekawe. Na pewno funkcję wywołujesz tak?

result = Dodaj(tab, 2, 2);

Jeśli tak to sprawdź debuggerem jaką wartość ma rejestr ecx.

0

EBX = 71890000
EDX = 00000001
ECX = 0037FCF8

Takie wartości są zaraz po instrukcjach mov. A więc zdecydowanie nie to co ma być. Ale co dziwnego się dzieje to to, że jak tak sobie cisnę tym debuggerem to wchodzi w "Disassembly" w Visualu po czym znowu wraca do tej mojej procedury i wtedy już:
EDX = 00000002
ECX = 00000002

Tak jakby przed w ogóle wywołaniem procedury w result = Dodaj(tab, 2, 2); ona się wykonuje samoczynnie o.0 Postawiłem breakpoint przed wywołaniem tej procedury i pojawił się error jeszcze przed wejsciem na breakpointa. A znacznik był w pliku. asm.. Czyli w jakiś sposób musi się ta procedura uruchamiać bez wywołania jej.

1

Podrzuć mi skompilowaną binarkę. Nie mam teraz pod ręką Windowsa, ale może coś zauważę.

0

Nie do końca wiedziałem co mam posłać, więc daje cały projekt. http://www.sendspace.com/file/787odc Ma ponad 10mb, więc jak mi wyjaśnisz co mam wyodrębnić z projektu to Ci wyślę.

Taka luźna dygresja - może program się nie zaczyna od maina tylko od procedury w .asm? Mogłem coś takiego przypadkowo ustawić w properties tych dwóch projektów? Bo solucja się składa z dwóch projektów. Jeden zawiera .asm i .def, drugi .h i .cpp

.exe: http://www.sendspace.com/file/8iq20p

0
Mossar napisał(a):

Taka luźna dygresja - może program się nie zaczyna od maina tylko od procedury w .asm? Mogłem coś takiego przypadkowo ustawić w properties tych dwóch projektów?
Tak, po raz pierwszy funkcja Dodaj jest wywoływana przez DllEntryPoint.

0

Ostatnie moje pytanie. Udało mi się przekazać do procedury dwuwymiarową tablicę oraz utworzyć w .asm nową tablicę dwywymiarową:

msk dd 500 dup(500 dup (0))

Następnie wykonuje wszelkie przekształcenia i chce zwrócić tą tablicę z powrotem do C++. Robię to w ten sposób:

lea eax, msk
ret

I teraz zaczynają się schody.

extern "C" int* __stdcall Dodaj(int* tab, int hei, int wid); 
 
int tab[100][100];
int *tab2;
//...
tab2 = Dodaj(&(tab[0][0]), 100,100);
    
    for(int k = 0; k <100 ; k++)
    {
        for(int l = 0; l <100; l++)
        {
            result += tab2[k*l+l]; //tutaj mam pytanie, czy muszę odbierać tą tablicę jako tablicę jednowymiarową i przechodzić po 
                                         //niej w taki cwaniacki sposób czy dam radę jakoś tak to ustawić, żebym mógł 
                                         //normalnie działać na dwuwymiarowej
        }
    }

A więc teoretycznie działa, ale w ramach czytelności kodu chciałbym, żeby ta tab2 była dwuwymiarowa, a nie jednowymiarowa, udająca dwuwymiarową.
Także bardziej jest to pytanie natury C++owej i aż mi wstyd, że nie potrafię tego rozwikłać. Oczywiście mógłbym napisać funkcje, która by przekształcała tą jednowymiarową tablicę w dwuwymiarową, ale pewnie musi się dać to zrobić od razu.

0

Wszystkie tablice de facto są jednowymiarowe po zejściu na poziom asemblera.Tak więc ciąg 12 bajtów może robić za tablicę char[12],albo [2][6] tudzież [3][4],ba,nawet i [3][2][2].Cała ta wymiarowość tablic jedynie ułatwia obliczanie adresu do zapisu/odczytu.

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