In Z-80 assembly, the return address is implicitly saved on the stack. Sure, it's not structured, meaning that you can call into code at any point (not just at the start of a procedure). One big difference is that the language construct doesn't pass arguments (and I don't think it saves save registers on the stack either - but my memory is fuzzy on this point).
That's a design code in Assembly. Move to a Motorola 68000, and well, it changes somewhat---pointers are passed in A0-A5, other data in D0-D7. You can still use the carry flag and say, D0 for error codes.
Move up to C, and the details about passing parameters to a function go away (unless you are mixing Assembly with C, and even then it can be isolated). I just give the parameters to the function and it's up to the compiler to stuff them into proper registers or on the stack or how ever it's done on a particular architecture.
Now, object oriented programming in C---another design pattern. Declare a structure with pointers to functions, and always pass the pointer to the structure to functions called via the function pointers. Again, the programmer is aware of the details. IN C++, the compiler handles those details.