[ASM] Program obliczający silnię

0
section .text
global _start

_start:
	mov	eax,4
	mov	ebx,1
	mov	ecx,ARG
middle:	
	cmp	ecx,1
	jle	end ;; skok, jeśli mniejsze lub równe
	imul	ebx,ecx
	dec	ecx
	jmp	middle

end:
	mov	eax,1
	int 80h
	
section .data

ARG	equ	4

Prosiłbym o pomoc w zdiagnozowaniu, dlaczego program nie wyświetla wyniku (24) na konsoli. Makefile napisany, nie wyświetla żadnych komunikatów, kompilacja przebiega prawidłowo, natomiast wyniku jak nie było, tak i nie ma.

1

Nie rozumiem pytania. Przecież ty tu NIC nie wyświetlasz. Ty tylko robisz exit z wartością kodu błędu 24. Możesz powiedzieć czego sie spodziewałeś?

1

Nie wywołujesz przerwania więc nic sie nie wypisuje.
Zrób jakaś procedure która będzie wypisywać dane z jakiegoś rejestru i wywołuj ją przy każdej iteracji middle.

0

Okej, już rozumiem. Zmodyfikowałem kod (wzorując się na innych programach) na tyle, że powinien wszystko wyświetlić (na górze argument, przez który mnożymy, na dole liczbę, która nam wyszła, czyli na górze 3 oraz 6 na dole - które uzyskujemy przez przerabianie zmiennej ARG. Tak to przynajmniej widzę).
Niestety nie wykonują się ani operacje mnożenia, ani dekrementacji (linijka 16 i 17), tak pisze kompilator. Prosiłbym o pomoc. Aktualnie stan pracy jest taki:

section .text
global _start

_start:
	mov	eax,4
	mov	ebx,1
	mov	ecx,ARG
	mov	edx,[length]
	int 80h

	mov sil,1


middle:	
	cmp	[ARG],sil
	jle	end ;; skok, jeśli mniejsze lub równe
	imul	sil,[ARG]
	dec	[ARG]
	jmp	middle

end:
	mov [ARG],sil
	add [ARG],49

	mov eax,4
	mov ebx,1
	mov ecx,ARG
	mov edx,[length]
	int 80h

	mov	eax,1
	int 80h
	
section .data

ARG	db	3, 0ah
length	dd	$-ARG

Prosiłbym o wyrozumiałość - wiem, że mogę jakieś trywialne błędy w tym momencie popełniać.

0

Serio?
cmp sil,[ARG] sil wynosi 1 a ARG 4. Czy sil <= ARG? Jak dla mnie jest, to skaczemy...

0

Poprawione.

0

Poza tym chyba nie do końca rozumiesz jak działa wypisywanie na ekran. Bo wydaje mi się (nie chce mi się szukać specyfikacji do int80h) że wypisujesz stringa. A to znaczy że on oczekuje na ZNAKI w tablicy a nie na LICZBY. Żeby wypisać na ekran 123 nie wystarczy mieć gdzieś liczby 123. Trzeba mieć 3 znaki, 1, 2, 3. Czyli 3 liczby, zgodnie z ASCII -> 49, 50, 51

0

W porządku, zmodyfikowałem program powyżej. Wprawdzie nadal program odczytuje liczby jako znaki w ASCII, ale powinien dawać jakieś logiczne rozwiązanie (przynajmniej na namniejszych silniach, od jeden do trzy). Póki co na zajęciach działamy dokładnie w taki sposób.

1

Aaaaa ale teraz to w ogóle robisz źle. Miałes zrobić odwrotnie! Pracować na LICZBACH a na koniec, przed wypisaniem dodać '0' do każdej cyfry. Teraz to robisz jakieś cuda na kiju.

0
section .text
global _start

_start:
	mov	eax,4
	mov	ebx,1
	mov	ecx,ARG
	mov	edx,[length]
	int 80h

	mov sil,1


middle:	
	cmp	[ARG],sil
	jle	end ;; skok, jeśli mniejsze lub równe
	imul	sil,[ARG]
	dec	[ARG]
	jmp	middle

end:
	mov [ARG],sil
	add [ARG],49

	mov eax,4
	mov ebx,1
	mov ecx,ARG
	mov edx,[length]
	int 80h

	mov	eax,1
	int 80h
	
section .data

ARG	db	3, 0ah
length	dd	$-ARG

Czy w taki sposób?

1

Mniej więcej, chociaż za użycie 49 zamiast '0' powinni rwać z ciebie pasy skóry...

0

W porządku. Nadal jednak konsola w momencie kompilowania informuje o takich błędach:

silnia.asm:16: error: invalid combination of opcode and operands
silnia.asm:17: error: operation size not specified
silnia.asm:22: error: operation size not specified

Czyli każdy z przypadków, kiedy wykonuję operację bezpośrednio na [ARG] (poza tymi, gdzie wpisywałem ją do rejestru). Co może być nie tak?

1

Dokładnie to co napisałeś. Zasadniczo nie można wykonywać operacji na pamięci. Trzeba przepisać do rejestru, wykonać operację a potem zapisać wynik. Nauka asemblera to dobry czas żeby zrozumieć jak działa procesor...

0

Okej, w porządku. Dlaczego operacje można wykonywać tylko na rejestrach procesora?

2

To akurat dość proste. Bo tak zbudowany jest procesor. To jest przecież tylko pewien układ elektroniczny, nic więcej. Masz tam kilka ścieżek na wejściu i kilka na wyjściu. Na wejściu pojawia się napięcie które symbolizuje binarne liczby (każda ścieżka to 1 bit) a na wyjściu dostajesz "wynik" też jako napięcia na odpowiednich ścieżkach. Siłą rzeczy musisz najpierw przenieść dane z pamięci operacyjnej na "wejścia" procesora.
Nie miałeś elektroniki albo techniki cyfrowej a studiach?

Czasem asemblery dla ułatwienia pozwalają na uproszczony zapis instrukcji, tzn że niby możesz wykonać instrukcję na pamięci, ale potem w czasie kompilacji to jest rozwijane do wpisania wartości do rejestru, wykonania akcji i przepisania wyniku z rejestru.

0

W porządku, dzięki. Tych przedmiotów jeszcze nie miałem, na I semestrze dopiero jestem.
Zastanawia mnie w takim razie, jak procesor jest w stanie przetworzyć w tak krótkim czasie tyle megabajtów, skoro same rejestry są malutkie w porównaniu do tych wartości (zaledwie kilka bajtów największe z nich).

2

A czemu miałby nie móc? Jak masz procesor 3GHz to znaczy że może wykonać 3 miliardy (!) cykli w ciągu sekundy. Co więcej procesory są teraz superskalarne, to znaczy że procesor ma więcej niż 1 jednostkę obliczeniową (np. ma kilka układów które mogą mnożyć). Do tego instrukcje procesora są dzielone na kawałki. 3 główne to fetch, decode, execute. Więc procesor ma osobny układ do odczytywania rozkazu (np. twojego ''mov eax, 1), osobny do rozkminiania co powinien zrobić (czyli do przygotowania sobie tej operacji) i osobny do wykonania obliczeń. To są osobne jednostki więc może być tak że procesor jednocześnie przetwarza 3 rozkazy, każdy w innej fazie.
Dzięki temu może tak być że w efekcie jakieś obliczenia są wykonywane w raptem kilku (<10) cyklach zegarowych. Więc jak masz 3GHz to możesz wykonać przykładowo 300 milionów obliczeń w ciągu sekundy. Rejestry mają 32 lub 64 bity, zależnie od procesora. Nawet dla 32 bitów to daje ci 4 bajty*300 milionów = 1.2 GB danych. To znaczy że mógłbyś zaalokować sobie 1 GB intów i puścić pętlę która je przykładowo zsumuje i wynik miałbyś w sekundę...

0

W porządku, rozumiem. Warto wiedzieć. Tymczasem wrzucam zaktualizowany kod i moje pytania - linijka z mnożeniem rejestrów jest błędna, mimo że wszystko wydaje się poprawnie zrobione. Zrobiłem kilka rejestrów do pracy nad liczbami (dil posłużył jako licznik, ebx jako przechowujący wartość wynikową). Co może być nie tak?
Jeszcze moje pytanie - czym się różni instrukcja "mov" od "add"?

section .text
global _start

_start:
	mov eax,4
	mov ebx,1
	mov ecx,ARG
	mov edx,[length]
	int 80h

	xor ebx,ebx

	mov dil,[ARG]
	mov ebx,1 ;; wpisujemy wartość początkową do ebx

middle:	
	cmp dil,1
	jl end ;; skok, jeśli mniejsze, więc nasz licznik 'dil' ma dojść do zera
	imul ebx,dil ;; operację silni wykonujemy od tyłu, więc mnożymy od największych wartości do najmniejszych
	dec dil
	jmp middle

end:
	mov [ARG],ebx ;; wpisujemy wartość wynikową do zmiennej ARG

	mov eax,4
	mov ebx,1
	mov ecx,ARG
	mov edx,[length]
	int 80h

	mov	eax,1
	int 80h
	
section .data

ARG	db	3, 0ah
length	dd	$-ARG

0

Po kolei:

  1. Pierwszy błąd jest taki ze deklarujesz sobie ARG jako db czyli jako BYTE, a potem wpisujesz tam wartość EBX czyli 4 bajty. Rozumiesz chyba że to nie do końca zadziała tak jak byś tego chciał?
  2. Ten kod na pewno nie wypisze ci na ekran poprawnej liczby, bo znów próbujesz wypisywać LICZBY a nie stringi.
  3. Twoje ostatnie pytanie wolałbym uznać za żart. Jeśli nim nie jest to uciekaj z tych studiów, bo pomyliłeś kierunki... mov przypisuje wartość z jednego miejsca w drugie a add (jak trudno sie domyślić...) dodaje dwie wartości...
0

Jak mi odpowiadasz w moich pytaniach, tak zastanawiam się nad specyfiką Twojej osoby. Z jednej strony uznałbym Cię jako autorytet w dziedzinie programowania - znasz i asemblera, i C++, i tylko do tych języków się do tej pory ograniczałem w moich pytaniach, a zapewne znasz więcej. I faktycznie mi niemal zawsze pomagasz, naprawdę to doceniam.
A z drugiej widzę półmózga, który chce początkującemu dowalić i nic więcej. Nie wiem skąd takie atawizmy u osoby podobno inteligentnej, Panie moderator. Mam wrażenie, że tylko szukasz okazji, żeby wylać na początkujących swoje frustracje.
Wiem, że operacja add ma dodawać, a mov przepisywać, ale ich działanie wydaje mi się identyczne. Taką hipotezę sobie wysnuwam na moim poziomie wiedzy i ją poddaję weryfikacji. I mimo tego, co mi odpisałeś, w ogóle nie zagłębiając się w kontekst pytania, nadal nie widzę różnicy w ich działaniu.

0

Mov działa jak operator przypisania w C++, czyli jest to zastąpienie tego co było wcześniej w pamięci lub w rejestrze nową wartością. A add to dodanie czegoś do jakiejś wartości która już jest w rejestrze/pamięci. Czyli jeśli mamy w rejestrze eax liczbę 5, to po mov eax, 2, będzie tam liczba dwa, a po add eax, 2, będzie tam liczba siedem.

0

Serio? Nie że jestem złośliwy ale jeśli nie widzisz różnicy między PRZYPISANIEM i DODAWANIEM to jest wśród nas tylko jeden półmózg jak to ładnie ująłeś. No ale wyjaśnijmy więc na przykładzie coś, co powinni byli ci wyłożyć najpóźniej w 1 klasie szkoły podstawowej.

  1. Mamy operację mov destination, source. Oznacza ona przypisanie. Jej działanie to:
    destination = source
    Więc jeśli destination miało wartość X a source Y to po wykonaniu mov destination, source obie lokalizacje mają tą samą wartość, którą jest Y
    W matematyce jak masz x=1 to znaczy że x wynosi 1.

  2. Mamy operacje add destination, operand. Oznacza ona dodawanie (z automatycznym przypisaniem, bo gdzieś wynik musi być zapisany, więc w tym przypadku leci do lewego operandu). Jej działanie to:
    destination = destination + source
    Więc jeśli destination miało wartość X a operand Y to po wykonaniu add destination, operand operand ma tą samą wartość, a destination ma wartość X+Y
    W matematyce jak masz
    x=1
    x = x+2
    to x finalnie wynosi 3

Ale nie martw się, zapamiętam żeby nie wylewać już swoich frustraci w twoich tematach...

0

W porządku, już rozumiem.
W jednym wypadku działamy na zwykłych liczbach, w drugim w grę wchodzą już inne czynniki, takie jak właściwości języka programowania, co potrafi zupełnie zmienić perspektywę nawet na najprostszą rzecz. Reagowanie na to z impulsywnością bez zrozumienia drugiej strony to dla mnie gburowatość.

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