Pop i push w asm.

0

Mam jedno pytanie odnośnie instrukcji pop i push w asemblerze, mianowicie, który kod jest poprawniejszy. Oczywiście oba programy będą działały tak samo, chodzi mi bardziej o konwencję i dobre nawyki w korzystaniu z pop i push.

1:

LABEL1:
 pop r17
 pop r16
 (KOD A)
 (KOD B)
 push r16
 push r17
 rjmp MAIN

LABEL2:
 pop r17
 pop r16
 (KOD C)
 (KOD B)
 push r16
 push r17
 rjmp MAIN

2:

LABEL1:
 pop r17
 pop r16
 (KOD A)
 rjmp LABEL3

LABEL2:
 pop r17
 pop r16
 (KOD C)
 rjmp LABEL3

LABEL3:
 (KOD B)
 push r16
 push r17
 rjmp MAIN

EDIT: Poprawiłem błędy w kolejności instrukcji.

0

W ogólności - kod 1. Lepszy z punktu widzenia czytelności (dla człowieka) i szybszy (unikamy niepotrzebnych skoków). Chyba że label1-label3 są ściśle związane - w jednej funkcji - wtedy można potraktować pop jako prolog funkncji.

0

Mhm rozumiem, dzięki wielkie za szybką odpowiedź. :)

0
  1. Mieszasz w kolejności wpychania/ pobierania rejestrów na stos. Stos to kolejka LIFO, tzn jeśli chcesz zachować wartości dwóch rejestrów i je potem wczytać to robisz: push r1, push r2, <tu kod="kod">, pop r2, pop r1
  2. Co do pytania to zależy od długości kodu B. Jeśli jest długi to po co go powtarzać?
  3. Nie bój się robić funkcji i wywoływać ich instrukcją call. W miarę nowe procesory x86 posiadają stos calli, tzn call wpycha wtedy aktualny adres nie tylko na stos ogólny, ale także na stos calli wewnątrz procesora. Ten wewnętrzny stos jest swego rodzaju podpowiedzią na temat tego, gdzie może prowadzić następna instrukcja ret (powrotu z procedury) - dzięki temu procesor może wykonać odpowiedni prefetching kodu.
0

1 -> myślę że tutaj jednak chodzi o pobranie parametrów ze stosu z wywołania funkcji. W przypadku pisania większych funkcji warto byłoby poczytać o ramce stosu - technika umożliwiające/upraszczająca definiowanie parametrów i zmiennych lokalnych (np. http://osdev.labedz.org/implementacja/laczenie_procedur/ramka.html, http://pl.wikibooks.org/wiki/Asembler_X86/Funkcje/NASM). Jeśli dobrze zgaduje, wtedy push r1 push r2 można zamienić po prostu na modyfikację rejestru esp.

2 -> Jeśli jest długi (dłuższy niż sam push zmiennych) to znaczy prawdopodobnie że jest to część jednej funkcji. Ciężko mi sobie wyobrazić dużo powtarzającego się kodu w dwóch zupełnie różnych funkcjach - jeśli jednak tak jest to... ja bym wydzielił z tego kolejną funkcję - czyli:

LABEL1:
 pop r17
 pop r16
 (KOD A)
 call FUNCTION_B
 push r16
 push r17
 rjmp MAIN
 
LABEL2:
 pop r17
 pop r16
 (KOD C)
 call FUNCTION_B
 push r17
 push r16
 rjmp MAIN

3 -> skąd wiesz że autor nie używa?

0

MASM czy standardowe makra FASMa (ew inne asembler) automatycznie dodają epilogi i prologi procedur i dodają mapowania parametrów na odpowiednie adresy względem aktualnej ramki stosu. Gdyby kolega chciał tutaj używać procedur to chyba by nie pisał takiego kodu.

Trzeba sobie zapamiętać bardzo ważne zasady:

  1. Ładowanie kodu z RAMu czy chociażby pamięci podręcznej dalekiego poziomu zajmuje dużo więcej czasu niż wykonanie tego kodu (jednokrotne). Morał z tego taki, że kod rzadko wykorzystywany, albo wykorzystywany w dość dużych odstępach czasu (np kilkadziesiąt milisekund) pasuje optymalizować pod względem rozmiaru, a kod wykonywany w pętlach (ale takich, że mieszczą się w szybkiej pamięci podręcznej kodu) należy optymalizować pod względem wykorzystania potoków wykonawczych.
  2. Podstawowa zasada optymalizacji to: nie optymalizuj. Druga zasada optymalizacji brzmi: jeszcze nie optymalizuj. Zalecam najpierw pisanie czytelnego kodu, a dopiero potem sforkowanie tego i stworzenie nieczytelnego i szybkiego potwora.
0

Stawiam pierwsze kroki w programowaniu niskopoziomowym i staram się wyszukiwać najlepsze rozwiązania, nie tylko działające, ale też możliwie poprawne i sensowne. Konwencje są równie ważne jak sama składnia języka, mam dwie książki, na których bazuję, ale czasem kilka słów ludzi z doświadczeniem lepiej rozwiązuje kłopotliwe kwestie. :D

Dziękuję za pomoc.

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