the real challenge is implementing the sheer amount of opcodes
If you want to write the most compact emulator, not necessarily the fastest, you can use the octal structure of the instruction encoding to condense your code greatly.
https://news.ycombinator.com/item?id=13045558
The first ~1/4 of the first-page opcode space is ALU ops, the next 1/4 is inc/dec/push/pop reg. The second half is less regular but you can still find big chunks that can be taken care of with only a few lines of code (e.g. 9x are exchanges with eAX, Bx are all immediate moves.)