Esto está inspirado en el desafío Intel 8086 que también está aquí, pero pensé que un desafío 6502 también sería interesante.
El reto
Pensé que sería divertido ver los resultados. Este es obviamente hacia el lado más avanzado del espectro. El desafío es escribir su propio emulador de CPU 6502. Esto implica, por supuesto, comprender su conjunto de instrucciones y su formato de codificación. Los recursos están vinculados al final de esto. El 6502 es uno de los procesadores del mundo real más fáciles de emular. Para los propósitos de este desafío, no tendrá que preocuparse por la sincronización del ciclo si no lo desea, ¡pero eso siempre es una ventaja para incluir!
¡NO COPIE EL CÓDIGO DE NADIE MÁS! Por supuesto, puedes echar un vistazo a otros emuladores para que te ayuden a entender, ¡pero no copiar y pegar! :)
Una vez que su código funciona, siempre puede hacer un esfuerzo adicional si lo desea y convertirlo en un emulador de Apple II, o NES, C64, VIC-20 o cualquiera de los otros miles de millones de sistemas antiguos basados en 6502 de la época.
Probar tu emulador
He compilado una suite de prueba 6502 donde encontré el código fuente aquí: http://code.google.com/p/hmc-6502/source/browse/trunk/emu/testvectors/AllSuiteA.asm
Mi versión compilada se puede descargar aquí: http://rubbermallet.org/AllSuiteA.zip
Cargue el binario de 48 KB en el espacio de memoria de su emulador a $ 4000, lo que deja 16 KB de RAM de lectura y escritura debajo. Cuando la prueba haya terminado de ejecutarse, el valor en la dirección $ 0210 debería ser $ FF, si su CPU pasó. Sabrá que la prueba finaliza cuando el contador del programa (PC) haya alcanzado la dirección $ 45C0.
También hay otras pruebas disponibles aquí: http://visual6502.org/wiki/index.php?title=6502TestPrograms
Haciendo algo más interactivo con él
Una vez que su CPU funciona, ¡probablemente querrá hacer algo más divertido que mirar la salida de prueba! Compilé una imagen ROM de Enhanced BASIC para el 6502. Tiene 16 KB, por lo que debe cargarlo en $ C000 de su espacio de memoria emulado, restablecer su 6502 virtual y comenzar la ejecución.
Descargue este ZIP, que contiene ehbasic.bin: http://rubbermallet.org/ehbasic.zip
La forma en que EhBASIC maneja la entrada / salida es muy simple. Cuando quiere escribir un personaje en la consola, escribe el byte en la ubicación de memoria $ F001. Entonces, cuando su emulador vea el 6502 intente escribir en esa ubicación, simplemente imprima el valor de ese carácter en la consola con un printf ("% c", valor); o como más te guste. (Este desafío no se limita a C, por supuesto)
Cuando sondea el ingreso de un personaje desde la consola, es bastante similar. Sigue leyendo desde la ubicación de memoria $ F004, donde debe tener el siguiente valor de caracteres ASCII del teclado esperando a ser leído. Si no hay más entradas para leer, debería devolver un valor de cero.
EhBASIC sondea el valor en esa ubicación hasta que no sea cero, lo que le permite saber que el byte es una entrada de teclado válida. Es por eso que si no hay más entradas para leer, el emulador debería devolver cero allí. EhBASIC girará en él hasta la siguiente clave válida cuando esté buscando entrada.
Si no borra ese valor a cero después de leer el último valor clave, hará que se repita como si estuviera presionando la tecla, ¡así que tenga cuidado de hacerlo correctamente!
Si su emulador funciona correctamente, esto es lo que verá impreso en su consola cuando ejecute la imagen ROM:
6502 EhBASIC [C]old/[W]arm ?
Presione C, luego presione enter y debería ver:
Memory size ?
31999 Bytes free
Enhanced BASIC 2.22
Ready
Los bytes libres pueden ser diferentes para usted, pero en mi emulador limité el área de memoria de escritura a un límite de 32 KB. Realmente podría llegar hasta donde comienza la ROM, que es la marca de 48 KB.
6502 enlaces de recursos de CPU
Aquí hay algunos recursos que deberían brindarle suficiente información para trabajar:
http://www.obelisk.demon.co.uk/6502/instructions.html
http://www.e-tradition.net/bytes/6502/6502_instruction_set.html
http://www.llx.com/~nparker/a2/opcodes.html <- este tiene información muy interesante
http://en.wikipedia.org/wiki/MOS_Technology_6502
Si tiene preguntas o necesita más información técnica, no dude en preguntarme. También hay una enorme riqueza de otra información 6502 en la web. ¡Google es tu amigo!
fuente

Respuestas:
Pensé en seguir adelante y publicar mi propia implementación. Está COMPLETAMENTE sin golf, pero es una implementación completa.
/ * Fake6502 CPU emulator core v1.1 ******************* * (c) 2011-2013 Mike Chambers * ************************************************** *** / #include <stdio.h> #include <stdint.h> // funciones suministradas externamente utern8_t externo read6502 (dirección uint16_t); extern void write6502 (dirección uint16_t, valor uint8_t); // 6502 define #define UNDOCUMENTED // cuando se define esto, se manejan códigos de operación no documentados. // de lo contrario, simplemente se tratan como NOP. // # define NES_CPU // cuando está definido, el decimal codificado en binario (BCD) // el indicador de estado no es aceptado por ADC y SBC. el 2A03 // CPU en el sistema de entretenimiento de Nintendo no // admite la operación BCD. #define FLAG_CARRY 0x01 #define FLAG_ZERO 0x02 #define FLAG_INTERRUPT 0x04 #define FLAG_DECIMAL 0x08 #define FLAG_BREAK 0x10 #define FLAG_CONSTANT 0x20 #define FLAG_OVERFLOW 0x40 #define FLAG_SIGN 0x80 #define BASE_STACK 0x100 #define saveaccum (n) a = (uint8_t) ((n) & 0x00FF) // macros modificadoras de bandera #define setcarry () status | = FLAG_CARRY #define clearcarry () status & = (~ FLAG_CARRY) #define setzero () status | = FLAG_ZERO #define clearzero () status & = (~ FLAG_ZERO) #define setinterrupt () status | = FLAG_INTERRUPT #define clearinterrupt () status & = (~ FLAG_INTERRUPT) #define setdecimal () status | = FLAG_DECIMAL #define cleardecimal () status & = (~ FLAG_DECIMAL) #define setoverflow () status | = FLAG_OVERFLOW #define clearoverflow () status & = (~ FLAG_OVERFLOW) #define setsign () status | = FLAG_SIGN #define clearsign () status & = (~ FLAG_SIGN) // macros de cálculo de marca #define zerocalc (n) {\ if ((n) & 0x00FF) clearzero (); \ else setzero (); \ } #define signcalc (n) {\ if ((n) & 0x0080) setsign (); \ else clearsign (); \ } #define carrycalc (n) {\ if ((n) & 0xFF00) setcarry (); \ else clearcarry (); \ } #define overflowcalc (n, m, o) {/ * n = resultado, m = acumulador, o = memoria * / \ if (((n) ^ (uint16_t) (m)) & ((n) ^ (o)) & 0x0080) setoverflow (); \ más clearoverflow (); \ } // 6502 registros de CPU uint16_t pc; uint8_t sp, a, x, y, estado = FLAG_CONSTANT; // variables auxiliares uint64_t instrucciones = 0; // realiza un seguimiento del total de instrucciones ejecutadas uint32_t clockticks6502 = 0, clockgoal6502 = 0; uint16_t oldpc, ea, reladdr, valor, resultado; uint8_t opcode, oldstatus; // algunas funciones generales utilizadas por otras funciones vacío push16 (uint16_t pushval) { write6502 (BASE_STACK + sp, (pushval >> 8) & 0xFF); write6502 (BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF); sp - = 2; } vacío push8 (uint8_t pushval) { write6502 (BASE_STACK + sp--, pushval); } uint16_t pull16 () { uint16_t temp16; temp16 = read6502 (BASE_STACK + ((sp + 1) & 0xFF)) | ((uint16_t) read6502 (BASE_STACK + ((sp + 2) & 0xFF)) << 8); sp + = 2; retorno (temp16); } uint8_t pull8 () { return (read6502 (BASE_STACK + ++ sp)); } anular reset6502 () { pc = (uint16_t) read6502 (0xFFFC) | ((uint16_t) read6502 (0xFFFD) << 8); a = 0; x = 0; y = 0; sp = 0xFD; estado | = FLAG_CONSTANT; } vacío estático (* addrtable [256]) (); vacío estático (* optable [256]) (); uint8_t penaop, penaaddr; // funciones de modo de direccionamiento, calcula direcciones efectivas static void imp () {// implícito } static void acc () {// acumulador } static void imm () {// inmediato ea = pc ++; } static void zp () {// página cero ea = (uint16_t) read6502 ((uint16_t) pc ++); } static void zpx () {// página cero, X ea = ((uint16_t) read6502 ((uint16_t) pc ++) + (uint16_t) x) & 0xFF; // envolvente de página cero } static void zpy () {// página cero, Y ea = ((uint16_t) read6502 ((uint16_t) pc ++) + (uint16_t) y) & 0xFF; // envolvente de página cero } static void rel () {// relativo para operaciones de bifurcación (valor inmediato de 8 bits, signo extendido) reladdr = (uint16_t) read6502 (pc ++); if (reladdr & 0x80) reladdr | = 0xFF00; } estático vacío abso () {// absoluto ea = (uint16_t) read6502 (pc) | ((uint16_t) read6502 (pc + 1) << 8); pc + = 2; } estático vacío absx () {// absoluto, X uint16_t página de inicio; ea = ((uint16_t) read6502 (pc) | ((uint16_t) read6502 (pc + 1) << 8)); startpage = ea & 0xFF00; ea + = (uint16_t) x; if (startpage! = (ea & 0xFF00)) {// un ciclo de penilty para cruzar páginas en algunos códigos de operación penaaddr = 1; } pc + = 2; } absy vacío estático () {// absoluto, Y uint16_t página de inicio; ea = ((uint16_t) read6502 (pc) | ((uint16_t) read6502 (pc + 1) << 8)); startpage = ea & 0xFF00; ea + = (uint16_t) y; if (startpage! = (ea & 0xFF00)) {// un ciclo de penilty para cruzar páginas en algunos códigos de operación penaaddr = 1; } pc + = 2; } static void ind () {// indirecto uint16_t eahelp, eahelp2; eahelp = (uint16_t) read6502 (pc) | (uint16_t) ((uint16_t) read6502 (pc + 1) << 8); eahelp2 = (eahelp y 0xFF00) | ((eahelp + 1) y 0x00FF); // replicar el error envolvente del límite de página 6502 ea = (uint16_t) read6502 (eahelp) | ((uint16_t) read6502 (eahelp2) << 8); pc + = 2; } estático vacío indx () {// (indirecto, X) uint16_t eahelp; eahelp = (uint16_t) (((uint16_t) read6502 (pc ++) + (uint16_t) x) & 0xFF); // ajuste de página cero para puntero de tabla ea = (uint16_t) read6502 (eahelp & 0x00FF) | ((uint16_t) read6502 ((eahelp + 1) & 0x00FF) << 8); } estático vacío indy () {// (indirecto), Y uint16_t eahelp, eahelp2, página de inicio; eahelp = (uint16_t) read6502 (pc ++); eahelp2 = (eahelp y 0xFF00) | ((eahelp + 1) y 0x00FF); // envolvente de página cero ea = (uint16_t) read6502 (eahelp) | ((uint16_t) read6502 (eahelp2) << 8); startpage = ea & 0xFF00; ea + = (uint16_t) y; if (startpage! = (ea & 0xFF00)) {// un ciclo de penilty para cruzar páginas en algunos códigos de operación penaaddr = 1; } } static uint16_t getvalue () { if (addrtable [opcode] == acc) return ((uint16_t) a); else return ((uint16_t) read6502 (ea)); } valor de valor vacío estático (uint16_t saveval) { if (addrtable [opcode] == acc) a = (uint8_t) (saveval & 0x00FF); de lo contrario, escriba 6502 (ea, (saveval & 0x00FF)); } // funciones del manejador de instrucciones adc vacío estático () { pena = 1; valor = getvalue (); resultado = (uint16_t) a + valor + (uint16_t) (estado & FLAG_CARRY); carrycalc (resultado); zerocalc (resultado); overflowcalc (resultado, a, valor); signcalc (resultado); #ifndef NES_CPU if (estado y FLAG_DECIMAL) { clearcarry (); if ((a & 0x0F)> 0x09) { a + = 0x06; } if ((a & 0xF0)> 0x90) { a + = 0x60; setcarry (); } clockticks6502 ++; } #terminara si saveaccum (resultado); } vacío estático y () { pena = 1; valor = getvalue (); resultado = (uint16_t) un & valor; zerocalc (resultado); signcalc (resultado); saveaccum (resultado); } estático vacío asl () { valor = getvalue (); resultado = valor << 1; carrycalc (resultado); zerocalc (resultado); signcalc (resultado); putvalue (resultado); } vacío estático bcc () { if ((estado y FLAG_CARRY) == 0) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } vacío estático bcs () { if ((estado y FLAG_CARRY) == FLAG_CARRY) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } estático vacío beq () { if ((estado y FLAG_ZERO) == FLAG_ZERO) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } bit vacío estático () { valor = getvalue (); resultado = (uint16_t) un & valor; zerocalc (resultado); estado = (estado y 0x3F) | (uint8_t) (valor & 0xC0); } vacío estático bmi () { if ((estado y FLAG_SIGN) == FLAG_SIGN) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } vacío estático bne () { if ((estado y FLAG_ZERO) == 0) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } estático vacío bpl () { if ((estado y FLAG_SIGN) == 0) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } vacío estático brk () { pc ++; push16 (pc); // inserta la siguiente dirección de instrucciones en la pila push8 (estado | FLAG_BREAK); // empujar el estado de la CPU para apilar setinterrupt (); // establecer bandera de interrupción pc = (uint16_t) read6502 (0xFFFE) | ((uint16_t) read6502 (0xFFFF) << 8); } estático vacío bvc () { if ((estado y FLAG_OVERFLOW) == 0) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } vacío estático bvs () { if ((estado y FLAG_OVERFLOW) == FLAG_OVERFLOW) { oldpc = pc; pc + = reladdr; if ((oldpc & 0xFF00)! = (pc & 0xFF00)) clockticks6502 + = 2; // verifica si el salto cruzó el límite de una página de lo contrario clockticks6502 ++; } } vacío estático clc () { clearcarry (); } static void cld () { cleardecimal (); } vacío estático cli () { clearinterrupt (); } static void clv () { clearoverflow (); } vacío vacío cmp () { pena = 1; valor = getvalue (); resultado = (uint16_t) a - valor; if (a> = (uint8_t) (valor & 0x00FF)) setcarry (); sino clearcarry (); if (a == (uint8_t) (valor & 0x00FF)) setzero (); más clearzero (); signcalc (resultado); } estático vacío cpx () { valor = getvalue (); resultado = (uint16_t) x - valor; if (x> = (uint8_t) (valor & 0x00FF)) setcarry (); sino clearcarry (); if (x == (uint8_t) (valor & 0x00FF)) setzero (); más clearzero (); signcalc (resultado); } estático vacío cpy () { valor = getvalue (); resultado = (uint16_t) y - valor; if (y> = (uint8_t) (valor & 0x00FF)) setcarry (); sino clearcarry (); if (y == (uint8_t) (valor & 0x00FF)) setzero (); más clearzero (); signcalc (resultado); } vacío estático dec () { valor = getvalue (); resultado = valor - 1; zerocalc (resultado); signcalc (resultado); putvalue (resultado); } dex vacío estático () { X--; zerocalc (x); Signcalc (x); } vacío estático dey () { y--; zerocalc (y); signcalc (y); } vacío estático eor () { pena = 1; valor = getvalue (); resultado = (uint16_t) un valor ^; zerocalc (resultado); signcalc (resultado); saveaccum (resultado); } static void inc () { valor = getvalue (); resultado = valor + 1; zerocalc (resultado); signcalc (resultado); putvalue (resultado); } vacío estático inx () { x ++; zerocalc (x); Signcalc (x); } vacío estático iny () { y ++; zerocalc (y); signcalc (y); } jmp vacío estático () { pc = ea; } estático vacío jsr () { push16 (pc - 1); pc = ea; } vacío estático lda () { pena = 1; valor = getvalue (); a = (uint8_t) (valor & 0x00FF); zerocalc (a); Signcalc (a); } estático vacío ldx () { pena = 1; valor = getvalue (); x = (uint8_t) (valor & 0x00FF); zerocalc (x); Signcalc (x); } estático vacío ldy () { pena = 1; valor = getvalue (); y = (uint8_t) (valor & 0x00FF); zerocalc (y); signcalc (y); } estático vacío lsr () { valor = getvalue (); resultado = valor >> 1; if (valor & 1) setcarry (); sino clearcarry (); zerocalc (resultado); signcalc (resultado); putvalue (resultado); } vacío estático nop () { interruptor (código de operación) { caso 0x1C: caso 0x3C: caso 0x5C: caso 0x7C: caso 0xDC: caso 0xFC: pena = 1; descanso; } } vacío estático ora () { pena = 1; valor = getvalue (); resultado = (uint16_t) a | valor; zerocalc (resultado); signcalc (resultado); saveaccum (resultado); } pha vacío estático () { push8 (a); } php vacío estático () { push8 (estado | FLAG_BREAK); } vacío estático pla () { a = pull8 (); zerocalc (a); Signcalc (a); } vacío estático plp () { estado = pull8 () | FLAG_CONSTANT; } rol vacío estático () { valor = getvalue (); resultado = (valor << 1) | (estado y FLAG_CARRY); carrycalc (resultado); zerocalc (resultado); signcalc (resultado); putvalue (resultado); } vacío estático ror () { valor = getvalue (); resultado = (valor >> 1) | ((estado y FLAG_CARRY) << 7); if (valor & 1) setcarry (); sino clearcarry (); zerocalc (resultado); signcalc (resultado); putvalue (resultado); } estático vacío rti () { estado = pull8 (); valor = pull16 (); pc = valor; } estático vacío rts () { valor = pull16 (); pc = valor + 1; } estático vacío sbc () { pena = 1; valor = getvalue () ^ 0x00FF; resultado = (uint16_t) a + valor + (uint16_t) (estado & FLAG_CARRY); carrycalc (resultado); zerocalc (resultado); overflowcalc (resultado, a, valor); signcalc (resultado); #ifndef NES_CPU if (estado y FLAG_DECIMAL) { clearcarry (); a - = 0x66; if ((a & 0x0F)> 0x09) { a + = 0x06; } if ((a & 0xF0)> 0x90) { a + = 0x60; setcarry (); } clockticks6502 ++; } #terminara si saveaccum (resultado); } sec vacío vacío () { setcarry (); } vacío estático sed () { setdecimal (); } vacío estático sei () { setinterrupt (); } vacío estático sta () { valor de valoración (a); } estático vacío stx () { valor de put (x); } sty vacío vacío () { valor de venta (y); } impuesto nulo estático () { x = a; zerocalc (x); Signcalc (x); } vacío estático tay () { y = a; zerocalc (y); signcalc (y); } vacío estático tsx () { x = sp; zerocalc (x); Signcalc (x); } txa vacío estático () { a = x; zerocalc (a); Signcalc (a); } txs de vacío estático () { sp = x; } tya vacío estático () { a = y; zerocalc (a); Signcalc (a); } // instrucciones no documentadas #ifdef INOCENTADO vacío estático lax () { lda (); ldx (); } saxo vacío estático () { sta (); stx (); putvalue (a & x); if (penalización && penalizaciónaddr) clockticks6502--; } DCP vacío estático () { dic(); cmp (); if (penalización && penalizaciónaddr) clockticks6502--; } isb vacío estático () { Cía(); sbc (); if (penalización && penalizaciónaddr) clockticks6502--; } vacío estático slo () { asl (); ora (); if (penalización && penalizaciónaddr) clockticks6502--; } estático vacío rla () { rol (); y(); if (penalización && penalizaciónaddr) clockticks6502--; } estático vacío sre () { lsr (); eor (); if (penalización && penalizaciónaddr) clockticks6502--; } vacío estático rra () { ror (); adc (); if (penalización && penalizaciónaddr) clockticks6502--; } #más #define lax nop #definir sax nop #define dcp nop #define isb nop #define slo nop #define rla nop #define sre nop #define rra nop #terminara si vacío estático (* addrtable [256]) () = { / * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | * / / * 0 * / imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, / * 0 * / / * 1 * / rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, / * 1 * / / * 2 * / abso, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, / * 2 * / / * 3 * / rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, / * 3 * / / * 4 * / imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, / * 4 * / / * 5 * / rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, / * 5 * / / * 6 * / imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, ind, abso, abso, abso, / * 6 * / / * 7 * / rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, / * 7 * / / * 8 * / imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, / * 8 * / / * 9 * / rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, / * 9 * / / * A * / imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, / * A * / / * B * / rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, / * B * / / * C * / imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, / * C * / / * D * / rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, / * D * / / * E * / imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, / * E * / / * F * / rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx / * F * / }; vacío estático (* optable [256]) () = { / * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | * / / * 0 * / brk, ora, nop, slo, nop, ora, asl, slo, php, ora, asl, nop, nop, ora, asl, slo, / * 0 * / / * 1 * / bpl, ora, nop, slo, nop, ora, asl, slo, clc, ora, nop, slo, nop, ora, asl, slo, / * 1 * / / * 2 * / jsr, y, nop, rla, bit, y, rol, rla, plp, y, rol, nop, bit, y, rol, rla, / * 2 * / / * 3 * / bmi, y, nop, rla, nop, y, rol, rla, sec, y, nop, rla, nop, y, rol, rla, / * 3 * / / * 4 * / rti, eor, nop, sre, nop, eor, lsr, sre, pha, eor, lsr, nop, jmp, eor, lsr, sre, / * 4 * / / * 5 * / bvc, eor, nop, sre, nop, eor, lsr, sre, cli, eor, nop, sre, nop, eor, lsr, sre, / * 5 * / / * 6 * / rts, adc, nop, rra, nop, adc, ror, rra, pla, adc, ror, nop, jmp, adc, ror, rra, / * 6 * / / * 7 * / bvs, adc, nop, rra, nop, adc, ror, rra, sei, adc, nop, rra, nop, adc, ror, rra, / * 7 * / / * 8 * / nop, sta, nop, sax, sty, sta, stx, sax, dey, nop, txa, nop, sty, sta, stx, sax, / * 8 * / / * 9 * / bcc, sta, nop, nop, sty, sta, stx, sax, tya, sta, txs, nop, nop, sta, nop, nop, / * 9 * / / * A * / ldy, lda, ldx, lax, ldy, lda, ldx, lax, tay, lda, tax, nop, ldy, lda, ldx, lax, / * A * / / * B * / bcs, lda, nop, lax, ldy, lda, ldx, lax, clv, lda, tsx, lax, ldy, lda, ldx, lax, / * B * / / * C * / cpy, cmp, nop, dcp, cpy, cmp, dec, dcp, iny, cmp, dex, nop, cpy, cmp, dec, dcp, / * C * / / * D * / bne, cmp, nop, dcp, nop, cmp, dec, dcp, cld, cmp, nop, dcp, nop, cmp, dec, dcp, / * D * / / * E * / cpx, sbc, nop, isb, cpx, sbc, inc, isb, inx, sbc, nop, sbc, cpx, sbc, inc, isb, / * E * / / * F * / beq, sbc, nop, isb, nop, sbc, inc, isb, sed, sbc, nop, isb, nop, sbc, inc, isb / * F * / }; tabla estática uint32_t ticktable [256] = { / * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | * / / * 0 * / 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, / * 0 * / / * 1 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 1 * / / * 2 * / 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, / * 2 * / / * 3 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 3 * / / * 4 * / 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, / * 4 * / / * 5 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 5 * / / * 6 * / 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, / * 6 * / / * 7 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 7 * / / * 8 * / 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, / * 8 * / / * 9 * / 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, / * 9 * / / * A * / 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, / * A * / / * B * / 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, / * B * / / * C * / 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, / * C * / / * D * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * D * / / * E * / 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, / * E * / / * F * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 / * F * / }; nmi6502 nulo () { push16 (pc); push8 (estado); estado | = FLAG_INTERRUPT; pc = (uint16_t) read6502 (0xFFFA) | ((uint16_t) read6502 (0xFFFB) << 8); } nulo irq6502 () { push16 (pc); push8 (estado); estado | = FLAG_INTERRUPT; pc = (uint16_t) read6502 (0xFFFE) | ((uint16_t) read6502 (0xFFFF) << 8); } uint8_t callexternal = 0; nulo (* loopexternal) (); exec6502 nulo (tintcount uint32_t) { clockgoal6502 + = tickcount; while (clockticks6502 <clockgoal6502) { opcode = read6502 (pc ++); pena = 0; penaaddr = 0; (* addrtable [código de operación]) (); (* optable [opcode]) (); clockticks6502 + = ticktable [código de operación]; if (penalización & y penalizaciónaddr) clockticks6502 ++; instrucciones ++; if (callexternal) (* loopexternal) (); } } nulo step6502 () { opcode = read6502 (pc ++); pena = 0; penaaddr = 0; (* addrtable [código de operación]) (); (* optable [opcode]) (); clockticks6502 + = ticktable [código de operación]; if (castigo & & castigoaddr) tics de reloj clockgoal6502 = clockticks6502; instrucciones ++; if (callexternal) (* loopexternal) (); } vacío hookexternal (vacío * funcptr) { if (funcptr! = (void *) NULL) { loopexternal = funcptr; callexternal = 1; } más callexternal = 0; }fuente
Un emulador MOS 6502 en Haskell. Las características incluyen:
Esta es una versión algo desarrollada de una implementación completa (con más características) que hice para este desafío que publicaré más adelante. A pesar del golf, el código sigue siendo sencillo. La única característica que falta es el modo BCD (próximamente ...)
Ejecuta el código ehBASIC:
Y el código, con menos de 300 líneas en total:
fuente
Para cualquier persona interesada, pensé en compartir mi implementación del 6502 en C #. Al igual que con otras publicaciones aquí, no tiene nada de golf, pero es una implementación completa de la característica.
Comencé este proyecto creando una hoja de cálculo de instrucciones cuando estaba aprendiendo acerca de la CPU. Me di cuenta de que podía usar esta hoja de cálculo para ahorrarme algo de escritura. Convertí esto en una tabla de archivos de texto que el emulador carga para ayudar a contar los ciclos y para una salida de desmontaje fácil.
Todo el proyecto está disponible en Github https://github.com/amensch/e6502
fuente