| /* |
| ** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com) |
| ** |
| ** |
| ** This program is free software; you can redistribute it and/or |
| ** modify it under the terms of version 2 of the GNU Library General |
| ** Public License as published by the Free Software Foundation. |
| ** |
| ** This program 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 |
| ** Library General Public License for more details. To obtain a |
| ** copy of the GNU Library General Public License, write to the Free |
| ** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| ** |
| ** Any permitted reproduction of these routines, in whole or in part, |
| ** must bear this legend. |
| ** |
| ** |
| ** nes6502.c |
| ** |
| ** NES custom 6502 (2A03) CPU implementation |
| ** $Id$ |
| */ |
| |
| |
| #include "types.h" |
| #include "nes6502.h" |
| #include "dis6502.h" |
| #include <stdio.h> |
| |
| |
| #define ADD_CYCLES(x) instruction_cycles += (x) |
| #define INC_CYCLES() instruction_cycles++ |
| /* #define ADD_CYCLES(x) remaining_cycles -= (x) */ |
| /* #define INC_CYCLES() remaining_cycles-- */ |
| |
| /* |
| ** Check to see if an index reg addition overflowed to next page |
| */ |
| #define CHECK_INDEX_OVERFLOW(addr, reg) \ |
| { \ |
| if ((uint8) (addr) < (reg)) \ |
| INC_CYCLES(); \ |
| } |
| |
| /* |
| ** Addressing mode macros |
| */ |
| |
| #define NO_READ(value) /* empty */ |
| |
| #define IMMEDIATE_BYTE(value) \ |
| { \ |
| value = bank_readbyte(PC++); \ |
| } |
| |
| |
| #define ABSOLUTE_ADDR(address) \ |
| { \ |
| address = bank_readaddress(PC); \ |
| PC += 2; \ |
| } |
| |
| #define ABSOLUTE(address, value) \ |
| { \ |
| ABSOLUTE_ADDR(address); \ |
| value = mem_read(address); \ |
| } |
| |
| #define ABSOLUTE_BYTE(value) \ |
| { \ |
| ABSOLUTE(temp, value); \ |
| } |
| |
| #define ABS_IND_X_ADDR(address) \ |
| { \ |
| address = (bank_readaddress(PC) + X) & 0xFFFF; \ |
| PC += 2; \ |
| CHECK_INDEX_OVERFLOW(address, X); \ |
| } |
| |
| #define ABS_IND_X(address, value) \ |
| { \ |
| ABS_IND_X_ADDR(address); \ |
| value = mem_read(address); \ |
| } |
| |
| #define ABS_IND_X_BYTE(value) \ |
| { \ |
| ABS_IND_X(temp, value); \ |
| } |
| |
| #define ABS_IND_Y_ADDR(address) \ |
| { \ |
| address = (bank_readaddress(PC) + Y) & 0xFFFF; \ |
| PC += 2; \ |
| CHECK_INDEX_OVERFLOW(address, Y); \ |
| } |
| |
| #define ABS_IND_Y(address, value) \ |
| { \ |
| ABS_IND_Y_ADDR(address); \ |
| value = mem_read(address); \ |
| } |
| |
| #define ABS_IND_Y_BYTE(value) \ |
| { \ |
| ABS_IND_Y(temp, value); \ |
| } |
| |
| #define ZERO_PAGE_ADDR(address) \ |
| { \ |
| IMMEDIATE_BYTE(address); \ |
| } |
| |
| #define ZERO_PAGE(address, value) \ |
| { \ |
| ZERO_PAGE_ADDR(address); \ |
| value = ZP_READ(address); \ |
| } |
| |
| #define ZERO_PAGE_BYTE(value) \ |
| { \ |
| ZERO_PAGE(btemp, value); \ |
| } |
| |
| /* Zero-page indexed Y doesn't really exist, just for LDX / STX */ |
| #define ZP_IND_X_ADDR(address) \ |
| { \ |
| address = bank_readbyte(PC++) + X; \ |
| } |
| |
| #define ZP_IND_X(bAddr, value) \ |
| { \ |
| ZP_IND_X_ADDR(bAddr); \ |
| value = ZP_READ(bAddr); \ |
| } |
| |
| #define ZP_IND_X_BYTE(value) \ |
| { \ |
| ZP_IND_X(btemp, value); \ |
| } |
| |
| #define ZP_IND_Y_ADDR(address) \ |
| { \ |
| address = bank_readbyte(PC++) + Y; \ |
| } |
| |
| #define ZP_IND_Y(address, value) \ |
| { \ |
| ZP_IND_Y_ADDR(address); \ |
| value = ZP_READ(address); \ |
| } |
| |
| #define ZP_IND_Y_BYTE(value) \ |
| { \ |
| ZP_IND_Y(btemp, value); \ |
| } |
| |
| /* |
| ** For conditional jump relative instructions |
| ** (BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS) |
| */ |
| #define RELATIVE_JUMP(cond) \ |
| { \ |
| if (cond) \ |
| { \ |
| IMMEDIATE_BYTE(btemp); \ |
| if (((int8) btemp + (uint8) PC) & 0xFF00) \ |
| ADD_CYCLES(4); \ |
| else \ |
| ADD_CYCLES(3); \ |
| PC += ((int8) btemp); \ |
| } \ |
| else \ |
| { \ |
| PC++; \ |
| ADD_CYCLES(2); \ |
| } \ |
| } |
| |
| /* |
| ** This is actually indexed indirect, but I call it |
| ** indirect X to avoid confusion |
| */ |
| #define INDIR_X_ADDR(address) \ |
| { \ |
| btemp = bank_readbyte(PC++) + X; \ |
| address = zp_address(btemp); \ |
| } |
| |
| #define INDIR_X(address, value) \ |
| { \ |
| INDIR_X_ADDR(address); \ |
| value = mem_read(address); \ |
| } |
| |
| #define INDIR_X_BYTE(value) \ |
| { \ |
| INDIR_X(temp, value); \ |
| } |
| |
| /* |
| ** This is actually indirect indexed, but I call it |
| ** indirect y to avoid confusion |
| */ |
| #define INDIR_Y_ADDR(address) \ |
| { \ |
| IMMEDIATE_BYTE(btemp); \ |
| address = (zp_address(btemp) + Y) & 0xFFFF; \ |
| /* ???? */ \ |
| CHECK_INDEX_OVERFLOW(address, Y); \ |
| } |
| |
| #define INDIR_Y(address, value) \ |
| { \ |
| INDIR_Y_ADDR(address); \ |
| value = mem_read(address); \ |
| } |
| |
| #define INDIR_Y_BYTE(value) \ |
| { \ |
| /*IMMEDIATE_BYTE(btemp); \ |
| temp = zp_address(btemp) + Y; \ |
| CHECK_INDEX_OVERFLOW(temp, Y); \ |
| value = mem_read(temp);*/ \ |
| INDIR_Y(temp, value); \ |
| } |
| |
| |
| #define JUMP(address) PC = bank_readaddress((address)) |
| |
| /* |
| ** Interrupt macros |
| */ |
| #define NMI() \ |
| { \ |
| PUSH(PC >> 8); \ |
| PUSH(PC & 0xFF); \ |
| CLEAR_FLAG(B_FLAG); \ |
| PUSH(P); \ |
| SET_FLAG(I_FLAG); \ |
| JUMP(NMI_VECTOR); \ |
| int_pending &= ~NMI_MASK; \ |
| ADD_CYCLES(INT_CYCLES); \ |
| } |
| |
| #define IRQ() \ |
| { \ |
| PUSH(PC >> 8); \ |
| PUSH(PC & 0xFF); \ |
| CLEAR_FLAG(B_FLAG); \ |
| PUSH(P); \ |
| SET_FLAG(I_FLAG); \ |
| JUMP(IRQ_VECTOR); \ |
| int_pending &= ~IRQ_MASK; \ |
| ADD_CYCLES(INT_CYCLES); \ |
| } |
| |
| /* |
| ** Instruction macros |
| */ |
| |
| /* Warning! NES CPU has no decimal mode, so by default this does no BCD! */ |
| #ifdef NES6502_DECIMAL |
| #define ADC(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| if (P & D_FLAG) \ |
| { \ |
| temp = (A & 0x0F) + (data & 0x0F) + (P & C_FLAG); \ |
| if (temp >= 10) \ |
| temp = (temp - 10) | 0x10; \ |
| temp += (A & 0xF0) + (data & 0xF0); \ |
| TEST_AND_FLAG(0 == ((A + data + (P & C_FLAG)) & 0xFF), Z_FLAG); \ |
| TEST_AND_FLAG(temp & 0x80, N_FLAG); \ |
| TEST_AND_FLAG(((~(A ^ data)) & (A ^ temp) & 0x80), V_FLAG); \ |
| if (temp > 0x9F) \ |
| temp += 0x60; \ |
| TEST_AND_FLAG(temp > 0xFF, C_FLAG); \ |
| A = (uint8) temp; \ |
| } \ |
| else \ |
| { \ |
| temp = A + data + (P & C_FLAG); \ |
| /* Set C on carry */ \ |
| TEST_AND_FLAG(temp > 0xFF, C_FLAG); \ |
| /* Set V on overflow */ \ |
| TEST_AND_FLAG(((~(A ^ data)) & (A ^ temp) & 0x80), V_FLAG); \ |
| A = (uint8) temp; \ |
| SET_NZ_FLAGS(A); \ |
| }\ |
| ADD_CYCLES(cycles); \ |
| } |
| #else |
| #define ADC(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| temp = A + data + (P & C_FLAG); \ |
| /* Set C on carry */ \ |
| TEST_AND_FLAG(temp > 0xFF, C_FLAG); \ |
| /* Set V on overflow */ \ |
| TEST_AND_FLAG(((~(A ^ data)) & (A ^ temp) & 0x80), V_FLAG); \ |
| A = (uint8) temp; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| #endif /* NES6502_DECIMAL */ |
| |
| /* undocumented */ |
| #define ANC(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| A &= data; \ |
| SET_NZ_FLAGS(A); \ |
| TEST_AND_FLAG(P & N_FLAG, C_FLAG); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define AND(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| A &= data; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #define ANE(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| A = (A | 0xEE) & X & data; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #ifdef NES6502_DECIMAL |
| #define ARR(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| data &= A; \ |
| if (P & D_FLAG) \ |
| { \ |
| temp = (data >> 1) | ((P & C_FLAG) << 7); \ |
| SET_NZ_FLAGS(temp); \ |
| TEST_AND_FLAG((temp ^ data) & 0x40, V_FLAG); \ |
| if (((data & 0x0F) + (data & 0x01)) > 5) \ |
| temp = (temp & 0xF0) | ((temp + 0x6) & 0x0F); \ |
| if (((data & 0xF0) + (data & 0x10)) > 0x50) \ |
| { \ |
| temp = (temp & 0x0F) | ((temp + 0x60) & 0xF0); \ |
| SET_FLAG(C_FLAG); \ |
| } \ |
| else \ |
| CLEAR_FLAG(C_FLAG); \ |
| A = (uint8) temp; \ |
| } \ |
| else \ |
| { \ |
| A = (data >> 1) | ((P & C_FLAG) << 7); \ |
| SET_NZ_FLAGS(A); \ |
| TEST_AND_FLAG(A & 0x40, C_FLAG); \ |
| TEST_AND_FLAG(((A >> 6) ^ (A >> 5)) & 1, V_FLAG); \ |
| }\ |
| ADD_CYCLES(cycles); \ |
| } |
| #else |
| #define ARR(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| data &= A; \ |
| A = (data >> 1) | ((P & C_FLAG) << 7); \ |
| SET_NZ_FLAGS(A); \ |
| TEST_AND_FLAG(A & 0x40, C_FLAG); \ |
| TEST_AND_FLAG((A >> 6) ^ (A >> 5), V_FLAG); \ |
| ADD_CYCLES(cycles); \ |
| } |
| #endif /* NES6502_DECIMAL */ |
| |
| #define ASL(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| TEST_AND_FLAG(data & 0x80, C_FLAG); \ |
| data <<= 1; \ |
| write_func(addr, data); \ |
| SET_NZ_FLAGS(data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define ASL_A() \ |
| { \ |
| TEST_AND_FLAG(A & 0x80, C_FLAG); \ |
| A <<= 1; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* undocumented */ |
| #define ASR(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| data &= A; \ |
| TEST_AND_FLAG(data & 0x01, C_FLAG); \ |
| A = data >> 1; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define BCC() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_CLEAR(C_FLAG))); \ |
| } |
| |
| #define BCS() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_SET(C_FLAG))); \ |
| } |
| |
| #define BEQ() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_SET(Z_FLAG))); \ |
| } |
| |
| #define BIT(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| TEST_AND_FLAG(0 == (data & A), Z_FLAG);\ |
| CLEAR_FLAG(N_FLAG | V_FLAG); \ |
| /* move bit 7/6 of data into N/V flags */ \ |
| SET_FLAG(data & (N_FLAG | V_FLAG)); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define BMI() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_SET(N_FLAG))); \ |
| } |
| |
| #define BNE() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_CLEAR(Z_FLAG))); \ |
| } |
| |
| #define BPL() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_CLEAR(N_FLAG))); \ |
| } |
| |
| /* Software interrupt type thang */ |
| #define BRK() \ |
| { \ |
| PC++; \ |
| PUSH(PC >> 8); \ |
| PUSH(PC & 0xFF); \ |
| SET_FLAG(B_FLAG); \ |
| PUSH(P); \ |
| SET_FLAG(I_FLAG); \ |
| JUMP(IRQ_VECTOR); \ |
| ADD_CYCLES(7); \ |
| } |
| |
| #define BVC() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_CLEAR(V_FLAG))); \ |
| } |
| |
| #define BVS() \ |
| { \ |
| RELATIVE_JUMP((IS_FLAG_SET(V_FLAG))); \ |
| } |
| |
| #define CLC() \ |
| { \ |
| CLEAR_FLAG(C_FLAG); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define CLD() \ |
| { \ |
| CLEAR_FLAG(D_FLAG); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define CLI() \ |
| { \ |
| CLEAR_FLAG(I_FLAG); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define CLV() \ |
| { \ |
| CLEAR_FLAG(V_FLAG); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* TODO: ick! */ |
| #define _COMPARE(reg, value) \ |
| { \ |
| temp = (reg) - (value); \ |
| /* C is clear when data > A */ \ |
| TEST_AND_FLAG(0 == (temp & 0x8000), C_FLAG); \ |
| SET_NZ_FLAGS((uint8) temp); /* handles Z flag */ \ |
| } |
| |
| #define CMP(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| _COMPARE(A, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define CPX(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| _COMPARE(X, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define CPY(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| _COMPARE(Y, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #define DCP(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| data--; \ |
| write_func(addr, data); \ |
| CMP(cycles, NO_READ); \ |
| } |
| |
| #define DEC(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| data--; \ |
| write_func(addr, data); \ |
| SET_NZ_FLAGS(data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define DEX() \ |
| { \ |
| X--; \ |
| SET_NZ_FLAGS(X); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define DEY() \ |
| { \ |
| Y--; \ |
| SET_NZ_FLAGS(Y); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* undocumented (double-NOP) */ |
| #define DOP(cycles) \ |
| { \ |
| PC++; \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define EOR(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| A ^= data; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define INC(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| data++; \ |
| write_func(addr, data); \ |
| SET_NZ_FLAGS(data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define INX() \ |
| { \ |
| X++; \ |
| SET_NZ_FLAGS(X); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define INY() \ |
| { \ |
| Y++; \ |
| SET_NZ_FLAGS(Y); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* undocumented */ |
| #define ISB(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| data++; \ |
| write_func(addr, data); \ |
| SBC(cycles, NO_READ); \ |
| } |
| |
| #ifdef NES6502_TESTOPS |
| #define JAM() \ |
| { \ |
| cpu_Jam(); \ |
| } |
| #elif defined(NSF_PLAYER) |
| #define JAM() \ |
| { \ |
| } |
| #else |
| #define JAM() \ |
| { \ |
| char jambuf[20]; \ |
| sprintf(jambuf, "JAM: PC=$%04X", PC); \ |
| ASSERT_MSG(jambuf); \ |
| ADD_CYCLES(2); \ |
| } |
| #endif /* NES6502_TESTOPS */ |
| |
| #define JMP_INDIRECT() \ |
| { \ |
| temp = bank_readaddress(PC); \ |
| /* bug in crossing page boundaries */ \ |
| if (0xFF == (uint8) temp) \ |
| PC = (bank_readbyte(temp & ~0xFF) << 8) | bank_readbyte(temp); \ |
| else \ |
| JUMP(temp); \ |
| ADD_CYCLES(5); \ |
| } |
| |
| #define JMP_ABSOLUTE() \ |
| { \ |
| JUMP(PC); \ |
| ADD_CYCLES(3); \ |
| } |
| |
| #define JSR() \ |
| { \ |
| PC++; \ |
| PUSH(PC >> 8); \ |
| PUSH(PC & 0xFF); \ |
| JUMP(PC - 1); \ |
| ADD_CYCLES(6); \ |
| } |
| |
| /* undocumented */ |
| #define LAS(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| A = X = S = (S & data); \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #define LAX(cycles, read_func) \ |
| { \ |
| read_func(A); \ |
| X = A; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define LDA(cycles, read_func) \ |
| { \ |
| read_func(A); \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define LDX(cycles, read_func) \ |
| { \ |
| read_func(X); \ |
| SET_NZ_FLAGS(X);\ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define LDY(cycles, read_func) \ |
| { \ |
| read_func(Y); \ |
| SET_NZ_FLAGS(Y);\ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define LSR(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| TEST_AND_FLAG(data & 0x01, C_FLAG); \ |
| data >>= 1; \ |
| write_func(addr, data); \ |
| SET_NZ_FLAGS(data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define LSR_A() \ |
| { \ |
| TEST_AND_FLAG(A & 0x01, C_FLAG); \ |
| A >>= 1; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* undocumented */ |
| #define LXA(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| A = X = ((A | 0xEE) & data); \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define NOP() \ |
| { \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define ORA(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| A |= data; \ |
| SET_NZ_FLAGS(A);\ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define PHA() \ |
| { \ |
| PUSH(A); \ |
| ADD_CYCLES(3); \ |
| } |
| |
| #define PHP() \ |
| { \ |
| /* B flag is pushed on stack as well */ \ |
| PUSH((P | B_FLAG)); \ |
| ADD_CYCLES(3); \ |
| } |
| |
| #define PLA() \ |
| { \ |
| A = PULL(); \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(4); \ |
| } |
| |
| #define PLP() \ |
| { \ |
| P = PULL(); \ |
| SET_FLAG(R_FLAG); /* ensure reserved flag is set */ \ |
| ADD_CYCLES(4); \ |
| } |
| |
| /* undocumented */ |
| #define RLA(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| if (P & C_FLAG) \ |
| { \ |
| TEST_AND_FLAG(data & 0x80, C_FLAG); \ |
| data = (data << 1) | 1; \ |
| } \ |
| else \ |
| { \ |
| TEST_AND_FLAG(data & 0x80, C_FLAG); \ |
| data <<= 1; \ |
| } \ |
| write_func(addr, data); \ |
| A &= data; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* 9-bit rotation (carry flag used for rollover) */ |
| #define ROL(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| if (P & C_FLAG) \ |
| { \ |
| TEST_AND_FLAG(data & 0x80, C_FLAG); \ |
| data = (data << 1) | 1; \ |
| } \ |
| else \ |
| { \ |
| TEST_AND_FLAG(data & 0x80, C_FLAG); \ |
| data <<= 1; \ |
| } \ |
| write_func(addr, data); \ |
| SET_NZ_FLAGS(data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define ROL_A() \ |
| { \ |
| if (P & C_FLAG) \ |
| { \ |
| TEST_AND_FLAG(A & 0x80, C_FLAG); \ |
| A = (A << 1) | 1; \ |
| } \ |
| else \ |
| { \ |
| TEST_AND_FLAG(A & 0x80, C_FLAG); \ |
| A <<= 1; \ |
| } \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define ROR(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| if (P & C_FLAG) \ |
| { \ |
| TEST_AND_FLAG(data & 1, C_FLAG); \ |
| data = (data >> 1) | 0x80; \ |
| } \ |
| else \ |
| { \ |
| TEST_AND_FLAG(data & 1, C_FLAG); \ |
| data >>= 1; \ |
| } \ |
| write_func(addr, data); \ |
| SET_NZ_FLAGS(data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define ROR_A() \ |
| { \ |
| if (P & C_FLAG) \ |
| { \ |
| TEST_AND_FLAG(A & 1, C_FLAG); \ |
| A = (A >> 1) | 0x80; \ |
| } \ |
| else \ |
| { \ |
| TEST_AND_FLAG(A & 1, C_FLAG); \ |
| A >>= 1; \ |
| } \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* undocumented */ |
| #define RRA(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| if (P & C_FLAG) \ |
| { \ |
| TEST_AND_FLAG(data & 1, C_FLAG); \ |
| data = (data >> 1) | 0x80; \ |
| } \ |
| else \ |
| { \ |
| TEST_AND_FLAG(data & 1, C_FLAG); \ |
| data >>= 1; \ |
| } \ |
| write_func(addr, data); \ |
| ADC(cycles, NO_READ); \ |
| } |
| |
| #define RTI() \ |
| { \ |
| P = PULL(); \ |
| SET_FLAG(R_FLAG); /* ensure reserved flag is set */ \ |
| PC = PULL(); \ |
| PC |= PULL() << 8; \ |
| ADD_CYCLES(6); \ |
| } |
| |
| #define RTS() \ |
| { \ |
| PC = PULL(); \ |
| PC = (PC | (PULL() << 8)) + 1; \ |
| ADD_CYCLES(6); \ |
| } |
| |
| /* undocumented */ |
| #define SAX(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| data = A & X; \ |
| write_func(addr, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* Warning! NES CPU has no decimal mode, so by default this does no BCD! */ |
| #ifdef NES6502_DECIMAL |
| #define SBC(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| /* NOT(C) is considered borrow */ \ |
| temp = A - data - ((P & C_FLAG) ^ C_FLAG); \ |
| if (P & D_FLAG) \ |
| { \ |
| uint8 al, ah; \ |
| al = (A & 0x0F) - (data & 0x0F) - ((P & C_FLAG) ^ C_FLAG); \ |
| ah = (A >> 4) - (data >> 4); \ |
| if (al & 0x10) \ |
| { \ |
| al -= 6; \ |
| ah--; \ |
| } \ |
| if (ah & 0x10) \ |
| ah -= 6; \ |
| TEST_AND_FLAG(temp < 0x100, C_FLAG); \ |
| TEST_AND_FLAG(((A ^ temp) & 0x80) && ((A ^ data) & 0x80), V_FLAG); \ |
| SET_NZ_FLAGS(temp & 0xFF); \ |
| A = (ah << 4) | (al & 0x0F); \ |
| } \ |
| else \ |
| { \ |
| TEST_AND_FLAG(((A ^ temp) & 0x80) && ((A ^ data) & 0x80), V_FLAG); \ |
| TEST_AND_FLAG(temp < 0x100, C_FLAG); \ |
| A = (uint8) temp; \ |
| SET_NZ_FLAGS(A & 0xFF); \ |
| } \ |
| ADD_CYCLES(cycles); \ |
| } |
| #else |
| #define SBC(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| temp = A - data - ((P & C_FLAG) ^ C_FLAG); \ |
| TEST_AND_FLAG(((A ^ data) & (A ^ temp) & 0x80), V_FLAG); \ |
| TEST_AND_FLAG(temp < 0x100, C_FLAG); \ |
| A = (uint8) temp; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| #endif /* NES6502_DECIMAL */ |
| |
| /* undocumented */ |
| #define SBX(cycles, read_func) \ |
| { \ |
| read_func(data); \ |
| temp = (A & X) - data; \ |
| TEST_AND_FLAG(temp < 0x100, C_FLAG); \ |
| X = temp & 0xFF; \ |
| SET_NZ_FLAGS(X); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define SEC_6502() \ |
| { \ |
| SET_FLAG(C_FLAG); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define SED() \ |
| { \ |
| SET_FLAG(D_FLAG); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define SEI() \ |
| { \ |
| SET_FLAG(I_FLAG); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* undocumented */ |
| #define SHA(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| data = A & X & ((uint8) ((addr >> 8) + 1)); \ |
| write_func(addr, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #define SHS(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| S = A & X; \ |
| data = S & ((uint8) ((addr >> 8) + 1)); \ |
| write_func(addr, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #define SHX(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| data = X & ((uint8) ((addr >> 8) + 1)); \ |
| write_func(addr, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #define SHY(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| data = Y & ((uint8) ((addr >> 8 ) + 1)); \ |
| write_func(addr, data); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* undocumented */ |
| #define SLO(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| TEST_AND_FLAG(data & 0x80, C_FLAG); \ |
| data <<= 1; \ |
| write_func(addr, data); \ |
| A |= data; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| /* unoffical */ |
| #define SRE(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr, data); \ |
| TEST_AND_FLAG(data & 1, C_FLAG); \ |
| data >>= 1; \ |
| write_func(addr, data); \ |
| A ^= data; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define STA(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| write_func(addr, A); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define STX(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| write_func(addr, X); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define STY(cycles, read_func, write_func, addr) \ |
| { \ |
| read_func(addr); \ |
| write_func(addr, Y); \ |
| ADD_CYCLES(cycles); \ |
| } |
| |
| #define TAX() \ |
| { \ |
| X = A; \ |
| SET_NZ_FLAGS(X);\ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define TAY() \ |
| { \ |
| Y = A; \ |
| SET_NZ_FLAGS(Y);\ |
| ADD_CYCLES(2); \ |
| } |
| |
| /* undocumented (triple-NOP) */ |
| #define TOP() \ |
| { \ |
| PC += 2; \ |
| ADD_CYCLES(4); \ |
| } |
| |
| #define TSX() \ |
| { \ |
| X = S; \ |
| SET_NZ_FLAGS(X);\ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define TXA() \ |
| { \ |
| A = X; \ |
| SET_NZ_FLAGS(A);\ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define TXS() \ |
| { \ |
| S = X; \ |
| ADD_CYCLES(2); \ |
| } |
| |
| #define TYA() \ |
| { \ |
| A = Y; \ |
| SET_NZ_FLAGS(A); \ |
| ADD_CYCLES(2); \ |
| } |
| |
| |
| /* |
| ** Stack and data fetching macros |
| */ |
| |
| /* Set/clear/test bits in the flags register */ |
| #define SET_FLAG(mask) P |= (mask) |
| #define CLEAR_FLAG(mask) P &= ~(mask) |
| #define IS_FLAG_SET(mask) (P & (mask)) |
| #define IS_FLAG_CLEAR(mask) (0 == IS_FLAG_SET((mask))) |
| |
| #define TEST_AND_FLAG(test, mask) \ |
| { \ |
| if ((test)) \ |
| SET_FLAG((mask)); \ |
| else \ |
| CLEAR_FLAG((mask)); \ |
| } |
| |
| |
| /* |
| ** flag register helper macros |
| */ |
| |
| /* register push/pull */ |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| |
| # define PUSH(value) stack_push((S--),(value)) |
| # define PULL() stack_pull((++S)) |
| |
| #else |
| |
| # define PUSH(value) stack_page[S--] = (uint8) (value) |
| # define PULL() stack_page[++S] |
| |
| #endif /* #ifdef NES6502_MEM_ACCESS_CTRL */ |
| |
| /* Sets the Z and N flags based on given data, taken from precomputed table */ |
| #define SET_NZ_FLAGS(value) P &= ~(N_FLAG | Z_FLAG); \ |
| P |= flag_table[(value)] |
| |
| #define GET_GLOBAL_REGS() \ |
| { \ |
| PC = reg_PC; \ |
| A = reg_A; \ |
| X = reg_X; \ |
| Y = reg_Y; \ |
| P = reg_P; \ |
| S = reg_S; \ |
| } |
| |
| #define SET_LOCAL_REGS() \ |
| { \ |
| reg_PC = PC; \ |
| reg_A = A; \ |
| reg_X = X; \ |
| reg_Y = Y; \ |
| reg_P = P; \ |
| reg_S = S; \ |
| } |
| |
| |
| /* static data */ |
| static nes6502_memread *pmem_read, *pmr; |
| static nes6502_memwrite *pmem_write, *pmw; |
| |
| /* lookup table for N/Z flags */ |
| static uint8 flag_table[256]; |
| |
| /* internal critical CPU vars */ |
| static uint32 reg_PC; |
| static uint8 reg_A, reg_P, reg_X, reg_Y, reg_S; |
| static uint8 int_pending; |
| static int dma_cycles; |
| |
| /* execution cycle count (can be reset by user) */ |
| static uint32 total_cycles = 0; |
| |
| /* memory region pointers */ |
| static uint8 *nes6502_banks[NES6502_NUMBANKS]; |
| static uint8 *ram = NULL; |
| static uint8 *stack_page = NULL; |
| |
| /* access flag for memory |
| * $$$ ben : I add this for the playing time calculation. |
| * Only if compiled with NES6502_MEM_ACCESS. |
| */ |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| |
| uint8 *acc_nes6502_banks[NES6502_NUMBANKS]; |
| static uint8 *acc_ram = NULL; |
| static uint8 *acc_stack_page = NULL; |
| uint8 nes6502_mem_access = 0; |
| |
| /* $$$ ben : |
| * Set memory access check flags, and store ORed frame global check |
| * for music time calculation. |
| */ |
| static void |
| chk_mem_access (uint8 * access, int flags) |
| { |
| uint8 oldchk = *access; |
| |
| if ((oldchk & flags) != flags) { |
| nes6502_mem_access |= flags; |
| *access = oldchk | flags; |
| } |
| } |
| |
| INLINE void |
| stack_push (uint8 s, uint8 v) |
| { |
| chk_mem_access (acc_stack_page + s, NES6502_WRITE_ACCESS); |
| stack_page[s] = v; |
| } |
| |
| INLINE uint8 |
| stack_pull (uint8 s) |
| { |
| chk_mem_access (acc_stack_page + s, NES6502_READ_ACCESS); |
| return stack_page[s]; |
| } |
| |
| INLINE uint8 |
| zp_read (register uint32 addr) |
| { |
| chk_mem_access (acc_ram + addr, NES6502_READ_ACCESS); |
| return ram[addr]; |
| } |
| |
| INLINE void |
| zp_write (register uint32 addr, uint8 v) |
| { |
| chk_mem_access (acc_ram + addr, NES6502_WRITE_ACCESS); |
| ram[addr] = v; |
| } |
| |
| #define ZP_READ(addr) zp_read((addr)) |
| #define ZP_WRITE(addr, value) zp_write((addr),(value)) |
| |
| #define bank_readbyte(address) _bank_readbyte((address), NES6502_READ_ACCESS) |
| #define bank_readbyte_pc(address) _bank_readbyte((address), NES6502_EXE_ACCESS) |
| |
| #else |
| # define chk_mem_access(access, flags) |
| |
| /* |
| ** Zero-page helper macros |
| */ |
| #define ZP_READ(addr) ram[(addr)] |
| #define ZP_WRITE(addr, value) ram[(addr)] = (uint8) (value) |
| |
| #define bank_readbyte(address) _bank_readbyte((address)) |
| #define bank_readbyte_pc(address) _bank_readbyte((address)) |
| |
| #endif /* #ifdef NES6502_MEM_ACCESS_CTRL */ |
| |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| int max_access[NES6502_NUMBANKS] = |
| { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| |
| INLINE uint8 |
| _bank_readbyte (register uint32 address, const uint8 flags) |
| #else |
| INLINE uint8 |
| _bank_readbyte (register uint32 address) |
| #endif |
| { |
| ASSERT (nes6502_banks[address >> NES6502_BANKSHIFT]); |
| |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| /* printf("chk_mem_access(acc_nes6502_banks[%d] + %d, %d)\n", address>>NES6502_BANKSHIFT, address & NES6502_BANKMASK, flags); */ |
| |
| if ((address & NES6502_BANKMASK) > max_access[address >> NES6502_BANKSHIFT]) { |
| max_access[address >> NES6502_BANKSHIFT] = address & NES6502_BANKMASK; |
| /* printf("max_access[%d] increased to %d\n", address>>NES6502_BANKSHIFT, max_access[address>>NES6502_BANKSHIFT]); */ |
| } |
| #endif |
| chk_mem_access (acc_nes6502_banks[address >> NES6502_BANKSHIFT] |
| + (address & NES6502_BANKMASK), flags); |
| |
| return nes6502_banks[address >> NES6502_BANKSHIFT][address & |
| NES6502_BANKMASK]; |
| } |
| |
| |
| INLINE void |
| bank_writebyte (register uint32 address, register uint8 value) |
| { |
| ASSERT (nes6502_banks[address >> NES6502_BANKSHIFT]); |
| |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| /* printf("chk_mem_access(acc_nes6502_banks[%d] + %d, %d)\n", address>>NES6502_BANKSHIFT, address & NES6502_BANKMASK, NES6502_WRITE_ACCESS); */ |
| |
| if ((address & NES6502_BANKMASK) > max_access[address >> NES6502_BANKSHIFT]) { |
| max_access[address >> NES6502_BANKSHIFT] = address & NES6502_BANKMASK; |
| /* printf("max_access[%d] increased to %d\n", address>>NES6502_BANKSHIFT, max_access[address>>NES6502_BANKSHIFT]); */ |
| } |
| #endif |
| |
| chk_mem_access (acc_nes6502_banks[address >> NES6502_BANKSHIFT] |
| + (address & NES6502_BANKMASK), NES6502_WRITE_ACCESS); |
| |
| nes6502_banks[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK] = |
| value; |
| } |
| |
| /* Read a 16bit word */ |
| #define READ_SNES_16(bank,offset) \ |
| (\ |
| (offset) [ (uint8 *) (bank) ] |\ |
| ((unsigned int)( ((offset)+1) [ (uint8 *) (bank) ] ) << 8)\ |
| ) |
| |
| INLINE uint32 |
| zp_address (register uint8 address) |
| { |
| chk_mem_access (acc_ram + address, NES6502_READ_ACCESS); |
| chk_mem_access (acc_ram + address + 1, NES6502_READ_ACCESS); |
| |
| #if defined (HOST_LITTLE_ENDIAN) && defined(HOST_UNALIGN_WORD) |
| /* TODO: this fails if src address is $xFFF */ |
| /* TODO: this fails if host architecture doesn't support byte alignment */ |
| /* $$$ ben : DONE */ |
| return (uint32) (*(uint16 *) (ram + address)); |
| #elif defined(TARGET_CPU_PPC) |
| return __lhbrx (ram, address); |
| #else |
| return READ_SNES_16 (ram, address); |
| /* uint32 x = (uint32) *(uint16 *)(ram + address); */ |
| /* return (x << 8) | (x >> 8); */ |
| /* #endif *//* TARGET_CPU_PPC */ |
| #endif /* HOST_LITTLE_ENDIAN */ |
| } |
| |
| INLINE uint32 |
| bank_readaddress (register uint32 address) |
| { |
| |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| { |
| const unsigned int offset = address & NES6502_BANKMASK; |
| uint8 *addr = acc_nes6502_banks[address >> NES6502_BANKSHIFT]; |
| |
| chk_mem_access (addr + offset + 0, NES6502_READ_ACCESS); |
| chk_mem_access (addr + offset + 1, NES6502_READ_ACCESS); |
| } |
| #endif |
| |
| #if defined (HOST_LITTLE_ENDIAN) && defined(HOST_UNALIGN_WORD) |
| /* TODO: this fails if src address is $xFFF */ |
| /* TODO: this fails if host architecture doesn't support byte alignment */ |
| /* $$$ ben : DONE */ |
| return (uint32) (*(uint16 *) (nes6502_banks[address >> NES6502_BANKSHIFT] + |
| (address & NES6502_BANKMASK))); |
| #elif defined(TARGET_CPU_PPC) |
| return __lhbrx (nes6502_banks[address >> NES6502_BANKSHIFT], |
| address & NES6502_BANKMASK); |
| #else |
| { |
| const unsigned int offset = address & NES6502_BANKMASK; |
| |
| return READ_SNES_16 (nes6502_banks[address >> NES6502_BANKSHIFT], offset); |
| } |
| /* uint32 x = (uint32) *(uint16 *)(nes6502_banks[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK)); */ |
| /* return (x << 8) | (x >> 8); */ |
| /* #endif *//* TARGET_CPU_PPC */ |
| #endif /* HOST_LITTLE_ENDIAN */ |
| } |
| |
| |
| /* read a byte of 6502 memory */ |
| static uint8 |
| mem_read (uint32 address) |
| { |
| /* TODO: following cases are N2A03-specific */ |
| /* RAM */ |
| if (address < 0x800) { |
| chk_mem_access (acc_ram + address, NES6502_READ_ACCESS); |
| return ram[address]; |
| } |
| /* always paged memory */ |
| /* else if (address >= 0x6000) */ |
| else if (address >= 0x8000) { |
| return bank_readbyte (address); |
| } |
| /* check memory range handlers */ |
| else { |
| for (pmr = pmem_read; pmr->min_range != 0xFFFFFFFF; pmr++) { |
| if ((address >= pmr->min_range) && (address <= pmr->max_range)) |
| return pmr->read_func (address); |
| } |
| } |
| |
| /* return paged memory */ |
| return bank_readbyte (address); |
| } |
| |
| /* write a byte of data to 6502 memory */ |
| static void |
| mem_write (uint32 address, uint8 value) |
| { |
| /* RAM */ |
| if (address < 0x800) { |
| chk_mem_access (acc_ram + address, NES6502_WRITE_ACCESS); |
| ram[address] = value; |
| return; |
| } |
| /* check memory range handlers */ |
| else { |
| for (pmw = pmem_write; pmw->min_range != 0xFFFFFFFF; pmw++) { |
| if ((address >= pmw->min_range) && (address <= pmw->max_range)) { |
| pmw->write_func (address, value); |
| return; |
| } |
| } |
| } |
| |
| /* write to paged memory */ |
| bank_writebyte (address, value); |
| } |
| |
| /* set the current context */ |
| void |
| nes6502_setcontext (nes6502_context * cpu) |
| { |
| int loop; |
| |
| ASSERT (cpu); |
| |
| /* Set the page pointers */ |
| for (loop = 0; loop < NES6502_NUMBANKS; loop++) { |
| nes6502_banks[loop] = cpu->mem_page[loop]; |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| acc_nes6502_banks[loop] = cpu->acc_mem_page[loop]; |
| #endif |
| } |
| |
| ram = nes6502_banks[0]; /* quicker zero-page/RAM references */ |
| stack_page = ram + STACK_OFFSET; |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| acc_ram = acc_nes6502_banks[0]; /* quicker zero-page/RAM references */ |
| acc_stack_page = acc_ram + STACK_OFFSET; |
| #endif |
| |
| pmem_read = cpu->read_handler; |
| pmem_write = cpu->write_handler; |
| |
| reg_PC = cpu->pc_reg; |
| reg_A = cpu->a_reg; |
| reg_P = cpu->p_reg; |
| reg_X = cpu->x_reg; |
| reg_Y = cpu->y_reg; |
| reg_S = cpu->s_reg; |
| int_pending = cpu->int_pending; |
| dma_cycles = cpu->dma_cycles; |
| } |
| |
| /* get the current context */ |
| void |
| nes6502_getcontext (nes6502_context * cpu) |
| { |
| int loop; |
| |
| /* Set the page pointers */ |
| for (loop = 0; loop < NES6502_NUMBANKS; loop++) { |
| cpu->mem_page[loop] = nes6502_banks[loop]; |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| cpu->acc_mem_page[loop] = acc_nes6502_banks[loop]; |
| #endif |
| } |
| |
| cpu->read_handler = pmem_read; |
| cpu->write_handler = pmem_write; |
| |
| cpu->pc_reg = reg_PC; |
| cpu->a_reg = reg_A; |
| cpu->p_reg = reg_P; |
| cpu->x_reg = reg_X; |
| cpu->y_reg = reg_Y; |
| cpu->s_reg = reg_S; |
| cpu->int_pending = int_pending; |
| cpu->dma_cycles = dma_cycles; |
| } |
| |
| /* DMA a byte of data from ROM */ |
| uint8 |
| nes6502_getbyte (uint32 address) |
| { |
| return bank_readbyte (address); |
| } |
| |
| /* get number of elapsed cycles */ |
| uint32 |
| nes6502_getcycles (boolean reset_flag) |
| { |
| uint32 cycles = total_cycles; |
| |
| if (reset_flag) |
| total_cycles = 0; |
| |
| return cycles; |
| } |
| |
| |
| /* Execute instructions until count expires |
| ** |
| ** Returns the number of cycles *actually* executed |
| ** (note that this can be from 0-6 cycles more than you wanted) |
| */ |
| int |
| nes6502_execute (int remaining_cycles) |
| { |
| int instruction_cycles, old_cycles = total_cycles; |
| uint32 temp, addr; /* for macros */ |
| uint32 PC; |
| uint8 A, X, Y, P, S; |
| uint8 opcode, data; |
| uint8 btemp, baddr; /* for macros */ |
| |
| GET_GLOBAL_REGS (); |
| |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| /* reset global memory access for this execute loop. */ |
| nes6502_mem_access = 0; |
| #endif |
| |
| /* Continue until we run out of cycles */ |
| |
| |
| while (remaining_cycles > 0) { |
| instruction_cycles = 0; |
| |
| /* check for DMA cycle burning */ |
| if (dma_cycles) { |
| if (remaining_cycles <= dma_cycles) { |
| dma_cycles -= remaining_cycles; |
| total_cycles += remaining_cycles; |
| goto _execute_done; |
| } else { |
| remaining_cycles -= dma_cycles; |
| total_cycles += dma_cycles; |
| dma_cycles = 0; |
| } |
| } |
| |
| if (int_pending) { |
| /* NMI has highest priority */ |
| if (int_pending & NMI_MASK) { |
| NMI (); |
| } |
| /* IRQ has lowest priority */ |
| else { /* if (int_pending & IRQ_MASK) */ |
| |
| if (IS_FLAG_CLEAR (I_FLAG)) |
| IRQ (); |
| } |
| } |
| |
| /* Fetch instruction */ |
| /* nes6502_disasm(PC, P, A, X, Y, S); */ |
| |
| opcode = bank_readbyte_pc (PC++); |
| |
| /* Execute instruction */ |
| |
| switch (opcode) { |
| case 0x00: /* BRK */ |
| BRK (); |
| break; |
| |
| case 0x01: /* ORA ($nn,X) */ |
| ORA (6, INDIR_X_BYTE); |
| break; |
| |
| /* JAM */ |
| case 0x02: /* JAM */ |
| case 0x12: /* JAM */ |
| case 0x22: /* JAM */ |
| case 0x32: /* JAM */ |
| case 0x42: /* JAM */ |
| case 0x52: /* JAM */ |
| case 0x62: /* JAM */ |
| case 0x72: /* JAM */ |
| case 0x92: /* JAM */ |
| case 0xB2: /* JAM */ |
| case 0xD2: /* JAM */ |
| case 0xF2: /* JAM */ |
| JAM (); |
| /* kill switch for CPU emulation */ |
| goto _execute_done; |
| |
| case 0x03: /* SLO ($nn,X) */ |
| SLO (8, INDIR_X, mem_write, addr); |
| break; |
| |
| case 0x04: /* NOP $nn */ |
| case 0x44: /* NOP $nn */ |
| case 0x64: /* NOP $nn */ |
| DOP (3); |
| break; |
| |
| case 0x05: /* ORA $nn */ |
| ORA (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0x06: /* ASL $nn */ |
| ASL (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x07: /* SLO $nn */ |
| SLO (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x08: /* PHP */ |
| PHP (); |
| break; |
| |
| case 0x09: /* ORA #$nn */ |
| ORA (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x0A: /* ASL A */ |
| ASL_A (); |
| break; |
| |
| case 0x0B: /* ANC #$nn */ |
| ANC (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x0C: /* NOP $nnnn */ |
| TOP (); |
| break; |
| |
| case 0x0D: /* ORA $nnnn */ |
| ORA (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0x0E: /* ASL $nnnn */ |
| ASL (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x0F: /* SLO $nnnn */ |
| SLO (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x10: /* BPL $nnnn */ |
| BPL (); |
| break; |
| |
| case 0x11: /* ORA ($nn),Y */ |
| ORA (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0x13: /* SLO ($nn),Y */ |
| SLO (8, INDIR_Y, mem_write, addr); |
| break; |
| |
| case 0x14: /* NOP $nn,X */ |
| case 0x34: /* NOP */ |
| case 0x54: /* NOP $nn,X */ |
| case 0x74: /* NOP $nn,X */ |
| case 0xD4: /* NOP $nn,X */ |
| case 0xF4: /* NOP ($nn,X) */ |
| DOP (4); |
| break; |
| |
| case 0x15: /* ORA $nn,X */ |
| ORA (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0x16: /* ASL $nn,X */ |
| ASL (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x17: /* SLO $nn,X */ |
| SLO (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x18: /* CLC */ |
| CLC (); |
| break; |
| |
| case 0x19: /* ORA $nnnn,Y */ |
| ORA (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0x1A: /* NOP */ |
| case 0x3A: /* NOP */ |
| case 0x5A: /* NOP */ |
| case 0x7A: /* NOP */ |
| case 0xDA: /* NOP */ |
| case 0xFA: /* NOP */ |
| NOP (); |
| break; |
| |
| case 0x1B: /* SLO $nnnn,Y */ |
| SLO (7, ABS_IND_Y, mem_write, addr); |
| break; |
| |
| case 0x1C: /* NOP $nnnn,X */ |
| case 0x3C: /* NOP $nnnn,X */ |
| case 0x5C: /* NOP $nnnn,X */ |
| case 0x7C: /* NOP $nnnn,X */ |
| case 0xDC: /* NOP $nnnn,X */ |
| case 0xFC: /* NOP $nnnn,X */ |
| TOP (); |
| break; |
| |
| case 0x1D: /* ORA $nnnn,X */ |
| ORA (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0x1E: /* ASL $nnnn,X */ |
| ASL (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x1F: /* SLO $nnnn,X */ |
| SLO (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x20: /* JSR $nnnn */ |
| JSR (); |
| break; |
| |
| case 0x21: /* AND ($nn,X) */ |
| AND (6, INDIR_X_BYTE); |
| break; |
| |
| case 0x23: /* RLA ($nn,X) */ |
| RLA (8, INDIR_X, mem_write, addr); |
| break; |
| |
| case 0x24: /* BIT $nn */ |
| BIT (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0x25: /* AND $nn */ |
| AND (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0x26: /* ROL $nn */ |
| ROL (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x27: /* RLA $nn */ |
| RLA (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x28: /* PLP */ |
| PLP (); |
| break; |
| |
| case 0x29: /* AND #$nn */ |
| AND (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x2A: /* ROL A */ |
| ROL_A (); |
| break; |
| |
| case 0x2B: /* ANC #$nn */ |
| ANC (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x2C: /* BIT $nnnn */ |
| BIT (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0x2D: /* AND $nnnn */ |
| AND (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0x2E: /* ROL $nnnn */ |
| ROL (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x2F: /* RLA $nnnn */ |
| RLA (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x30: /* BMI $nnnn */ |
| BMI (); |
| break; |
| |
| case 0x31: /* AND ($nn),Y */ |
| AND (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0x33: /* RLA ($nn),Y */ |
| RLA (8, INDIR_Y, mem_write, addr); |
| break; |
| |
| case 0x35: /* AND $nn,X */ |
| AND (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0x36: /* ROL $nn,X */ |
| ROL (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x37: /* RLA $nn,X */ |
| RLA (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x38: /* SEC */ |
| SEC_6502 (); |
| break; |
| |
| case 0x39: /* AND $nnnn,Y */ |
| AND (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0x3B: /* RLA $nnnn,Y */ |
| RLA (7, ABS_IND_Y, mem_write, addr); |
| break; |
| |
| case 0x3D: /* AND $nnnn,X */ |
| AND (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0x3E: /* ROL $nnnn,X */ |
| ROL (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x3F: /* RLA $nnnn,X */ |
| RLA (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x40: /* RTI */ |
| RTI (); |
| break; |
| |
| case 0x41: /* EOR ($nn,X) */ |
| EOR (6, INDIR_X_BYTE); |
| break; |
| |
| case 0x43: /* SRE ($nn,X) */ |
| SRE (8, INDIR_X, mem_write, addr); |
| break; |
| |
| case 0x45: /* EOR $nn */ |
| EOR (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0x46: /* LSR $nn */ |
| LSR (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x47: /* SRE $nn */ |
| SRE (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x48: /* PHA */ |
| PHA (); |
| break; |
| |
| case 0x49: /* EOR #$nn */ |
| EOR (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x4A: /* LSR A */ |
| LSR_A (); |
| break; |
| |
| case 0x4B: /* ASR #$nn */ |
| ASR (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x4C: /* JMP $nnnn */ |
| JMP_ABSOLUTE (); |
| break; |
| |
| case 0x4D: /* EOR $nnnn */ |
| EOR (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0x4E: /* LSR $nnnn */ |
| LSR (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x4F: /* SRE $nnnn */ |
| SRE (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x50: /* BVC $nnnn */ |
| BVC (); |
| break; |
| |
| case 0x51: /* EOR ($nn),Y */ |
| EOR (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0x53: /* SRE ($nn),Y */ |
| SRE (8, INDIR_Y, mem_write, addr); |
| break; |
| |
| case 0x55: /* EOR $nn,X */ |
| EOR (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0x56: /* LSR $nn,X */ |
| LSR (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x57: /* SRE $nn,X */ |
| SRE (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x58: /* CLI */ |
| CLI (); |
| break; |
| |
| case 0x59: /* EOR $nnnn,Y */ |
| EOR (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0x5B: /* SRE $nnnn,Y */ |
| SRE (7, ABS_IND_Y, mem_write, addr); |
| break; |
| |
| case 0x5D: /* EOR $nnnn,X */ |
| EOR (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0x5E: /* LSR $nnnn,X */ |
| LSR (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x5F: /* SRE $nnnn,X */ |
| SRE (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x60: /* RTS */ |
| RTS (); |
| break; |
| |
| case 0x61: /* ADC ($nn,X) */ |
| ADC (6, INDIR_X_BYTE); |
| break; |
| |
| case 0x63: /* RRA ($nn,X) */ |
| RRA (8, INDIR_X, mem_write, addr); |
| break; |
| |
| case 0x65: /* ADC $nn */ |
| ADC (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0x66: /* ROR $nn */ |
| ROR (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x67: /* RRA $nn */ |
| RRA (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0x68: /* PLA */ |
| PLA (); |
| break; |
| |
| case 0x69: /* ADC #$nn */ |
| ADC (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x6A: /* ROR A */ |
| ROR_A (); |
| break; |
| |
| case 0x6B: /* ARR #$nn */ |
| ARR (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x6C: /* JMP ($nnnn) */ |
| JMP_INDIRECT (); |
| break; |
| |
| case 0x6D: /* ADC $nnnn */ |
| ADC (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0x6E: /* ROR $nnnn */ |
| ROR (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x6F: /* RRA $nnnn */ |
| RRA (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0x70: /* BVS $nnnn */ |
| BVS (); |
| break; |
| |
| case 0x71: /* ADC ($nn),Y */ |
| ADC (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0x73: /* RRA ($nn),Y */ |
| RRA (8, INDIR_Y, mem_write, addr); |
| break; |
| |
| case 0x75: /* ADC $nn,X */ |
| ADC (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0x76: /* ROR $nn,X */ |
| ROR (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x77: /* RRA $nn,X */ |
| RRA (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0x78: /* SEI */ |
| SEI (); |
| break; |
| |
| case 0x79: /* ADC $nnnn,Y */ |
| ADC (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0x7B: /* RRA $nnnn,Y */ |
| RRA (7, ABS_IND_Y, mem_write, addr); |
| break; |
| |
| case 0x7D: /* ADC $nnnn,X */ |
| ADC (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0x7E: /* ROR $nnnn,X */ |
| ROR (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x7F: /* RRA $nnnn,X */ |
| RRA (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0x80: /* NOP #$nn */ |
| case 0x82: /* NOP #$nn */ |
| case 0x89: /* NOP #$nn */ |
| case 0xC2: /* NOP #$nn */ |
| case 0xE2: /* NOP #$nn */ |
| DOP (2); |
| break; |
| |
| case 0x81: /* STA ($nn,X) */ |
| STA (6, INDIR_X_ADDR, mem_write, addr); |
| break; |
| |
| case 0x83: /* SAX ($nn,X) */ |
| SAX (6, INDIR_X_ADDR, mem_write, addr); |
| break; |
| |
| case 0x84: /* STY $nn */ |
| STY (3, ZERO_PAGE_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x85: /* STA $nn */ |
| STA (3, ZERO_PAGE_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x86: /* STX $nn */ |
| STX (3, ZERO_PAGE_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x87: /* SAX $nn */ |
| SAX (3, ZERO_PAGE_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x88: /* DEY */ |
| DEY (); |
| break; |
| |
| case 0x8A: /* TXA */ |
| TXA (); |
| break; |
| |
| case 0x8B: /* ANE #$nn */ |
| ANE (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0x8C: /* STY $nnnn */ |
| STY (4, ABSOLUTE_ADDR, mem_write, addr); |
| break; |
| |
| case 0x8D: /* STA $nnnn */ |
| STA (4, ABSOLUTE_ADDR, mem_write, addr); |
| break; |
| |
| case 0x8E: /* STX $nnnn */ |
| STX (4, ABSOLUTE_ADDR, mem_write, addr); |
| break; |
| |
| case 0x8F: /* SAX $nnnn */ |
| SAX (4, ABSOLUTE_ADDR, mem_write, addr); |
| break; |
| |
| case 0x90: /* BCC $nnnn */ |
| BCC (); |
| break; |
| |
| case 0x91: /* STA ($nn),Y */ |
| STA (6, INDIR_Y_ADDR, mem_write, addr); |
| break; |
| |
| case 0x93: /* SHA ($nn),Y */ |
| SHA (6, INDIR_Y_ADDR, mem_write, addr); |
| break; |
| |
| case 0x94: /* STY $nn,X */ |
| STY (4, ZP_IND_X_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x95: /* STA $nn,X */ |
| STA (4, ZP_IND_X_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x96: /* STX $nn,Y */ |
| STX (4, ZP_IND_Y_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x97: /* SAX $nn,Y */ |
| SAX (4, ZP_IND_Y_ADDR, ZP_WRITE, baddr); |
| break; |
| |
| case 0x98: /* TYA */ |
| TYA (); |
| break; |
| |
| case 0x99: /* STA $nnnn,Y */ |
| STA (5, ABS_IND_Y_ADDR, mem_write, addr); |
| break; |
| |
| case 0x9A: /* TXS */ |
| TXS (); |
| break; |
| |
| case 0x9B: /* SHS $nnnn,Y */ |
| SHS (5, ABS_IND_Y_ADDR, mem_write, addr); |
| break; |
| |
| case 0x9C: /* SHY $nnnn,X */ |
| SHY (5, ABS_IND_X_ADDR, mem_write, addr); |
| break; |
| |
| case 0x9D: /* STA $nnnn,X */ |
| STA (5, ABS_IND_X_ADDR, mem_write, addr); |
| break; |
| |
| case 0x9E: /* SHX $nnnn,Y */ |
| SHX (5, ABS_IND_Y_ADDR, mem_write, addr); |
| break; |
| |
| case 0x9F: /* SHA $nnnn,Y */ |
| SHA (5, ABS_IND_Y_ADDR, mem_write, addr); |
| break; |
| |
| case 0xA0: /* LDY #$nn */ |
| LDY (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xA1: /* LDA ($nn,X) */ |
| LDA (6, INDIR_X_BYTE); |
| break; |
| |
| case 0xA2: /* LDX #$nn */ |
| LDX (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xA3: /* LAX ($nn,X) */ |
| LAX (6, INDIR_X_BYTE); |
| break; |
| |
| case 0xA4: /* LDY $nn */ |
| LDY (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xA5: /* LDA $nn */ |
| LDA (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xA6: /* LDX $nn */ |
| LDX (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xA7: /* LAX $nn */ |
| LAX (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xA8: /* TAY */ |
| TAY (); |
| break; |
| |
| case 0xA9: /* LDA #$nn */ |
| LDA (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xAA: /* TAX */ |
| TAX (); |
| break; |
| |
| case 0xAB: /* LXA #$nn */ |
| LXA (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xAC: /* LDY $nnnn */ |
| LDY (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xAD: /* LDA $nnnn */ |
| LDA (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xAE: /* LDX $nnnn */ |
| LDX (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xAF: /* LAX $nnnn */ |
| LAX (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xB0: /* BCS $nnnn */ |
| BCS (); |
| break; |
| |
| case 0xB1: /* LDA ($nn),Y */ |
| LDA (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0xB3: /* LAX ($nn),Y */ |
| LAX (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0xB4: /* LDY $nn,X */ |
| LDY (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0xB5: /* LDA $nn,X */ |
| LDA (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0xB6: /* LDX $nn,Y */ |
| LDX (4, ZP_IND_Y_BYTE); |
| break; |
| |
| case 0xB7: /* LAX $nn,Y */ |
| LAX (4, ZP_IND_Y_BYTE); |
| break; |
| |
| case 0xB8: /* CLV */ |
| CLV (); |
| break; |
| |
| case 0xB9: /* LDA $nnnn,Y */ |
| LDA (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0xBA: /* TSX */ |
| TSX (); |
| break; |
| |
| case 0xBB: /* LAS $nnnn,Y */ |
| LAS (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0xBC: /* LDY $nnnn,X */ |
| LDY (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0xBD: /* LDA $nnnn,X */ |
| LDA (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0xBE: /* LDX $nnnn,Y */ |
| LDX (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0xBF: /* LAX $nnnn,Y */ |
| LAX (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0xC0: /* CPY #$nn */ |
| CPY (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xC1: /* CMP ($nn,X) */ |
| CMP (6, INDIR_X_BYTE); |
| break; |
| |
| case 0xC3: /* DCP ($nn,X) */ |
| DCP (8, INDIR_X, mem_write, addr); |
| break; |
| |
| case 0xC4: /* CPY $nn */ |
| CPY (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xC5: /* CMP $nn */ |
| CMP (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xC6: /* DEC $nn */ |
| DEC (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0xC7: /* DCP $nn */ |
| DCP (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0xC8: /* INY */ |
| INY (); |
| break; |
| |
| case 0xC9: /* CMP #$nn */ |
| CMP (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xCA: /* DEX */ |
| DEX (); |
| break; |
| |
| case 0xCB: /* SBX #$nn */ |
| SBX (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xCC: /* CPY $nnnn */ |
| CPY (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xCD: /* CMP $nnnn */ |
| CMP (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xCE: /* DEC $nnnn */ |
| DEC (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0xCF: /* DCP $nnnn */ |
| DCP (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0xD0: /* BNE $nnnn */ |
| BNE (); |
| break; |
| |
| case 0xD1: /* CMP ($nn),Y */ |
| CMP (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0xD3: /* DCP ($nn),Y */ |
| DCP (8, INDIR_Y, mem_write, addr); |
| break; |
| |
| case 0xD5: /* CMP $nn,X */ |
| CMP (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0xD6: /* DEC $nn,X */ |
| DEC (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0xD7: /* DCP $nn,X */ |
| DCP (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0xD8: /* CLD */ |
| CLD (); |
| break; |
| |
| case 0xD9: /* CMP $nnnn,Y */ |
| CMP (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0xDB: /* DCP $nnnn,Y */ |
| DCP (7, ABS_IND_Y, mem_write, addr); |
| break; |
| |
| case 0xDD: /* CMP $nnnn,X */ |
| CMP (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0xDE: /* DEC $nnnn,X */ |
| DEC (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0xDF: /* DCP $nnnn,X */ |
| DCP (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0xE0: /* CPX #$nn */ |
| CPX (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xE1: /* SBC ($nn,X) */ |
| SBC (6, INDIR_X_BYTE); |
| break; |
| |
| case 0xE3: /* ISB ($nn,X) */ |
| ISB (8, INDIR_X, mem_write, addr); |
| break; |
| |
| case 0xE4: /* CPX $nn */ |
| CPX (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xE5: /* SBC $nn */ |
| SBC (3, ZERO_PAGE_BYTE); |
| break; |
| |
| case 0xE6: /* INC $nn */ |
| INC (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0xE7: /* ISB $nn */ |
| ISB (5, ZERO_PAGE, ZP_WRITE, baddr); |
| break; |
| |
| case 0xE8: /* INX */ |
| INX (); |
| break; |
| |
| case 0xE9: /* SBC #$nn */ |
| case 0xEB: /* USBC #$nn */ |
| SBC (2, IMMEDIATE_BYTE); |
| break; |
| |
| case 0xEA: /* NOP */ |
| NOP (); |
| break; |
| |
| case 0xEC: /* CPX $nnnn */ |
| CPX (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xED: /* SBC $nnnn */ |
| SBC (4, ABSOLUTE_BYTE); |
| break; |
| |
| case 0xEE: /* INC $nnnn */ |
| INC (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0xEF: /* ISB $nnnn */ |
| ISB (6, ABSOLUTE, mem_write, addr); |
| break; |
| |
| case 0xF0: /* BEQ $nnnn */ |
| BEQ (); |
| break; |
| |
| case 0xF1: /* SBC ($nn),Y */ |
| SBC (5, INDIR_Y_BYTE); |
| break; |
| |
| case 0xF3: /* ISB ($nn),Y */ |
| ISB (8, INDIR_Y, mem_write, addr); |
| break; |
| |
| case 0xF5: /* SBC $nn,X */ |
| SBC (4, ZP_IND_X_BYTE); |
| break; |
| |
| case 0xF6: /* INC $nn,X */ |
| INC (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0xF7: /* ISB $nn,X */ |
| ISB (6, ZP_IND_X, ZP_WRITE, baddr); |
| break; |
| |
| case 0xF8: /* SED */ |
| SED (); |
| break; |
| |
| case 0xF9: /* SBC $nnnn,Y */ |
| SBC (4, ABS_IND_Y_BYTE); |
| break; |
| |
| case 0xFB: /* ISB $nnnn,Y */ |
| ISB (7, ABS_IND_Y, mem_write, addr); |
| break; |
| |
| case 0xFD: /* SBC $nnnn,X */ |
| SBC (4, ABS_IND_X_BYTE); |
| break; |
| |
| case 0xFE: /* INC $nnnn,X */ |
| INC (7, ABS_IND_X, mem_write, addr); |
| break; |
| |
| case 0xFF: /* ISB $nnnn,X */ |
| ISB (7, ABS_IND_X, mem_write, addr); |
| break; |
| } |
| |
| /* Calculate remaining/elapsed clock cycles */ |
| remaining_cycles -= instruction_cycles; |
| total_cycles += instruction_cycles; |
| } |
| |
| _execute_done: |
| |
| /* restore local copy of regs */ |
| SET_LOCAL_REGS (); |
| |
| /* Return our actual amount of executed cycles */ |
| return (total_cycles - old_cycles); |
| } |
| |
| /* Initialize tables, etc. */ |
| void |
| nes6502_init (void) |
| { |
| int index; |
| |
| /* Build the N / Z flag lookup table */ |
| flag_table[0] = Z_FLAG; |
| |
| for (index = 1; index < 256; index++) |
| flag_table[index] = (index & 0x80) ? N_FLAG : 0; |
| |
| reg_A = reg_X = reg_Y = 0; |
| reg_S = 0xFF; /* Stack grows down */ |
| } |
| |
| |
| /* Issue a CPU Reset */ |
| void |
| nes6502_reset (void) |
| { |
| reg_P = Z_FLAG | R_FLAG | I_FLAG; /* Reserved bit always 1 */ |
| int_pending = dma_cycles = 0; /* No pending interrupts */ |
| reg_PC = bank_readaddress (RESET_VECTOR); /* Fetch reset vector */ |
| /* TODO: 6 cycles for RESET? */ |
| } |
| |
| /* Non-maskable interrupt */ |
| void |
| nes6502_nmi (void) |
| { |
| int_pending |= NMI_MASK; |
| } |
| |
| /* Interrupt request */ |
| void |
| nes6502_irq (void) |
| { |
| int_pending |= IRQ_MASK; |
| } |
| |
| /* Set dma period (in cycles) */ |
| void |
| nes6502_setdma (int cycles) |
| { |
| dma_cycles += cycles; |
| } |
| |
| #ifdef NES6502_MEM_ACCESS_CTRL |
| void |
| nes6502_chk_mem_access (uint8 * access, int flags) |
| { |
| chk_mem_access (access, flags); |
| } |
| #endif |
| |
| /* |
| ** $Log$ |
| ** Revision 1.3 2008/03/25 15:56:11 slomo |
| ** Patch by: Andreas Henriksson <andreas at fatal dot set> |
| ** * gst/nsf/Makefile.am: |
| ** * gst/nsf/dis6502.h: |
| ** * gst/nsf/fds_snd.c: |
| ** * gst/nsf/fds_snd.h: |
| ** * gst/nsf/fmopl.c: |
| ** * gst/nsf/fmopl.h: |
| ** * gst/nsf/gstnsf.c: |
| ** * gst/nsf/log.c: |
| ** * gst/nsf/log.h: |
| ** * gst/nsf/memguard.c: |
| ** * gst/nsf/memguard.h: |
| ** * gst/nsf/mmc5_snd.c: |
| ** * gst/nsf/mmc5_snd.h: |
| ** * gst/nsf/nes6502.c: |
| ** * gst/nsf/nes6502.h: |
| ** * gst/nsf/nes_apu.c: |
| ** * gst/nsf/nes_apu.h: |
| ** * gst/nsf/nsf.c: |
| ** * gst/nsf/nsf.h: |
| ** * gst/nsf/osd.h: |
| ** * gst/nsf/types.h: |
| ** * gst/nsf/vrc7_snd.c: |
| ** * gst/nsf/vrc7_snd.h: |
| ** * gst/nsf/vrcvisnd.c: |
| ** * gst/nsf/vrcvisnd.h: |
| ** Update our internal nosefart to nosefart-2.7-mls to fix segfaults |
| ** on some files. Fixes bug #498237. |
| ** Remove some // comments, fix some compiler warnings and use pow() |
| ** instead of a slow, selfmade implementation. |
| ** |
| ** Revision 1.2 2003/05/01 22:34:19 benjihan |
| ** New NSF plugin |
| ** |
| ** Revision 1.1 2003/04/08 20:53:00 ben |
| ** Adding more files... |
| ** |
| ** Revision 1.6 2000/07/04 04:50:07 matt |
| ** minor change to includes |
| ** |
| ** Revision 1.5 2000/07/03 02:18:16 matt |
| ** added a few notes about potential failure cases |
| ** |
| ** Revision 1.4 2000/06/09 15:12:25 matt |
| ** initial revision |
| ** |
| */ |