Files
AppleWin/source/CPU/cpu_general.inl
TomCh 839c493b67 Support IIe aux slot: 80-col(1KiB) card via generic read ptrs (#1341, PR #1391)
Better support the IIe's aux slot when it's empty or has the 80-col(1KiB) card.
- any h/w using this alternate CPU emulation, (ie. aux=empty or 80-col(1KiB)), then the 'mem' cache is completely bypassed.
Debugger: change all the direct 'mem' accesses to go via ReadByteFromMemory().
TestCPU6502: test the alt CPU emulation code too.
2025-04-03 21:10:02 +01:00

308 lines
11 KiB
C++

/*
AppleWin : An Apple //e emulator for Windows
Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2010, Tom Charlesworth, Michael Pohoreski
AppleWin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
AppleWin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with AppleWin; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Description: 6502/65C02 emulation
*
* Author: Various
*/
/****************************************************************************
*
* GENERAL PURPOSE MACROS
*
***/
#undef AF_TO_EF
#undef EF_TO_AF
#define AF_TO_EF flagc = (regs.ps & AF_CARRY); \
flagn = (regs.ps & AF_SIGN); \
flagv = (regs.ps & AF_OVERFLOW); \
flagz = (regs.ps & AF_ZERO);
#define EF_TO_AF regs.ps = (regs.ps & ~(AF_CARRY | AF_SIGN | \
AF_OVERFLOW | AF_ZERO)) \
| flagc \
| flagn \
| (flagv ? AF_OVERFLOW : 0) \
| (flagz ? AF_ZERO : 0) \
| AF_RESERVED | AF_BREAK;
// CYC(a): This can be optimised, as only certain opcodes will affect uExtraCycles
#define CYC(a) uExecutedCycles += (a)+uExtraCycles;
#define _POP (*(mem+((regs.sp >= _6502_STACK_END) ? (regs.sp = _6502_STACK_BEGIN) : ++regs.sp)))
#define _POP_ALT ( /*TODO: Support reads from IO & Floating bus*/\
*(memshadow[_6502_STACK_PAGE]-_6502_STACK_BEGIN+((regs.sp >= _6502_STACK_END) ? (regs.sp = _6502_STACK_BEGIN) : ++regs.sp)) \
)
#define _PUSH(a) *(mem+regs.sp--) = (a); \
if (regs.sp < _6502_STACK_BEGIN) \
regs.sp = _6502_STACK_END;
#define _PUSH_ALT(a) { \
LPBYTE page = memwrite[_6502_STACK_PAGE]; \
if (page) { \
*(page+(regs.sp & 0xFF)) = (BYTE)(a); \
} \
regs.sp--; \
if (regs.sp < _6502_STACK_BEGIN) \
regs.sp = _6502_STACK_END; \
}
#define _READ(addr) ( \
((addr & 0xF000) == APPLE_IO_BEGIN) \
? IORead[(addr>>4) & 0xFF](regs.pc,addr,0,0,uExecutedCycles) \
: *(mem+addr) \
)
#define _READ_ALT(addr) ( \
(memreadPageType[addr >> 8] == MEM_Normal) \
? *(memshadow[addr >> 8]+(addr&0xff)) \
: (memreadPageType[addr >> 8] == MEM_IORead) \
? IORead[(addr >> 4) & 0xFF](regs.pc, addr, 0, 0, uExecutedCycles) \
: MemReadFloatingBus(uExecutedCycles) \
)
#define _READ_WITH_IO_F8xx(addr) ( /* GH#827 */\
((addr & 0xF000) == APPLE_IO_BEGIN) \
? IORead[(addr>>4) & 0xFF](regs.pc,addr,0,0,uExecutedCycles) \
: (addr >= 0xF800) \
? IO_F8xx(regs.pc,addr,0,0,uExecutedCycles) \
: *(mem+addr) \
)
#define SETNZ(a) { \
flagn = ((a) & 0x80); \
flagz = !((a) & 0xFF); \
}
#define SETZ(a) flagz = !((a) & 0xFF);
#define _WRITE(a) { \
{ \
memdirty[addr >> 8] = 0xFF; \
LPBYTE page = memwrite[addr >> 8]; \
if (page) \
*(page+(addr & 0xFF)) = (BYTE)(a); \
else if ((addr & 0xF000) == APPLE_IO_BEGIN) \
IOWrite[(addr>>4) & 0xFF](regs.pc,addr,1,(BYTE)(a),uExecutedCycles);\
} \
}
#define _WRITE_ALT(a) { \
{ \
memdirty[addr >> 8] = 0xFF; \
LPBYTE page = memwrite[addr >> 8]; \
if (page) { \
*(page+(addr & 0xFF)) = (BYTE)(a); \
if (memVidHD) /* GH#997 */\
*(memVidHD + addr) = (BYTE)(a); \
} \
else if ((addr & 0xF000) == APPLE_IO_BEGIN) \
IOWrite[(addr>>4) & 0xFF](regs.pc,addr,1,(BYTE)(a),uExecutedCycles);\
} \
}
#define _WRITE_WITH_IO_F8xx(a) { /* GH#827 */\
if (addr >= 0xF800) \
IO_F8xx(regs.pc,addr,1,(BYTE)(a),uExecutedCycles); \
else { \
memdirty[addr >> 8] = 0xFF; \
LPBYTE page = memwrite[addr >> 8]; \
if (page) { \
*(page+(addr & 0xFF)) = (BYTE)(a); \
if (memVidHD) /* GH#997 */\
*(memVidHD + addr) = (BYTE)(a); \
} \
else if ((addr & 0xF000) == APPLE_IO_BEGIN) \
IOWrite[(addr>>4) & 0xFF](regs.pc,addr,1,(BYTE)(a),uExecutedCycles);\
} \
}
#define ON_PAGECROSS_REPLACE_HI_ADDR if ((base ^ addr) >> 8) {addr = (val<<8) | (addr&0xff);} /* GH#282 */
//
// ExtraCycles:
// +1 if branch taken
// +1 if page boundary crossed
#define BRANCH_TAKEN { \
base = regs.pc; \
regs.pc += addr; \
if ((base ^ regs.pc) & 0xFF00) \
uExtraCycles=2; \
else \
uExtraCycles=1; \
}
//
// TODO Optimization Note: uExtraCycles = ((base ^ addr) >> 8) & 1;
#define CHECK_PAGE_CHANGE if ((base ^ addr) & 0xFF00) \
uExtraCycles=1;
#define READ_BYTE_ALT(pc) _READ_ALT(pc)
#define READ_WORD_ALT(pc) (_READ_ALT(pc) | (_READ_ALT((pc+1))<<8))
/****************************************************************************
*
* ADDRESSING MODE MACROS
*
***/
#define _ABS addr = *(LPWORD)(mem+regs.pc); regs.pc += 2;
#define _ABS_ALT \
addr = READ_WORD_ALT(regs.pc); \
regs.pc += 2;
#define _IABSX addr = *(LPWORD)(mem+(*(LPWORD)(mem+regs.pc))+(WORD)regs.x); regs.pc += 2;
#define _IABSX_ALT \
base = READ_WORD_ALT(regs.pc) + (WORD)regs.x; \
addr = READ_WORD_ALT(base); \
regs.pc += 2;
// Not optimised for page-cross
#define _ABSX_CONST base = *(LPWORD)(mem+regs.pc); addr = base+(WORD)regs.x; regs.pc += 2;
#define _ABSX_CONST_ALT \
base = READ_WORD_ALT(regs.pc); \
addr = base + (WORD)regs.x; \
regs.pc += 2;
// Optimised for page-cross
#define _ABSX_OPT _ABSX_CONST; CHECK_PAGE_CHANGE;
#define _ABSX_OPT_ALT _ABSX_CONST_ALT; CHECK_PAGE_CHANGE;
// Not optimised for page-cross
#define _ABSY_CONST base = *(LPWORD)(mem+regs.pc); addr = base+(WORD)regs.y; regs.pc += 2;
#define _ABSY_CONST_ALT \
base = READ_WORD_ALT(regs.pc); \
addr = base + (WORD)regs.y; \
regs.pc += 2;
// Optimised for page-cross
#define _ABSY_OPT _ABSY_CONST; CHECK_PAGE_CHANGE;
#define _ABSY_OPT_ALT _ABSY_CONST_ALT; CHECK_PAGE_CHANGE;
// TODO Optimization Note (just for IABSCMOS): uExtraCycles = ((base & 0xFF) + 1) >> 8;
#define _IABS_CMOS base = *(LPWORD)(mem+regs.pc); \
addr = *(LPWORD)(mem+base); \
if ((base & 0xFF) == 0xFF) uExtraCycles=1; \
regs.pc += 2;
#define _IABS_CMOS_ALT \
base = READ_WORD_ALT(regs.pc); \
addr = READ_WORD_ALT(base); \
if ((base & 0xFF) == 0xFF) uExtraCycles=1; \
regs.pc += 2;
#define _IABS_NMOS base = *(LPWORD)(mem+regs.pc); \
if ((base & 0xFF) == 0xFF) \
addr = *(mem+base)+((WORD)*(mem+(base&0xFF00))<<8); \
else \
addr = *(LPWORD)(mem+base); \
regs.pc += 2;
#define _IABS_NMOS_ALT \
base = READ_WORD_ALT(regs.pc); \
if ((base & 0xFF) == 0xFF) \
addr = READ_BYTE_ALT(base) | (READ_BYTE_ALT((base&0xFF00))<<8); /* NB. Requires double-parenthesis for 2nd macro */\
else \
addr = READ_WORD_ALT(base); \
regs.pc += 2;
#define IMM addr = regs.pc++;
#define _INDX base = ((*(mem+regs.pc++))+regs.x) & 0xFF; \
if (base == 0xFF) \
addr = *(mem+0xFF)+(((WORD)*mem)<<8); \
else \
addr = *(LPWORD)(mem+base);
#define _INDX_ALT \
base = (READ_BYTE_ALT(regs.pc)+regs.x) & 0xFF; regs.pc++; \
if (base == 0xFF) \
addr = READ_BYTE_ALT(0xFF) | (READ_BYTE_ALT(0x00)<<8); \
else \
addr = READ_WORD_ALT(base);
// Not optimised for page-cross
#define _INDY_CONST if (*(mem+regs.pc) == 0xFF) /*no extra cycle for page-crossing*/ \
base = *(mem+0xFF)+(((WORD)*mem)<<8); \
else \
base = *(LPWORD)(mem+*(mem+regs.pc)); \
regs.pc++; \
addr = base+(WORD)regs.y;
#define _INDY_CONST_ALT \
base = READ_BYTE_ALT(regs.pc); \
if (base == 0xFF) \
base = READ_BYTE_ALT(0xFF) | (READ_BYTE_ALT(0x00)<<8); \
else \
base = READ_WORD_ALT(base); \
regs.pc++; \
addr = base+(WORD)regs.y;
// Optimised for page-cross
#define _INDY_OPT _INDY_CONST; CHECK_PAGE_CHANGE;
#define _INDY_OPT_ALT _INDY_CONST_ALT; CHECK_PAGE_CHANGE;
#define _IZPG base = *(mem+regs.pc++); \
if (base == 0xFF) \
addr = *(mem+0xFF)+(((WORD)*mem)<<8); \
else \
addr = *(LPWORD)(mem+base);
#define _IZPG_ALT \
base = READ_BYTE_ALT(regs.pc); regs.pc++; \
if (base == 0xFF) \
addr = READ_BYTE_ALT(0xFF) | (READ_BYTE_ALT(0x00)<<8); \
else \
addr = READ_WORD_ALT(base);
#define _REL addr = (signed char)*(mem+regs.pc++);
#define _REL_ALT addr = (signed char)READ_BYTE_ALT(regs.pc); regs.pc++;
// TODO Optimization Note:
// . Opcodes that generate zero-page addresses can't be accessing $C000..$CFFF
// so they could be paired with special READZP/WRITEZP macros (instead of READ/WRITE)
#define _ZPG addr = *(mem+regs.pc++);
#define _ZPGX addr = ((*(mem+regs.pc++))+regs.x) & 0xFF;
#define _ZPGY addr = ((*(mem+regs.pc++))+regs.y) & 0xFF;
#define _ZPG_ALT addr = READ_BYTE_ALT(regs.pc); regs.pc++;
#define _ZPGX_ALT addr = (READ_BYTE_ALT(regs.pc) + regs.x) & 0xFF; regs.pc++;
#define _ZPGY_ALT addr = (READ_BYTE_ALT(regs.pc) + regs.y) & 0xFF; regs.pc++;
// Tidy 3 char opcodes & addressing modes to keep the opcode table visually aligned, clean, and readable.
#undef asl
#undef lsr
#undef rol
#undef ror
#define asl ASLA
#define lsr LSRA
#define rol ROLA
#define ror RORA
#undef idx
#undef imm
#undef izp
#undef rel
#undef zpx
#undef zpy
#define idx INDX
#define imm IMM
#define izp IZPG
#define rel REL
#define zpx ZPGX
#define zpy ZPGY