@sim085
The operating system would tell the process what instruction to execute and where to store the result and the operating system would know the allowed memory space because of this small program you mentioned. is this right? or I am confusing things?
The memory resources are maintained by the OS (exec.library). At early startup phase the OS pinpoints the amount of chip (at address 0) and ranger memory (at address $c00000). Other memory (accelerators, Z3 etc) is added to the system by AutoConfig(TM) (expansion.library).
When program is loaded, the OS allocates memory from the memory pool for the program sections. The program execution begins at the beginning of the first section.
While running programs can also allocate memory memory dynamically using the exec.library functions. This memory is also obtained from the memory pool. Typically programs also free most of the memory they allocate, returning it to the memory pool.
When the program is finished the control returns to the OS. At this point the OS unloads the program sections, returning th memory occupied by them to the memory pool.
The CPU itself doesn't care what memory it accesses, it accesses exactly what the program tells it to. In case non-existing memory is accessed, several things can occur depending on the CPU and bus type. Older systems (typically 68000 to 68020) have undefined behaviour, the access might go unnoticed, might return random or mirrored content or it might totally screw the system. A3000, A4000, and expanded systems with 68030 to 68060 CPUs typically have more control. With these systems/CPUs accesses to non-existant memory generate an exception which the software can then handle. By default the program just gurus, but in some cases more advances things can be done: For example virtual memory (Gigamem, VMEM) or extra debugging (Enforcer, CyberGuard, muForce).
(This is a slight simplification, but I wanted to keep it understandable. I hope this helps clarify things.)