From 2d30a08daf8759ef46cfc61061a87ad1c1e21ec7 Mon Sep 17 00:00:00 2001 From: Rob McMullen Date: Sat, 16 Dec 2017 22:31:48 -0800 Subject: [PATCH] Created CPU struct to allow multiple instances of running processors at the same time * note: this is not multithreading; it simply allows a program to step through more than one CPU in the same process --- 6502-emu.c | 24 +- 6502.c | 676 +++++++++++++++++++++++++++-------------------------- 6502.h | 40 ++-- 6850.c | 22 +- Makefile | 2 +- 5 files changed, 390 insertions(+), 374 deletions(-) diff --git a/6502-emu.c b/6502-emu.c index 015d87f..bdbb75c 100644 --- a/6502-emu.c +++ b/6502-emu.c @@ -19,21 +19,21 @@ void step_delay() nanosleep(&req, &rem); } -void run_cpu(long cycle_stop, int verbose, int mem_dump, int break_pc, int fast) +void run_cpu(CPU * cpu, long cycle_stop, int verbose, int mem_dump, int break_pc, int fast) { long cycles = 0; int cycles_per_step = (CPU_FREQ / (ONE_SECOND / STEP_DURATION)); for (;;) { for (cycles %= cycles_per_step; cycles < cycles_per_step;) { - if (mem_dump) save_memory(NULL); - cycles += step_cpu(verbose); - if ((cycle_stop > 0) && (total_cycles >= cycle_stop)) goto end; + if (mem_dump) save_memory(cpu, NULL); + cycles += step_cpu(cpu, verbose); + if ((cycle_stop > 0) && (cpu->total_cycles >= cycle_stop)) goto end; step_uart(); - if (break_pc >= 0 && PC == (uint16_t)break_pc) { + if (break_pc >= 0 && cpu->PC == (uint16_t)break_pc) { fprintf(stderr, "break at %04x\n", break_pc); - save_memory(NULL); + save_memory(cpu, NULL); goto end; } } @@ -96,6 +96,7 @@ int main(int argc, char *argv[]) int verbose, interactive, mem_dump, break_pc, fast; long cycles; int opt; + CPU *cpu; verbose = 0; interactive = 0; @@ -164,7 +165,11 @@ int main(int argc, char *argv[]) usage(argv); exit(EXIT_FAILURE); } - if (load_rom(argv[optind], load_addr) != 0) { + + cpu = create_cpu(); + init_uart(cpu); + + if (load_rom(cpu, argv[optind], load_addr) != 0) { printf("Error loading \"%s\".\n", argv[optind]); return EXIT_FAILURE; } @@ -172,10 +177,9 @@ int main(int argc, char *argv[]) if (interactive) raw_stdin(); // allow individual keystrokes to be detected init_tables(); - init_uart(); - reset_cpu(a, x, y, sp, sr, pc); - run_cpu(cycles, verbose, mem_dump, break_pc, fast); + reset_cpu(cpu, a, x, y, sp, sr, pc); + run_cpu(cpu, cycles, verbose, mem_dump, break_pc, fast); return EXIT_SUCCESS; } diff --git a/6502.c b/6502.c index c31c17b..25d89c9 100644 --- a/6502.c +++ b/6502.c @@ -5,339 +5,338 @@ #include "6502.h" int lengths[NUM_MODES]; // instruction length table, indexed by addressing mode -uint8_t * (*get_ptr[NUM_MODES])(); // addressing mode decoder table +uint8_t * (*get_ptr[NUM_MODES])(CPU *); // 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 N_flag(int8_t val) +static inline void N_flag(CPU *cpu, int8_t val) { - SR.bits.sign = val < 0; + cpu->SR.bits.sign = val < 0; } -static inline void Z_flag(uint8_t val) +static inline void Z_flag(CPU *cpu, uint8_t val) { - SR.bits.zero = val == 0; + cpu->SR.bits.zero = val == 0; } /* Stack Helpers */ -static inline void stack_push(uint8_t val) +static inline void stack_push(CPU *cpu, uint8_t val) { - memory[0x100+(SP--)] = val; + cpu->memory[0x100+(cpu->SP--)] = val; } -static inline uint8_t stack_pull() +static inline uint8_t stack_pull(CPU *cpu) { - return memory[0x100+(++SP)]; + return cpu->memory[0x100+(++cpu->SP)]; } -/* Memory read/write wrappers */ +/* cpu->memory read/write wrappers */ -static inline uint8_t * read_ptr() +static inline uint8_t * read_ptr(CPU *cpu) { - return read_addr = get_ptr[inst.mode](); + return cpu->read_addr = get_ptr[inst.mode](cpu); } -static inline uint8_t * write_ptr() +static inline uint8_t * write_ptr(CPU *cpu) { - return write_addr = get_ptr[inst.mode](); + return cpu->write_addr = get_ptr[inst.mode](cpu); } /* Branch logic common to all branch instructions */ -static inline void take_branch() +static inline void take_branch(CPU *cpu) { uint16_t oldPC; - oldPC = PC + 2; // PC has already moved to point to the next instruction - PC = read_ptr() - memory; - if ((PC ^ oldPC) & 0xff00) extra_cycles += 1; // addr crosses page boundary - extra_cycles += 1; + oldPC = cpu->PC + 2; // PC has already moved to point to the next instruction + cpu->PC = read_ptr(cpu) - cpu->memory; + if ((cpu->PC ^ oldPC) & 0xff00) cpu->extra_cycles += 1; // addr crosses page boundary + cpu->extra_cycles += 1; } /* Instruction Implementations */ -static void inst_ADC() +static void inst_ADC(CPU *cpu) { - uint8_t operand = * read_ptr(); - unsigned int tmp = A + operand + (SR.bits.carry & 1); - if (SR.bits.decimal) { - tmp = (A & 0x0f) + (operand & 0x0f) + (SR.bits.carry & 1); + uint8_t operand = * read_ptr(cpu); + unsigned int tmp = cpu->A + operand + (cpu->SR.bits.carry & 1); + if (cpu->SR.bits.decimal) { + tmp = (cpu->A & 0x0f) + (operand & 0x0f) + (cpu->SR.bits.carry & 1); if (tmp >= 10) tmp = (tmp - 10) | 0x10; - tmp += (A & 0xf0) + (operand & 0xf0); + tmp += (cpu->A & 0xf0) + (operand & 0xf0); if (tmp > 0x9f) tmp += 0x60; } - SR.bits.carry = tmp > 0xFF; - SR.bits.overflow = ((A^tmp)&(operand^tmp)&0x80) != 0; - A = tmp & 0xFF; - N_flag(A); - Z_flag(A); + cpu->SR.bits.carry = tmp > 0xFF; + cpu->SR.bits.overflow = ((cpu->A^tmp)&(operand^tmp)&0x80) != 0; + cpu->A = tmp & 0xFF; + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_AND() +static void inst_AND(CPU *cpu) { - A &= * read_ptr(); - N_flag(A); - Z_flag(A); + cpu->A &= * read_ptr(cpu); + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_ASL() +static void inst_ASL(CPU *cpu) { - uint8_t tmp = * read_ptr(); - SR.bits.carry = (tmp & 0x80) != 0; + uint8_t tmp = * read_ptr(cpu); + cpu->SR.bits.carry = (tmp & 0x80) != 0; tmp <<= 1; - N_flag(tmp); - Z_flag(tmp); - * write_ptr() = tmp; + N_flag(cpu, tmp); + Z_flag(cpu, tmp); + * write_ptr(cpu) = tmp; } -static void inst_BCC() +static void inst_BCC(CPU *cpu) { - if (!SR.bits.carry) { - take_branch(); + if (!cpu->SR.bits.carry) { + take_branch(cpu); } } -static void inst_BCS() +static void inst_BCS(CPU *cpu) { - if (SR.bits.carry) { - take_branch(); + if (cpu->SR.bits.carry) { + take_branch(cpu); } } -static void inst_BEQ() +static void inst_BEQ(CPU *cpu) { - if (SR.bits.zero) { - take_branch(); + if (cpu->SR.bits.zero) { + take_branch(cpu); } } -static void inst_BIT() +static void inst_BIT(CPU *cpu) { - uint8_t tmp = * read_ptr(); - N_flag(tmp); - Z_flag(tmp & A); - SR.bits.overflow = (tmp & 0x40) != 0; + uint8_t tmp = * read_ptr(cpu); + N_flag(cpu, tmp); + Z_flag(cpu, tmp & cpu->A); + cpu->SR.bits.overflow = (tmp & 0x40) != 0; } -static void inst_BMI() +static void inst_BMI(CPU *cpu) { - if (SR.bits.sign) { - take_branch(); + if (cpu->SR.bits.sign) { + take_branch(cpu); } } -static void inst_BNE() +static void inst_BNE(CPU *cpu) { - if (!SR.bits.zero) { - take_branch(); + if (!cpu->SR.bits.zero) { + take_branch(cpu); } } -static void inst_BPL() +static void inst_BPL(CPU *cpu) { - if (!SR.bits.sign) { - take_branch(); + if (!cpu->SR.bits.sign) { + take_branch(cpu); } } -static void inst_BRK() +static void inst_BRK(CPU *cpu) { 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; + memcpy(&newPC, &cpu->memory[IRQ_VEC], sizeof(newPC)); + cpu->PC += 2; + stack_push(cpu, cpu->PC >> 8); + stack_push(cpu, cpu->PC & 0xFF); + cpu->SR.bits.brk = 1; + stack_push(cpu, cpu->SR.byte); + cpu->SR.bits.interrupt = 1; + cpu->PC = newPC; + cpu->jumping = 1; } -static void inst_BVC() +static void inst_BVC(CPU *cpu) { - if (!SR.bits.overflow) { - take_branch(); + if (!cpu->SR.bits.overflow) { + take_branch(cpu); } } -static void inst_BVS() +static void inst_BVS(CPU *cpu) { - if (SR.bits.overflow) { - take_branch(); + if (cpu->SR.bits.overflow) { + take_branch(cpu); } } -static void inst_CLC() +static void inst_CLC(CPU *cpu) { - SR.bits.carry = 0; + cpu->SR.bits.carry = 0; } -static void inst_CLD() +static void inst_CLD(CPU *cpu) { - SR.bits.decimal = 0; + cpu->SR.bits.decimal = 0; } -static void inst_CLI() +static void inst_CLI(CPU *cpu) { - SR.bits.interrupt = 0; + cpu->SR.bits.interrupt = 0; } -static void inst_CLV() +static void inst_CLV(CPU *cpu) { - SR.bits.overflow = 0; + cpu->SR.bits.overflow = 0; } -static void inst_CMP() +static void inst_CMP(CPU *cpu) { - uint8_t operand = * read_ptr(); - uint8_t tmpDiff = A - operand; - N_flag(tmpDiff); - Z_flag(tmpDiff); - SR.bits.carry = A >= operand; + uint8_t operand = * read_ptr(cpu); + uint8_t tmpDiff = cpu->A - operand; + N_flag(cpu, tmpDiff); + Z_flag(cpu, tmpDiff); + cpu->SR.bits.carry = cpu->A >= operand; } -static void inst_CPX() +static void inst_CPX(CPU *cpu) { - uint8_t operand = * read_ptr(); - uint8_t tmpDiff = X - operand; - N_flag(tmpDiff); - Z_flag(tmpDiff); - SR.bits.carry = X >= operand; + uint8_t operand = * read_ptr(cpu); + uint8_t tmpDiff = cpu->X - operand; + N_flag(cpu, tmpDiff); + Z_flag(cpu, tmpDiff); + cpu->SR.bits.carry = cpu->X >= operand; } -static void inst_CPY() +static void inst_CPY(CPU *cpu) { - uint8_t operand = * read_ptr(); - uint8_t tmpDiff = Y - operand; - N_flag(tmpDiff); - Z_flag(tmpDiff); - SR.bits.carry = Y >= operand; + uint8_t operand = * read_ptr(cpu); + uint8_t tmpDiff = cpu->Y - operand; + N_flag(cpu, tmpDiff); + Z_flag(cpu, tmpDiff); + cpu->SR.bits.carry = cpu->Y >= operand; } -static void inst_DEC() +static void inst_DEC(CPU *cpu) { - uint8_t tmp = * read_ptr(); + uint8_t tmp = * read_ptr(cpu); tmp--; - N_flag(tmp); - Z_flag(tmp); - * write_ptr() = tmp; + N_flag(cpu, tmp); + Z_flag(cpu, tmp); + * write_ptr(cpu) = tmp; } -static void inst_DEX() +static void inst_DEX(CPU *cpu) { - X--; - N_flag(X); - Z_flag(X); + cpu->X--; + N_flag(cpu, cpu->X); + Z_flag(cpu, cpu->X); } -static void inst_DEY() +static void inst_DEY(CPU *cpu) { - Y--; - N_flag(Y); - Z_flag(Y); + cpu->Y--; + N_flag(cpu, cpu->Y); + Z_flag(cpu, cpu->Y); } -static void inst_EOR() +static void inst_EOR(CPU *cpu) { - A ^= * read_ptr(); - N_flag(A); - Z_flag(A); + cpu->A ^= * read_ptr(cpu); + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_INC() +static void inst_INC(CPU *cpu) { - uint8_t tmp = * read_ptr(); + uint8_t tmp = * read_ptr(cpu); tmp++; - N_flag(tmp); - Z_flag(tmp); - * write_ptr() = tmp; + N_flag(cpu, tmp); + Z_flag(cpu, tmp); + * write_ptr(cpu) = tmp; } -static void inst_INX() +static void inst_INX(CPU *cpu) { - X++; - N_flag(X); - Z_flag(X); + cpu->X++; + N_flag(cpu, cpu->X); + Z_flag(cpu, cpu->X); } -static void inst_INY() +static void inst_INY(CPU *cpu) { - Y++; - N_flag(Y); - Z_flag(Y); + cpu->Y++; + N_flag(cpu, cpu->Y); + Z_flag(cpu, cpu->Y); } -static void inst_JMP() +static void inst_JMP(CPU *cpu) { - PC = read_ptr() - memory; - jumping = 1; + cpu->PC = read_ptr(cpu) - cpu->memory; + cpu->jumping = 1; } -static void inst_JSR() +static void inst_JSR(CPU *cpu) { - uint16_t newPC = read_ptr() - memory; - PC += 2; - stack_push(PC >> 8); - stack_push(PC & 0xFF); - PC = newPC; - jumping = 1; + uint16_t newPC = read_ptr(cpu) - cpu->memory; + cpu->PC += 2; + stack_push(cpu, cpu->PC >> 8); + stack_push(cpu, cpu->PC & 0xFF); + cpu->PC = newPC; + cpu->jumping = 1; } -static void inst_LDA() +static void inst_LDA(CPU *cpu) { - A = * read_ptr(); - N_flag(A); - Z_flag(A); + cpu->A = * read_ptr(cpu); + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_LDX() +static void inst_LDX(CPU *cpu) { - X = * read_ptr(); - N_flag(X); - Z_flag(X); + cpu->X = * read_ptr(cpu); + N_flag(cpu, cpu->X); + Z_flag(cpu, cpu->X); } -static void inst_LDY() +static void inst_LDY(CPU *cpu) { - Y = * read_ptr(); - N_flag(Y); - Z_flag(Y); + cpu->Y = * read_ptr(cpu); + N_flag(cpu, cpu->Y); + Z_flag(cpu, cpu->Y); } -static void inst_LSR() +static void inst_LSR(CPU *cpu) { - uint8_t tmp = * read_ptr(); - SR.bits.carry = tmp & 1; + uint8_t tmp = * read_ptr(cpu); + cpu->SR.bits.carry = tmp & 1; tmp >>= 1; - N_flag(tmp); - Z_flag(tmp); - * write_ptr() = tmp; + N_flag(cpu, tmp); + Z_flag(cpu, tmp); + * write_ptr(cpu) = tmp; } -static void inst_NOP() +static void inst_NOP(CPU *cpu) { // thrown away, just used to compute any extra cycles for the multi-byte // NOP statements - read_ptr(); + read_ptr(cpu); } -static void inst_ORA() +static void inst_ORA(CPU *cpu) { - A |= * read_ptr(); - N_flag(A); - Z_flag(A); + cpu->A |= * read_ptr(cpu); + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_PHA() +static void inst_PHA(CPU *cpu) { - stack_push(A); + stack_push(cpu, cpu->A); } -static void inst_PHP() +static void inst_PHP(CPU *cpu) { union StatusReg pushed_sr; @@ -346,279 +345,279 @@ static void inst_PHP() // unexpected, but it's what the real hardware does. // // See http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit - pushed_sr.byte = SR.byte; + pushed_sr.byte = cpu->SR.byte; pushed_sr.bits.brk = 1; - stack_push(pushed_sr.byte); + stack_push(cpu, pushed_sr.byte); } -static void inst_PLA() +static void inst_PLA(CPU *cpu) { - A = stack_pull(); - N_flag(A); - Z_flag(A); + cpu->A = stack_pull(cpu); + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_PLP() +static void inst_PLP(CPU *cpu) { - SR.byte = stack_pull(); - SR.bits.unused = 1; - SR.bits.brk = 0; + cpu->SR.byte = stack_pull(cpu); + cpu->SR.bits.unused = 1; + cpu->SR.bits.brk = 0; } -static void inst_ROL() +static void inst_ROL(CPU *cpu) { - int tmp = (* read_ptr()) << 1; - tmp |= SR.bits.carry & 1; - SR.bits.carry = tmp > 0xFF; + int tmp = (* read_ptr(cpu)) << 1; + tmp |= cpu->SR.bits.carry & 1; + cpu->SR.bits.carry = tmp > 0xFF; tmp &= 0xFF; - N_flag(tmp); - Z_flag(tmp); - * write_ptr() = tmp; + N_flag(cpu, tmp); + Z_flag(cpu, tmp); + * write_ptr(cpu) = tmp; } -static void inst_ROR() +static void inst_ROR(CPU *cpu) { - int tmp = * read_ptr(); - tmp |= SR.bits.carry << 8; - SR.bits.carry = tmp & 1; + int tmp = * read_ptr(cpu); + tmp |= cpu->SR.bits.carry << 8; + cpu->SR.bits.carry = tmp & 1; tmp >>= 1; - N_flag(tmp); - Z_flag(tmp); - * write_ptr() = tmp; + N_flag(cpu, tmp); + Z_flag(cpu, tmp); + * write_ptr(cpu) = tmp; } -static void inst_RTI() +static void inst_RTI(CPU *cpu) { - SR.byte = stack_pull(); - SR.bits.unused = 1; - PC = stack_pull(); - PC |= stack_pull() << 8; - //PC += 1; - jumping = 1; + cpu->SR.byte = stack_pull(cpu); + cpu->SR.bits.unused = 1; + cpu->PC = stack_pull(cpu); + cpu->PC |= stack_pull(cpu) << 8; + //cpu->PC += 1; + cpu->jumping = 1; } -static void inst_RTS() +static void inst_RTS(CPU *cpu) { - PC = stack_pull(); - PC |= stack_pull() << 8; - PC += 1; - jumping = 1; + cpu->PC = stack_pull(cpu); + cpu->PC |= stack_pull(cpu) << 8; + cpu->PC += 1; + cpu->jumping = 1; } -static void inst_SBC() +static void inst_SBC(CPU *cpu) { - uint8_t operand = * read_ptr(); + uint8_t operand = * read_ptr(cpu); unsigned int tmp, lo, hi; - tmp = A - operand - 1 + (SR.bits.carry & 1); - SR.bits.overflow = ((A^tmp)&(A^operand)&0x80) != 0; - if (SR.bits.decimal) { - lo = (A & 0x0f) - (operand & 0x0f) - 1 + SR.bits.carry; - hi = (A >> 4) - (operand >> 4); + tmp = cpu->A - operand - 1 + (cpu->SR.bits.carry & 1); + cpu->SR.bits.overflow = ((cpu->A^tmp)&(cpu->A^operand)&0x80) != 0; + if (cpu->SR.bits.decimal) { + lo = (cpu->A & 0x0f) - (operand & 0x0f) - 1 + cpu->SR.bits.carry; + hi = (cpu->A >> 4) - (operand >> 4); if (lo & 0x10) lo -= 6, hi--; if (hi & 0x10) hi -= 6; - A = (hi << 4) | (lo & 0x0f); + cpu->A = (hi << 4) | (lo & 0x0f); } else { - A = tmp & 0xFF; + cpu->A = tmp & 0xFF; } - SR.bits.carry = tmp < 0x100; - N_flag(A); - Z_flag(A); + cpu->SR.bits.carry = tmp < 0x100; + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_SEC() +static void inst_SEC(CPU *cpu) { - SR.bits.carry = 1; + cpu->SR.bits.carry = 1; } -static void inst_SED() +static void inst_SED(CPU *cpu) { - SR.bits.decimal = 1; + cpu->SR.bits.decimal = 1; } -static void inst_SEI() +static void inst_SEI(CPU *cpu) { - SR.bits.interrupt = 1; + cpu->SR.bits.interrupt = 1; } -static void inst_STA() +static void inst_STA(CPU *cpu) { - * write_ptr() = A; - extra_cycles = 0; // STA has no addressing modes that use the extra cycle + * write_ptr(cpu) = cpu->A; + cpu->extra_cycles = 0; // STA has no addressing modes that use the extra cycle } -static void inst_STX() +static void inst_STX(CPU *cpu) { - * write_ptr() = X; + * write_ptr(cpu) = cpu->X; } -static void inst_STY() +static void inst_STY(CPU *cpu) { - * write_ptr() = Y; + * write_ptr(cpu) = cpu->Y; } -static void inst_TAX() +static void inst_TAX(CPU *cpu) { - X = A; - N_flag(X); - Z_flag(X); + cpu->X = cpu->A; + N_flag(cpu, cpu->X); + Z_flag(cpu, cpu->X); } -static void inst_TAY() +static void inst_TAY(CPU *cpu) { - Y = A; - N_flag(Y); - Z_flag(Y); + cpu->Y = cpu->A; + N_flag(cpu, cpu->Y); + Z_flag(cpu, cpu->Y); } -static void inst_TSX() +static void inst_TSX(CPU *cpu) { - X = SP; - N_flag(X); - Z_flag(X); + cpu->X = cpu->SP; + N_flag(cpu, cpu->X); + Z_flag(cpu, cpu->X); } -static void inst_TXA() +static void inst_TXA(CPU *cpu) { - A = X; - N_flag(A); - Z_flag(A); + cpu->A = cpu->X; + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -static void inst_TXS() +static void inst_TXS(CPU *cpu) { - SP = X; + cpu->SP = cpu->X; } -static void inst_TYA() +static void inst_TYA(CPU *cpu) { - A = Y; - N_flag(A); - Z_flag(A); + cpu->A = cpu->Y; + N_flag(cpu, cpu->A); + Z_flag(cpu, cpu->A); } -/* Addressing Implementations */ +/* cpu->Addressing Implementations */ -uint8_t * get_IMPL() +uint8_t * get_IMPL(CPU *cpu) { // dummy implementation; for completeness necessary for cycle counting NOP // instructions - return &memory[0]; + return &cpu->memory[0]; } -uint8_t * get_IMM() +uint8_t * get_IMM(CPU *cpu) { - return &memory[(uint16_t) (PC+1)]; + return &cpu->memory[(uint16_t) (cpu->PC+1)]; } -uint16_t get_uint16() +uint16_t get_uint16(CPU *cpu) { // used only as part of other modes uint16_t index; - memcpy(&index, get_IMM(), sizeof(index)); // hooray for optimising compilers + memcpy(&index, get_IMM(cpu), sizeof(index)); // hooray for optimising compilers return index; } -uint8_t * get_ZP() +uint8_t * get_ZP(CPU *cpu) { - return &memory[* get_IMM()]; + return &cpu->memory[* get_IMM(cpu)]; } -uint8_t * get_ZPX() +uint8_t * get_ZPX(CPU *cpu) { - return &memory[((* get_IMM()) + X) & 0xFF]; + return &cpu->memory[((* get_IMM(cpu)) + cpu->X) & 0xFF]; } -uint8_t * get_ZPY() +uint8_t * get_ZPY(CPU *cpu) { - return &memory[((* get_IMM()) + Y) & 0xFF]; + return &cpu->memory[((* get_IMM(cpu)) + cpu->Y) & 0xFF]; } -uint8_t * get_ACC() +uint8_t * get_ACC(CPU *cpu) { - return &A; + return &cpu->A; } -uint8_t * get_ABS() +uint8_t * get_ABS(CPU *cpu) { - return &memory[get_uint16()]; + return &cpu->memory[get_uint16(cpu)]; } -uint8_t * get_ABSX() +uint8_t * get_ABSX(CPU *cpu) { uint16_t ptr; - ptr = (uint16_t)(get_uint16() + X); - if ((uint8_t)ptr < X) extra_cycles ++; - return &memory[ptr]; + ptr = (uint16_t)(get_uint16(cpu) + cpu->X); + if ((uint8_t)ptr < cpu->X) cpu->extra_cycles ++; + return &cpu->memory[ptr]; } -uint8_t * get_ABSY() +uint8_t * get_ABSY(CPU *cpu) { uint16_t ptr; - ptr = (uint16_t)(get_uint16() + Y); - if ((uint8_t)ptr < Y) extra_cycles ++; - return &memory[ptr]; + ptr = (uint16_t)(get_uint16(cpu) + cpu->Y); + if ((uint8_t)ptr < cpu->Y) cpu->extra_cycles ++; + return &cpu->memory[ptr]; } -uint8_t * get_IND() +uint8_t * get_IND(CPU *cpu) { uint16_t ptr; - memcpy(&ptr, get_ABS(), sizeof(ptr)); - return &memory[ptr]; + memcpy(&ptr, get_ABS(cpu), sizeof(ptr)); + return &cpu->memory[ptr]; } -uint8_t * get_XIND() +uint8_t * get_XIND(CPU *cpu) { uint16_t ptr; - ptr = ((* get_IMM()) + X) & 0xFF; + ptr = ((* get_IMM(cpu)) + cpu->X) & 0xFF; if (ptr == 0xff) { // check for wraparound in zero page - ptr = memory[ptr] + (memory[ptr & 0xff00] << 8); + ptr = cpu->memory[ptr] + (cpu->memory[ptr & 0xff00] << 8); } else { - memcpy(&ptr, &memory[ptr], sizeof(ptr)); + memcpy(&ptr, &cpu->memory[ptr], sizeof(ptr)); } - return &memory[ptr]; + return &cpu->memory[ptr]; } -uint8_t * get_INDY() +uint8_t * get_INDY(CPU *cpu) { uint16_t ptr; - ptr = * get_IMM(); + ptr = * get_IMM(cpu); if (ptr == 0xff) { // check for wraparound in zero page - ptr = memory[ptr] + (memory[ptr & 0xff00] << 8); + ptr = cpu->memory[ptr] + (cpu->memory[ptr & 0xff00] << 8); } else { - memcpy(&ptr, &memory[ptr], sizeof(ptr)); + memcpy(&ptr, &cpu->memory[ptr], sizeof(ptr)); } - ptr += Y; - if ((uint8_t)ptr < Y) extra_cycles ++; - return &memory[ptr]; + ptr += cpu->Y; + if ((uint8_t)ptr < cpu->Y) cpu->extra_cycles ++; + return &cpu->memory[ptr]; } -uint8_t * get_REL() +uint8_t * get_REL(CPU *cpu) { - return &memory[(uint16_t) (PC + (int8_t) * get_IMM())]; + return &cpu->memory[(uint16_t) (cpu->PC + (int8_t) * get_IMM(cpu))]; } -uint8_t * get_JMP_IND_BUG() +uint8_t * get_JMP_IND_BUG(CPU *cpu) { uint8_t * addr; uint16_t ptr; - ptr = get_uint16(); + ptr = get_uint16(cpu); if ((ptr & 0xff) == 0xff) { // Bug when crosses a page boundary. When using relative index ($xxff), // instead of using the last byte of the page and the first byte of the // next page, it uses the first byte of the same page. E.g. jmp ($baff) // would use the value at $baff as the LSB, but $ba00 as the high byte // instead of $bb00. This was fixed in the 65C02 - ptr = memory[ptr] + (memory[ptr & 0xff00] << 8); + ptr = cpu->memory[ptr] + (cpu->memory[ptr & 0xff00] << 8); } else { - addr = &memory[ptr]; + addr = &cpu->memory[ptr]; memcpy(&ptr, addr, sizeof(ptr)); } - return &memory[ptr]; + return &cpu->memory[ptr]; } @@ -920,31 +919,30 @@ void init_tables() // this is only done at runtime to improve code readability. instructions[0xFF] = (Instruction) {"???", inst_NOP, IMPL, 7}; } -void reset_cpu(int _a, int _x, int _y, int _sp, int _sr, int _pc) +void reset_cpu(CPU *cpu, int _a, int _x, int _y, int _sp, int _sr, int _pc) { - A = _a; - X = _x; - Y = _y; - SP = _sp; + cpu->A = _a; + cpu->X = _x; + cpu->Y = _y; + cpu->SP = _sp; - SR.byte = _sr; - SR.bits.interrupt = 1; - SR.bits.unused = 1; + cpu->SR.byte = _sr; + cpu->SR.bits.interrupt = 1; + cpu->SR.bits.unused = 1; if (_pc < 0) - memcpy(&PC, &memory[-_pc], sizeof(PC)); + memcpy(&cpu->PC, &cpu->memory[-_pc], sizeof(cpu->PC)); else - PC = _pc; + cpu->PC = _pc; - total_cycles = 0; + cpu->total_cycles = 0; } -int load_rom(char * filename, int load_addr) +int load_rom(CPU *cpu, char * filename, int load_addr) { int loaded_size, max_size; - memset(memory, 0, sizeof(memory)); // clear ram first - + memset(cpu->memory, 0, sizeof(cpu->memory)); // clear ram first FILE * fp = fopen(filename, "r"); if (fp == NULL) { printf("Error: could not open file\n"); @@ -952,45 +950,55 @@ int load_rom(char * filename, int load_addr) } max_size = 0x10000 - load_addr; - loaded_size = (int)fread(&memory[load_addr], 1, (size_t)max_size, fp); + loaded_size = (int)fread(&cpu->memory[load_addr], 1, (size_t)max_size, fp); fprintf(stderr, "Loaded $%04x bytes: $%04x - $%04x\n", loaded_size, load_addr, load_addr + loaded_size - 1); fclose(fp); return 0; } -int step_cpu(int verbose) // returns cycle count +int step_cpu(CPU *cpu, int verbose) // returns cycle count { - inst = instructions[memory[PC]]; + inst = instructions[cpu->memory[cpu->PC]]; if (verbose) { // almost match for NES dump for easier comparison - printf("%04X ", PC); + printf("%04X ", cpu->PC); if (lengths[inst.mode] == 3) - printf("%02X %02X %02X", memory[PC], memory[PC+1], memory[PC+2]); + printf("%02X %02X %02X", cpu->memory[cpu->PC], cpu->memory[cpu->PC+1], cpu->memory[cpu->PC+2]); else if (lengths[inst.mode] == 2) - printf("%02X %02X ", memory[PC], memory[PC+1]); + printf("%02X %02X ", cpu->memory[cpu->PC], cpu->memory[cpu->PC+1]); else - printf("%02X ", memory[PC]); - printf(" %-10s A:%02X X:%02X Y:%02X P:%02X SP:%02X CYC:%3d\n", inst.mnemonic, A, X, Y, SR.byte, SP, (int)((total_cycles * 3) % 341)); + printf("%02X ", cpu->memory[cpu->PC]); + printf(" %-10s A:%02X X:%02X Y:%02X P:%02X SP:%02X CYC:%3d\n", inst.mnemonic, cpu->A, cpu->X, cpu->Y, cpu->SR.byte, cpu->SP, (int)((cpu->total_cycles * 3) % 341)); } - jumping = 0; - extra_cycles = 0; - inst.function(); - if (jumping == 0) PC += lengths[inst.mode]; + cpu->jumping = 0; + cpu->extra_cycles = 0; + inst.function(cpu); + if (cpu->jumping == 0) cpu->PC += lengths[inst.mode]; // 7 cycle instructions (e.g. ROL $nnnn,X) don't have a penalty cycle for // crossing a page boundary. - if (inst.cycles == 7) extra_cycles = 0; + if (inst.cycles == 7) cpu->extra_cycles = 0; - total_cycles += inst.cycles + extra_cycles; - return inst.cycles + extra_cycles; + cpu->total_cycles += inst.cycles + cpu->extra_cycles; + return inst.cycles + cpu->extra_cycles; } -void save_memory(char * filename) { // dump memory for analysis (slows down emulation significantly) +void save_memory(CPU *cpu, char * filename) { // dump memory for analysis (slows down emulation significantly) if (filename == NULL) filename = "memdump"; FILE * fp = fopen(filename, "w"); - fwrite(&memory, sizeof(memory), 1, fp); + fwrite(&cpu->memory, sizeof(cpu->memory), 1, fp); fclose(fp); } + +CPU *create_cpu() // create CPU structure +{ + CPU *cpu; + + cpu = malloc(sizeof(CPU)); + reset_cpu(cpu, 0, 0, 0, 0xff, 0, 0); + + return cpu; +} diff --git a/6502.h b/6502.h index e865a36..8985c19 100644 --- a/6502.h +++ b/6502.h @@ -10,18 +10,6 @@ #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 -uint8_t extra_cycles; -uint64_t total_cycles; - -void * read_addr; -void * write_addr; - struct StatusBits{ bool carry:1; // bit 0 bool zero:1; @@ -38,7 +26,21 @@ union StatusReg { // this means we can access the status register as a byte, or uint8_t byte; }; -union StatusReg SR; +typedef struct { + 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 + uint8_t extra_cycles; + uint64_t total_cycles; + union StatusReg SR; + + void * read_addr; + void * write_addr; + int jumping; // used to check that we don't need to increment the PC after a jump +} CPU; typedef enum { ACC, @@ -59,7 +61,7 @@ typedef enum { typedef struct { char * mnemonic; - void (*function)(); + void (*function)(CPU *); Mode mode; uint8_t cycles; } Instruction; @@ -68,10 +70,12 @@ Instruction instructions[0x100]; void init_tables(); -void reset_cpu(int _a, int _x, int _y, int _sp, int _sr, int _pc); +void reset_cpu(CPU *cpu, int _a, int _x, int _y, int _sp, int _sr, int _pc); -int load_rom(char * filename, int load_addr); +int load_rom(CPU *cpu, char * filename, int load_addr); -int step_cpu(int verbose); +int step_cpu(CPU *cpu, int verbose); -void save_memory(char * filename); +void save_memory(CPU *cpu, char * filename); + +CPU * create_cpu(); diff --git a/6850.c b/6850.c index f810d5f..3ded922 100644 --- a/6850.c +++ b/6850.c @@ -8,8 +8,8 @@ int n; -void init_uart() { - memory[DATA_ADDR] = 0; +void init_uart(CPU *cpu) { + cpu->memory[DATA_ADDR] = 0; uart_SR.byte = 0; uart_SR.bits.TDRE = 1; // we are always ready to output data @@ -26,15 +26,15 @@ int stdin_ready() { return poll(&fds, 1, 0) == 1; // timeout = 0 } -void step_uart() { - if (write_addr == &memory[DATA_ADDR]) { - putchar(memory[DATA_ADDR]); - if (memory[DATA_ADDR] == '\b') printf(" \b"); +void step_uart(CPU *cpu) { + if (cpu->write_addr == &cpu->memory[DATA_ADDR]) { + putchar(cpu->memory[DATA_ADDR]); + if (cpu->memory[DATA_ADDR] == '\b') printf(" \b"); fflush(stdout); - write_addr = NULL; - } else if (read_addr == &memory[DATA_ADDR]) { + cpu->write_addr = NULL; + } else if (cpu->read_addr == &cpu->memory[DATA_ADDR]) { uart_SR.bits.RDRF = 0; - read_addr = NULL; + cpu->read_addr = NULL; } /* update input register if empty */ @@ -54,6 +54,6 @@ void step_uart() { } } - memory[DATA_ADDR] = incoming_char; - memory[CTRL_ADDR] = uart_SR.byte; + cpu->memory[DATA_ADDR] = incoming_char; + cpu->memory[CTRL_ADDR] = uart_SR.byte; } diff --git a/Makefile b/Makefile index 29ff36d..763ef95 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS = -Wall -Wpedantic -Ofast -std=gnu99 +CFLAGS = -Wall -Wpedantic -g -std=gnu99 LDFLAGS = -Ofast OBJ := 6502-emu.o 6502.o 6850.o