It depends on the platform you're targeting.
Something like a SNES will always have the same hardware no matter what (okay, leave out the expansion chips inside cartridges, or fold them into "same hardware").
Something like a PC will have any old random shite you care to plug into it.
Something like an Xbox 360 is kind of halfway between.
With a SNES you can rely on the fact that a vertical blanking interrupt will happen 50 times a second no matter what, and you get so many instructions before your video output is halfway down the screen no matter what, and then if you want to play silly games like changing a PPU register to dick about with the screen mode you can. You have to take into account things like it taking more time for certain types of memory access like if you cross a 256-byte page boundary, but since when you get down this far you're doing it all in assembler you can place your code and data exactly where you want to get exactly the behaviour you want.
When you emulate this, you must emulate every aspect of this behaviour correctly or things will fall apart. Think about things like the "open bus bugs" that trip up emulation - some games rely on silly tricks like expecting the capacitance on the pins of the chips to hold a value like a crazy single-byte DRAM for a moment while they look somewhere else. If your emulator just goes "okay no devices are enabled so the bus is simply pulled high to 0xff" that will fail.
With a PC you've got a wider variety of hardware, and a deeper level of abstraction. You don't have to care about flipping registers at just the right time, you just throw some textures and polygons at the GPU and let it do its thing. It'll tell you when it's ready for more. This is why you get the same experience between your AMD GPU, your NVidia GPU, your Intel GPU, and hell why not just have software OpenGL too (because it's slow but that's how we used to do it in the olden days).
So if you wanted to run a SNES game on modern hardware you'd be better off emulating the hardware directly, bugs and all, and then running the "real" game binary.
But if you wanted to run something like an Xbox 360 game - written using "modern" development techniques, where a lot of the hardware is abstracted away behind drivers and libraries - you might well just break it down into functional blocks of code, rewrite them for your native CPU, and shim the calls to the libraries to talk to the ones you have on your native system.
A bit of code that rotates a set of vertices and flings them at the GPU will look pretty much the same in C on any platform, but it'll compile to very different assembly language. That's okay though! If you understand how the instructions relate to the data you can just kind of translate one set of opcodes to another and expect it to be more-or-less right. You can even (kind of) do this automatically!