Complementing sibling answer, the architecture used in CP/M, and somewhat mimicked by MS-DOS, allowed very high portability of the system.
Ideally, no user space program should access the hardware directly. All its interactions should be through int 21h. The kernel implemented filesystem access and other system routines, occasionally making bios calls. Bios calls were very simple calls to a jump table in a predefined location that implemented lower level hardware accesses. So, when a program wanted to, for example, access a file, it called the BDOS with specified register values and the kernel, if required, made bios calls to satisfy the request.
This, of course, incurred some latency. For performance critical applications like games this approach was just too costly. Since IBM-PC was just very very common, apps started accessing the hardware directly. It was more performant but non-portable.
For classic CP/M apps, that never accessed the hardware directly, if you could build a machine with a compatible bios, you instantly had access to all CP/M software library available.