/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * Vinace * Copyright (C) P.Y. Rollo 2009 * * Vinace is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Vinace is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "c-processor6502.hpp" #include // http://apple1.chez.com/Apple1project/Docs/m6502/6502-6510-8500-8502%20Opcodes.htm /* Getting and Setting Individual Bits of Processor Status */ #define SET(mask) ( P |= (mask) ) #define CLR(mask) ( P &= (~(mask)) ) #define SET_IF(mask, cond) ( P= ( (cond)?(P|(mask)):(P &(~(mask))) )) #define GET(mask) (((P&(mask))>0)) #define SET_P_FOR(value) (P = ( (P&0x7D) | ((value)&0x80) | ((value)?0:2) )) #define VECTOR_NMI 0xFFFA #define VECTOR_RESET 0xFFFC #define VECTOR_IRQ 0xFFFE #define VECTOR_BRK 0xFFFE CProcessor6502::CProcessor6502(CMemory *mem):CProcessor(mem, NUMBER_OF_SIGNALS) { } bool CProcessor6502::process_signals() { // Reset if (signals[SIGNAL_RESET]) { signals[SIGNAL_RESET] = 0; reset(); return false; } // Non masquable interrupts if (signals[SIGNAL_NMI]) { signals[SIGNAL_NMI] = 0; cycles += 7; push_word(PC); push(P); PC = read_word(VECTOR_NMI); return false; } // Interrupt request if (signals[SIGNAL_IRQ]) { if (!GET(I_BIT)) { // I is set, that means an IRQ is already being processed cycles += 7; push_word(PC); CLR(B_BIT); push(P); SET(I_BIT); PC = read_word(VECTOR_IRQ); signals[SIGNAL_IRQ]--; return false; } } // Break if (signals[SIGNAL_BRK]) { cycles += 7; push_word(PC); SET(B_BIT); push(P); SET(I_BIT); PC = read_word(VECTOR_BRK); signals[SIGNAL_BRK] = 0; return false; } return true; // OK all signals have been processed } void CProcessor6502::reset() { A = X = Y = 0; P = 0x20; S = 0xFF; PC = read_word(VECTOR_RESET); printf("Processor reset, jumping to $%04X.\n", PC); } void CProcessor6502::branch(BYTE operand) { word = (operand & 0x80)?operand-256:operand; check_page_crossing(PC, word); PC+=word; cycles++; } void CProcessor6502::opadd(BYTE operand) { word = operand + A + GET(C_BIT); SET_IF(V_BIT, !((operand^A) & 0x80) && ((A^word) & 0x80)); if (GET(D_BIT)) { if ((word & 0x0F) > 0x09) word += 0x06; if ((word & 0xF0) > 0x90) word += 0x60; } A = word; SET_IF(C_BIT, word>>8); SET_P_FOR(A); } void CProcessor6502::opsub(BYTE operand) { word = 0xFF - operand + A + GET(C_BIT); SET_IF(V_BIT, !((operand^A) & 0x80) && ((A^word) & 0x80)); if (GET(D_BIT)) { if ((word & 0x0F) > 0x09) word -= 0x06; if ((word & 0xF0) > 0x90) word -= 0x60; } A = word; SET_IF(C_BIT, word >> 8); // TODO : In decimal mode, the Z bit is not set if 0 SET_P_FOR(A); } void CProcessor6502::opcmp(BYTE operand1, BYTE operand2) { word = 0x100 - operand2 + operand1; SET_IF(C_BIT, operand1>=operand2); SET_P_FOR(word & 0xff); } // Shift bits to the left and the most significant goes to C // if rotate is set then C is inserted at the right instead of 0 void CProcessor6502::oprol(WORD addr, bool rotate) { operand = read_byte(addr); byte = (operand << 1 | (GET(C_BIT) & rotate)); SET_IF(C_BIT, operand & 0x80); operand = operand << 1; SET_P_FOR(byte); write_byte(addr, byte); } // Shift bits to the right and the less significant goes to C // if rotate is set then C is inserted at the left instead of 0 void CProcessor6502::opror(WORD addr, bool rotate) { operand = read_byte(addr); byte = (operand >> 1 | ((GET(C_BIT) & rotate) << 7)); SET_IF(C_BIT, operand & 0x01); operand = operand >> 1; SET_P_FOR(byte); write_byte(addr, byte); } void CProcessor6502::opdec(WORD addr) { byte = read_byte(addr) - 1; SET_P_FOR(byte); write_byte(addr, byte); } void CProcessor6502::opinc(WORD addr) { byte = read_byte(addr) + 1; SET_P_FOR(byte); write_byte(addr, byte); } void CProcessor6502::process_instruction() { // Processing last_PC = PC; opcode = next_byte(); switch (opcode) { case 0x69: /* ADC #imm */ cycles += 2; opadd(next_byte()); break; case 0x6D: /* ADC abs */ cycles += 4; opadd(read_byte(eaabs())); break; case 0x65: /* ADC zp */ cycles += 3; opadd(read_byte(eazp())); break; case 0x61: /* ADC (zp,X) */ cycles += 6; opadd(read_byte(eazpxind())); break; case 0x71: /* ADC (zp),Y */ cycles += 5; opadd(read_byte(eazpindy())); break; case 0x75: /* ADC zp,X */ cycles += 4; opadd(read_byte(eazp(X))); break; case 0x7D: /* ADC abs,X */ cycles += 4; opadd(read_byte(eaabs(X))); break; case 0x79: /* ADC abs,Y */ cycles += 4; opadd(read_byte(eaabs(Y))); break; case 0x29: /* AND #imm */ cycles += 2; A &= next_byte(); SET_P_FOR(A); break; case 0x2D: /* AND abs */ cycles += 4; A &= read_byte(eaabs()); SET_P_FOR(A); break; case 0x25: /* AND zp */ cycles += 3; A &= read_byte(eazp()); SET_P_FOR(A); break; case 0x21: /* AND (zp,X) */ cycles += 6; A &= read_byte(eazpxind()); SET_P_FOR(A); break; case 0x31: /* AND (zp),Y */ cycles += 5; A &= read_byte(eazpindy()); SET_P_FOR(A); break; case 0x35: /* AND zp,X */ cycles += 4; A &= read_byte(eazp(X)); SET_P_FOR(A); break; case 0x3D: /* AND abs,X */ cycles += 4; A &= read_byte(eaabs(X)); SET_P_FOR(A); break; case 0x39: /* AND abs,Y */ cycles += 4; A &= read_byte(eaabs(Y)); SET_P_FOR(A); break; case 0x0E: /* ASL abs */ cycles += 6; oprol(eaabs(), false); break; case 0x06: /* ASL zp */ cycles += 6; oprol(eazp(), false); break; case 0x0A: /* ASL acc */ cycles += 2; SET_IF(C_BIT, A & 0x80); byte = A << 1; A = byte; SET_P_FOR(A); break; case 0x16: /* ASL zp,X */ cycles += 6; oprol(eazp(X), false); break; case 0x1E: /* ASL abs,X */ cycles = 7; oprol(eaabs(X), false); break; case 0x90: /* BCC rr */ cycles += 2; operand = next_byte(); if ( !GET(C_BIT) ) branch( operand ); break; case 0xB0: /* BCS rr */ cycles += 2; operand = next_byte(); if ( GET(C_BIT) ) branch( operand ); break; case 0xF0: /* BEQ rr */ cycles += 2; operand = next_byte(); if ( GET(Z_BIT) ) branch( operand ); break; case 0x2C: /* BIT abs */ cycles += 4; operand = read_byte(eaabs()); SET_IF(V_BIT, operand & 0x40 ); SET_IF(N_BIT, operand & 0x80 ); SET_IF(Z_BIT, not (A & operand)); break; case 0x24: /* BIT zp */ cycles += 3; operand = read_byte(eazp()); SET_IF(V_BIT, operand & 0x40 ); SET_IF(N_BIT, operand & 0x80 ); SET_IF(Z_BIT, not (A & operand)); break; case 0x30: /* BMI rr */ cycles += 2; operand = next_byte(); if ( GET(N_BIT) ) branch( operand ); break; case 0xD0: /* BNE rr */ cycles += 2; operand = next_byte(); if ( !GET(Z_BIT) ) branch( operand ); break; case 0x10: /* BPL rr */ cycles += 2; operand = next_byte(); if ( !GET(N_BIT) ) branch( operand ); break; case 0x00: /* BRK */ signal(SIGNAL_BRK); break; case 0x50: /* BVC rr */ cycles += 2; operand = next_byte(); if ( !GET(V_BIT) ) branch( operand ); break; case 0x70: /* BVS rr */ cycles += 2; operand = next_byte(); if ( GET(V_BIT) ) branch( operand ); break; case 0x18: /* CLC */ cycles += 2; CLR(C_BIT); break; case 0xD8: /* CLD */ cycles += 2; CLR(D_BIT); break; case 0x58: /* CLI */ cycles += 2; CLR(I_BIT); break; case 0xB8: /* CLV */ cycles += 2; CLR(V_BIT); break; case 0xC9: /* CMP #imm */ cycles += 2; opcmp(A, next_byte()); break; case 0xCD: /* CMP abs */ cycles += 4; opcmp(A, read_byte(eaabs())); break; case 0xC5: /* CMP zp */ cycles += 3; opcmp(A, read_byte(eazp())); break; case 0xC1: /* CMP (zp,X) */ cycles += 6; opcmp(A, read_byte(eazpxind())); break; case 0xD1: /* CMP (zp),Y */ cycles += 5; opcmp(A, read_byte(eazpindy())); break; case 0xD5: /* CMP zp,X */ cycles += 4; opcmp(A, read_byte(eazp(X))); break; case 0xDD: /* CMP abs,X */ cycles += 4; opcmp(A, read_byte(eaabs(X))); break; case 0xD9: /* CMP abs,Y */ cycles += 4; opcmp(A, read_byte(eaabs(Y))); break; case 0xE0: /* CPX #imm */ cycles += 2; opcmp(X, next_byte()); break; case 0xEC: /* CPX abs */ cycles += 4; opcmp(X, read_byte(eaabs())); break; case 0xE4: /* CPX zp */ cycles += 3; opcmp(X, read_byte(eazp())); break; case 0xC0: /* CPY #imm */ cycles += 2; opcmp(Y, next_byte()); break; case 0xCC: /* CPY abs */ cycles += 4; opcmp(Y, read_byte(eaabs())); break; case 0xC4: /* CPY zp */ cycles += 3; opcmp(Y, read_byte(eazp())); break; case 0xCE: /* DEC abs */ cycles += 6; opdec(eaabs()); break; case 0xC6: /* DEC zp */ cycles += 5; opdec(eazp()); break; case 0xD6: /* DEC zp,X */ cycles += 6; opdec(eazp(X)); break; case 0xDE: /* DEC abs,X */ cycles += 7; opdec(eaabs(X)); break; case 0xCA: /* DEX */ cycles += 2; X--; SET_P_FOR(X); break; case 0x88: /* DEY */ cycles += 2; Y--; SET_P_FOR(Y); break; case 0x49: /* EOR #imm */ cycles += 2; A ^= next_byte(); SET_P_FOR(A); break; case 0x4D: /* EOR abs */ cycles += 4; A ^= read_byte(eaabs()); SET_P_FOR(A); break; case 0x45: /* EOR zp */ cycles += 3; A ^= read_byte(eazp()); SET_P_FOR(A); break; case 0x41: /* EOR (zp,X) */ cycles += 6; A ^= read_byte(eazpxind()); SET_P_FOR(A); break; case 0x51: /* EOR (zp),Y */ cycles += 5; A ^= read_byte(eazpindy()); SET_P_FOR(A); break; case 0x55: /* EOR zp,X */ cycles += 4; A ^= read_byte(eazp(X)); SET_P_FOR(A); break; case 0x5D: /* EOR abs,X */ cycles += 4; A ^= read_byte(eaabs(X)); SET_P_FOR(A); break; case 0x59: /* EOR abs,Y */ cycles += 4; A ^= read_byte(eaabs(Y)); SET_P_FOR(A); break; case 0xEE: /* INC abs */ cycles += 6; opinc(eaabs()); break; case 0xE6: /* INC zp */ cycles += 5; opinc(eazp()); break; case 0xF6: /* INC zp,X */ cycles += 6; opinc(eazp(X)); break; case 0xFE: /* INC abs,X */ cycles += 7; opinc(eaabs(X)); break; case 0xE8: /* INX */ cycles += 2; X++; SET_P_FOR(X); break; case 0xC8: /* INY */ cycles += 2; Y++; SET_P_FOR(Y); break; case 0x4C: /* JMP abs */ cycles += 3; PC = eaabs(); break; case 0x6C: /* JMP abs */ cycles += 5; PC = read_word(eaabs()); break; case 0x20: /* JSR abs */ cycles += 6; push_word(PC+1); PC = eaabs(); break; case 0xA9: /* LDA #imm */ cycles += 2; A = next_byte(); SET_P_FOR(A); break; case 0xAD: /* LDA abs */ cycles += 4; A = read_byte(eaabs()); SET_P_FOR(A); break; case 0xA5: /* LDA zp */ cycles += 4; A = read_byte(eazp()); SET_P_FOR(A); break; case 0xA1: /* LDA (zp,X) */ cycles += 6; A = read_byte(eazpxind()); SET_P_FOR(A); break; case 0xB1: /* LDA (zp),Y */ cycles += 5; A = read_byte(eazpindy()); SET_P_FOR(A); break; case 0xB5: /* LDA zp,X */ cycles += 4; A = read_byte(eazp(X)); SET_P_FOR(A); break; case 0xBD: /* LDA abs,X */ cycles += 4; A = read_byte(eaabs(X)); SET_P_FOR(A); break; case 0xB9: /* LDA abs,Y */ cycles += 4; A = read_byte(eaabs(Y)); SET_P_FOR(A); break; case 0xA2: /* LDX #imm */ cycles += 2; X = next_byte(); SET_P_FOR(X); break; case 0xAE: /* LDX abs */ cycles += 4; X = read_byte(eaabs()); SET_P_FOR(X); break; case 0xA6: /* LDX zp */ cycles += 3; X = read_byte(eazp()); SET_P_FOR(X); break; case 0xBE: /* LDX abs,Y */ cycles += 4; X = read_byte(eaabs(Y)); SET_P_FOR(X); break; case 0xB6: /* LDX zp,Y */ cycles += 4; X = read_byte(eazp(Y)); SET_P_FOR(X); break; case 0xA0: /* LDY #imm */ cycles += 2; Y = next_byte(); SET_P_FOR(Y); break; case 0xAC: /* LDY abs */ cycles += 4; Y = read_byte(eaabs()); SET_P_FOR(Y); break; case 0xA4: /* LDY zp */ cycles += 3; Y = read_byte(eazp()); SET_P_FOR(Y); break; case 0xBC: /* LDY abs,X */ cycles += 4; Y = read_byte(eaabs(X)); SET_P_FOR(Y); break; case 0xB4: /* LDY zp,X */ cycles += 4; Y = read_byte(eazp(X)); SET_P_FOR(Y); break; case 0x4E: /* LSR abs */ cycles += 6; opror(eaabs(), false); break; case 0x46: /* LSR zp */ cycles += 5; opror(eazp(), false); break; case 0x4A: /* LSR acc */ cycles += 2; SET_IF(C_BIT, A & 0x01); A >>= 1; SET_P_FOR(A); break; case 0x56: /* LSR zp,X */ cycles += 6; opror(eazp(X), false); break; case 0x5E: /* LSR abs,X */ cycles += 7; opror(eaabs(X), false); break; case 0xEA: /* NOP */ cycles += 2; break; case 0x09: /* ORA #imm */ cycles += 2; A |= next_byte(); SET_P_FOR(A); break; case 0x0D: /* ORA abs */ cycles += 4; A |= read_byte(eaabs()); SET_P_FOR(A); break; case 0x05: /* ORA zp */ cycles += 3; A |= read_byte(eazp()); SET_P_FOR(A); break; case 0x01: /* ORA (zp,X) */ cycles += 6; A |= read_byte(eazpxind()); SET_P_FOR(A); break; case 0x11: /* ORA (zp),Y */ cycles += 5; A |= read_byte(eazpindy()); SET_P_FOR(A); break; case 0x15: /* ORA zp,X */ cycles += 4; A |= read_byte(eazp(X)); SET_P_FOR(A); break; case 0x1D: /* ORA abs,X */ cycles += 4; A |= read_byte(eaabs(X)); SET_P_FOR(A); break; case 0x19: /* ORA abs,Y */ cycles += 4; A |= read_byte(eaabs(Y)); SET_P_FOR(A); break; case 0x48: /* PHA */ cycles += 3; push(A); break; case 0x08: /* PHP */ cycles += 3; push(P); break; case 0x68: /* PLA */ cycles += 4; A = pull(); SET_P_FOR(A); break; case 0x28: /* PLP */ cycles += 4; P = pull() | 0x20; /* fix bug in bit5 of P */ break; case 0x2E: /* ROL abs */ cycles += 6; oprol(eaabs(), true); break; case 0x26: /* ROL zp */ cycles += 5; oprol(eazp(), true); break; case 0x2A: /* ROL acc */ cycles += 2; operand = (A << 1) | GET(C_BIT); SET_IF(C_BIT, A & 0x80); A = operand; SET_P_FOR(A); break; case 0x36: /* ROL zp,X */ cycles += 6; oprol(eazp(X), true); break; case 0x3E: /* ROL abs,X */ cycles += 7; oprol(eaabs(X), true); break; case 0x6E: /* ROR abs */ cycles += 6; opror(eaabs(), true); break; case 0x66: /* ROR zp */ cycles += 5; opror(eazp(), true); break; case 0x6A: /* ROR acc */ cycles += 2; operand = (A >> 1) | (GET(C_BIT) << 7); SET_IF(C_BIT, A & 0x01); A = operand; SET_P_FOR(A); break; case 0x76: /* ROR zp,X */ cycles += 6; opror(eazp(X), true); break; case 0x7E: /* ROR abs,X */ cycles += 7; opror(eaabs(X), true); break; case 0x40: /* RTI */ cycles += 6; P = pull() | 0x20; /* bit 5 bug of 6502 */ PC = pull_word(); break; case 0x60: /* RTS */ cycles += 6; PC = pull_word(); PC++; break; case 0xE9: /* SBC #imm */ cycles += 2; opsub(next_byte()); break; case 0xED: /* SBC abs */ cycles += 4; opsub(read_byte(eaabs())); break; case 0xE5: /* SBC zp */ cycles += 3; opsub(read_byte(eazp())); break; case 0xE1: /* SBC (zp,X) */ cycles += 6; opsub(read_byte(eazpxind())); break; case 0xF1: /* SBC (zp),Y */ cycles += 5; opsub(read_byte(eazpindy())); break; case 0xF5: /* SBC zp,X */ cycles += 4; opsub(read_byte(eazp(X))); break; case 0xFD: /* SBC abs,X */ cycles += 4; opsub(read_byte(eaabs(X))); break; case 0xF9: /* SBC abs,Y */ cycles += 4; opsub(read_byte(eaabs(Y))); break; case 0x38: /* SEC */ cycles += 2; SET(C_BIT); break; case 0xF8: /* SED */ cycles += 2; SET(D_BIT); break; case 0x78: /* SEI */ cycles += 2; SET(I_BIT); break; case 0x8D: /* STA abs */ cycles += 4; write_byte(eaabs(), A); break; case 0x85: /* STA zp */ cycles += 3; write_byte(eazp(), A); break; case 0x81: /* STA (zp,X) */ cycles += 6; write_byte(eazpxind(), A); break; case 0x91: /* STA (zp),Y */ cycles += 6; write_byte(eazpindy(), A); break; case 0x95: /* STA zp,X */ cycles += 4; write_byte(eazp(X), A); break; case 0x9D: /* STA abs,X */ cycles += 5; write_byte(eaabs(X), A); break; case 0x99: /* STA abs,Y */ cycles += 5; write_byte(eaabs(Y), A); break; case 0x8E: /* STX abs */ cycles += 4; write_byte(eaabs(), X); break; case 0x86: /* STX zp */ cycles += 3; write_byte(eazp(), X); break; case 0x96: /* STX zp,Y */ cycles += 4; write_byte(eazp(Y), X); break; case 0x8C: /* STY abs */ cycles += 4; write_byte(eaabs(), Y); break; case 0x84: /* STY zp */ cycles += 3; write_byte(eazp(), Y); break; case 0x94: /* STY zp,X */ cycles += 4; write_byte(eazp(X), Y); break; case 0xAA: /* TAX */ cycles += 2; X = A; SET_P_FOR(X); break; case 0xA8: /* TAY */ cycles += 2; Y = A; SET_P_FOR(Y); break; case 0xBA: /* TSX */ cycles += 2; X = S; SET_P_FOR(X); break; case 0x8A: /* TXA */ cycles += 2; A = X; SET_P_FOR(A); break; case 0x9A: /* TXS */ cycles += 2; S = X; break; case 0x98: /* TYA */ cycles += 2; A = Y; SET_P_FOR(A); break; default: cycles += 2; // An instruction MUST consume some time (or the proc may stuck) printf("Unknown opcode $%02X at $%04X\n", opcode, PC); } } void CProcessor6502::write_byte(WORD addr, BYTE byte) { memory->write(addr, byte); } BYTE CProcessor6502::read_byte(WORD addr) { return memory->read(addr); } WORD CProcessor6502::read_word(WORD addr) { return memory->read(addr)|(memory->read(addr+1)<<8); } BYTE CProcessor6502::next_byte() { return read_byte(PC++); } WORD CProcessor6502::next_word() { return next_byte()+(next_byte()<<8); } void CProcessor6502::push(BYTE byte) { write_byte( (S--) + 0x0100, byte); } BYTE CProcessor6502::pull() { return read_byte( (++S) + 0x0100); } void CProcessor6502::push_word(WORD word) { push( word >> 8 ); push( word & 0xFF ); } WORD CProcessor6502::pull_word() { return pull() + ((WORD)pull()<<8); } void CProcessor6502::check_page_crossing(WORD addr, int offset) { if ( ((addr+offset)^addr) & 0xff00) cycles++; } // Effective addressing calculation WORD CProcessor6502::eazp(BYTE offset) { return (next_byte()+offset)&0xff; } WORD CProcessor6502::eaabs(BYTE offset, bool extracycle) { addr = next_word()+offset; // In most cases, changing page costs one extra cycle if (extracycle) check_page_crossing(addr,offset); return addr; } /* ($00,X) */ WORD CProcessor6502::eazpxind() { return read_word(eazp(X)); } /* ($00),Y */ WORD CProcessor6502::eazpindy(bool extracycle) { addr = read_word(eazp()); if (extracycle) check_page_crossing(addr,Y); return addr+Y; }