8080 assembly language makes it easy to understand what the restrictions are. Z80 much less so.

Much of the instruction set was carried over from the 8008, that's where you get MOV & ALU operations, the pseudo-register M standing for memory at address HL, etc. The 8080 then added to that some extensions that weren't as orthogonal, but greatly improved the usability.

That memory copy example, rewritten for the 8008, would take more than 30 instructions, constantly juggling pointers in and out of HL, because there was absolutely no other way to access memory. For that you needed an extra free register as well (no XCHG instruction!), so only a single 8 bit register would be available to use as a counter. Or you could store the counter in memory, but in that case there would be even more instructions to first load the address of that variable into HL!

In 8080 assembly as defined by Intel, each of these extended instructions has a unique mnemonic. LDAX = load A extended, etc. There's a one-to-one correspondence with the opcodes, so it's easy to memorize the encoding (best in octal), and what register can be used for what purpose.

Zilog added even more unorthogonal extensions to this set, but "simplified" the assembly so that one mnemonic could produce many different opcodes, some with an additional prefix. Most of these don't provide any benefit over using the existing 8080 opcodes, and you have to memorize lots of seemingly arbitrary restrictions. If you first learn Z80, those make no sense at all.