Buenas
Te digo como lo hago yo en ZEsarUX, que es más o menos como lo hacen todos los emuladores.
Yo tengo un temporizador a 50 hz que me sirve de disparador de muchos eventos
Por una parte, la velocidad de la cpu. Tu sabes que un Z80 del spectrum 48k ejecuta 69888 ciclos cada 1/50 segundos? Pues lo que tienes que ir haciendo es contar ciclos de cada instrucción, por ejemplo, la lectura de un opcode son 4 ciclos, una lectura de memoria son 3 ciclos, etc. Vas ejecutando instrucciones y sumando ciclos y cuando llegues a esos 69888 ciclos, te metes en una pausa hasta que se dispare la interrupción de 1/50, así de simple
En cuanto a dibujar la pantalla, si lo haces de golpe, 50 veces por Segundo, solo leyendo la memoria en ese momento, se verán bien el 90% de los juegos pero no tendrás efectos de color en alta resolución, ni franjas en el border, y además muchos juegos tendrán parpadeos (esto es como lo hago yo en ZEsarUX con el setting real video a off)
Para hacerlo bien, a medida que ejecutes instrucciones, debes de ir guardándote en algún buffer cada scanline como está en ese momento. Piensa que en un spectrum real, hay una tv con una posición de electrón que barre la pantalla de manera horizontal y vertical. Si te guardas cada scanline a medida que se van ejecutando instrucciones, luego al final de cada interrupción, puedes volcar todo ese buffer en pantalla y entonces si, se verá exactamente igual que un Spectrum
En cuanto al sonido, puedes hacer de manera parecida a la pantalla. Te vas guardando en un buffer los valores de speaker y chip ay en cada momento, y al final de tu frame de pantalla, lanzas ese buffer de audio a la tarjeta de sonido
En fin, es fácil y complejo a la vez. Recuerda que la memoria contenida te agrega ciclos extra, esto si no lo haces, habrán muchos juegos que funcionaran acelerados (como un Inves, que no tiene memoria contenida)
Yo con ZEsarUX empecé casi como tú: tenía una rutina que me mostraba el contenido de la pantalla de vídeo... aunque como en ese momento no sabía como hacer una ventana gráfica, solo tenía una ventana de texto.., por lo que hacia una especie de OCR para buscar caracteres en la memoria del Spectrum y los mostraba en pantalla de texto (una terminal Linux de texto pura)
Desde ahí empecé a emular cada instrucción del Z80, una a una.... Y el resto es historia
En cuanto al lenguaje yo soy partidario de C, creo que pocos lenguajes te darán más rendimiento que este, exceptuando el assembler. Mi primer emulador (ZXSpectr) estaba en assembler para x86 y el rendimiento era brutal, eso si, era mucho más simple que ZEsarUX
Ánimo!
Saludos
César