Tablice w assemblerze

0

Witam.

Pisze program w C# z dllką w asemblerze i próbuję obliczyć rozmiar przesłanej tablicy.
Siedzę nad tym juz z 3 godziny i dalej nie wiem czemu przy podaniu jakiegoś stringa funkcja raz działa a raz sie wysypuje:

kod c#

[DllImport("bibliotekaASM.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int zlicz(string tab);

private void button4_Click(object sender, EventArgs e)
{

         int pom=0;
        string tab = "1111111fdgsdgdgd";

        pom = zlicz(tab);


    }



```asm


zlicz proc 


push ebp
mov  ebp, esp
 
mov  ebx, [ebp+8]           ; początek tablicy znaków

xor eax, eax

sprawdz:

cmp byte ptr[ebx],0    ; sprawdzmy czy koniec znaków 
je koniec
inc ebx
inc eax 
jmp sprawdz
koniec:	

pop ebp

ret 

zlicz endp

Gdyby ktoś znalazł jakiś błąd którego ja nie dostrzegam byłbym wdzięczny.

0

A ten C# string to jest faktycznie gołe char* zakończone nullbytem?

0

jeśli podam tablice charów z 4 elementami program wyrzuca ilość elementów = 16. ale tak samo raz dziala a raz wywala error:
"An unhandled exception of type 'System.ExecutionEngineException' occurred in GUI.exe"

Pierwszy raz spotykam się z czymś takim, że program dla jednego stringa działa, a dla drugiego już nie, albo dla jednego stringa raz działa a raz nie ......

1

Nie no jak podasz tablice charów z 4 elementami i nie zakończysz jej 0 to cuda ci zapewne wypisze. Zrób może POPRAWNE dane dla tego zadania najpierw? Bo teraz to ja przypuszczam że ten twój program skanuje jakieś losowe obszary pamięci i wyniki które zwraca są zależne od tego co tam akurat leży ;]

0

Najlepiej gdyby mi w 100% przypadkach działało na stringach różnych długości. Dla przykładu, moja funkcja dla stringa o ilości elementów 554 zawsze prawidłowo działa ZAWSZE. To jest po prostu niemożliwe, żeby dla innych nie działało.......

0

Już wiem od czego zależy to, że program się wysypuje. Gdy string jest odpowiednio długi (powyżej 100 znaków 128????) program działa ze stringami poniżej 128 znaków bywają kłopoty. Czy ktoś wie o co tu chodzi?

0
ret 4
1

A ten C# string to jest faktycznie gołe char* zakończone nullbytem?

Nie, ale (domyślny) marshaling zamienia to na gołe char* zakończone nullbytem. Jest tez sporo innych opcji marshalowania, do wyboru zależy co kto lubi.

Nie no jak podasz tablice charów z 4 elementami i nie zakończysz jej 0 to cuda ci zapewne wypisze.

A char[] to już w ogóle sie inaczej marshaluje, nie idź tą drogą.

mov  ebx, [ebp+8]           ; początek tablicy znaków

A to ebx to dlaczego nadpisujesz? Bo domyślna calling convention dla .net to stdcall (zresztą specyfikujesz ją samemu), a w stdcall nie masz prawa zmienić wartości ebx w funkcji (eax, ecx i edx owszem).
Dodaj push ebx na początku i pop ebx na końcu.

ret 4

Dokładnie.
Tylko bez tego ten program nie powinien nigdy w ogóle działać. Pewnie proc samo to już zrobiło (nie wiem czy to możliwe, jestem trochę fuzzy jeśli chodzi o makra masmowe)?

Btw. uwagi merytoryczne:
cmp byte ptr[ebx],0 ; sprawdzmy czy koniec znaków
test x x jest szybsze/krótrze/lepsze od cmp x, 0 (wymaga pomocniczego rejestru)

mov ebx, XYZ
xor eax, eax
...
; w pętli
inc ebx
inc eax
...

Zamiast tego wersja bez nadmiarowego dodawania i rejestru:

mov ebx, XYZ
...
; w pętli
inc ebx
...
mov eax, ebx
sub eax, XYZ
0

Niestety nic to nie pomogło ;/ dalej dla krótkich stringów nie działa, a dla długich śmiga. Na innym forum dostałem podpowiedź, że muszę zmarshalować tablicę do ANSI string używając:

[DllImport("bibliotekaASM.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int zlicz([MarshalAs(UnmanagedType.LPStr)] String tab);


niestety z tym również taki sam efekt;/  z marshalowaniem zmieną rejestrów również nie działa
0

Niestety nic to nie pomogło ;/ dalej dla krótkich stringów nie działa, a dla długich śmiga. Na innym forum dostałem podpowiedź, że muszę zmarshalować tablicę do ANSI string używając:

Bezsensowna podpowiedź bo to nic nie zmieni - domyśla wartość jest ok (domyślnie jest UnmanagedType.LPTStr, ale dla ASCII te opcje są równowazne).

Spróbuj zobaczyć co sie dzieje natywnie na poziomie asemblera podczas debugowania. Albo podeślij tą dllkę (i najlepiej to .exe w którym z niej koszystasz) to mogę sam to zrobić (jeśli nie czujesz się dobrze z natywnymi debuggerami). Tak będzie chyba najprościej/najszybciej.

0

Jeśli masz czas to mogę ci wysłać na maila cały projekt z VS.

0

Tylko bez tego ten program nie powinien nigdy w ogóle działać. Pewnie proc samo to już zrobiło (nie wiem czy to możliwe, jestem trochę fuzzy jeśli chodzi o makra masmowe)?
Na pewno nie, bo w takiej formie jak użyto przecież nie wie ile jest parametrów.
Możliwe że .NET ma własny stos i po wywołaniu funkcji natywnej robi mov sp, coś tam przywracając swój.

0

Czyżby projekt na politechnike śląską z JA ? :) Jeśli tak to pisz na pw, w zeszłym roku walczyłem z tym samym problemem. Z chęcią wyjaśnię.

0

wie ktoś może jak zapisać dane do tablicy w asm. mam coś takiego w c#

[DllImport("bibliotekaASM.dll", CallingConvention = CallingConvention.StdCall)]
private static extern char wypmacasm(string tabela, string tab2);

w asm:
```asm
wypmacasm proc tab: DWORD, tab2: DWORD

mov esi, tab
mov eax, DWORD ptr[esi]          //w eax mam pierwszy element tablicy

ret
wypmacasm endp

i jak teraz tą wartość z rejestru eax zapisać do np pierwszego elementu tablicy tab2 ?

0

Tak samo jak odczytałeś?

mov edi, tab2 ;albo jakis inny rejestr, ważne żebyś mógł nim latać po pamięci
mov dword ptr [edi], eax
0

Dzieki, nie wpadłem na to:D ale to rozwiązanie działa dla np zmiennej typu int, którą wysyłam do procedury przez referencję i w procedurze asm robie np

mov eax,50
mov edi, zmiennaint 
mov dword ptr [edi], eax

i to działa, gdy chcę wypisać w c# tą zmienna to wypisze się 50, ale jeśli chodzi o stringa(wysyłany również przez referencję piszę w c# więc jest to słowo ref) to taki zapis:

mov esi, tab
mov eax, DWORD ptr[esi]
mov edi, tab2
mov dword ptr [edi], eax


powinien zamienic pierwszy element w tab2 pierwszym elementem z tab1, ale wywala błędy,
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll

Additional information: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.

Czemu tak się dzieje ktoś wie ?
0

Nie jest prawdą to co mówisz bo nie rozumiesz chyba że operandy maja INNY ROZMIAR. eax ma 32 bity / 4 bajty a jeden znak char ma 1 bajt. Robisz tam dword ptr więc traktujesz wartość pod podanym adresem jako dworda czyli double word czyli 4 bajty. Ale jeśli twoja tablica nie ma 4 bajtów / jesteś na indeksie który jest dalej niż 4 pola od końca tablicy to wyskoczysz poza tablicę.

0

Czyli w asm korzystać z rejestrów 1 bajtowych np AL ? a dwordy pozamieniać na byte ?

0

No jeśli chcesz kopiować pojedyncze bajty to tak, korzystaj z 1bajtowych rejestrów i byte ptr na wskaźnikach

0

No tak tylko wtedy nie mogę nic wyciągnąć z tablicy ani wrzucić do niej bo muszę korzystać z rejestrów 2 bajtowych bx, si, di albo bp a jak zrobię tak:

wypmacasm proc tab: word, p: word
mov si, tab
mov ax, word ptr[si]      /////////tutaj crash
mov di, p
mov word ptr [di], ax
ret 
wypmacasm endp

to dalej wywala przy mov ax.... a przy dword to przechodziło i działało poprawnie, jeszcze wcześniej abym mógł używać rejestrów 2bajtowych musiałem zmienić dyrektywę na .model small z .model flat. Nie da się jakoś tego zrobić na dword aby kopiowało mi tą wartość ?

tutaj wywala dopiero przy ret

mov esi, tab
mov eax, dword ptr[esi]
mov edi, p
mov dword ptr [edi], eax

tutaj nic nie wywala ale nic nie kopiuje do 1 elementu tablicy

mov esi, tab
mov eax, dword ptr[esi]
mov edi, p
mov dword ptr [edi]+4, eax
0

Chłopie czy ty masz JAKIEKOLWIEK pojęcie o asemblerze? Bo mam wrażenie że nie a próbujesz coś w nim zrobić. Chociaż C jakos ogarniasz? o_O Słabo mi...

Rozumiesz jaka jest różnica między wskaźnikiem a obiektem wskazywanym? Bo ewidentnie dla ciebie to jest jedno i to samo.

mov si, tab

WTF? Kto ci kazał zmieniać z jakiegoś powodu rejestr w którym przechowujesz adres tablicy? Niezaleznie od tego czy w tablicy masz bajty, inty czy jeszcze coś innego, wskaźnik ma cały czas TEN SAM ROZMIAR. A ty z jakiegoś powodu uznałeś że skoro elementy tablicy będą miały mniej niż 4 bajty to wskaźnik też. o_O

Poza tym nadal to jest źle bo word to 2 bajty a nie jeden.

W twoim przypadku, skoro używasz 32 bitowego asemblera, wskaźniki mają 32 bity (4 bajty) więc są dwordami.

0
Shalom napisał(a):

Chłopie czy ty masz JAKIEKOLWIEK pojęcie o asemblerze?

Na pewno nie takie jak ty, uczę się...

Zrobiłem coś takiego

wypmacasm proc tab: dword, p: dword, rmac: DWORD, ilzn: DWORD


mov esi, tab
mov al, byte ptr[esi]
mov edi, p                       //gdy zakomentuję te 2 linie  
mov byte ptr [edi], al        //wszystko śmiga


ret 
wypmacasm endp

kompiluje się ale przy recie taki error
Unhandled exception at 0x771EED0B (ntdll.dll) in GUI.exe: 0xC0000374: Sterta została uszkodzona (parameters: 0x77224270).

i dlaczego to działa, gdy funkcja zwraca chara to normalnie zwraca pierwszy element tablicy a zapisać już tak się nie da .. ..
mov esi, tab
mov eax, DWORD ptr[esi]

0

A jak teraz wygląda wywołanie tej twojej metody? Bo poza tym retem co do którego nie jestem pewien jak c# przywraca ci stos, to generalnie kod wygląda ok. Może źle to wywołujesz? Na pewno podajesz poprawna tablicę jako parametr? Nie ma aby czasem 0 elementów albo coś?
A ten kod na końcu to ci może zwrócić co najwyżej pierwsze 4 bajty z tablicy sklejone w inta, na pewno nie jeden znak...

0

wywołanie funkcji

[DllImport("mylib.dll", CallingConvention = CallingConvention.StdCall)]
private static extern char wypmacasm(string tabela, ref string tab2);
private void button4_Click(object sender, EventArgs e)
{
tablica = "German forces surrendered bla bla bla...."
char pom = 'x';
string p = "qwerty";

        pom=wypmacasm(tablica, ref  p);


        textBox6.Text = pom.ToString();
        textBox7.Text = p.ToString();
     }

```asm

wypmacasm proc tab: dword, p: dword

mov esi, tab
mov eax, dword ptr[esi]
;
;kod powyżej i poniżej zwraca to samo, w textbox6 jest literka G
;
;mov esi, tab
;mov al, byte ptr[esi]
;
;
ret 
wypmacasm endp

natomiast


mov esi, tab
mov al, byte ptr[esi]
mov edi, p
mov byte ptr [edi], al

Już nie działa jak wcześniej napisałem. Nie mam pojęcia co jest nie tak...

0

No ja tu widzę kilka rzeczy które są "nie tak".

  1. Nie wiem dokładnie jak ten twój ref z C# przetłumaczy się na asemblera ale obawiam sie że wcale nie tak jak byś tego chciał. Jeśli już to zrobiłbym konwersje samodzielnie a do asemblera pchał jakieś char* bo przynajmniej wiadomo co masz... Bo teraz to niby masz referencje ale do stringa, więc jak C# zrobi marshalling to pewnie wychodzi z tego jakiś const char* i program sie sypie jak próbujesz w tej tablicy pisać.
  2. To co piszesz z tą literką G to jakiś bullshit chyba że z jakiegoś powodu C# zamienia ci tego stringa na tablicę intów a nie charów, bo jak zrobisz:
    mov eax, dword ptr[esi]
    to do eax polecą 4 bajty z adresu [esi] i jeśli z tego sie robi jedna literka to jest to jakiś mindfuck. Niemniej nie wiem jak C# wykonuje tą konwersje, więc może faktycznie robi takie cuda.
0

ten ref chyba działa tak jak chcę, bo gdy próbowałem z intem to normalnie do inta wysyłało wartość taką jaką chciałem(bez ref nic nie zmieniło), ale z tymi tablicami to jest jakaś magia .....

0

@alokk321 bo z intem to jest co innego! Int to prymityw a string to jest obiekt który ulega u ciebie automatycznej konwersji. Cały czas chyba nie rozumiesz ze string to NIE JEST TABLICA tylko ci go C# konwertuje do wskaźnika na ciąg znaków.

edit: krótka wymiana zdań z @msm pozwoliła mi odkryć jak to sie dzieje że ci ta funkcja zwraca jedną literkę mimo że ładujesz do eax 4 bajty. Bo przecież z funkcji zwracasz char więc zapewne C# odczytuje jako wartość zwróconą tylko 1 bajt z akumulatora czyli al, gdzie akurat siedzi ta pierwsza literka ;] Więc działa ci to niejako "przypadkiem" ;]

0

Aha, to ma sens ;)

więc zostanę przy

mov esi, tab
mov al, byte ptr[esi]

bo przy kopiowaniu to będzie mieć znaczenie, tyle, że dalej nie wiem jak kopiowac ...

0

Ok mam :P problem leżał po stronie c# w asm ten kod jest jak najbardziej poprawny:

mov esi, tab
mov al, byte ptr[esi]
mov edi, p
mov byte ptr [edi], al

a w c#

[DllImport("bibliotekaASM.dll", CallingConvention = CallingConvention.StdCall)]
private static extern char wypmacasm(string tabela, !!!!!!!! char* macierz!!!!!!!!!!, int rmac, int ilzn);

/// a w funkcji buttona:
char pom = 'x';
string p = "qwerty";
!!!!!!!!! fixed(char* s = p) !!!!!!!!!!!!!!

         pom=wypmacasm(tablica[0],   s, rmac[0], ilzn[0])

        textBox6.Text = pom.ToString();
        textBox7.Text = p.ToString();

teraz kopiuje 1 element z pierwszej tablicy na miejsce 1 elementu 2 tablicy:) Jak będę mieć jeszcze jakieś problemy bede pisać:p dzięki @Shalom ;p
0

@Shalom jak to jest, ty programujesz w javie, ogarniasz bazy danych i assemblera? o_O mega full stack developer

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