Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - CPU Register VS Stack when call functions

Reply to topic
Author Message
  • Joined: 15 Jul 2022
  • Posts: 29
Reply with quote
CPU Register VS Stack when call functions
Post Posted: Fri Dec 01, 2023 6:20 pm
Hello everyone,

I'd like your opinion on a question for which I can't find an answer.
I'm a beginner in assembler programming, and I'm currently factoring my code into functions.

Whe calling a function, is it better to :
- pass all function arguments through CPU registers (faster), if they can all fit inside ?
- pass all function arguments through the Stack (slower), this seems to be the convention when developing in assembler ?
- read all variables directly from the RAM ?

On one hand, I say to myself: frankly, if it works by passing everything through the registers, why bother?
On the other hand, I also like the idea of writing clean code that respects convention.

If we focus on performance:
- CPU Register is the fastest way to read/write bytes in terms of cycles.
- Read or Write from RAM (through 'LD X, (HL)') is 7 cycles
- Read or Write from Stack (through POP and PUSH) is 10 and 11 cycles.

The note on the following page answers my question: https://www.smspower.org/Development/Stack
Quote
In general, this is not recommended in Z80 code. It takes more space and is far slower than passing parameters in registers. If you need to pass in more parameters than can fit into the available registers, consider using a memory structure instead.


But searching on internet and reading some blogs like this one, seems to give a different answer:
Quote
There may be times when we need to read or write data super quickly... and there's a clever trick for this... as mentioned before, the Stack pointer offers the fastest reading and writing the Z80 can offer.

https://www.chibiakumas.com/z80/index.php#Lesson8 (at Stack Misuse! chapter).

I'd like to read your opinions

Thanks.
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Fri Dec 01, 2023 6:22 pm
Passing in registers is the normal way for assembly, but you need to take care with which registers your functions use or preserve. Passing on the stack is very slow in Z80, and is really the domain of C compilers. Directly accessing memory is perfectly fine, the only reason to go to registers is if you need to pass values from different places or intermediate values that don’t belong in memory.
  View user's profile Send private message Visit poster's website
  • Joined: 06 Mar 2022
  • Posts: 671
  • Location: London, UK
Reply with quote
Post Posted: Fri Dec 01, 2023 7:00 pm
If you're academically interested in the general subject of how to pass parameters to subroutines and functions (and deal with return values), the term you're looking for is "calling convention" which is a part of a wider topic of Application Binary Interface (ABI) and becomes important when designing software which must be interoperable with other software (e.g. writing programs designed to run on an operating system)

Of particular relevance for SMS game development in this community are SDCC's Z80 calling conventions, since these apply to games developed with devkitsms. You can read about them in the user manual - from p71. and you'll see from this that the convention is to try using registers for parameter passing if possible, only resorting to the stack when necessary.

When writing a game in assembler, you might as well just go for whatever works best for your use-case but given that most games will use significant global static memory structures as part of their design it often makes the most sense to manipulate that memory directly rather than pass parameters at all. This might feel counterintuitive if you come from a high level programming background. This doesn't really work for small generic routines with multiple calling sites, so there passing by register is typical, with the obvious downside that it adds some not insignificant cognitive overhead when writing the code.

Another alternative for small reusable routines which in really simple cases can obviate the need for any storage at all is parameterised assembler macros.

If your routine isn't even close to performance critical then using the stack might feel like the most straightforward convention, although if you're also pushing and popping registers inside the routine anyway you might prefer to do all that outside the routine and just go nuts with the registers. Eljay's smslib project is specifically designed for maximal performance and leaves the responsibility for managing registers entirely up to the user (and also uses macros over subroutines pretty much wherever possible).
  View user's profile Send private message Visit poster's website
  • Joined: 15 Jul 2022
  • Posts: 29
Reply with quote
Post Posted: Fri Dec 01, 2023 8:04 pm
Thanks for your answers.
Pass arguments trough CPU registers is what I did naturally.
It was in the middle of my project that the question came to me.
It also makes sense in terms of performance to use the stack only when necessary.

But I can confirm that, having never done such low-level programming before, it's counter-intuitive at first to manipulate variables directly in memory.
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Fri Dec 01, 2023 9:51 pm
It’s not far from the “classical” C approach of putting all your state in globals. Obviously you will need to take more care but the cost of abstracting it with pointers to structures tends to be too much most of the time.
  View user's profile Send private message Visit poster's website
Reply to topic



Back to the top of this page

Back to SMS Power!