summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2020-11-21 21:08:43 +0100
committerAndreas Baumann <mail@andreasbaumann.cc>2020-11-21 21:08:43 +0100
commit052899e196c6a2660651b9896ceed3313e7d0bac (patch)
tree9deb31de468693326a2b426db76f0e99ffe0c9c2
parent56ae2c7b25157e44c50ab982236fee8bbcca0463 (diff)
download6502-052899e196c6a2660651b9896ceed3313e7d0bac.tar.gz
6502-052899e196c6a2660651b9896ceed3313e7d0bac.tar.bz2
started a simple emulator
-rw-r--r--LINKS10
-rw-r--r--README3
-rw-r--r--doc/65c02_reset.txt9
-rw-r--r--emu/6502.c256
-rw-r--r--emu/6502.h60
-rw-r--r--emu/emu.c21
-rw-r--r--emu/emul.h9
-rw-r--r--emu/memory.c26
-rw-r--r--emu/memory.h23
-rw-r--r--roms/simple.asm23
10 files changed, 440 insertions, 0 deletions
diff --git a/LINKS b/LINKS
index 7a99468..b5eff15 100644
--- a/LINKS
+++ b/LINKS
@@ -42,6 +42,7 @@ https://github.com/alimansfield2016/ATM65C02_EMU, nice idea, c++ code quite bad
https://github.com/alimansfield2016/ATM65C02_EMU.git, sadly in Javascript
https://github.com/chanmix51/soft65c02, sadly in rust
https://sbc.rictor.org/simulator.html
+https://bbc.godbolt.org/
other/todo:
https://news.ycombinator.com/item?id=22077052
@@ -104,8 +105,11 @@ apple emulator:
https://damian.pecke.tt/2015/04/06/turning-the-arduino-uno-into-an-apple-ii.html
6502 emulators:
+https://www.gianlucaghettini.net/mos-6502-cpu-emulator-in-c/
https://github.com/gianlucag/mos6502.git
https://github.com/skilldrick/6502js
+http://rubbermallet.org/fake6502.c
+https://github.com/davepoo/6502Emulator
other emulators:
https://github.com/pdewacht/oberon-risc-emu (RISC-5 Wirth Emulator, as inspiration)
@@ -116,3 +120,9 @@ https://en.wikipedia.org/wiki/Texas_Instruments_TMS9918
SPI, serial, XModem:
https://coronax.wordpress.com/tag/6502/page/2/
+
+documentation on the 6502:
+http://www.obelisk.me.uk/6502/
+https://sta.c64.org/cbm64mem.html
+https://www.pagetable.com/?p=410
+https://github.com/Klaus2m5/6502_65C02_functional_tests
diff --git a/README b/README
index d208914..483b070 100644
--- a/README
+++ b/README
@@ -102,6 +102,9 @@ certain interrupts, I get a match in the status register).
Button press trigers two events (on press and release) effectively
switching off/on the counter.
+21.11.2020:
+work on a simple 6502 emulator while watching youtube series
+
commands
--------
diff --git a/doc/65c02_reset.txt b/doc/65c02_reset.txt
new file mode 100644
index 0000000..c0a8fce
--- /dev/null
+++ b/doc/65c02_reset.txt
@@ -0,0 +1,9 @@
+1111111111111111 11111000 ffff r f8 : SED
+0000000000000101 11100100 0005 r e4 :
+0000000100000000 01111000 0100 r 78 :
+0000000111111111 10010101 01ff r 95 :
+0000000111111110 00111001 01fe r 39 :
+1111111111111100 00000000 fffc r 00 :
+1111111111111101 11111000 fffd r f8 :
+1111100000000000 01111000 f800 r 78 : xxx
+
diff --git a/emu/6502.c b/emu/6502.c
new file mode 100644
index 0000000..0bb1993
--- /dev/null
+++ b/emu/6502.c
@@ -0,0 +1,256 @@
+#include "6502.h"
+#include "memory.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static const uint16_t reset_vector = 0xFFFC;
+static const uint16_t ZP_base = 0x0;
+static const uint16_t SP_base = 0x100;
+
+void cpu_6502_init( cpu_6502_t *cpu, struct memory_t *memory )
+{
+ cpu->memory = memory;
+ cpu->debug = false;
+}
+
+uint16_t cpu_6502_read_word( cpu_6502_t *cpu, uint16_t addr )
+{
+ return cpu_6502_read_byte( cpu, addr ) + ( cpu_6502_read_byte( cpu, addr + 1 ) << 8 );
+}
+
+void cpu_6502_push_byte( cpu_6502_t *cpu, uint8_t data )
+{
+ cpu_6502_write_byte( cpu, SP_base + cpu->SP, data );
+ cpu->SP--;
+}
+
+void cpu_6502_push_word( cpu_6502_t *cpu, uint16_t data )
+{
+ cpu_6502_push_byte( cpu, ( data >> 8 ) & 0xFF );
+ cpu_6502_push_byte( cpu, data & 0xFF );
+}
+
+uint8_t cpu_6502_pop_byte( cpu_6502_t *cpu )
+{
+ uint8_t data;
+
+ cpu->SP++;
+ data = cpu_6502_read_byte( cpu, SP_base + cpu->SP );
+
+ return data;
+}
+
+uint16_t cpu_6502_pop_word( cpu_6502_t *cpu )
+{
+ uint8_t low;
+ uint8_t high;
+ uint16_t data;
+
+ low = cpu_6502_pop_byte( cpu );
+ high = cpu_6502_pop_byte( cpu );
+
+ data = ( high << 8 ) + low;
+
+ return data;
+}
+
+void cpu_6502_reset( cpu_6502_t *cpu )
+{
+ cpu->PC = cpu_6502_read_word( cpu, reset_vector );
+}
+
+uint8_t cpu_6502_read_byte( cpu_6502_t *cpu, uint16_t addr )
+{
+ return cpu->memory->read( cpu->memory, addr );
+}
+
+void cpu_6502_write_byte( cpu_6502_t *cpu, uint16_t addr, uint8_t data )
+{
+ cpu->memory->write( cpu->memory, addr, data );
+}
+
+void cpu_6502_write_word( cpu_6502_t *cpu, uint16_t addr, uint16_t data )
+{
+ cpu->memory->write( cpu->memory, addr, ( data && 0x00FF ) );
+ cpu->memory->write( cpu->memory, addr + 1, ( data && 0x00FF ) );
+}
+
+void cpu_6502_run( cpu_6502_t *cpu )
+{
+ while( true ) {
+ cpu_6502_step( cpu );
+ }
+}
+
+static void handle_zero( cpu_6502_t *cpu, uint8_t x )
+{
+ if( x == 0 ) {
+ cpu->PS |= PS_Z;
+ } else {
+ cpu->PS &= ~PS_Z;
+ }
+}
+
+static void handle_sign( cpu_6502_t *cpu, uint8_t x )
+{
+ if( x & 0x80 ) {
+ cpu->PS |= PS_N;
+ } else {
+ cpu->PS &= ~ PS_N;
+ }
+}
+
+static bool is_negative( cpu_6502_t *cpu )
+{
+ return cpu->PS & PS_N;
+}
+
+static bool is_overflow( cpu_6502_t *cpu )
+{
+ return cpu->PS & PS_V;
+}
+
+static bool is_brk( cpu_6502_t *cpu )
+{
+ return cpu->PS & PS_B;
+}
+
+static bool is_decimal( cpu_6502_t *cpu )
+{
+ return cpu->PS & PS_D;
+}
+
+static bool is_interrupt( cpu_6502_t *cpu )
+{
+ return cpu->PS & PS_I;
+}
+
+static bool is_zero( cpu_6502_t *cpu )
+{
+ return cpu->PS & PS_Z;
+}
+
+static bool is_carry( cpu_6502_t *cpu )
+{
+ return cpu->PS & PS_C;
+}
+
+void cpu_6502_print_state( cpu_6502_t *cpu, uint8_t opcode )
+{
+ fprintf( stderr, "PC: %04X SP: 01%02X PS: %02X %c%c-%c%c%c%c%c A: %02X X: %02X Y: %02X OP: %02X\n",
+ cpu->PC, cpu->SP, cpu->PS,
+ is_negative( cpu ) ? 'N' : 'n',
+ is_overflow( cpu ) ? 'V' : 'v',
+ is_brk( cpu ) ? 'B' : 'b',
+ is_decimal( cpu ) ? 'D' : 'd',
+ is_interrupt( cpu ) ? 'I' : 'i',
+ is_zero( cpu ) ? 'Z' : 'z',
+ is_carry( cpu ) ? 'C' : 'c',
+ cpu->A, cpu->X, cpu->Y, opcode );
+}
+
+static void update_negative_and_sign( cpu_6502_t *cpu, uint8_t x )
+{
+ handle_zero( cpu, x );
+ handle_sign( cpu, x );
+}
+
+void cpu_6502_print_memory( cpu_6502_t *cpu, uint16_t base, uint8_t size )
+{
+ for( uint16_t addr = base; addr <= base + size; addr++ ) {
+ fprintf( stderr, " %02X", cpu_6502_read_byte( cpu, addr ) );
+ }
+ fputs( "\n", stderr );
+}
+
+void cpu_6502_print_stack( cpu_6502_t *cpu )
+{
+ cpu_6502_print_memory( cpu, SP_base, 255 );
+}
+
+void cpu_6502_print_zerop_page( cpu_6502_t *cpu )
+{
+ cpu_6502_print_memory( cpu, ZP_base, 255 );
+}
+
+void cpu_6502_step( cpu_6502_t *cpu )
+{
+ uint8_t opcode;
+ uint8_t operand8;
+
+ opcode = cpu_6502_read_byte( cpu, cpu->PC );
+ cpu->PC++;
+
+ if( cpu->debug ) {
+ cpu_6502_print_state( cpu, opcode );
+ cpu_6502_print_stack( cpu );
+ cpu_6502_print_zerop_page( cpu );
+ }
+
+ switch( opcode ) {
+ case LDX_IMM:
+ operand8 = cpu_6502_read_byte( cpu, cpu->PC );
+ cpu->PC++;
+ cpu->X = operand8;
+ update_negative_and_sign( cpu, cpu->X );
+ break;
+
+ case LDX_ZERO:
+ operand8 = cpu_6502_read_byte( cpu, cpu->PC );
+ cpu->PC++;
+ cpu->X = cpu_6502_read_byte( cpu, operand8 );
+ update_negative_and_sign( cpu, cpu->X );
+ break;
+
+ case LDY_IMM:
+ operand8 = cpu_6502_read_byte( cpu, cpu->PC );
+ cpu->PC++;
+ cpu->Y = operand8;
+ update_negative_and_sign( cpu, cpu->Y );
+ break;
+
+ case LDY_ZERO:
+ operand8 = cpu_6502_read_byte( cpu, cpu->PC );
+ cpu->PC++;
+ cpu->Y = cpu_6502_read_byte( cpu, operand8 );
+ update_negative_and_sign( cpu, cpu->Y );
+ break;
+
+ case LDA_IMM:
+ operand8 = cpu_6502_read_byte( cpu, cpu->PC );
+ cpu->PC++;
+ cpu->A = operand8;
+ update_negative_and_sign( cpu, cpu->A );
+ break;
+
+ case LDA_ZERO:
+ operand8 = cpu_6502_read_byte( cpu, cpu->PC );
+ cpu->PC++;
+ cpu->A = cpu_6502_read_byte( cpu, operand8 );
+ update_negative_and_sign( cpu, cpu->A );
+ break;
+
+ case JMP_ABS:
+ cpu->PC = cpu_6502_read_word( cpu, cpu->PC );
+ break;
+
+ case JSR_ABS:
+ cpu_6502_push_word( cpu, cpu->PC + 1 );
+ cpu->PC = cpu_6502_read_word( cpu, cpu->PC );
+ break;
+
+ case RTS_IMPL:
+ cpu->PC = cpu_6502_pop_word( cpu );
+ cpu->PC++;
+ break;
+
+ case TXS_IMPL:
+ cpu->SP = cpu->X;
+ break;
+
+ default:
+ fprintf( stderr, "ERROR: Illegal opcode %02X at PC %04X\n", opcode, cpu->PC );
+ exit( EXIT_FAILURE );
+ }
+}
diff --git a/emu/6502.h b/emu/6502.h
new file mode 100644
index 0000000..1af25bc
--- /dev/null
+++ b/emu/6502.h
@@ -0,0 +1,60 @@
+#ifndef CPU_6502_H
+#define CPU_6502_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+typedef struct
+{
+ uint8_t A;
+ uint8_t X;
+ uint8_t Y;
+ uint8_t SP;
+ uint16_t PC;
+ uint8_t PS;
+
+ struct memory_t *memory;
+
+ bool debug;
+} cpu_6502_t;
+
+enum {
+ PS_N = 0x80,
+ PS_V = 0x40,
+ PS_B = 0x10,
+ PS_D = 0x08,
+ PS_I = 0x04,
+ PS_Z = 0x02,
+ PS_C = 0x01
+};
+
+enum {
+ LDX_IMM = 0xA2,
+ LDX_ZERO = 0xA6,
+ LDY_IMM = 0xA0,
+ LDY_ZERO = 0xA4,
+ LDA_IMM = 0xA9,
+ LDA_ZERO = 0xA5,
+ JMP_ABS = 0x4C,
+ JSR_ABS = 0x20,
+ RTS_IMPL = 0x60,
+ TXS_IMPL = 0x9A
+};
+
+void cpu_6502_init( cpu_6502_t *cpu, struct memory_t *memory );
+void cpu_6502_reset( cpu_6502_t *cpu );
+uint8_t cpu_6502_read_byte( cpu_6502_t *cpu, uint16_t addr );
+uint16_t cpu_6502_read_word( cpu_6502_t *cpu, uint16_t addr );
+void cpu_6502_write_byte( cpu_6502_t *cpu, uint16_t addr, uint8_t data );
+void cpu_6502_push_byte( cpu_6502_t *cpu, uint8_t data );
+void cpu_6502_push_word( cpu_6502_t *cpu, uint16_t data );
+uint8_t cpu_6502_pop_byte( cpu_6502_t *cpu );
+uint16_t cpu_6502_pop_word( cpu_6502_t *cpu );
+void cpu_6502_run( cpu_6502_t *cpu );
+void cpu_6502_print_state( cpu_6502_t *cpu, uint8_t opcode );
+void cpu_6502_print_memory( cpu_6502_t *cpu, uint16_t base, uint8_t size );
+void cpu_6502_print_stack( cpu_6502_t *cpu );
+void cpu_6502_print_zerop_page( cpu_6502_t *cpu );
+void cpu_6502_step( cpu_6502_t *cpu );
+
+#endif
diff --git a/emu/emu.c b/emu/emu.c
new file mode 100644
index 0000000..fbd9bb7
--- /dev/null
+++ b/emu/emu.c
@@ -0,0 +1,21 @@
+#include "emul.h"
+#include "6502.h"
+#include "memory.h"
+
+#include <stdlib.h>
+
+int main( int argc, char *argv[] )
+{
+ cpu_6502_t cpu;
+ memory_t memory;
+
+ memory_init( &memory );
+ memory_load( &memory, ROM_START, ROM_SIZE, "./rom.bin" );
+
+ cpu_6502_init( &cpu, &memory );
+ cpu.debug = true;
+ cpu_6502_reset( &cpu );
+ cpu_6502_run( &cpu );
+
+ exit( EXIT_SUCCESS );
+}
diff --git a/emu/emul.h b/emu/emul.h
new file mode 100644
index 0000000..cefa020
--- /dev/null
+++ b/emu/emul.h
@@ -0,0 +1,9 @@
+#ifndef EMUL_H
+#define EMUL_H
+
+enum {
+ ROM_START = 0xF800,
+ ROM_SIZE = 2048
+};
+
+#endif
diff --git a/emu/memory.c b/emu/memory.c
new file mode 100644
index 0000000..c9d953b
--- /dev/null
+++ b/emu/memory.c
@@ -0,0 +1,26 @@
+#include "memory.h"
+
+#include <stdio.h>
+
+void memory_init( memory_t *memory )
+{
+ memory->read = memory_read;
+ memory->write = memory_write;
+}
+
+uint8_t memory_read( memory_t *memory, uint16_t addr )
+{
+ return memory->cell[addr];
+}
+
+void memory_write( memory_t *memory, uint16_t addr, uint8_t data )
+{
+ memory->cell[addr] = data;
+}
+
+void memory_load( memory_t *memory, uint16_t addr, uint16_t size, const char *filename )
+{
+ FILE *f = fopen( filename, "rb" );
+ fread( &memory->cell[addr], size, 1, f );
+ fclose( f );
+}
diff --git a/emu/memory.h b/emu/memory.h
new file mode 100644
index 0000000..35fd0ef
--- /dev/null
+++ b/emu/memory.h
@@ -0,0 +1,23 @@
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include <stdint.h>
+
+enum {
+ MEMORY_SIZE = 65535
+};
+
+typedef struct memory_t
+{
+ uint8_t cell[MEMORY_SIZE];
+
+ uint8_t (*read)( struct memory_t *memory, uint16_t addr );
+ void (*write)( struct memory_t *memory, uint16_t addr, uint8_t data );
+} memory_t;
+
+void memory_init( memory_t *memory );
+uint8_t memory_read( memory_t *memory, uint16_t addr );
+void memory_write( memory_t *memory, uint16_t addr, uint8_t data );
+void memory_load( memory_t *memory, uint16_t addr, uint16_t size, const char *filename );
+
+#endif
diff --git a/roms/simple.asm b/roms/simple.asm
new file mode 100644
index 0000000..7e57ee4
--- /dev/null
+++ b/roms/simple.asm
@@ -0,0 +1,23 @@
+ .org #$f800
+
+reset:
+ ldx #$FF
+ txs
+ lda #42
+ lda $0
+ ldx #41
+ ldx $1
+ jsr sub1
+
+fini:
+ jmp fini
+
+sub1:
+ ldy #40
+ ldy $2
+ rts
+
+ .org #$fffa
+ .word $0
+ .word reset
+ .word $0