» »

[ASM] Pomoč, ne najdem napake

[ASM] Pomoč, ne najdem napake

c0dehunter ::

Lep pozdrav, ker se že en teden ukvarjam s to nalogo (brez uspeha), sem se odločil zatežit na ST forum, če bo kdo svetoval :)

V zbirnem jeziku napišite rekurzivno funkcijo, ki izračuna n-ti člen zaporedja:

f(n) = f(n-3) + f(n-2), n > 2, f(0) = 1, f(1)=1, f(2) = 7


Naredil sem že okoli 5 verzij pa mi nobena ne dela. Spodnja je še najboljši približek (vendar na koncu zašteka - "Cannot read memory at 0x0000001")

bits 32
extern _printf
global _main

section .data
	izpis db "Rezultat za f(%d)= %d",10,0 ;za izpis zaporedja
	n dd 0
	
section .text
	_rekurz:		
		mov eax, [esp+4]   	   ;trenutni argument shranimo v eax

		cmp eax,0				;primerjamo, če je argument 0 in v tem primeru v eax shranimo 1
		je .vrni1
		
		cmp eax,1
		je .vrni1
		
		cmp eax,2				;primerjamo argument z 2
		je .vrni7
		
		sub eax,2			;n=n-2
		mov edx, eax		;eax (n) shranimo v drug register, saj se bo vrednost eax spremenila
		push eax			;na sklad potisnemo eax (n-2)
		call _rekurz		;klicemo funkcijo za trenutni (n-2) - rezultat bo v eax
		add esp,4			;pobrišemo argument
		mov ecx,eax			;dobljeno vrednost shranimo v ecx, začasno
		
		mov eax,edx			;v eax nazaj shrani tisti trenutni n (n-2)
		dec eax				;in se zmanjsamo za 1 (n-2 -1 = n-3)
		push eax			;in to potisnemo na sklad na naslednji klic
		call _rekurz		;klicemo funkcijo za trenutni (n-3) - rezultat bo v eax
		add esp,4	
		
		add eax,ecx			;rezultata seštejemo
		
		ret
			
	.vrni1:
		mov eax,1
		ret
		
	.vrni7:
		mov eax,7
		ret
		
	.konec:
		ret
		
	_main:
		pushad
		
		; klic funkcije - rad bi izracunal 10. clen
		push 10
		call _rekurz
		add esp,4
		
		; izpis rezultata
		push eax
		push 10
		push dword izpis
		call _printf
		add esp, 8
		
		popad
		ret
		
		
		

mikesr ::

lp
mov edx, eax        ;eax (n) shranimo v drug register, saj se bo vrednost eax spremenila

Ta n se bo ob rekurzivnem klicu vedno znova spremeninjal(tudi v registru ker ga vedno znova tja shranjujes).. Torej to je ena napaka

vrednost moras shranit v sklad.. torej najprej ko v register preneses parameter, preveris pogoje(ce je 0 1 ali 2).. potem pa v sklad pushas:
- rezerviran prostor za rezultat
- n-2(od shranjene vrednosti v registru odstejes 2)
- n-3
No in pol rekurzivno klices funkcijo.parameter se vzame n-3. rezultat shranis na rezerviran prostor, pocistis vrh sklada, spet klices rekurzivno funkcijo(parameter n-2) in na koncu vrnjeno vrednost eax pristejes k prvem rezultatu.

Zgodovina sprememb…

  • spremenil: mikesr ()

c0dehunter ::

mikser, 100x hvala!

Edino tega ne razumem najbolj: "potem pa v sklad pushas: -rezerviran prostor za rezultat". Lahko to malo bolj razložiš?
Sumim, da se to nanaša na
mov ecx,eax         ;dobljeno vrednost shranimo v ecx, začasno

, vendar ne vem kako implementirat.

Sedanja koda (rekurzivne funkcije) - rezultat še vedno ni prav:
_rekurz:		
		mov eax, [esp+4]   	   ;trenutni argument shranimo v eax

		cmp eax,0				;primerjamo, če je argument 0 in v tem primeru v eax shranimo 1
		je .vrni1
		
		cmp eax,1
		je .vrni1
		
		cmp eax,2				;primerjamo argument z 2
		je .vrni7
		
		sub eax,2			;n=n-2
		push eax			;eax (n) shranimo na sklad, saj se bo vrednost eax spremenila
		dec eax
		push eax			;n=n-3
		
		call _rekurz		;klicemo funkcijo za trenutni (n-3) - rezultat bo v eax
		add esp,4			;pobrišemo argument
		mov esi,eax			;dobljeno vrednost shranimo v esi, začasno

		call _rekurz		;klicemo funkcijo za trenutni (n-3) - rezultat bo v eax
		add esp,4

		add eax,esi			;rezultata seštejemo
		
		ret

bigbada ::

;V zbirnem jeziku napišite rekurzivno funkcijo, ki izračuna n-ti člen zaporedja:
;f(n) = f(n-3) + f(n-2), n > 2, f(0) = 1, f(1)=1, f(2) = 7
;V glavnem programu izračunajte člene od 3 do 15 in rezultat izpišite.
;Pomoč: dogovor klica funkcije cdecl, organizacija funkcijskega klicnega okna.

bits 32
extern printf
global main

section .data
  izpis db "f(%d)=%d",10,0
  stevec dd 0

main:
  pushad
  mov dword [stevec], 3          ;stevec nastavimo na 3
  .zanka:                        ;loop 
  push dword [stevec]		 ;stevec pushnemo na sklad
  call funkcija                  ;klic funkcije
  add esp, 4                     ;pocistimo za sabo
  push eax                       ;eax in stevec na sklad + izpis
  push dword [stevec]            
  push izpis
  call printf
  add esp, 12			 ;pocistimo, ker smo 3 pushnili na sklad po 4bajte
  inc dword [stevec]             ;stevec povecujemo za 1
  cmp dword [stevec], 15         ;to delamo dokler ne dosezemo 15, potem koncamo
  jle .zanka
  popad
  ret

funkcija:			;funkcija
    mov eax, [esp+4]            ;trenutni argument shranimo v eax
    cmp eax, 0		        ;eax primerjamo z 0, ce je 0 jump v nadaljuj1, drugace shrani 1 v eax
    jg .nadaljuj1
    mov eax, 1         
    jmp .konec
  .nadaljuj1:                   ;nadaljuj1
    cmp eax, 1                  ;eax primerjamo z 1.. ce je enak 1 gremo v nadaljuj2, drugace
    jg .nadaljuj2
    mov eax, 1                  ;v eax damo 1
    jmp .konec
  .nadaljuj2:                   ;nadaljuj2
    cmp eax, 2                  ;ce je eax 2 gremo v nadaljuj 3 
    jg .nadaljuj3
    mov eax, 7                  ;v eax damo 7
    jmp .konec                  ;skok na konec
  .nadaljuj3:                   ;nadaljuj3
    sub eax, 2                  ;(eax-2)
    push eax                    ;damo na sklad
    sub eax, 1                  ;odstejemo se 1... (eax-3)
    push eax                    ;damo na sklad za naslednji klic
    call funkcija               ;klicemo funkcijo, rezultat se shrani v eax
    add esp, 4                  ;pocistimo
    pop ebx                     
    push eax                    ;eax in ebx damo na sklad
    push ebx
    call funkcija               ;klic funkcije, rezultata seštejemo 
    add esp, 4
    add eax, dword [esp]
    add esp, 4
  .konec:
    ret


Zanima me, če mi lahko kdo razlozi zadnjih par vrstic kode.. pop ebx pa add eax, dword [esp].. Kaj tu naredimo.. Ni mi čisto jasno? Zakaj kar ebx, od kje?

Zgodovina sprememb…

  • spremenilo: bigbada ()

mikesr ::

lp sosolci :)

bom kar prilepil kodo, da bo bolj razumljivo:
      
      push 0           ;rezultat , lahko je tudi sub esp, 4
      sub ecx, 2       ;(n-2)
      push ecx         ;shranimo v sklad
      sub ecx, 1       ;(n-3)
      push ecx         ;shranimo v sklad

      call _rekurzija   ;klic funkcije z n-3 parametrom
      add [esp+8], eax  ;rezultat shranimo v sklad(na rezerviran prostor)
      add esp, 4        ;izbrisemo vrh sklada(n-3)

      call _rekurzija    ;klic funkcije z n-2 parametrom
      add eax, [esp+4]   ;rezultat + prej shranjenen rezultat
      add esp, 8         ;pocistimo sklad

Zgodovina sprememb…

  • spremenil: mikesr ()

c0dehunter ::

Aha, zdaj mi je pa jasno :) Sem preštudiral, popravu kodo in zdej dela. Hvala!


Vredno ogleda ...

TemaSporočilaOglediZadnje sporočilo
TemaSporočilaOglediZadnje sporočilo
»

Delo za datotekami in zbirkami v zbirniku...

Oddelek: Programiranje
11738 (606) Brane2
»

asm - vnos stevilk s tipkovnice

Oddelek: Programiranje
51188 (1044) scarymovie
»

[Assembler] scanf How-To

Oddelek: Programiranje
61496 (1423) c0dehunter
»

[nasm] scanf

Oddelek: Programiranje
272795 (2522) Brane2
»

[NALOGA][NASM] problem pri povezovanju s C-jevskimi funkcijami

Oddelek: Programiranje
61309 (1167) c00L3r

Več podobnih tem