I am learning assembly language in my spare time to become a better developer.
I understand the difference between stack-based machines and register-based machines at a conceptual level, but I am wondering how stack-based machines are actually implemented. If a virtual machine, e.g. JVM or .NET, runs on a register-based architecture, e.g. x86 or x64, then it must use registers at the assembly level (as far as I am concerned). I am obviously missing something here. Therefore I am unsure of the distinction at assembly language.
I have read articles on here e.g. Stack-based machine depends on a register-based machine? and also on Wikipedia, but I don't believe they answer my question directly.
then it must use registers at the assembly level
That's not a requirement, processors have a cpu stack that behaves a lot like the virtual stack in the intermediary language. You can translate the instructions almost one-to-one to cpu instructions. Certainly one of the reasons that stack based VMs are popular, the jitter is easy to implement.
The only hangup with doing it that way is that the machine code isn't very efficient. It is the job of the jitter optimizer to find ways to use the cpu registers efficiently and make the code faster that way.
The opposite problem is present in register-based VMs. It is a harder problem to solve since the real CPU doesn't have as many registers as the VM. So the jitter must find ways to use the stack to spill registers that the hardware doesn't provide.