From 6b2d4c09a30277219b0abadae861b4eaf842d654 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Mar 2019 14:06:04 -0700 Subject: [PATCH] First commit --- Makefile | 18 + cpu.c | 1286 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cpu.h | 40 ++ emulate.c | 137 ++++++ 4 files changed, 1481 insertions(+) create mode 100644 Makefile create mode 100644 cpu.c create mode 100644 cpu.h create mode 100644 emulate.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e71976 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +CC = gcc + +CFLAGS = -O3 + +default: cpu.o cpu.h emulate.c + $(CC) $(CFLAGS) cpu.o emulate.c -o A1Emu + +cpu.o: cpu.c cpu.h + $(CC) $(CFLAGS) -c cpu.c + +ifeq ($(OS),Windows_NT) +clean: + del A1Emu.exe cpu.o +else +clean: + rm A1Emu cpu.o +endif + diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000..e4776cf --- /dev/null +++ b/cpu.c @@ -0,0 +1,1286 @@ +/* + * 6502 Emulator + * + * Spring Break programming project + * + * 3/5/2019 + * by Ben Jones + */ + +#include +#include +#include +#include +#include +#include +#include "cpu.h" + +uint8_t get_absolute(CPU_6502 *cpu, uint8_t index, uint8_t (*read)(uint16_t)){ + return read((((uint16_t) read(cpu->PC_reg + 1)) | (((uint16_t) read(cpu->PC_reg + 2))<<8)) + index); +} + +uint8_t get_indirect_X(CPU_6502 *cpu, uint8_t index, uint8_t (*read)(uint16_t)){ + return read(((uint16_t) read(cpu->X_reg + index)) | (((uint16_t) read(cpu->X_reg + index + 1))<<8)); +} + +uint8_t get_indirect_Y(CPU_6502 *cpu, uint8_t index, uint8_t (*read)(uint16_t)){ + return read((((uint16_t) read(index)) | (((uint16_t) read(index + 1))<<8)) + cpu->Y_reg); +} + +void set_absolute(CPU_6502 *cpu, uint8_t index, uint8_t (*read)(uint16_t), void (*write)(uint16_t, uint8_t), uint8_t value){ + write((((uint16_t) read(cpu->PC_reg + 1)) | (((uint16_t) read(cpu->PC_reg + 2))<<8)) + index, value); +} + +void set_indirect_X(CPU_6502 *cpu, uint8_t index, uint8_t (*read)(uint16_t), void (*write)(uint16_t, uint8_t), uint8_t value){ + write(((uint16_t) read(cpu->X_reg + index)) | (((uint16_t) read(cpu->X_reg + index + 1))<<8), value); +} + +void set_indirect_Y(CPU_6502 *cpu, uint8_t index, uint8_t (*read)(uint16_t), void (*write)(uint16_t, uint8_t), uint8_t value){ + write((((uint16_t) read(index)) | (((uint16_t) read(index + 1))<<8)) + cpu->Y_reg, value); +} + +void push(CPU_6502 *cpu, void (*write)(uint16_t, uint8_t), uint8_t value){ + write(0x100 | cpu->SP_reg, value); + cpu->SP_reg -= 1; +} + +uint8_t pop(CPU_6502 *cpu, uint8_t (*read)(uint16_t)){ + cpu->SP_reg += 1; + return read(0x100 | cpu->SP_reg); +} + +//Execute a single 6502 instruction, updating the state of the CPU +void execute_6502(CPU_6502 *cpu, uint8_t (*read)(uint16_t), void (*write)(uint16_t, uint8_t)){ + //Various intermediate values for calculation + uint8_t value1; + uint8_t value2; + uint8_t value3; + uint16_t value_absolute; + uint8_t prev; + //The first byte at PC uniquely determines the operation + uint8_t opcode; + + opcode = read(cpu->PC_reg); + + //ADC + if(opcode == 0x69 || opcode == 0x65 || opcode == 0x75 || opcode == 0x6D || opcode == 0x7D || opcode == 0x79 || opcode == 0x61 || opcode == 0x71){ + if(opcode == 0x69){//Immediate + value1 = read(cpu->PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0x65){//Zero page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0x75){//Zero page X + value1 = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0x6D){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0x7D){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x79){//Absolute Y + value1 = get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x61){//Indirect X + value1 = get_indirect_X(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } else if(opcode == 0x71){//Indirect Y + value1 = get_indirect_Y(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } + + prev = cpu->A_reg; + //If the carry flag is set, add 1 more to result + if(cpu->P_reg&(1<A_reg += value1; + //If the decimal flag is set, correct the output + if(cpu->P_reg&(1<A_reg&0xF) > 9){ + cpu->A_reg += 0x6; + } + } + + //Set the carry bit accordingly + if(cpu->A_reg < prev){ + cpu->P_reg |= (1<P_reg &= ~(1<A_reg&0x80)) || (!(value1&0x80) && !(prev&0x80) && (cpu->A_reg&0x80))){ + cpu->P_reg |= (1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= (1<P_reg &= ~(1<A_reg){ + cpu->P_reg |= (1<P_reg &= ~(1<PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0x25){//Zero page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0x35){//Zero page X + value1 = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0x2D){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0x3D){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x39){//Absolute Y + value1 = get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x21){//Indirect X + value1 = get_indirect_X(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } else if(opcode == 0x31){//Indirect Y + value1 = get_indirect_Y(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } + + //Perform the AND + cpu->A_reg &= value1; + + //Set the zero flag + if(!cpu->A_reg){ + cpu->P_reg |= (1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= (1<P_reg &= ~(1<A_reg; + } else if(opcode == 0x06){//Zero page + value1 = read(read(cpu->PC_reg + 1)); + } else if(opcode == 0x16){//Zero page X + value1 = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + } else if(opcode == 0x0E){//Absolute + value1 = get_absolute(cpu, 0, read); + } else if(opcode == 0x1E){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + } + + //Set the carry flag + if(value1&0x80){ + cpu->P_reg |= (1<P_reg &= ~(1<A_reg = value1; + cpu->PC_reg += 1; + } else if(opcode == 0x06){//Zero page + write(read(cpu->PC_reg + 1), value1); + cpu->PC_reg += 2; + } else if(opcode == 0x16){//Zero page X + write((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF, value1); + cpu->PC_reg += 2; + } else if(opcode == 0x0E){//Absolute + set_absolute(cpu, 0, read, write, value1); + cpu->PC_reg += 3; + } else if(opcode == 0x1E){//Absolute X + set_absolute(cpu, cpu->X_reg, read, write, value1); + cpu->PC_reg += 3; + } + + //Set the zero flag + if(!value1){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else {//If it is a positive jump + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //BCS + } else if(opcode == 0xB0){//Branch carry set + if(cpu->P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else {//If it is a positive jump + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //BEQ + } else if(opcode == 0xF0){//Branch equals + if(cpu->P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else {//If it is a postive jump + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //BIT + } else if(opcode == 0x24 || opcode == 0x2C){ + if(opcode == 0x24){//Zero page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0x2C){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } + + //Set the zero flag + if(!(value1&cpu->A_reg)){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else { + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //BNE + } else if(opcode == 0xD0){//Branch not equal + if(!(cpu->P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else { + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //BPL + } else if(opcode == 0x10){//Branch positive + if(!(cpu->P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else {//If it is a positive jump + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //BRK + } else if(opcode == 0x00){//Break (forced interrupt) + //Set the break flag + cpu->P_reg |= 1<PC_reg += 2; + push(cpu, write, (cpu->PC_reg&0xFF00)>>8); + push(cpu, write, cpu->PC_reg&0xFF); + push(cpu, write, cpu->P_reg); + cpu->P_reg |= 1<PC_reg = (((uint16_t) read(0xFFFF))<<8) | read(0xFFFE); + //BVC + } else if(opcode == 0x50){//Branch overflow clear + if(!(cpu->P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else {//If it is a positive jump + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //BVS + } else if(opcode == 0x70){//Branch overflow set + if(cpu->P_reg&(1<PC_reg + 1); + if(value1&0x80){//If it is a negative jump + cpu->PC_reg -= ((uint16_t) ((~value1) - 1))&0xFF; + } else {//If it is a positive jump + cpu->PC_reg += value1 + 2; + } + } else { + cpu->PC_reg += 2; + } + //CLC + } else if(opcode == 0x18){//Clear carry flag + cpu->P_reg &= ~(1<PC_reg += 1; + //CLD + } else if(opcode == 0xD8){//Clear decimal flag + cpu->P_reg &= ~(1<PC_reg += 1; + //CLI + } else if(opcode == 0x58){//Clear interrupt flag + cpu->P_reg &= ~(1<PC_reg += 1; + //CLV + } else if(opcode == 0xB8){//Clear overflow flag + cpu->P_reg &= ~(1<PC_reg += 1; + //CMP + } else if(opcode == 0xC9 || opcode == 0xC5 || opcode == 0xD5 || opcode == 0xCD || opcode == 0xDD || opcode == 0xD9 || opcode == 0xC1 || opcode == 0xD1){ + if(opcode == 0xC9){//Immediate + value1 = read(cpu->PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0xC5){//Zero page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0xD5){//Zero page X + value1 = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0xCD){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0xDD){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0xD9){//Absolute Y + value1 = get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0xC1){//Indirect X + value1 = get_indirect_X(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } else if(opcode == 0xD1){//Indirect Y + value1 = get_indirect_Y(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } + + value2 = cpu->A_reg - value1; + + //Set the zero flag + if(!value2){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<A_reg >= value1){ + cpu->P_reg |= 1<P_reg &= ~(1<PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0xE4){//Zero Page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0xEC){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } + + value2 = cpu->X_reg - value1; + + //Set the zero flag + if(!value2){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<X_reg >= value1){ + cpu->P_reg |= 1<P_reg &= ~(1<PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0xC4){//Zero Page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0xCC){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } + + value2 = cpu->Y_reg - value1; + + //Set the zero flag + if(!value2){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<Y_reg >= value1){ + cpu->P_reg |= 1<P_reg &= ~(1<PC_reg + 1); + value1 = read(value2); + value1--; + write(value2, value1); + cpu->PC_reg += 2; + } else if(opcode == 0xD6){//Zero page X + value2 = read(cpu->PC_reg + 1); + value1 = read((value2 + cpu->X_reg)&0xFF); + value1--; + write((value2 + cpu->X_reg)&0xFF, value1); + cpu->PC_reg += 2; + } else if(opcode == 0xCE){//Absolute + value1 = get_absolute(cpu, 0, read) - 1; + set_absolute(cpu, 0, read, write, value1); + cpu->PC_reg += 3; + } else if(opcode == 0xDE){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read) - 1; + set_absolute(cpu, cpu->X_reg, read, write, value1); + cpu->PC_reg += 3; + } + + //Set the zero flag + if(!value1){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<X_reg--; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->X_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<X_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg--; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->Y_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0x45){//Zero page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0x55){//Zero page X + value1 = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0x4D){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0x5D){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x59){//Absolute Y + value1 = get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x41){//Indirect X + value1 = get_indirect_X(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } else if(opcode == 0x51){//Indirect Y + value1 = get_indirect_Y(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } + + cpu->A_reg ^= value1; + + //Set the zero flag + if(!cpu->A_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<PC_reg + 1); + value1 = read(value2); + value1++; + write(value2, value1); + cpu->PC_reg += 2; + } else if(opcode == 0xF6){//Zero page X + value2 = read(cpu->PC_reg + 1); + value1 = read((value2 + cpu->X_reg)&0xFF); + value1++; + write((value2 + cpu->X_reg)&0xFF, value1); + cpu->PC_reg += 2; + } else if(opcode == 0xEE){//Absolute + value1 = get_absolute(cpu, 0, read) + 1; + set_absolute(cpu, 0, read, write, value1); + cpu->PC_reg += 3; + } else if(opcode == 0xFE){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read) + 1; + set_absolute(cpu, cpu->X_reg, read, write, value1); + cpu->PC_reg += 3; + } + + //Set the zero flag + if(!value1){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<X_reg++; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->X_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<X_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg++; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->Y_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<PC_reg = ((uint16_t) read(cpu->PC_reg + 1)) | (((uint16_t) read(cpu->PC_reg + 2))<<8); + } else if(opcode == 0x6C){//Indirect + value_absolute = ((uint16_t) read(cpu->PC_reg + 1)) | (((uint16_t) read(cpu->PC_reg + 2))<<8); + value1 = read(value_absolute); + value2 = read((value_absolute&0xFF00) | ((value_absolute + 1)&0xFF)); + cpu->PC_reg = (((uint16_t) value2)<<8) | value1; + //printf("indirect jump: %04x %02x %02x\n", (int) value_absolute, (int) value1, (int) value2); + //DEBUG_STEP = 1; + } + //JSR + } else if(opcode == 0x20){ + push(cpu, write, (cpu->PC_reg + 2)&0xFF); + push(cpu, write, (cpu->PC_reg + 2)>>8); + cpu->PC_reg = ((uint16_t) read(cpu->PC_reg + 1)) | (((uint16_t) read(cpu->PC_reg + 2))<<8); + //LDA + } else if(opcode == 0xA9 || opcode == 0xA5 || opcode == 0xB5 || opcode == 0xAD || opcode == 0xBD || opcode == 0xB9 || opcode == 0xA1 || opcode == 0xB1){ + if(opcode == 0xA9){//Immediate + cpu->A_reg = read(cpu->PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0xA5){//Zero page + cpu->A_reg = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0xB5){//Zero page X + cpu->A_reg = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0xAD){//Absolute + cpu->A_reg = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0xBD){//Absolute X + cpu->A_reg = get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0xB9){//Absolute Y + cpu->A_reg = get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0xA1){//Indirect X + cpu->A_reg = get_indirect_X(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } else if(opcode == 0xB1){//Indirect Y + cpu->A_reg = get_indirect_Y(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } + + //Set the zero flag + if(!cpu->A_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<X_reg = read(cpu->PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0xA6){//Zero page + cpu->X_reg = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0xB6){//Zero page Y + cpu->X_reg = read((read(cpu->PC_reg + 1) + cpu->Y_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0xAE){//Absolute + cpu->X_reg = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0xBE){//Absolute Y + cpu->X_reg = get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } + + //Set the zero flag + if(!cpu->X_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<X_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg = read(cpu->PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0xA4){//Zero page + cpu->Y_reg = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0xB4){//Zero page X + cpu->Y_reg = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0xAC){//Absolute + cpu->Y_reg = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0xBC){//Absolute X + cpu->Y_reg = get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } + + //Set the zero flag + if(!cpu->Y_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg; + cpu->A_reg >>= 1; + cpu->PC_reg++; + } else if(opcode == 0x46){//Zero page + value2 = read(cpu->PC_reg + 1); + value1 = read(value2); + write(value2, value1>>1); + cpu->PC_reg += 2; + } else if(opcode == 0x56){//Zero page X + value2 = read(cpu->PC_reg + 1); + value1 = read((value2 + cpu->X_reg)&0xFF); + write((value2 + cpu->X_reg)&0xFF, value1>>1); + cpu->PC_reg += 2; + } else if(opcode == 0x4E){//Absolute + value1 = get_absolute(cpu, 0, read); + set_absolute(cpu, 0, read, write, value1>>1); + cpu->PC_reg += 3; + } else if(opcode == 0x5E){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + set_absolute(cpu, cpu->X_reg, read, write, value1>>1); + cpu->PC_reg += 3; + } + + //Set the carry flag + if(value1&0x1){ + cpu->P_reg |= 1<P_reg &= ~(1<>1)){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg &= ~(1<A_reg |= read(cpu->PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0x05){//Zero page + cpu->A_reg |= read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0x15){//Zero page X + cpu->A_reg |= read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0x0D){//Absolute + cpu->A_reg |= get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0x1D){//Absolute X + cpu->A_reg |= get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x19){//Absolute Y + cpu->A_reg |= get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0x01){//Indirect X + cpu->A_reg |= get_indirect_X(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } else if(opcode == 0x11){//Indirect Y + cpu->A_reg |= get_indirect_Y(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } + + //Set zero flag + if(!cpu->A_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg); + cpu->PC_reg += 1; + //PHP + } else if(opcode == 0x08){ + push(cpu, write, cpu->P_reg); + cpu->PC_reg += 1; + //PLA + } else if(opcode == 0x68){ + cpu->A_reg = pop(cpu, read); + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->A_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg = pop(cpu, read); + cpu->PC_reg += 1; + //ROL + } else if(opcode == 0x2A || opcode == 0x26 || opcode == 0x36 || opcode == 0x2E || opcode == 0x3E){ + //Determine whether to append a 1 or 0 for bit 0 + if(cpu->P_reg&(1<A_reg; + cpu->A_reg = (value1<<1) | value2; + cpu->PC_reg += 1; + } else if(opcode == 0x26){//Zero page + value3 = read(cpu->PC_reg + 1); + value1 = read(value3); + write(value3, (value1<<1) | value2); + cpu->PC_reg += 2; + } else if(opcode == 0x36){//Zero page X + value3 = read(cpu->PC_reg + 1); + value1 = read((value3 + cpu->X_reg)&0xFF); + write((value3 + cpu->X_reg)&0xFF, (value1<<1) | value2); + cpu->PC_reg += 2; + } else if(opcode == 0x2E){//Absolute + value1 = get_absolute(cpu, 0, read); + set_absolute(cpu, 0, read, write, (value1<<1) | value2); + cpu->PC_reg += 3; + } else if(opcode == 0x3E){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + set_absolute(cpu, cpu->X_reg, read, write, (value1<<1) | value2); + cpu->PC_reg += 3; + } + + value2 = (value1<<1) | value2; + //value1 stores the value before + //value2 stores the value after + + //Set the carry flag + if(value1&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<P_reg&(1<A_reg; + cpu->A_reg = (value1>>1) | value2; + cpu->PC_reg += 1; + } else if(opcode == 0x66){//Zero page + value3 = read(cpu->PC_reg + 1); + value1 = read(value3); + write(value3, (value1>>1) | value2); + cpu->PC_reg += 2; + } else if(opcode == 0x76){//Zero page X + value3 = read(cpu->PC_reg + 1); + value1 = read((value3 + cpu->X_reg)&0xFF); + write((value3 + cpu->X_reg)&0xFF, (value1>>1) | value2); + cpu->PC_reg += 2; + } else if(opcode == 0x6E){//Absolute + value1 = get_absolute(cpu, 0, read); + set_absolute(cpu, 0, read, write, (value1>>1) | value2); + cpu->PC_reg += 3; + } else if(opcode == 0x7E){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + set_absolute(cpu, cpu->X_reg, read, write, (value1>>1) | value2); + cpu->PC_reg += 3; + } + + value2 = (value1>>1) | value2; + //value1 stores the value before + //value2 stores the value after + + //Set the carry flag + if(value1&1){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<P_reg |= 1<P_reg &= ~(1<P_reg = pop(cpu, read); + value1 = pop(cpu, read); + value2 = pop(cpu, read); + cpu->PC_reg = (((uint16_t) value1)<<8) | value2; + //RTS + } else if(opcode == 0x60){ + value1 = pop(cpu, read); + value2 = pop(cpu, read); + cpu->PC_reg = (((uint16_t) value1)<<8) | value2; + cpu->PC_reg++; + //SBC + } else if(opcode == 0xE9 || opcode == 0xE5 || opcode == 0xF5 || opcode == 0xED || opcode == 0xFD || opcode == 0xF9 || opcode == 0xE1 || opcode == 0xF1){ + if(opcode == 0xE9){//Immediate + value1 = read(cpu->PC_reg + 1); + cpu->PC_reg += 2; + } else if(opcode == 0xE5){//Zero page + value1 = read(read(cpu->PC_reg + 1)); + cpu->PC_reg += 2; + } else if(opcode == 0xF5){//Zero page X + value1 = read((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF); + cpu->PC_reg += 2; + } else if(opcode == 0xED){//Absolute + value1 = get_absolute(cpu, 0, read); + cpu->PC_reg += 3; + } else if(opcode == 0xFD){//Absolute X + value1 = get_absolute(cpu, cpu->X_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0xF9){//Absolute Y + value1 = get_absolute(cpu, cpu->Y_reg, read); + cpu->PC_reg += 3; + } else if(opcode == 0xE1){//Indirect X + value1 = get_indirect_X(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } else if(opcode == 0xF1){//Indirect Y + value1 = get_indirect_Y(cpu, read(cpu->PC_reg + 1), read); + cpu->PC_reg += 2; + } + + value2 = cpu->A_reg; + value3 = (~value1);//One's complement + if(cpu->P_reg&(1<A_reg += value3; + } else { + cpu->A_reg += value3; + } + + //If the CPU is in decimal state, correct output + if((cpu->P_reg&(1<A_reg&0xF) > 9){ + cpu->A_reg -= 6; + } + + //Set the overflow flag + if(((value2&0x80) && (value3&0x80) && !(cpu->A_reg&0x80)) || (!(value2&0x80) && !(value3&0x80) && (cpu->A_reg&0x80))){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg <= value2){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<P_reg |= 1<PC_reg += 1; + //SED + } else if(opcode == 0xF8){ + cpu->P_reg |= 1<PC_reg += 1; + //SEI + } else if(opcode == 0x78){ + cpu->P_reg |= 1<PC_reg += 1; + //STA + } else if(opcode == 0x85 || opcode == 0x95 || opcode == 0x8D || opcode == 0x9D || opcode == 0x99 || opcode == 0x81 || opcode == 0x91){ + if(opcode == 0x85){//Zero page + write(read(cpu->PC_reg + 1), cpu->A_reg); + cpu->PC_reg += 2; + } else if(opcode == 0x95){//Zero page X + write((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF, cpu->A_reg); + cpu->PC_reg += 2; + } else if(opcode == 0x8D){//Absolute + set_absolute(cpu, 0, read, write, cpu->A_reg); + cpu->PC_reg += 3; + } else if(opcode == 0x9D){//Absolute X + set_absolute(cpu, cpu->X_reg, read, write, cpu->A_reg); + cpu->PC_reg += 3; + } else if(opcode == 0x99){//Absolute Y + set_absolute(cpu, cpu->Y_reg, read, write, cpu->A_reg); + cpu->PC_reg += 3; + } else if(opcode == 0x81){//Indirect X + set_indirect_X(cpu, read(cpu->PC_reg + 1), read, write, cpu->A_reg); + cpu->PC_reg += 2; + } else if(opcode == 0x91){//Indirect Y + set_indirect_Y(cpu, read(cpu->PC_reg + 1), read, write, cpu->A_reg); + cpu->PC_reg += 2; + } + //STX + } else if(opcode == 0x86 || opcode == 0x96 || opcode == 0x8E){ + if(opcode == 0x86){//Zero page + write(read(cpu->PC_reg + 1), cpu->X_reg); + cpu->PC_reg += 2; + } else if(opcode == 0x96){//Zero page Y + write((read(cpu->PC_reg + 1) + cpu->Y_reg)&0xFF, cpu->X_reg); + cpu->PC_reg += 2; + } else if(opcode == 0x8E){//Absolute + set_absolute(cpu, 0, read, write, cpu->X_reg); + cpu->PC_reg += 3; + } + //STY + } else if(opcode == 0x84 || opcode == 0x94 || opcode == 0x8C){ + if(opcode == 0x84){//Zero page + write(read(cpu->PC_reg + 1), cpu->Y_reg); + cpu->PC_reg += 2; + } else if(opcode == 0x94){//Zero page X + write((read(cpu->PC_reg + 1) + cpu->X_reg)&0xFF, cpu->Y_reg); + cpu->PC_reg += 2; + } else if(opcode == 0x8C){//Absolute + set_absolute(cpu, 0, read, write, cpu->Y_reg); + cpu->PC_reg += 3; + } + //TAX + } else if(opcode == 0xAA){ + cpu->X_reg = cpu->A_reg; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->X_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<X_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg = cpu->A_reg; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->Y_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<Y_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<X_reg = cpu->SP_reg; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->X_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<X_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg = cpu->X_reg; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->A_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<SP_reg = cpu->X_reg; + cpu->PC_reg += 1; + //TYA + } else if(opcode == 0x98){ + cpu->A_reg = cpu->Y_reg; + cpu->PC_reg += 1; + + //Set the zero flag + if(!cpu->A_reg){ + cpu->P_reg |= 1<P_reg &= ~(1<A_reg&0x80){ + cpu->P_reg |= 1<P_reg &= ~(1<PC_reg += 1; + //Unknown operation + } else { + printf("Error: Unknown operation 0x%x\n", (int) opcode); + exit(1); + } +} + +void reset_6502(CPU_6502 *cpu, uint8_t (*read)(uint16_t)){ + cpu->SP_reg -= 3;//This actually happens on the chip + cpu->PC_reg = ((uint16_t) read(0xFFFD))<<8 | read(0xFFFC); +} + diff --git a/cpu.h b/cpu.h new file mode 100644 index 0000000..c06745d --- /dev/null +++ b/cpu.h @@ -0,0 +1,40 @@ +/* + * 6502 Emulator + * + * Spring Break programming project + * + * 3/5/2019 + * by Ben Jones + */ + +#include + +//Bits of the status register and correseponding flags +#define CARRY 0 +#define ZERO 1 +#define INTERRUPT 2 +#define DECIMAL 3 +#define BREAK 4 +#define OVERFLOW 6 +#define NEGATIVE 7 + +typedef struct CPU_6502 CPU_6502; + +//Store the state of cpu +struct CPU_6502{ + //The ALU loads and stores to and from the accumulator + uint8_t A_reg; + //Index registers + uint8_t X_reg; + uint8_t Y_reg; + //The stack pointer is actually a 16 bit value but the first byte is hard-wired to 0x01, so you only need 8 bits to store it + uint8_t SP_reg; + uint16_t PC_reg; + //The bits status register are all of the processor flags, but it is its own register whose value can be pushed to the stack + uint8_t P_reg; +}; + + +void execute_6502(CPU_6502 *cpu, uint8_t (*read)(uint16_t), void (*write)(uint16_t, uint8_t)); + +void reset_6502(CPU_6502 *cpu, uint8_t (*read)(uint16_t)); diff --git a/emulate.c b/emulate.c new file mode 100644 index 0000000..5cfff42 --- /dev/null +++ b/emulate.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include "cpu.h" + +unsigned DEBUG_STEP = 0; + +uint8_t memory[0x10000]; + +void print_state(CPU_6502 cpu){ + printf("A: %02x X:%02x Y:%02x SP:%02x P:%02x PC:%02x\n", (int) cpu.A_reg, (int) cpu.X_reg, (int) cpu.Y_reg, (int) cpu.SP_reg, (int) cpu.P_reg, (int) cpu.PC_reg); + printf("\nNext: %02x %02x %02x\n", (int) memory[cpu.PC_reg], (int) memory[cpu.PC_reg + 1], (int) memory[cpu.PC_reg + 2]); +} + +//Special read memory routine for memory-mapped I/O +uint8_t read_mem(uint16_t index){ + uint8_t output; + + if(DEBUG_STEP){ + printf("READ: %04x ", index); + } + output = memory[index]; + if(index == 0xD010){ + memory[0xD011] &= 0x7F; + } else if((index&0xFF0F) == 0xD002){ + output = 0; + } + if(DEBUG_STEP){ + printf("%02x\n", memory[index]); + } + return output; +} + +//Special write memory routine for memory mapped I/O +void write_mem(uint16_t index, uint8_t value){ + uint8_t x; + uint8_t y; + + if(DEBUG_STEP){ + printf("WRITE: %02x --> %04x\n", value, index); + } + if((index&0xFF0F) == 0xD002){ + if((value&0x7F) == '\n' || (value&0x7F) == '\r'){ + printf("\n"); + } else if((value&0x7F) >= 0x20 && (value&0x7F) != 127){ + //Output a character + printf("%c", value&0x7F); + } + } + memory[index] = value; +} + +int main(){ + CPU_6502 cpu; + FILE *fp; + unsigned char key_hit; + unsigned int count = 0; + unsigned long int last_time; + unsigned long int next_time; + struct timeval current_time; + + cpu.A_reg = 0; + cpu.X_reg = 0; + cpu.Y_reg = 0; + cpu.SP_reg = 0; + cpu.P_reg = 0; + cpu.PC_reg = 0xE000; + + //Load Integer Basic + fp = fopen("BASIC", "rb"); + if(fp){ + fread(memory + 0xE000, 1, 0x2000, fp); + } else { + printf("file error\n"); + exit(1); + } + fclose(fp); + + //The emulator does not support ACI at the moment + + //Load Woz's monitor + fp = fopen("WOZMON", "rb"); + if(fp){ + fread(memory + 0xFF00, 1, 0x100, fp); + } else { + printf("file error 3\n"); + exit(1); + } + fclose(fp); + reset_6502(&cpu, read_mem);//Reset the cpu + + //Initialize the timing + gettimeofday(¤t_time, NULL); + last_time = current_time.tv_usec + current_time.tv_sec*1000000; + while(1){ + /*Limit the speed of the processor to + * a realistic 4usec per instruction + * + * Will count cpu cycles soon to make + * better timing. + */ + do{ + gettimeofday(¤t_time, NULL); + next_time = current_time.tv_usec + current_time.tv_sec*1000000; + } while(next_time - last_time < 4); + last_time = last_time + 4; + + //Execute the instruction + execute_6502(&cpu, read_mem, write_mem); + count = count + 1; + + //Debugging I/O + if(DEBUG_STEP){ + print_state(cpu); + key_hit = getchar(); + if(key_hit == '|'){ + DEBUG_STEP = 0; + } else if(key_hit != ' '){ + memory[0xD010] = key_hit|0x80; + memory[0xD011] = 0xFF; + } + } + + //Handle keyboard I/O + if(!(count%200) && kbhit()){ + key_hit = getch(); + + if(key_hit == '|'){ + DEBUG_STEP = 1; + } else { + memory[0xD010] = key_hit|0x80; + memory[0xD011] |= 0x80; + } + } + } +}