From 378169d56b89e20d7a1a019cd6d6d35c43aea35a Mon Sep 17 00:00:00 2001 From: DavidBuchanan314 Date: Tue, 3 Jan 2017 19:39:48 +0000 Subject: [PATCH] Initial commit --- .gitignore | 2 + 6502.c | 887 +++++++++++++++++++++++++++++++++++++++++++ 6502.h | 70 ++++ 6850.c | 55 +++ 6850.h | 26 ++ Makefile | 22 ++ README.md | 81 +++- examples/ehbasic.rom | Bin 0 -> 16384 bytes main.c | 71 ++++ 9 files changed, 1213 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 6502.c create mode 100644 6502.h create mode 100644 6850.c create mode 100644 6850.h create mode 100644 Makefile create mode 100644 examples/ehbasic.rom create mode 100644 main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a1ca78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +6502-emu diff --git a/6502.c b/6502.c new file mode 100644 index 0000000..fdff692 --- /dev/null +++ b/6502.c @@ -0,0 +1,887 @@ +#include +#include +#include + +#include "6502.h" + +//#define DEBUG + +int lengths[NUM_MODES]; // instruction length table, indexed by addressing mode +uint8_t * (*get_ptr[NUM_MODES])(); // addressing mode decoder table +Instruction instructions[0x100]; // instruction data table +Instruction inst; // the current instruction (used for convenience) +int jumping; // used to check that we don't need to increment the PC after a jump + +/* Flag Checks */ + +static inline void Nflag(int8_t val) +{ + SR.bits.sign = val < 0; +} + +static inline void Zflag(uint8_t val) +{ + SR.bits.zero = val == 0; +} + +/* Stack Helpers */ + +static inline void stack_push(uint8_t val) +{ + memory[0x100+(SP--)] = val; +} + +static inline uint8_t stack_pull() +{ + return memory[0x100+(++SP)]; +} + +/* Memory read/write wrappers */ + +static inline uint8_t * read_ptr() +{ + return readAddr = get_ptr[inst.mode](); +} + +static inline uint8_t * write_ptr() +{ + return writeAddr = get_ptr[inst.mode](); +} + +/* Instruction Implementations */ + +static void inst_ADC() +{ + uint8_t operand = * read_ptr(); + int tmp = A + operand + (SR.bits.carry & 1); + SR.bits.carry = tmp > 0xFF; + SR.bits.overflow = ((A^tmp)&(operand^tmp)&0x80) != 0; + A = tmp & 0xFF; + Nflag(A); + Zflag(A); +} + +static void inst_AND() +{ + A &= * read_ptr(); + Nflag(A); + Zflag(A); +} + +static void inst_ASL() +{ + uint8_t tmp = * read_ptr(); + SR.bits.carry = (tmp & 0x80) != 0; + tmp <<= 1; + Nflag(tmp); + Zflag(tmp); + * write_ptr() = tmp; +} + +static void inst_BCC() +{ + if (!SR.bits.carry) { + PC = read_ptr() - memory; + } +} + +static void inst_BCS() +{ + if (SR.bits.carry) { + PC = read_ptr() - memory; + } +} + +static void inst_BEQ() +{ + if (SR.bits.zero) { + PC = read_ptr() - memory; + } +} + +static void inst_BIT() +{ + uint8_t tmp = * read_ptr(); + Nflag(tmp); + Zflag(tmp & A); + SR.bits.overflow = (tmp & 0x40) != 0; +} + +static void inst_BMI() +{ + if (SR.bits.sign) { + PC = read_ptr() - memory; + } +} + +static void inst_BNE() +{ + if (!SR.bits.zero) { + PC = read_ptr() - memory; + } +} + +static void inst_BPL() +{ + if (!SR.bits.sign) { + PC = read_ptr() - memory; + } +} + +static void inst_BRK() +{ + uint16_t newPC; + memcpy(&newPC, &memory[IRQ_VEC], sizeof(newPC)); + PC += 2; + stack_push(PC >> 8); + stack_push(PC & 0xFF); + SR.bits.brk = 1; + stack_push(SR.byte); + SR.bits.interrupt = 1; + PC = newPC; + jumping = 1; +} + +static void inst_BVC() +{ + if (!SR.bits.overflow) { + PC = read_ptr() - memory; + } +} + +static void inst_BVS() +{ + if (SR.bits.overflow) { + PC = read_ptr() - memory; + } +} + +static void inst_CLC() +{ + SR.bits.carry = 0; +} + +static void inst_CLD() +{ + SR.bits.decimal = 0; +} + +static void inst_CLI() +{ + SR.bits.interrupt = 0; +} + +static void inst_CLV() +{ + SR.bits.overflow = 0; +} + +static void inst_CMP() +{ + uint8_t operand = * read_ptr(); + uint8_t tmpDiff = A - operand; + Nflag(tmpDiff); + Zflag(tmpDiff); + SR.bits.carry = A >= operand; +} + +static void inst_CPX() +{ + uint8_t operand = * read_ptr(); + uint8_t tmpDiff = X - operand; + Nflag(tmpDiff); + Zflag(tmpDiff); + SR.bits.carry = X >= operand; +} + +static void inst_CPY() +{ + uint8_t operand = * read_ptr(); + uint8_t tmpDiff = Y - operand; + Nflag(tmpDiff); + Zflag(tmpDiff); + SR.bits.carry = Y >= operand; +} + +static void inst_DEC() +{ + uint8_t tmp = * read_ptr(); + tmp--; + Nflag(tmp); + Zflag(tmp); + * write_ptr() = tmp; +} + +static void inst_DEX() +{ + X--; + Nflag(X); + Zflag(X); +} + +static void inst_DEY() +{ + Y--; + Nflag(Y); + Zflag(Y); +} + +static void inst_EOR() +{ + A ^= * read_ptr(); + Nflag(A); + Zflag(A); +} + +static void inst_INC() +{ + uint8_t tmp = * read_ptr(); + tmp++; + Nflag(tmp); + Zflag(tmp); + * write_ptr() = tmp; +} + +static void inst_INX() +{ + X++; + Nflag(X); + Zflag(X); +} + +static void inst_INY() +{ + Y++; + Nflag(Y); + Zflag(Y); +} + +static void inst_JMP() +{ + PC = read_ptr() - memory; + jumping = 1; +} + +static void inst_JSR() +{ + uint16_t newPC = read_ptr() - memory; + PC += 2; + stack_push(PC >> 8); + stack_push(PC & 0xFF); + PC = newPC; + jumping = 1; +} + +static void inst_LDA() +{ + A = * read_ptr(); + Nflag(A); + Zflag(A); +} + +static void inst_LDX() +{ + X = * read_ptr(); + Nflag(X); + Zflag(X); +} + +static void inst_LDY() +{ + Y = * read_ptr(); + Nflag(Y); + Zflag(Y); +} + +static void inst_LSR() +{ + uint8_t tmp = * read_ptr(); + SR.bits.carry = tmp & 1; + tmp >>= 1; + Nflag(tmp); + Zflag(tmp); + * write_ptr() = tmp; +} + +static void inst_NOP() +{ + // nothing +} + +static void inst_ORA() +{ + A |= * read_ptr(); + Nflag(A); + Zflag(A); +} + +static void inst_PHA() +{ + stack_push(A); +} + +static void inst_PHP() +{ + SR.bits.brk = 1; // this is slightly unexpected, but it's what the real hardware does. + stack_push(SR.byte); +} + +static void inst_PLA() +{ + A = stack_pull(); + Nflag(A); + Zflag(A); +} + +static void inst_PLP() +{ + SR.byte = stack_pull(); + SR.bits.unused = 1; +} + +static void inst_ROL() +{ + int tmp = (* read_ptr()) << 1; + tmp |= SR.bits.carry & 1; + SR.bits.carry = tmp > 0xFF; + tmp &= 0xFF; + Nflag(tmp); + Zflag(tmp); + * write_ptr() = tmp; +} + +static void inst_ROR() +{ + int tmp = * read_ptr(); + tmp |= SR.bits.carry << 8; + SR.bits.carry = tmp & 1; + tmp >>= 1; + Nflag(tmp); + Zflag(tmp); + * write_ptr() = tmp; +} + +static void inst_RTI() +{ + SR.byte = stack_pull(); + SR.bits.unused = 1; + PC = stack_pull(); + PC |= stack_pull() << 8; + //PC += 1; + jumping = 1; +} + +static void inst_RTS() +{ + PC = stack_pull(); + PC |= stack_pull() << 8; + PC += 1; + jumping = 1; +} + +static void inst_SBC() +{ + uint8_t operand = ~(* read_ptr()); // identical to ACD with the operand inverted + int tmp = A + operand + (SR.bits.carry & 1); + SR.bits.carry = tmp > 0xFF; + SR.bits.overflow = ((A^tmp)&(operand^tmp)&0x80) != 0; + A = tmp & 0xFF; + Nflag(A); + Zflag(A); +} + +static void inst_SEC() +{ + SR.bits.carry = 1; +} + +static void inst_SED() +{ + SR.bits.decimal = 1; + printf("DECIMAL! :(\r\n"); // TODO +} + +static void inst_SEI() +{ + SR.bits.interrupt = 1; +} + +static void inst_STA() +{ + * write_ptr() = A; +} + +static void inst_STX() +{ + * write_ptr() = X; +} + +static void inst_STY() +{ + * write_ptr() = Y; +} + +static void inst_TAX() +{ + X = A; + Nflag(X); + Zflag(X); +} + +static void inst_TAY() +{ + Y = A; + Nflag(Y); + Zflag(Y); +} + +static void inst_TSX() +{ + X = SP; + Nflag(X); + Zflag(X); +} + +static void inst_TXA() +{ + A = X; + Nflag(A); + Zflag(A); +} + +static void inst_TXS() +{ + SP = X; +} + +static void inst_TYA() +{ + A = Y; + Nflag(A); + Zflag(A); +} + +/* Addressing Implementations */ + +uint8_t * get_IMM() +{ + return &memory[PC+1]; +} + +uint16_t get_uint16() +{ // used only as part of other modes + uint16_t index; + memcpy(&index, get_IMM(), sizeof(index)); // hooray for optimising compilers + return index; +} + +uint8_t * get_ZP() +{ + return &memory[memory[PC+1]]; +} + +uint8_t * get_ZPX() +{ + return &memory[(memory[PC+1] + X) & 0xFF]; +} + +uint8_t * get_ZPY() +{ + return &memory[(memory[PC+1] + Y) & 0xFF]; +} + +uint8_t * get_ACC() +{ + return &A; +} + +uint8_t * get_ABS() +{ + return &memory[get_uint16()]; +} + +uint8_t * get_ABSX() +{ + return &memory[get_uint16()+X]; +} + +uint8_t * get_ABSY() +{ + return &memory[get_uint16()+Y]; +} + +uint8_t * get_IND() +{ + uint16_t ptr; + memcpy(&ptr, get_ABS(), sizeof(ptr)); + return &memory[ptr]; +} + +uint8_t * get_XIND() +{ + uint16_t ptr; + memcpy(&ptr, get_ZPX(), sizeof(ptr)); + return &memory[ptr]; +} + +uint8_t * get_INDY() +{ + uint16_t ptr; + memcpy(&ptr, get_ZP(), sizeof(ptr)); + ptr += Y; + return &memory[ptr]; +} + +uint8_t * get_REL() +{ + return &memory[PC + (int8_t) memory[PC+1]]; +} + + +/* Construction of Tables */ + +void init_tables() // this is only done at runtime to improve code readability. +{ + /* Instruction Lengths */ + + lengths[ACC] = 1; + lengths[ABS] = 3; + lengths[ABSX] = 3; + lengths[ABSY] = 3; + lengths[IMM] = 2; + lengths[IMPL] = 1; + lengths[IND] = 3; + lengths[XIND] = 2; + lengths[INDY] = 2; + lengths[REL] = 2; + lengths[ZP] = 2; + lengths[ZPX] = 2; + lengths[ZPY] = 2; + + /* Addressing Modes */ + + get_ptr[ACC] = get_ACC; + get_ptr[ABS] = get_ABS; + get_ptr[ABSX] = get_ABSX; + get_ptr[ABSY] = get_ABSY; + get_ptr[IMM] = get_IMM; + get_ptr[IND] = get_IND; + get_ptr[XIND] = get_XIND; + get_ptr[INDY] = get_INDY; + get_ptr[REL] = get_REL; + get_ptr[ZP] = get_ZP; + get_ptr[ZPX] = get_ZPX; + get_ptr[ZPY] = get_ZPY; + + /* Instructions */ + + instructions[0x00] = (Instruction) {"BRK impl", inst_BRK, IMPL}; + instructions[0x01] = (Instruction) {"ORA X,ind", inst_ORA, XIND}; + instructions[0x02] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x03] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x04] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x05] = (Instruction) {"ORA zpg", inst_ORA, ZP}; + instructions[0x06] = (Instruction) {"ASL zpg", inst_ASL, ZP}; + instructions[0x07] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x08] = (Instruction) {"PHP impl", inst_PHP, IMPL}; + instructions[0x09] = (Instruction) {"ORA #", inst_ORA, IMM}; + instructions[0x0A] = (Instruction) {"ASL A", inst_ASL, ACC}; + instructions[0x0B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x0C] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x0D] = (Instruction) {"ORA abs", inst_ORA, ABS}; + instructions[0x0E] = (Instruction) {"ASL abs", inst_ASL, ABS}; + instructions[0x0F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x10] = (Instruction) {"BPL rel", inst_BPL, REL}; + instructions[0x11] = (Instruction) {"ORA ind,Y", inst_ORA, INDY}; + instructions[0x12] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x13] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x14] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x15] = (Instruction) {"ORA zpg,X", inst_ORA, ZPX}; + instructions[0x16] = (Instruction) {"ASL zpg,X", inst_ASL, ZPX}; + instructions[0x17] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x18] = (Instruction) {"CLC impl", inst_CLC, IMPL}; + instructions[0x19] = (Instruction) {"ORA abs,Y", inst_ORA, ABSY}; + instructions[0x1A] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x1B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x1C] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x1D] = (Instruction) {"ORA abs,X", inst_ORA, ABSX}; + instructions[0x1E] = (Instruction) {"ASL abs,X", inst_ASL, ABSX}; + instructions[0x1F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x20] = (Instruction) {"JSR abs", inst_JSR, ABS}; + instructions[0x21] = (Instruction) {"AND X,ind", inst_AND, XIND}; + instructions[0x22] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x23] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x24] = (Instruction) {"BIT zpg", inst_BIT, ZP}; + instructions[0x25] = (Instruction) {"AND zpg", inst_AND, ZP}; + instructions[0x26] = (Instruction) {"ROL zpg", inst_ROL, ZP}; + instructions[0x27] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x28] = (Instruction) {"PLP impl", inst_PLP, IMPL}; + instructions[0x29] = (Instruction) {"AND #", inst_AND, IMM}; + instructions[0x2A] = (Instruction) {"ROL A", inst_ROL, ACC}; + instructions[0x2B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x2C] = (Instruction) {"BIT abs", inst_BIT, ABS}; + instructions[0x2D] = (Instruction) {"AND abs", inst_AND, ABS}; + instructions[0x2E] = (Instruction) {"ROL abs", inst_ROL, ABS}; + instructions[0x2F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x30] = (Instruction) {"BMI rel", inst_BMI, REL}; + instructions[0x31] = (Instruction) {"AND ind,Y", inst_AND, INDY}; + instructions[0x32] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x33] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x34] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x35] = (Instruction) {"AND zpg,X", inst_AND, ZPX}; + instructions[0x36] = (Instruction) {"ROL zpg,X", inst_ROL, ZPX}; + instructions[0x37] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x38] = (Instruction) {"SEC impl", inst_SEC, IMPL}; + instructions[0x39] = (Instruction) {"AND abs,Y", inst_AND, ABSY}; + instructions[0x3A] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x3B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x3C] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x3D] = (Instruction) {"AND abs,X", inst_AND, ABSX}; + instructions[0x3E] = (Instruction) {"ROL abs,X", inst_ROL, ABSX}; + instructions[0x3F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x40] = (Instruction) {"RTI impl", inst_RTI, IMPL}; + instructions[0x41] = (Instruction) {"EOR X,ind", inst_EOR, XIND}; + instructions[0x42] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x43] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x44] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x45] = (Instruction) {"EOR zpg", inst_EOR, ZP}; + instructions[0x46] = (Instruction) {"LSR zpg", inst_LSR, ZP}; + instructions[0x47] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x48] = (Instruction) {"PHA impl", inst_PHA, IMPL}; + instructions[0x49] = (Instruction) {"EOR #", inst_EOR, IMM}; + instructions[0x4A] = (Instruction) {"LSR A", inst_LSR, ACC}; + instructions[0x4B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x4C] = (Instruction) {"JMP abs", inst_JMP, ABS}; + instructions[0x4D] = (Instruction) {"EOR abs", inst_EOR, ABS}; + instructions[0x4E] = (Instruction) {"LSR abs", inst_LSR, ABS}; + instructions[0x4F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x50] = (Instruction) {"BVC rel", inst_BVC, REL}; + instructions[0x51] = (Instruction) {"EOR ind,Y", inst_EOR, INDY}; + instructions[0x52] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x53] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x54] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x55] = (Instruction) {"EOR zpg,X", inst_EOR, ZPX}; + instructions[0x56] = (Instruction) {"LSR zpg,X", inst_LSR, ZPX}; + instructions[0x57] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x58] = (Instruction) {"CLI impl", inst_CLI, IMPL}; + instructions[0x59] = (Instruction) {"EOR abs,Y", inst_EOR, ABSY}; + instructions[0x5A] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x5B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x5C] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x5D] = (Instruction) {"EOR abs,X", inst_EOR, ABSX}; + instructions[0x5E] = (Instruction) {"LSR abs,X", inst_LSR, ABSX}; + instructions[0x5F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x60] = (Instruction) {"RTS impl", inst_RTS, IMPL}; + instructions[0x61] = (Instruction) {"ADC X,ind", inst_ADC, XIND}; + instructions[0x62] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x63] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x64] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x65] = (Instruction) {"ADC zpg", inst_ADC, ZP}; + instructions[0x66] = (Instruction) {"ROR zpg", inst_ROR, ZP}; + instructions[0x67] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x68] = (Instruction) {"PLA impl", inst_PLA, IMPL}; + instructions[0x69] = (Instruction) {"ADC #", inst_ADC, IMM}; + instructions[0x6A] = (Instruction) {"ROR A", inst_ROR, ACC}; + instructions[0x6B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x6C] = (Instruction) {"JMP ind", inst_JMP, IND}; + instructions[0x6D] = (Instruction) {"ADC abs", inst_ADC, ABS}; + instructions[0x6E] = (Instruction) {"ROR abs", inst_ROR, ABS}; + instructions[0x6F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x70] = (Instruction) {"BVS rel", inst_BVS, REL}; + instructions[0x71] = (Instruction) {"ADC ind,Y", inst_ADC, INDY}; + instructions[0x72] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x73] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x74] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x75] = (Instruction) {"ADC zpg,X", inst_ADC, ZPX}; + instructions[0x76] = (Instruction) {"ROR zpg,X", inst_ROR, ZPX}; + instructions[0x77] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x78] = (Instruction) {"SEI impl", inst_SEI, IMPL}; + instructions[0x79] = (Instruction) {"ADC abs,Y", inst_ADC, ABSY}; + instructions[0x7A] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x7B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x7C] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x7D] = (Instruction) {"ADC abs,X", inst_ADC, ABSX}; + instructions[0x7E] = (Instruction) {"ROR abs,X", inst_ROR, ABSX}; + instructions[0x7F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x80] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x81] = (Instruction) {"STA X,ind", inst_STA, XIND}; + instructions[0x82] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x83] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x84] = (Instruction) {"STY zpg", inst_STY, ZP}; + instructions[0x85] = (Instruction) {"STA zpg", inst_STA, ZP}; + instructions[0x86] = (Instruction) {"STX zpg", inst_STX, ZP}; + instructions[0x87] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x88] = (Instruction) {"DEY impl", inst_DEY, IMPL}; + instructions[0x89] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x8A] = (Instruction) {"TXA impl", inst_TXA, IMPL}; + instructions[0x8B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x8C] = (Instruction) {"STY abs", inst_STY, ABS}; + instructions[0x8D] = (Instruction) {"STA abs", inst_STA, ABS}; + instructions[0x8E] = (Instruction) {"STX abs", inst_STX, ABS}; + instructions[0x8F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x90] = (Instruction) {"BCC rel", inst_BCC, REL}; + instructions[0x91] = (Instruction) {"STA ind,Y", inst_STA, INDY}; + instructions[0x92] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x93] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x94] = (Instruction) {"STY zpg,X", inst_STY, ZPX}; + instructions[0x95] = (Instruction) {"STA zpg,X", inst_STA, ZPX}; + instructions[0x96] = (Instruction) {"STX zpg,Y", inst_STX, ZPY}; + instructions[0x97] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x98] = (Instruction) {"TYA impl", inst_TYA, IMPL}; + instructions[0x99] = (Instruction) {"STA abs,Y", inst_STA, ABSY}; + instructions[0x9A] = (Instruction) {"TXS impl", inst_TXS, IMPL}; + instructions[0x9B] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x9C] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x9D] = (Instruction) {"STA abs,X", inst_STA, ABSX}; + instructions[0x9E] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0x9F] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xA0] = (Instruction) {"LDY #", inst_LDY, IMM}; + instructions[0xA1] = (Instruction) {"LDA X,ind", inst_LDA, XIND}; + instructions[0xA2] = (Instruction) {"LDX #", inst_LDX, IMM}; + instructions[0xA3] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xA4] = (Instruction) {"LDY zpg", inst_LDY, ZP}; + instructions[0xA5] = (Instruction) {"LDA zpg", inst_LDA, ZP}; + instructions[0xA6] = (Instruction) {"LDX zpg", inst_LDX, ZP}; + instructions[0xA7] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xA8] = (Instruction) {"TAY impl", inst_TAY, IMPL}; + instructions[0xA9] = (Instruction) {"LDA #", inst_LDA, IMM}; + instructions[0xAA] = (Instruction) {"TAX impl", inst_TAX, IMPL}; + instructions[0xAB] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xAC] = (Instruction) {"LDY abs", inst_LDY, ABS}; + instructions[0xAD] = (Instruction) {"LDA abs", inst_LDA, ABS}; + instructions[0xAE] = (Instruction) {"LDX abs", inst_LDX, ABS}; + instructions[0xAF] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xB0] = (Instruction) {"BCS rel", inst_BCS, REL}; + instructions[0xB1] = (Instruction) {"LDA ind,Y", inst_LDA, INDY}; + instructions[0xB2] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xB3] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xB4] = (Instruction) {"LDY zpg,X", inst_LDY, ZPX}; + instructions[0xB5] = (Instruction) {"LDA zpg,X", inst_LDA, ZPX}; + instructions[0xB6] = (Instruction) {"LDX zpg,Y", inst_LDX, ZPY}; + instructions[0xB7] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xB8] = (Instruction) {"CLV impl", inst_CLV, IMPL}; + instructions[0xB9] = (Instruction) {"LDA abs,Y", inst_LDA, ABSY}; + instructions[0xBA] = (Instruction) {"TSX impl", inst_TSX, IMPL}; + instructions[0xBB] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xBC] = (Instruction) {"LDY abs,X", inst_LDY, ABSX}; + instructions[0xBD] = (Instruction) {"LDA abs,X", inst_LDA, ABSX}; + instructions[0xBE] = (Instruction) {"LDX abs,Y", inst_LDX, ABSY}; + instructions[0xBF] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xC0] = (Instruction) {"CPY #", inst_CPY, IMM}; + instructions[0xC1] = (Instruction) {"CMP X,ind", inst_CMP, XIND}; + instructions[0xC2] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xC3] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xC4] = (Instruction) {"CPY zpg", inst_CPY, ZP}; + instructions[0xC5] = (Instruction) {"CMP zpg", inst_CMP, ZP}; + instructions[0xC6] = (Instruction) {"DEC zpg", inst_DEC, ZP}; + instructions[0xC7] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xC8] = (Instruction) {"INY impl", inst_INY, IMPL}; + instructions[0xC9] = (Instruction) {"CMP #", inst_CMP, IMM}; + instructions[0xCA] = (Instruction) {"DEX impl", inst_DEX, IMPL}; + instructions[0xCB] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xCC] = (Instruction) {"CPY abs", inst_CPY, ABS}; + instructions[0xCD] = (Instruction) {"CMP abs", inst_CMP, ABS}; + instructions[0xCE] = (Instruction) {"DEC abs", inst_DEC, ABS}; + instructions[0xCF] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xD0] = (Instruction) {"BNE rel", inst_BNE, REL}; + instructions[0xD1] = (Instruction) {"CMP ind,Y", inst_CMP, INDY}; + instructions[0xD2] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xD3] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xD4] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xD5] = (Instruction) {"CMP zpg,X", inst_CMP, ZPX}; + instructions[0xD6] = (Instruction) {"DEC zpg,X", inst_DEC, ZPX}; + instructions[0xD7] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xD8] = (Instruction) {"CLD impl", inst_CLD, IMPL}; + instructions[0xD9] = (Instruction) {"CMP abs,Y", inst_CMP, ABSY}; + instructions[0xDA] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xDB] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xDC] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xDD] = (Instruction) {"CMP abs,X", inst_CMP, ABSX}; + instructions[0xDE] = (Instruction) {"DEC abs,X", inst_DEC, ABSX}; + instructions[0xDF] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xE0] = (Instruction) {"CPX #", inst_CPX, IMM}; + instructions[0xE1] = (Instruction) {"SBC X,ind", inst_SBC, XIND}; + instructions[0xE2] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xE3] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xE4] = (Instruction) {"CPX zpg", inst_CPX, ZP}; + instructions[0xE5] = (Instruction) {"SBC zpg", inst_SBC, ZP}; + instructions[0xE6] = (Instruction) {"INC zpg", inst_INC, ZP}; + instructions[0xE7] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xE8] = (Instruction) {"INX impl", inst_INX, IMPL}; + instructions[0xE9] = (Instruction) {"SBC #", inst_SBC, IMM}; + instructions[0xEA] = (Instruction) {"NOP impl", inst_NOP, IMPL}; + instructions[0xEB] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xEC] = (Instruction) {"CPX abs", inst_CPX, ABS}; + instructions[0xED] = (Instruction) {"SBC abs", inst_SBC, ABS}; + instructions[0xEE] = (Instruction) {"INC abs", inst_INC, ABS}; + instructions[0xEF] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xF0] = (Instruction) {"BEQ rel", inst_BEQ, REL}; + instructions[0xF1] = (Instruction) {"SBC ind,Y", inst_SBC, INDY}; + instructions[0xF2] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xF3] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xF4] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xF5] = (Instruction) {"SBC zpg,X", inst_SBC, ZPX}; + instructions[0xF6] = (Instruction) {"INC zpg,X", inst_INC, ZPX}; + instructions[0xF7] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xF8] = (Instruction) {"SED impl", inst_SED, IMPL}; + instructions[0xF9] = (Instruction) {"SBC abs,Y", inst_SBC, ABSY}; + instructions[0xFA] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xFB] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xFC] = (Instruction) {"???", inst_NOP, IMPL}; + instructions[0xFD] = (Instruction) {"SBC abs,X", inst_SBC, ABSX}; + instructions[0xFE] = (Instruction) {"INC abs,X", inst_INC, ABSX}; + instructions[0xFF] = (Instruction) {"???", inst_NOP, IMPL}; +} + +void reset_cpu() +{ + A = 0; + X = 0; + Y = 0; + SP = 0xFF; + + SR.byte = 0; + SR.bits.interrupt = 1; + SR.bits.unused = 1; + + memcpy(&PC, &memory[RST_VEC], sizeof(PC)); +} + +int load_rom(char * filename) +{ // TODO allow more flexible loading + memset(memory, 0, sizeof(memory)); // clear ram first + + FILE * fp = fopen(filename, "r"); + if (fp == NULL) { + printf("Error: could not open file\n"); + return -1; + } + + if (!fread(&memory[0xC000], 0x4000, 1, fp)) { + printf("Error: ROM file too short.\n"); + return -1; + } + + fclose(fp); + return 0; +} + +void step_cpu() +{ + inst = instructions[memory[PC]]; + + #ifdef DEBUG + printf("PC=%04X OPCODE=%02X: %s\r\n", PC, memory[PC], inst.mnemonic); + printf("A=%02X X=%02X Y=%02X SR=%02X SP=%02X\r\n", A, X, Y, SR.byte, SP); + + /* dump memory for analysis (slows down emulation significantly) */ + + FILE * fp = fopen("memdump", "w"); + fwrite(&memory, sizeof(memory), 1, fp); + fclose(fp); + #endif + + jumping = 0; + inst.function(); + if (jumping == 0) PC += lengths[inst.mode]; +} diff --git a/6502.h b/6502.h new file mode 100644 index 0000000..65a7e66 --- /dev/null +++ b/6502.h @@ -0,0 +1,70 @@ +#include + +#define CPU_FREQ 4000000 +#define STEP_DURATION 10000000 // 10ms +#define ONE_SECOND 1e9 +#define NUM_MODES 13 + +#define NMI_VEC 0xFFFA +#define RST_VEC 0xFFFC +#define IRQ_VEC 0xFFFE + +uint8_t memory[1<<16]; +uint8_t A; +uint8_t X; +uint8_t Y; +uint16_t PC; +uint8_t SP; // points to first empty stack location + +void * readAddr; +void * writeAddr; + +struct StatusBits{ + int carry:1; // bit 0 + int zero:1; + int interrupt:1; + int decimal:1; + int brk:1; // "break" is a reserved word :( + int unused:1; + int overflow:1; + int sign:1; // bit 7 +}; + +union StatusReg { // this means we can access the status register as a byte, or as individual bits. + struct StatusBits bits; + uint8_t byte; +}; + +union StatusReg SR; + +typedef enum { + ACC, + ABS, + ABSX, + ABSY, + IMM, + IMPL, + IND, + XIND, + INDY, + REL, + ZP, + ZPX, + ZPY +} Mode; + +typedef struct { + char * mnemonic; + void (*function)(); + Mode mode; +} Instruction; + +Instruction instructions[0x100]; + +void init_tables(); + +void reset_cpu(); + +int load_rom(char * filename); + +void step_cpu(); diff --git a/6850.c b/6850.c new file mode 100644 index 0000000..fae3860 --- /dev/null +++ b/6850.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +#include "6502.h" +#include "6850.h" + +int n; + +void init_uart() { + memory[DATA_ADDR] = 0; + + uart_SR.byte = 0; + uart_SR.bits.TDRE = 1; // we are always ready to output data + + uart_SR.bits.RDRF = 0; + incomingChar = 0; + +} + +int stdin_ready() { + struct pollfd fds; + fds.fd = 0; // stdin + fds.events = POLLIN; + return poll(&fds, 1, 0) == 1; // timeout = 0 +} + +void step_uart() { + if (writeAddr == &memory[DATA_ADDR]) { + putchar(memory[DATA_ADDR]); + fflush(stdout); + writeAddr = NULL; + } else if (readAddr == &memory[DATA_ADDR]) { + uart_SR.bits.RDRF = 0; + readAddr = NULL; + } + + /* update input register if empty */ + if ((n++ % 10000) == 0) { // polling stdin every cycle is performance intensive. This is a bit of a dirty hack. + if (!uart_SR.bits.RDRF && stdin_ready()) { // the real hardware has no buffer. Remote the RDRF check for more accurate emulation. + if (read(0, &incomingChar, 1) != 1) { + printf("Warning: read() returned 0\n"); + } + if (incomingChar == 0x18) { // CTRL+X + printf("\r\n"); + exit(0); + } + uart_SR.bits.RDRF = 1; + } + } + + memory[DATA_ADDR] = incomingChar; + memory[CTRL_ADDR] = uart_SR.byte; +} diff --git a/6850.h b/6850.h new file mode 100644 index 0000000..793cb38 --- /dev/null +++ b/6850.h @@ -0,0 +1,26 @@ +#define CTRL_ADDR 0xA000 +#define DATA_ADDR 0xA001 + +struct UartStatusBits{ + int RDRF:1; // bit 0 + int TDRE:1; + int DCD:1; + int CTS:1; + int FE:1; + int OVRN:1; + int PE:1; + int IRQ:1; // bit 7 +}; + +union UartStatusReg { + struct UartStatusBits bits; + uint8_t byte; +}; + +union UartStatusReg uart_SR; + +uint8_t incomingChar; + +void init_uart(); + +void step_uart(); diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..84c4f12 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +CC=gcc +CFLAGS=-Wall -Ofast -std=gnu99 + +all: 6502-emu + +6502-emu: main.o 6502.o 6850.o + $(CC) -o 6502-emu main.c 6502.o 6850.o + +main.o: main.c + $(CC) $(CFLAGS) -c main.c + +6502.o: 6502.c + $(CC) $(CFLAGS) -c 6502.c + +6850.o: 6850.c + $(CC) $(CFLAGS) -c 6850.c + +clean: + rm *.o 6502-emu + +test: 6502-emu + ./6502-emu examples/ehbasic.rom diff --git a/README.md b/README.md index bb86d1d..ec548f5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,81 @@ # 6502-emu -A simple 6502 emulator, with I/O via a 6850 UART. +A relatively simple and bug-free 6502 emulator. It is capable of running ehBasic. + +Many 6502 emulators make extensive use of macros, which I personally think makes +code harder to understand. At the other extreme, some emulators implement each +addressing mode of each instruction separately, which is a big waste of time and +makes the code harder to maintain. + +I chose a middle-ground, and used lookup tables for instruction decoding and address +decoding. This also gets rid of the common "giant opcode switch statement" (which +can be seen in my CHIP8 emulator project). + +### Usage Example: + +``` +[david@D-ARCH 6502-emu]$ ./6502-emu examples/ehbasic.rom + +Enhanced 6502 BASIC 2.22 (c) Lee Davison +[C]old/[W]arm ? + +Memory size ? + +40191 Bytes free + +Enhanced BASIC 2.22 + +Ready +10 X1=59 +15 Y1=21 +20 I1=-1.0 +23 I2=1.0 +26 R1=-2.0 +28 R2=1.0 +30 S1=(R2-R1)/X1 +35 S2=(I2-I1)/Y1 +40 FOR Y=0 TO Y1 +50 I3=I1+S2*Y +60 FOR X=0 TO X1 +70 R3=R1+S1*X +73 Z1=R3 +76 Z2=I3 +80 FOR N=0 TO 30 +90 A=Z1*Z1 +95 B=Z2*Z2 +100 IF A+B>4.0 GOTO 130 +110 Z2=2*Z1*Z2+I3 +115 Z1=A-B+R3 +120 NEXT N +130 PRINT CHR$(63-N); +140 NEXT X +150 PRINT +160 NEXT Y +170 END + +RUN +??????>>>>>===============<<<<<<;;;:7143;<<<<====>>>>>>>>>>> +?????>>>================<<<<<<<;;;984+18:;;<<<<=====>>>>>>>> +????>>>===============<<<<<<<;;::85 )/:;;;;<<=====>>>>>>> +???>>===============<<<<<<;:9999875 689::::;<<=====>>>>> +??>>=============<<<<;;;;::7/ '3 56446;<======>>>> +??>===========<<<;;;;;;:::863 +8:;<======>>> +?>========<<<;6::::::::997! &89;<<======>> +?====<<<<<;;;:83567.678874 ,:<<=======> +?=<<<<<<;;;;:986' /4 +:<<<======> +?<<<<<<;;::8675( ( 9;<<<======> +?;;:999:88460 '9:;<<<======> +?;;:999:88460 '9:;<<<======> +?<<<<<<;;::8675( ( 9;<<<======> +?=<<<<<<;;;;:986' /4 +:<<<======> +?====<<<<<;;;:83567.678874 ,:<<=======> +?>========<<<;6::::::::997! &89;<<======>> +??>===========<<<;;;;;;:::863 +8:;<======>>> +??>>=============<<<<;;;;::7/ '3 56446;<======>>>> +???>>===============<<<<<<;:9999875 689::::;<<=====>>>>> +????>>>===============<<<<<<<;;::85 )/:;;;;<<=====>>>>>>> +?????>>>================<<<<<<<;;;984+18:;;<<<<=====>>>>>>>> +??????>>>>>===============<<<<<<;;;:7143;<<<<====>>>>>>>>>>> + +Ready + +``` diff --git a/examples/ehbasic.rom b/examples/ehbasic.rom new file mode 100644 index 0000000000000000000000000000000000000000..89509ff7857e6ef27ac101af6a5e28254b7c7d41 GIT binary patch literal 16384 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 P^#97}2~uiktn|MDbHP +#include +#include +#include + +#include "6502.h" +#include "6850.h" + +struct termios oldTermios; + +void step_delay() +{ + struct timespec req, rem; + + req.tv_sec = 0; + req.tv_nsec = STEP_DURATION; + + nanosleep(&req, &rem); +} + +void run_cpu() +{ + for (;;) { + // CPU timing is currently very far from being cycle-accurate + for (int i = 0; i < (CPU_FREQ / (ONE_SECOND / STEP_DURATION)); i++) { + step_cpu(); + step_uart(); + } + step_delay(); // remove this for more speed + } +} + +void restore_stdin() +{ + tcsetattr(0, TCSANOW, &oldTermios); +} + +void raw_stdin() +{ + struct termios newTermios; + + tcgetattr(0, &oldTermios); + newTermios = oldTermios; + cfmakeraw(&newTermios); + tcsetattr(0, TCSANOW, &newTermios); + atexit(restore_stdin); +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("Usage: %s file.rom\n", argv[0]); + printf("The first 16k of \"file.rom\" is loaded into the last 16k of memory.\n"); + return EXIT_FAILURE; + } + + if (load_rom(argv[1]) != 0) { + printf("Error loading \"%s\".\n", argv[1]); + return EXIT_FAILURE; + } + + raw_stdin(); // allow individual keystrokes to be detected + + init_tables(); + init_uart(); + + reset_cpu(); + run_cpu(); + + return EXIT_SUCCESS; +}