[MASM + VS2005 + C++] środowisko szaleje

0

Witam,
utworzyłem sobie projekt zgodnie ze schematem:

Tworzymy plik mojadll.asm (na podstawie http://www.i-lo.tarnow.pl/edu/inf/prg/win32asm/pages/17.htm):

;-------------------------------------------------------------------------
.386

.MODEL FLAT, STDCALL

OPTION CASEMAP:NONE

INCLUDE \masm32\include\windows.inc

.CODE

DllEntry PROC hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD

mov eax, TRUE
ret

DllEntry ENDP

;-------------------------------------------------------------------------
; To jest pusta funkcja. Nic nie robi. Wstawiłem ją tutaj, aby ci pokazać,
; gdzie należy umieszczać własne funkcje w bibliotece DLL
;-------------------------------------------------------------------------

TestFunction PROC

ret

TestFunction ENDP

END DllEntry
;-------------------------------------------------------------------------

Tworzymy plik mojadll.def:

;-------------------------------------------------------------------------
LIBRARY DLLSkeleton
EXPORTS TestFunction
;-------------------------------------------------------------------------

Tworzymy nowy projekt w VS .NET 2005 (a przy tym solution) sample1, przy czym najlepiej zaznaczyć opcję utworzenia osobnego katalogu dla solution:

New -> Project -> Win32 Console Application

Dalej dodajemy do solution nowy projekt mojadll:

prawy klik na solution Add -> New Project -> Win32 Project, Application type: DLL, Empty Project (!!!)

Jeśli nie zaznaczyliśmy empty project to z projektu biblioteki Usuwamy wszystkie pliki cpp i h. Przenosimy wcześniej stworzone pliki asm i def do katalogu projektu. Dodajemy do Source Files projektu plik mojadll.asm, jeśli przy dodawaniu nie pojawi się automatycznie okno "Custom Build Rules" to po dodaniu pliku:

prawy klik na projekcie mojadll -> Custom Build Rules

i w oknie wybieramy MASMa.
Następnie dodajemy do Source Files projektu plik mojadll.def i we właściwościach projektu mojadll wybieramy:

Configuration Properties -> Linker -> Input

i wpisujemy w pole Module Definition File nazwę mojadll.def.

Projekt powinien się kompilować i powinny powstawać w wyniku jego działania: plik exe i biblioteka dll z eksportowaną funkcją TestFunction.

Dodatkowe informacje:

  • plik def jest potrzebny do utworzenia dll,
  • plik lib nie jest potrzebny jeśli odwołujemy się do biblioteki dynamicznie czyli przez getprocaddress.

Na początku wszystko było ładnie, pięknie. Pisałem sobie projekt, wszystko działało.

W pewnym momencie VS zaczęło rzucać błędami z kosmosu:

------ Build started: Project: DLLint, Configuration: Debug Win32 ------
Assembling...
Assembling: .\DLLint.asm
.\DLLint.asm(1) : error A2044: invalid character in file
.\DLLint.asm(3) : error A2008: syntax error : .
.\DLLint.asm(5) : error A2008: syntax error : P
.\DLLint.asm(7) : error A2109: only white space or comment can follow backslash
.\DLLint.asm(10) : error A2008: syntax error : .
.\DLLint.asm(12) : error A2008: syntax error : l
.\DLLint.asm(13) : error A2008: syntax error : m
.\DLLint.asm(14) : error A2008: syntax error : r
.\DLLint.asm(15) : error A2008: syntax error : D
.\DLLint.asm(20) : error A2008: syntax error : u
.\DLLint.asm(21) : error A2008: syntax error : p
.\DLLint.asm(22) : error A2008: syntax error : m
.\DLLint.asm(23) : error A2008: syntax error : a
.\DLLint.asm(24) : error A2008: syntax error : p
.\DLLint.asm(25) : error A2008: syntax error : m
.\DLLint.asm(26) : error A2008: syntax error : r
.\DLLint.asm(27) : error A2008: syntax error : s
.\DLLint.asm(32) : error A2008: syntax error : d
.\DLLint.asm(33) : error A2008: syntax error : p
.\DLLint.asm(34) : error A2008: syntax error : m
.\DLLint.asm(35) : error A2008: syntax error : s
.\DLLint.asm(36) : error A2008: syntax error : p
.\DLLint.asm(37) : error A2008: syntax error : m
.\DLLint.asm(38) : error A2008: syntax error : r
.\DLLint.asm(39) : error A2008: syntax error : o
.\DLLint.asm(44) : error A2008: syntax error : i
.\DLLint.asm(45) : error A2008: syntax error : p
.\DLLint.asm(46) : error A2008: syntax error : f
.\DLLint.asm(47) : error A2008: syntax error : f
.\DLLint.asm(48) : error A2008: syntax error : f
.\DLLint.asm(49) : error A2008: syntax error : f
.\DLLint.asm(50) : error A2008: syntax error : p
.\DLLint.asm(51) : error A2008: syntax error : m
.\DLLint.asm(52) : error A2008: syntax error : r
.\DLLint.asm(53) : error A2008: syntax error : s
.\DLLint.asm(55) : error A2008: syntax error : E
.\DLLint.asm(55) : error A2088: END directive required at end of file
Project : error PRJ0019: A tool returned an error code from "Assembling..."
Build log was saved at "file://c:\Documents and Settings\Ala\Desktop\interJA\DLLint\Debug\BuildLog.htm"
DLLint - 38 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

Kod jest bardzo nieskomplikowany i na pewno wczoraj działał...

.386

.MODEL FLAT, STDCALL

OPTION CASEMAP:NONE

INCLUDE d:\masm32\include\windows.inc

.CODE

DllEntry PROC hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
    mov  eax, TRUE
    ret
DllEntry ENDP

;***************************************************************
;****** Procedura testowa ***** dodawanie

sumuj PROC a: DWORD , b: DWORD
        pushad
        mov eax, a
        add b, eax
        popad
        mov eax, b
        ret            ;koniec funkcji
sumuj ENDP

;***************************************************************
;****** Procedura testowa ***** odejmowanie

odejmuj PROC a: DWORD , b: DWORD
        pushad
        mov eax, a
        sub b, eax
        popad
        mov eax, b
        ret            ;koniec funkcji
odejmuj ENDP

;***************************************************************
;****** Procedura testowa ***** HARDCORE - sinus()

sinus PROC kat:DQ;, wynik:PTR
        pushad
        finit
        fld     kat                     ; wczytanie zmiennej do rejestru st0
        fsin
        fst kat
        popad
        mov eax, kat
        ret            ;koniec funkcji
sinus ENDP

END DllEntry 

Co do wywoływania funkcji z C++ kompilator nie ma zastrzeżeń.

Spotkał się kiedyś ktoś z czymś podobnym?

0

Bo to MASM i musisz to skompilowac w masmie a nie w vs. Visual studio oferuje tylko inline asm czyli wstawki assemblerowe

0

spróbuj dodać jeszcze fpu.lib i fpu.inc z folderu masma choć nie wygląda na to by problem w tym tkwił. może gdzieś trzeba dopisać/ustawić parametry kompilacji masma w Visualu np w project->properties

marekczarek napisał(a)

Bo to MASM i musisz to skompilowac w masmie a nie w vs. Visual studio oferuje tylko inline asm czyli wstawki assemblerowe

Tak to kompilatory kompilują a nie środowiska programistyczne :)
Jak myślisz dlaczego w Custom Build Rules zaznaczył MASM? Jesteś świadom tego że MASM to bardzo ważny part VisualStudio?:D

0

Nie mam już siły do tych wątków z kompilowaniem hello-worldów masmem... Powiem tylko, że komuś powinno się urwać jaja za używanie pushad/popad.

0
Świętowit napisał(a)

Nie mam już siły do tych wątków z kompilowaniem hello-worldów masmem... Powiem tylko, że komuś powinno się urwać jaja za używanie pushad/popad.

To jest ~20 część tutorialu Iczelion`a więc to nie taki helloworld :)
Co chcesz od pushad?:) taka biedna aplikacja na wydajności nie ucierpi a rozmiar się zmiejszy:)

0

Tutoriale Iczeliona to hello-world.

Poza tym nawet Iczelion, chociaż na momentami bardzo kiepski styl, nie jest na tyle pojebany żeby pushad sadzić.

Rozmiar się nie zmniejszy, a zwiększy przy normalnym kodzie, zaufaj mi. Używając pushad pięknie sobie utrudniasz rozmieszczanie rzeczy w rejestrach, poza tym jest wolne w c**** i marnuje stos.

0

Zesrasz się a pushad dalej będzie jednym bajtem kodu :)
Niczego sobie nie utrudniasz chyba że nie jesteś świadom co ten rozkaz robi...
W tym programie faktycznie marnuje stos ale kiedy wymagane jest zachowanie wszystkich rejestrów nic się nie marnuje ;p
Fakt pushad/popad są bardzo wolne - ale nie optymalizujmy helloWordów :d

0
Fallen napisał(a)

Zesrasz się a pushad dalej będzie jednym bajtem kodu :)
Niczego sobie nie utrudniasz chyba że nie jesteś świadom co ten rozkaz robi...

Tja, tylko popad odtwarza wszystkie rejestry potem, co jest problematyczne bo stan (z rejestrów) trzeba przenosić albo poprzez modyfikację kontekstu na stosie albo zmienne globalne - znacznie dłuższe instrukcje, potencjalnie kilka dodatkowych. Poza tym praktycznie nigdy pushad/popad nie są koniecze/przydatne. Poza tym faktycznie trzeba mieć pojęcie jak działają konwencje wywołania...

MrokU napisał(a)
sumuj PROC a: DWORD , b: DWORD
        pushad
        mov eax, a
        add b, eax
        popad
        mov eax, b
        ret            ;koniec funkcji
sumuj ENDP

Masm sam z siebie przy 'PROC' dodaje prolog i epilog funkcji tworzący ramkę stosu i ew. zachowuje rejestry, które mają być niezmienione - esi, edi i ebx. Pomijając przyrost kodu i redundancję wynikające z tego sam kod wyżej jest idiotyczny, głównie przez silenie się na zastosowanie *adów. Wersja 'po ludzku':

sumuj PROC a: DWORD , b: DWORD
        mov eax, [a]
        add eax, [b]
        ret 
sumuj ENDP

Widzisz, nie masz innych rejestrów w użyciu poza akumulatorem więc i tak popad/pushad nie robi nic poza marnowaniem kilka razy tyle czasu ile zajmie wykonanie normalnie napisanej funkcji. Bez tego gówna też nie trzeba też bawić się w modyfikowanie argumentu i 'cofanie' wyniku do akumulatora... No i co, nadal nie widzisz utrudniania?

Pushad/popad powinny być zakazane do czasu napisania, powiedzmy, stu tysięcy linii kodu w asm...

0

Załóżmy że wynik a+b był ponad 32 bitowy i coś się zapisało w edx gdzie była potrzebna zmienna do prawidłowego funkcjonowania programu.. ty byś się domyślił prędzej czy później ale początkujący by się pewnie wściekał.
To jest kod z tutorialu 'nooby' nie wiedzą jeszcze co robią wszystkie rozkazy dlatego szablonowo iczelion wszędzie tak daje na wypadek gdyby początkujący próbowali coś samemu zrobić i się nie zniechęcili że coś mu nie działa.

0
Fallen napisał(a)

Załóżmy że wynik a+b był ponad 32 bitowy i coś się zapisało w edx

Jakim prawem?

Fallen napisał(a)

gdzie była potrzebna zmienna do prawidłowego funkcjonowania programu.

Zmiennych o dłuższym czasie życia nie umieszcza się w 'niestałych rejestrach' czyli eax, ecx i edx, które mają swoje dodatkowe przeznaczenie. Nikomu to nie przeszkadza bo to kwestia poprawnej organizacji kodu tylko i wyłącznie.

Fallen napisał(a)

To jest kod z tutorialu 'nooby' nie wiedzą jeszcze co robią wszystkie rozkazy dlatego szablonowo iczelion wszędzie tak daje na wypadek gdyby początkujący próbowali coś samemu zrobić i się nie zniechęcili że coś im nie działa.

Widziałeś kiedyś tutoriale Iczeliona? Nie, Iczelion, mimo kiep1skiego stylu, nie używa praktycznie wcale pushad/popad.

Właśnie, początkujący powinni pisać proste rzeczy żeby zrozumieć jak to działa, a nie robić ochnastą lekcję programownia pod Windows zamiast uczyć się podstaw.

0

A to nie wystarczy JO, jeśli już ma być chronione?

I co to właściwie za różnica jak będzie zapisane bo nie rozumiem? Jakim cudem do edx 0-o

0

@msm, weź pod uwagę różnice pomiędzy JO i JC...

0

----Ofttop się zrobił :d

Świętowit napisał(a)

Fallen napisał:
Załóżmy że wynik a+b był ponad 32 bitowy i coś się zapisało w edx

Jakim prawem?

Myślałem o mnożeniu - mój błąd ;/

Świętowit napisał(a)

Zmiennych o dłuższym czasie życia nie umieszcza się w 'niestałych rejestrach' czyli eax, ecx i edx, które mają swoje dodatkowe przeznaczenie. Nikomu to nie przeszkadza bo to kwestia poprawnej organizacji kodu tylko i wyłącznie.

No to zagwarantował że rejestry bazowe i indexowe się nie zmienią zanim zaczął w ogóle pisać.

Świętowit napisał(a)

Widziałeś kiedyś tutoriale Iczeliona?

Kiedyś się z nich uczyłem dlatego kod mi znajomo wygląda.

0

Myślałem o mnożeniu - mój błąd ;/

Poczytaj najpierw jak działa mnożenie w asm...

0
MSM napisał(a)

Poczytaj najpierw jak działa mnożenie w asm...

Nie zapędziłeś się przypadkiem?

0

Nie zapędziłeś się przypadkiem?

Pewnie tak, ale wygląda na to że jestem nie do życia bo nawet nie wiem gdzie.

Z tego co pamiętam to mnożenie polega właśnie na tym że parametr jest mnożony z A... i wrzuca wynik do innego rejestru. A jeśli są dwubajtowe to połowę do AX (młodsze) a połowę do właśnie EX (starsze). Czyli to jakby celowe.

edit:
...

Idę lepiej od komputera zanim do końca zrobię z siebie idiotę :|

0

Mnożenie zawsze daje wynik o długości będącej sumą długości czynników. Z tego wynika, że rejestr/zestaw rejestrów docelowy aby pomieścić wynik musi być dwukrotnie większy niż źródłowy. Więc mnożąc 8bit*8bit otrzymujemy wynik 16bit, trafiający do AX (16bit). Dalej, 'normalnie' mnożąc 2x 16 bit otrzymujemy wynik w dx:ax, zabytek z czasów procesorów 16bit. Z 32 to już chyba oczywiste?

Jedna uwaga - dodatkowe formy instrukcji imul pozwalają określić rejestr docelowy, wynik wyląduje w nim, nie w (e)ax... i tylko w nim, górna część pójdzie wpizdu.

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