mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
a4d0dacec2
Original commit message from CVS: 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.
2609 lines
57 KiB
C
2609 lines
57 KiB
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$
|
|
*/
|
|
|
|
|
|
#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() \
|
|
{ \
|
|
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 ();
|
|
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
|
|
**
|
|
*/
|