News:

11 March 2016 - Forum Rules

Main Menu

[rusty asm] Call a routine from a routine

Started by tryphon, December 06, 2012, 04:54:48 AM

Previous topic - Next topic

tryphon

Hi,

my assembler is quite rusty, and I'd want confirmation for the following : let's call a routine some piece of code that is called by :
jal ROUTINE
and ends with a :
jr ra

Suppose I want to call a routine from the inside of a routine.

I must save the ra value before, don't I ?

I can do (assuming t0 is left unmodified by ROUTINE, and without considering delay issues) :
mov t0, ra ; or the contrary, I never remember, but you the idea is t0 <- ra
jal ROUTINE
mov ra, t0


or putting ra in the stack ?

Sorry for the stupidness of the question.

Pikachumanson

#1
Edit: nvm i was thinking in 6502.

That looks like assembly I learned in college. Yeah you would save ra.

tryphon

That's entirely my fault, but you answered before I finished my question :)


tryphon

It's MIPS assembler, I'm currently hacking a PS2 game.

Thanks for confirmation :)

PhOeNiX

Normally, ra it's stored in the stack together with other needed registers at the start of your routine, e.g.:

addi $sp, $sp, -20
sw  $ra, 16($sp)
sw $s3, 12($sp)
sw $s2, 8($sp)
sw $s1, 4($sp)
sw $s0, 0($sp)


ax registers like a0,a1 etc. are usually used for parameters to routines and vx registers like v0 etc. for return values. Temporary registers like t0 etc. are not guaranteed to contain the same value through routine calls, so they not need to be stored in the stack before the routine starts.

Zoinkity

RA (as in return address) is automatically set whenever you use a 'jump and link' or 'branch and link' opcode.  If you don't want to set RA you can use a different register via JALR. 


Doesn't typical design style always keep the first 0x10 of the stack free for use within the routine, sometimes explicitly marked volatile?  That's followed by any variables above A3 that get passed via the stack, and then variable storage would go after...

Pyriel

#7
JALR still uses $RA to store the return address (PC+8).  It just lets you specify the address to jump to on a register instead encoding it into the OP.  If you're going to use any variant of JAL on a PS2, you need to ensure that $RA's value will be restored.  How you accomplish that depends on what you're doing.  If you're just writing code from scratch to assemble and run, you should follow the general rules and preserve on the stack.  If you're doing something like making cheat codes, or modifying the game by inserting code into the ELF somehow, you can get away with other tricks (I've stuffed it onto $S* registers that are saved by target routines before to save operations).  Although, if what you're doing is complex in any way, you'll probably be better off following the usual etiquette regardless of your goals.

Edit: I take that back.  There is a rarely used option in the JALR op-code that lets you specify the link register ("JALR link, target" as opposed to just "JALR target" with $RA being implied as the link register).  I've never even seen it in a game ELF.  It seems like doing that would just make things confusing.

Gemini

"JALR link, target" isn't used at all because the convention doesn't bother to take advantage of anything but ra for subroutine return address, even if you're allowed to use whatever you want for your pure assembly code. Still, all compiled code ends with "jr ra" and using another return register could be potentially error prone when you start mixing up assembled and compiled.

tryphon

In fact, I modify an existing game and am affected by size issues, so enlarging the stack, storing ra then getting back its value and decreasing the stack would necessite more op.

jalr link, adress is not an option (although it's interesting and I did not know about that) because both routines are in the game and end both by jr ra.

Thanks for all these enlightenments :)

Zoinkity

I can understand where the confusion would lie, since compilers will accept "JALR addr" and set target to RA if not provided.  Either is acceptable though, as is clearly noted in the MIPS manual.

That may be the case with PSX, but N64 uses JALR with different targets rather often.  Therein lies the difference in compilers though; the usual case is a sort of switch routine, jumping a table-ripped address and usually using a Tx variable to hold the return, and in this resepect is handled much the same way that the error handler might be.  This of course only works because you're controlling the situation, and I'd guess is a compiler-specific method of handling this particular case.

Actually, since when hacking you explicitly control the situation, you can always shuffle RA into another register (OR reg, R0, RA) to avoid stack overhead.  In fact you don't even need to shuffle it back necessarily, since you can also set what register is used for the jump back.
There's no threat of it being corrupted by threading or interrupts.  Both of those will make a complete copy of all the registers in use and swap them back before returning.  Only if a register is explicitly used in your code will it be corrupted.

Pyriel

Personally, I just assumed it was another add-on operation like the supposedly EE-specific op codes for the PS2, until I thought about how many unused bits that would leave JALR with, and double-checked the instruction set.

Quote from: tryphon on December 06, 2012, 03:00:00 PM
In fact, I modify an existing game and am affected by size issues, so enlarging the stack, storing ra then getting back its value and decreasing the stack would necessite more op.

jalr link, adress is not an option (although it's interesting and I did not know about that) because both routines are in the game and end both by jr ra.

Thanks for all these enlightenments :)
What is it that you're doing?  It sounds like you're trying to add JALs to a routine that doesn't already have them, and it therefore doesn't bother saving $ra.  There are ways around that, but just how depends on whether you're doing run-time modification or patching the ELF.