prozeduren



  • hallo im rahmen meines studiums muss ich lernen mit dem MIPS-Simulator SPIM
    zu programmieren, die aufgabe besteht darin eine funktion zu schreiben die 2 zahlen iterativ multipliziert und dann ein rahmenprogramm das die argumente einliest, die funktion aufruft und anschließend das ergebnis ausgibt,

    ich habe leider keine ahnung von :
    1. framepointern - was ist ein framepointer?
    2. wie funktioniert das mit kellerrahmen erstellen rücksprungaddresse retten ?
    3. sowieso funktionen aufrufen?

    kann mir einer ein GANZ einfaches beispiel schreiben?

    bisher habe ich das ganze so gelöst ( es funktioniert entspricht aber nicht den konventionen):

    .data
    aufforderung:	.asciiz "Geben Sie bitte eine zahl ein: "
    ausgabe		:	.asciiz "Das Produkt der beiden Zahlen ist: "
    
    .text
    main:		addi $t0, $zero, 1
    			addi $t1, $zero, 1
    
    loop:		li $v0, 4 				# code für print_string
    			la $a0, aufforderung	# lade string
    			syscall
    
    			li $v0, 5				# code für read_int
    			syscall
    
    			sw $v0, ($sp)			# speichere eingabe auf stackpointer
    			addi $sp, $sp, -4		# stack um 1 wort verschieben
    
    			slt $t2, $t1, $t0		# $t2 := 1 wenn $t1 > $t0 
    			addi $t0, $t0, 1
    
    			beq $t2, $zero, loop 	# springe zu loop wenn $t2 == 0			
    
    			jal prod
    
    			move $t0, $v0			# $v0 nach $t0
    
    			li $v0, 4 				# code für print_string
    			la $a0, ausgabe			# lade string
    			syscall
    
    			li $v0, 1				# print_int
    			move $a0, $t0			# argument übergeben
    			syscall
    
    exit:		li $v0, 10				# code für ende des programmes
    			syscall 
    
    prod:		addi $sp, $sp, 4		# stack um 1 wort verschieben
    			lw $t0, ($sp)			# lade eingegebne zahl2 in $t0
    
    			addi $sp, $sp, 4		# stack um 1 wort verschieben
    			lw $t1, ($sp)			# lade eingegebne zahl1 in $t1
    
    			addi $t2, $zero, 1		# zählervariable auf 1 setzen
    			add $v0, $zero, $t0		# $v0 := $t0
    
    loop2:		add $v0, $v0, $t0		# addiere eingegebene zahl2 zu $v0
    			addi $t2, $t2, 1
    			bne $t2, $t1, loop2		# vergleiche $t2 mit $t1 springe 
    
    			jr $ra					# springe zu caller
    


  • korgan schrieb:

    1. framepointern - was ist ein framepointer?

    Ganz allgemein ein Zeiger, der auf einen Prozedur-Frame zeigt.
    Das ist eine Datenstruktur auf dem Stack, die je nach Aufrufkonvention in unterschiedlicher Reihenfolge lokale Variablen, die Ruecksprungadresse und Parameter der Funktion enthaelt.
    idR. ist es naheliegend, wie bei cdecl oder stdcall zuerst die Parameter, als naechstes die Ruecksprungadresse und zuletzt lokale Variablen abzulegen.
    Der Frame- oder Base-Pointer wird dann oft zwischen dem Ablegen der Ruecksprungadresse und dem Anlegen der lokalen Variablen auf sp gesetzt, so dass er auf die Ruecksprungadresse zeigt. Relativ dazu koennen dann lokale Variablen und Parameter adressiert werden.

    korgan schrieb:

    2. wie funktioniert das mit kellerrahmen erstellen rücksprungaddresse retten ?

    Die Ruecksprungadresse wird beim MIPS AFAIR in r25(? ich glaube bei dir heisst das $ra) abgelegt. Den packst du dann also auf den Stack. Wie du Parameter auf den Stack bringst, scheinst du ja auch schon zu wissen. Platz fuer lokale Variablen schaffst du bei einem descending stack, indem du die benoetigte Zahl Bytes von sp abziehst (bei MIPS muessen die Daten AFAIR auch dword aligned sein, also immer vielfache von 4). Dann hast du von sp bis sp + x (x die Zahl, die du abgezogen hast) Platz im RAM, um allerlei lokalen Kram abzulegen.

    korgan schrieb:

    3. sowieso funktionen aufrufen?

    Siehe zu 1 und http://de.wikipedia.org/wiki/Aufrufkonvention
    Oder wo hakt es? Ich meine, du rufst in deinem Code doch schon Funktionen auf.

    Ein Beispiel kann ich dir leider nicht geben, da ich aktuell weder in MIPS drin bin, noch die SPIM-Syntax kenne.



  • danke soweit, das hat schonmal sehr geholfen?

    aber um sicher zu gehen kannst du dir meinen quellcode mal anschaun und mir in etwa sagen was noch zu ändern ist?



  • Die Parameter scheinst du direkt nach dem Einlesen auf den Stack zu packen. Dann fehlt IMHO eigentlich nur noch, dass du die Ruecksprungadresse in $ra am Anfang deiner "prod" auf den Stack sicherst, genauso wie alle in der Prozedur verwendeten/veraenderten Register(*) und beides am Ende vor dem Ruecksprung wiederherstellst. Lokale Variablen sind da ja offensichtlich nicht noetig.

    edit:
    *: Hatte ich vergessen zu erwaehnen. Vor den lokalen Variablen sicherst du idR. zuerst Register, die in der proc veraendert werden.


Log in to reply