A lot of times I'll preserve registers above frame rather than below
push rsi
push rdi
push rbx
push rbp
mov rbp, rsp
..... Function ....
leave
pop rbx
pop rdi
pop rsi
ret
This alleviates the need to know where the stack is pointing as LEAVE will fix that up. If values were passed on stack then RET must reflect that and those parameters are accessed by RBP + 16, plus the the number of pushes * 8 or 4 if 32 bit. This method become particularly handy when creating a dynamic array of pointers that are only needed inside procedure