The point is that "the system has only one program running at all times" is not an explanation for why there's no dynamic memory allocation, because modern operating systems use virtual memory to give the illusion of a flat address space that the program is in full control over. You can use the .data/.bss sections of an executable exactly as you would use memory in a SNES game.
And in fact, on many game consoles newer than the SNES (such as the PS1, N64, GC/Wii, DS/GBA, etc.) there's no operating system and the game is in full control of the hardware and games frequently and extensively use dynamic memory allocation. Whether you manage memory statically or dynamically & whether you have an operating system or not below your program are almost completely orthogonal.
Rather, the reason why SNES games don't use dynamic memory management is because it's impossible to do efficiently on the SNES's processor. Dynamic memory management requires working with pointers, and the 65816 is really bad at handling pointers for several reasons:
- Registers are 16 bits while (far) addresses are 24 bits, so pointers to anything besides "data in a specific ROM bank" are awkward and slow.
- There are only three general-purpose registers, so register pressure is extreme. You can store pointers in the direct page to alleviate this, but addressing modes relative to direct-page pointers are slow and extremely limited.
- There is no adder integrated into the address-generation unit. Instructions that access memory at an offset from a pointer have to spend an extra clock cycle or two going through the ALU.
- Stack operations are slow and limited, so parameter passing is a pain and local variables are non-existent.
All of these factors mean that idiomatic and efficient 65xx code uses static, global variables at fixed addresses for everything. When you need dynamism, you make an array and index into it instead of making general-purpose memory allocations.
But as you get into more modern 32- or 64-bit processors, this changes. You have more registers and better addressing modes, so the slowness and awkwardness of working with pointers is gone; and addresses are so long that instructions operating on static memory addresses are actually slower due to the increased code size. So, idiomatic code for modern processors is pointer-heavy and can benefit from dynamic memory allocation.