I have written several emulators from scratch, the process I go through is this.
1) Analyse target machine and create coding budget
Look at the machine you want to run the emulator on and allocate run time to each section of the code. This may seem a strange starting point but it saves you a lot of grief later on. For example, I wrote a spectrum emulator, I knew from day one it would have to run on everything from a low end GSM handset up to a state of the art PC, so I coded the emulator to run in three modes. Hyper realistic, realistic, and speed.
In hyper realistic I emulated hblank, vblank, realtime video update, everything. In realistic mode I updated the display every scanline, in speed mode I just updated the display once per frame (20ms).
In order to do this I had to have a cpu that could run for a set number of T states.
2) Work out what is going to be the most difficult part of the emulator
The hardest part of the speccy emulator was all the undocumented opcodes, so this didn't really impact the design at all. The gameboy emulator was a different matter, all the different memory maps, and the paging system makes a simple thing like reading a byte from memory a slow process. The gameboy emulator had to run on low end devices, so I had to come up with a fast way of doing this before I started the project.
3) write a test harness.
the test harness is going to be where you do all your testing, so starting with this cements all the features you are going to need in the finished system.
4) write a dissassembler, and imbed into the harness.
Why do I start with the disassembler? By writing this first you get to know the structure of the cpu very well. I also write in such a way that I can use the disassembler as a template for the cpu core itself.
5) Write the cpu core and TEST it
Use macros for every memory access, then if you want to re-use the core for a different memory system later you only have to change two macros
the next stages can be swapped around depending on what you are trying to emulate
6) write game loading routines
7) write basic display handler
8) code hooks for sound (don't attempt to write the sound yet)
9) write input device handling
at this stage you should be able to run some games from within the test harness, and when they crash (which they will!!!) you will be glad you have invested the time in it.
10) write run time harness
11) write sound handlers
12) run lots of games
some things to watch out for.
a) stack overflow
on some devices (Speccy for one) the stack wrapping around was legal and in fact used by some coders. The danger comes if you are using a 32 bit value for the stack pointer instead of wrapping around from 0xffff to 0x0000 you would end up at 0x10000
b) undocumented opcodes
frequently used by old-school coders
anyway hope this helps