📄 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.6 2003/03/24 14:50:51 Rick Exp $
*/
/*
** changes for nester:
** changes are marked with DCR
**
** removed dis6502 #include
** nullified ASSERT
** nullified INLINE
**
** - Darren Ranalli
*/
#if defined(__GNUC__) // gcc 3.0.3 arm-wince-pe?
typedef long unsigned int size_t;
typedef int ssize_t;
typedef unsigned long time_t;
typedef unsigned long clock_t;
typedef short wchar_t;
#define NULL ((void*)0)
#endif
#include <stdlib.h>
#include <stdio.h>
#include "types.h"
#include "nes6502.h"
//#include "dis6502.h" //DCR
// from nesterJppc
#define ram ram_l
#define stack stack_l
//DCR
#define ASSERT(CONDITION)
//DCR
#define INLINE __inline // for WindowsCE
//#define NES6502_DISASM
#if defined(__GNUC__) && !defined(NES6502_DISASM)
#define NES6502_JUMPTABLE
#endif /* __GNUC__ */
//cpu.total_cycles += (x);
#define ADD_CYCLES(x) \
{ \
remaining_cycles -= (x); \
}
// Rick
static uint32 * current_PC = NULL;
static uint8 ** current_last_bank_ptr = NULL;
#define PC_TO_PTR() \
{ \
uint32 t = PC >> NES6502_BANKSHIFT; \
/* last_bank_ptr = cpu.mem_page[t] - (t << NES6502_BANKSHIFT); */\
last_bank_ptr = mem_page[t] - (t << NES6502_BANKSHIFT); \
PC += (uint32)last_bank_ptr; \
}
#define PTR_TO_PC() \
PC -= (uint32)last_bank_ptr;
/*
** Check to see if an index reg addition overflowed to next page
*/
#define PAGE_CROSS_CHECK(addr, reg) \
{ \
if ((reg) > (uint8) (addr)) \
ADD_CYCLES(1); \
}
#define EMPTY_READ(value) /* empty */
/*
** Addressing mode macros
*/
/* Immediate */
#if 0
#define IMMEDIATE_BYTE(value) \
{ \
/* value = bank_readbyte(PC++); */ \
value = bank_readbyte(PC); \
PC++; \
}
/* Absolute */
#define ABSOLUTE_ADDR(address) \
{ \
address = bank_readword(PC); \
PC += 2; \
}
#else
#define IMMEDIATE_BYTE(value) \
value = *(uint8 *)PC++;
#define ABSOLUTE_ADDR(address) \
{ \
address = *(uint8 *)PC | ((uint16)(*((uint8 *)PC + 1)) << 8); \
PC += 2; \
}
#endif
#define ABSOLUTE(address, value) \
{ \
ABSOLUTE_ADDR(address); \
value = mem_readbyte(address); \
}
#define ABSOLUTE_BYTE(value) \
{ \
ABSOLUTE(temp, value); \
}
/* Absolute indexed X */
#define ABS_IND_X_ADDR(address) \
{ \
ABSOLUTE_ADDR(address); \
address = (address + X) & 0xFFFF; \
PAGE_CROSS_CHECK(address, X); \
}
#define ABS_IND_X(address, value) \
{ \
ABS_IND_X_ADDR(address); \
value = mem_readbyte(address); \
}
#define ABS_IND_X_BYTE(value) \
{ \
ABS_IND_X(temp, value); \
}
/* Absolute indexed Y */
#define ABS_IND_Y_ADDR(address) \
{ \
ABSOLUTE_ADDR(address); \
address = (address + Y) & 0xFFFF; \
PAGE_CROSS_CHECK(address, Y); \
}
#define ABS_IND_Y(address, value) \
{ \
ABS_IND_Y_ADDR(address); \
value = mem_readbyte(address); \
}
#define ABS_IND_Y_BYTE(value) \
{ \
ABS_IND_Y(temp, value); \
}
/* Zero-page */
#define ZERO_PAGE_ADDR(address) \
{ \
IMMEDIATE_BYTE(address); \
}
#define ZERO_PAGE(address, value) \
{ \
ZERO_PAGE_ADDR(address); \
value = ZP_READBYTE(address); \
}
#define ZERO_PAGE_BYTE(value) \
{ \
ZERO_PAGE(btemp, value); \
}
/* Zero-page indexed X */
#define ZP_IND_X_ADDR(address) \
{ \
ZERO_PAGE_ADDR(address); \
address += X; \
}
#define ZP_IND_X(address, value) \
{ \
ZP_IND_X_ADDR(address); \
value = ZP_READBYTE(address); \
}
#define ZP_IND_X_BYTE(value) \
{ \
ZP_IND_X(btemp, value); \
}
/* Zero-page indexed Y */
/* Not really an adressing mode, just for LDx/STx */
#define ZP_IND_Y_ADDR(address) \
{ \
ZERO_PAGE_ADDR(address); \
address += Y; \
}
#define ZP_IND_Y_BYTE(value) \
{ \
ZP_IND_Y_ADDR(btemp); \
value = ZP_READBYTE(btemp); \
}
/* Indexed indirect */
#define INDIR_X_ADDR(address) \
{ \
IMMEDIATE_BYTE(btemp); \
btemp += X; \
address = zp_readword(btemp); \
}
#define INDIR_X(address, value) \
{ \
INDIR_X_ADDR(address); \
value = mem_readbyte(address); \
}
#define INDIR_X_BYTE(value) \
{ \
INDIR_X(temp, value); \
}
/* Indirect indexed */
#define INDIR_Y_ADDR(address) \
{ \
IMMEDIATE_BYTE(btemp); \
address = (zp_readword(btemp) + Y) & 0xFFFF; \
PAGE_CROSS_CHECK(address, Y); \
}
#define INDIR_Y(address, value) \
{ \
INDIR_Y_ADDR(address); \
value = mem_readbyte(address); \
}
#define INDIR_Y_BYTE(value) \
{ \
INDIR_Y(temp, value); \
}
/* Stack push/pull */
#define PUSH(value) stack[S--] = (uint8) (value)
#define PULL() stack[++S]
/*
** flag register helper macros
*/
/* Theory: Z and N flags are set in just about every
** instruction, so we will just store the value in those
** flag variables, and mask out the irrelevant data when
** we need to check them (branches, etc). This makes the
** zero flag only really be 'set' when z_flag == 0.
** The rest of the flags are stored as true booleans.
*/
/* Scatter flags to separate variables */
#define SCATTER_FLAGS(value) \
{ \
n_flag = (value) & N_FLAG; \
v_flag = (value) & V_FLAG; \
b_flag = (value) & B_FLAG; \
/* d_flag = (value) & D_FLAG; */ \
i_flag = (value) & I_FLAG; \
z_flag = !((value) & Z_FLAG); \
c_flag = (value) & C_FLAG; \
}
/* Combine flags into flag register */
/*
#define COMBINE_FLAGS() \
( \
(n_flag & N_FLAG) | \
(v_flag ? V_FLAG : 0) | \
R_FLAG | \
(b_flag ? B_FLAG : 0) | \
(d_flag ? D_FLAG : 0) | \
(i_flag ? I_FLAG : 0) | \
(z_flag ? 0 : Z_FLAG) | \
(c_flag ? C_FLAG : 0) \
)
*/
// from nesterJppc
#define COMBINE_FLAGS() \
( \
(n_flag & N_FLAG) | \
R_FLAG | i_flag | b_flag | c_flag | \
(v_flag ? V_FLAG : 0) | \
(z_flag ? 0 : Z_FLAG) \
)
/* Set N and Z flags based on given value */
#define SET_NZ_FLAGS(value) n_flag = z_flag = (value);
/* For BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS */
#define RELATIVE_BRANCH(condition) \
{ \
if (condition) \
{ \
IMMEDIATE_BYTE(btemp); \
PTR_TO_PC(); \
if (((int8) btemp + (PC & 0x00FF)) & 0x100) \
ADD_CYCLES(1); \
ADD_CYCLES(3); \
PC += ((int8) btemp); \
PC_TO_PTR(); \
} \
else \
{ \
PC++; \
ADD_CYCLES(2); \
} \
}
#define JUMP(address) \
{ \
PC = bank_readword((address)); \
PC_TO_PTR(); \
}
/*
** Interrupt macros
*/
#define NMI_PROC() \
{ \
PTR_TO_PC(); \
PUSH(PC >> 8); \
PUSH(PC & 0xFF); \
b_flag = 0; \
PUSH(COMBINE_FLAGS()); \
i_flag = I_FLAG; /* i_flag = 1; */\
JUMP(NMI_VECTOR); \
}
#define IRQ_PROC() \
{ \
PTR_TO_PC(); \
PUSH(PC >> 8); \
PUSH(PC & 0xFF); \
b_flag = 0; \
PUSH(COMBINE_FLAGS()); \
i_flag = I_FLAG; /* i_flag = 1; */\
JUMP(IRQ_VECTOR); \
}
#define NMI() \
{ \
NMI_PROC(); \
ADD_CYCLES(INT_CYCLES); \
}
#define IRQ() \
{ \
IRQ_PROC(); \
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 (d_flag) \
{ \
temp = (A & 0x0F) + (data & 0x0F) + (c_flag ? 1 : 0); \
if (temp >= 10) \
temp = (temp - 10) | 0x10; \
temp += (A & 0xF0) + (data & 0xF0); \
z_flag = (A + data + (c_flag ? 1 : 0)) & 0xFF; \
n_flag = temp; \
v_flag = ((~(A ^ data)) & (A ^ temp) & 0x80); \
if (temp > 0x9F) \
temp += 0x60; \
c_flag = (temp > 0xFF); \
A = (uint8) temp; \
} \
else \
{ \
temp = A + data + (c_flag ? 1 : 0); \
c_flag = (temp > 0xFF); \
v_flag = ((~(A ^ data)) & (A ^ temp) & 0x80); \
A = (uint8) temp; \
SET_NZ_FLAGS(A); \
}\
ADD_CYCLES(cycles); \
}
#else
#define ADC(cycles, read_func) \
{ \
read_func(data); \
temp = A + data + c_flag; /* temp = A + data + (c_flag ? 1 : 0); */\
c_flag = (temp > 0xFF); \
v_flag = ((~(A ^ data)) & (A ^ temp) & 0x80); \
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); \
c_flag = n_flag >> 7; /* c_flag = (n_flag & N_FLAG) ? 1 : 0;*/ \
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 (d_flag) \
{ \
temp = (data >> 1) | (c_flag ? 0x80 : 0); \
SET_NZ_FLAGS(temp); \
v_flag = (temp ^ data) & 0x40; \
if (((data & 0x0F) + (data & 0x01)) > 5) \
temp = (temp & 0xF0) | ((temp + 0x6) & 0x0F); \
if (((data & 0xF0) + (data & 0x10)) > 0x50) \
{ \
temp = (temp & 0x0F) | ((temp + 0x60) & 0xF0); \
c_flag = 1; \
} \
else \
c_flag = 0; \
A = (uint8) temp; \
} \
else \
{ \
A = (data >> 1) | (c_flag ? 0x80 : 0); \
SET_NZ_FLAGS(A); \
c_flag = A & 0x40; \
v_flag = ((A >> 6) ^ (A >> 5)) & 1; \
}\
ADD_CYCLES(cycles); \
}
#else
#define ARR(cycles, read_func) \
{ \
read_func(data); \
data &= A; \
A = (data >> 1) | (c_flag << 7); \
SET_NZ_FLAGS(A); \
c_flag = (A >> 6) & C_FLAG; \
v_flag = ((A >> 6) ^ (A >> 5)) & 1; \
ADD_CYCLES(cycles); \
}
#endif /* NES6502_DECIMAL */
#define ASL(cycles, read_func, write_func, addr) \
{ \
read_func(addr, data); \
c_flag = data >> 7; \
data <<= 1; \
write_func(addr, data); \
SET_NZ_FLAGS(data); \
ADD_CYCLES(cycles); \
}
#define ASL_A() \
{ \
c_flag = A >> 7; \
/* A <<= 1; */\
A = (uint8)(A << 1); \
SET_NZ_FLAGS(A); \
ADD_CYCLES(2); \
}
/* undocumented */
#define ASR(cycles, read_func) \
{ \
read_func(data); \
data &= A; \
c_flag = data & 0x01; \
A = data >> 1; \
SET_NZ_FLAGS(A); \
ADD_CYCLES(cycles); \
}
#define BCC() \
{ \
RELATIVE_BRANCH(0 == c_flag); \
}
#define BCS() \
{ \
RELATIVE_BRANCH(0 != c_flag); \
}
#define BEQ() \
{ \
RELATIVE_BRANCH(0 == z_flag); \
}
#define BIT(cycles, read_func) \
{ \
read_func(data); \
z_flag = data & A; \
/* move bit 7/6 of data into N/V flags */ \
n_flag = data; \
v_flag = data & V_FLAG; \
ADD_CYCLES(cycles); \
}
#define BMI() \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -