📄 nes6502.c
字号:
/*** 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: nes6502.c,v 1.2 2003/01/09 19:50:03 jkeil Exp $*/#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); \}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -