From f25aaf2bb33f131a0259da9e0216326d1212d661 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Nov 2023 15:32:02 -0500 Subject: [PATCH 1/8] Adjust 65c02 STA abs,x behaviour. --- .../6502/Implementation/6502Implementation.hpp | 15 +++++++++++++++ Processors/6502/Implementation/6502Storage.cpp | 4 +++- Processors/6502/Implementation/6502Storage.hpp | 5 +++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index b55b3596f..4f48669d5 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -544,6 +544,21 @@ template void Proces break; } continue; + case CycleAddXToAddressLowReadSTA: + next_address_.full = address_.full + x_; + address_.halves.low = next_address_.halves.low; + + // Cf. https://groups.google.com/g/comp.sys.apple2/c/RuTGaRxu5Iw/m/uyFLEsF8ceIJ + // + // STA abs,X has been fixed for the PX (page-crossing) case by adding a dummy read of the + // program counter, so the change was rW -> W. In the non-PX case it still reads the destination + // address, so there is no change: RW -> RW. + if(!is_65c02(personality) || next_address_.full == address_.full) { + throwaway_read(address_.full); + } else { + throwaway_read(pc_.full - 1); + } + break; case CycleAddXToAddressLowRead: next_address_.full = address_.full + x_; address_.halves.low = next_address_.halves.low; diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 1bb88d5c9..c9fa2a59b 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -18,6 +18,7 @@ using namespace CPU::MOS6502; #define AbsoluteXr CycleLoadAddressAbsolute, CycleAddXToAddressLow, OperationCorrectAddressHigh #define AbsoluteYr CycleLoadAddressAbsolute, CycleAddYToAddressLow, OperationCorrectAddressHigh #define AbsoluteXw CycleLoadAddressAbsolute, CycleAddXToAddressLowRead, OperationCorrectAddressHigh +#define AbsoluteXwSTA CycleLoadAddressAbsolute, CycleAddXToAddressLowReadSTA, OperationCorrectAddressHigh #define AbsoluteYw CycleLoadAddressAbsolute, CycleAddYToAddressLowRead, OperationCorrectAddressHigh #define Zero OperationLoadAddressZeroPage #define ZeroX CycleLoadAddessZeroX @@ -43,6 +44,7 @@ using namespace CPU::MOS6502; #define AbsoluteWrite(op) Program(Absolute, Write(op)) #define AbsoluteXWrite(op) Program(AbsoluteXw, Write(op)) +#define AbsoluteXWriteSTA(op) Program(AbsoluteXwSTA, Write(op)) #define AbsoluteYWrite(op) Program(AbsoluteYw, Write(op)) #define ZeroWrite(op) Program(Zero, Write(op)) #define ZeroXWrite(op) Program(ZeroX, Write(op)) @@ -162,7 +164,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) { /* 0x96 STX zpg, y */ ZeroYWrite(OperationSTX), /* 0x97 SAX zpg, y */ ZeroYWrite(OperationSAX), /* 0x98 TYA */ Program(OperationTYA), /* 0x99 STA abs, y */ AbsoluteYWrite(OperationSTA), /* 0x9a TXS */ Program(OperationTXS), /* 0x9b SHS abs, y */ AbsoluteYWrite(OperationSHS), - /* 0x9c SHY abs, x */ AbsoluteXWrite(OperationSHY), /* 0x9d STA abs, x */ AbsoluteXWrite(OperationSTA), + /* 0x9c SHY abs, x */ AbsoluteXWrite(OperationSHY), /* 0x9d STA abs, x */ AbsoluteXWriteSTA(OperationSTA), /* 0x9e SHX abs, y */ AbsoluteYWrite(OperationSHX), /* 0x9f SHA abs, y */ AbsoluteYWrite(OperationSHA), /* 0xa0 LDY # */ Immediate(OperationLDY), /* 0xa1 LDA x, ind */ IndexedIndirectRead(OperationLDA), /* 0xa2 LDX # */ Immediate(OperationLDX), /* 0xa3 LAX x, ind */ IndexedIndirectRead(OperationLAX), diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 5984e4c27..850f51bea 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -77,6 +77,11 @@ class ProcessorStorage { CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 OperationCorrectAddressHigh, // copies next_address_ to address_ + // Implements 65c02-compatible version of CycleAddXToAddressLowRead specialised for STA; on that processor + // a non-page-crossing `STA abs, x` acts exactly like a 6502, doing a read of the target address before + // the write, but a page-crossing store instead performs throaway read from PC-1. + CycleAddXToAddressLowReadSTA, + OperationIncrementPC, // increments the PC CycleFetchOperandFromAddress, // fetches operand_ from address_ CycleWriteOperandToAddress, // writes operand_ to address_ From bf5ed98f35111adf0db18cc66ef735465f263dbf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Dec 2023 15:39:34 -0500 Subject: [PATCH 2/8] Generalise 65c02 behaviour. Partly to convince myself: 1. this change alters behaviour of `CycleAddXToAddressLowRead` 2. which affects only `AbsoluteXw` and the 65c02-specific `JMP (abs, x)`; 3. `AbsoluteXw` is then used only by `AbsoluteXWrite` and `AbsoluteXReadModifyWrite`; 4. `AbsoluteXWrite` is used for abs, x addressing by `SHY`, `STA` and `STZ`; 5. `AbsoluteXReadModifyWrite` is used for `ASL`, `ASO`, `ROL`, `RLA`, `LSR`, `LSE`, `ROR`, `RRA`, `DEC`, `DCP`, `INC` and `INS`. ... though many of the latter are replaced by instance of `FastAbsoluteXReadModifyWrite` for the 65c02 which don't include a dummy access at all if the page boundary is crossed so the issue is moot. --- .../6502/Implementation/6502Implementation.hpp | 7 +------ Processors/6502/Implementation/6502Storage.cpp | 4 +--- Processors/6502/Implementation/6502Storage.hpp | 12 +++++++----- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 4f48669d5..7c6b5ea8e 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -544,7 +544,7 @@ template void Proces break; } continue; - case CycleAddXToAddressLowReadSTA: + case CycleAddXToAddressLowRead: next_address_.full = address_.full + x_; address_.halves.low = next_address_.halves.low; @@ -559,11 +559,6 @@ template void Proces throwaway_read(pc_.full - 1); } break; - case CycleAddXToAddressLowRead: - next_address_.full = address_.full + x_; - address_.halves.low = next_address_.halves.low; - page_crossing_stall_read(); - break; case CycleAddYToAddressLow: next_address_.full = address_.full + y_; address_.halves.low = next_address_.halves.low; diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index c9fa2a59b..1bb88d5c9 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -18,7 +18,6 @@ using namespace CPU::MOS6502; #define AbsoluteXr CycleLoadAddressAbsolute, CycleAddXToAddressLow, OperationCorrectAddressHigh #define AbsoluteYr CycleLoadAddressAbsolute, CycleAddYToAddressLow, OperationCorrectAddressHigh #define AbsoluteXw CycleLoadAddressAbsolute, CycleAddXToAddressLowRead, OperationCorrectAddressHigh -#define AbsoluteXwSTA CycleLoadAddressAbsolute, CycleAddXToAddressLowReadSTA, OperationCorrectAddressHigh #define AbsoluteYw CycleLoadAddressAbsolute, CycleAddYToAddressLowRead, OperationCorrectAddressHigh #define Zero OperationLoadAddressZeroPage #define ZeroX CycleLoadAddessZeroX @@ -44,7 +43,6 @@ using namespace CPU::MOS6502; #define AbsoluteWrite(op) Program(Absolute, Write(op)) #define AbsoluteXWrite(op) Program(AbsoluteXw, Write(op)) -#define AbsoluteXWriteSTA(op) Program(AbsoluteXwSTA, Write(op)) #define AbsoluteYWrite(op) Program(AbsoluteYw, Write(op)) #define ZeroWrite(op) Program(Zero, Write(op)) #define ZeroXWrite(op) Program(ZeroX, Write(op)) @@ -164,7 +162,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) { /* 0x96 STX zpg, y */ ZeroYWrite(OperationSTX), /* 0x97 SAX zpg, y */ ZeroYWrite(OperationSAX), /* 0x98 TYA */ Program(OperationTYA), /* 0x99 STA abs, y */ AbsoluteYWrite(OperationSTA), /* 0x9a TXS */ Program(OperationTXS), /* 0x9b SHS abs, y */ AbsoluteYWrite(OperationSHS), - /* 0x9c SHY abs, x */ AbsoluteXWrite(OperationSHY), /* 0x9d STA abs, x */ AbsoluteXWriteSTA(OperationSTA), + /* 0x9c SHY abs, x */ AbsoluteXWrite(OperationSHY), /* 0x9d STA abs, x */ AbsoluteXWrite(OperationSTA), /* 0x9e SHX abs, y */ AbsoluteYWrite(OperationSHX), /* 0x9f SHA abs, y */ AbsoluteYWrite(OperationSHA), /* 0xa0 LDY # */ Immediate(OperationLDY), /* 0xa1 LDA x, ind */ IndexedIndirectRead(OperationLDA), /* 0xa2 LDX # */ Immediate(OperationLDX), /* 0xa3 LAX x, ind */ IndexedIndirectRead(OperationLAX), diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 850f51bea..4083c0df0 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -73,14 +73,16 @@ class ProcessorStorage { CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 - CycleAddXToAddressLowRead, // calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 OperationCorrectAddressHigh, // copies next_address_ to address_ - // Implements 65c02-compatible version of CycleAddXToAddressLowRead specialised for STA; on that processor - // a non-page-crossing `STA abs, x` acts exactly like a 6502, doing a read of the target address before - // the write, but a page-crossing store instead performs throaway read from PC-1. - CycleAddXToAddressLowReadSTA, + // Calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l. + // + // 6502: schedules a throwaway read from address_. + // 65C02: schedules a throaway read from PC-1 if a page boundary was crossed; + // otherwise does as per the 6502. + // + CycleAddXToAddressLowRead, OperationIncrementPC, // increments the PC CycleFetchOperandFromAddress, // fetches operand_ from address_ From d33deb676fe5695c854726dafe888636894195b7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Dec 2023 15:55:00 -0500 Subject: [PATCH 3/8] Adjust (abs, y) addressing. --- .../Implementation/6502Implementation.hpp | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 7c6b5ea8e..1c4bcbe5d 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -544,6 +544,17 @@ template void Proces break; } continue; + case CycleAddYToAddressLow: + next_address_.full = address_.full + y_; + address_.halves.low = next_address_.halves.low; + if(address_.halves.high != next_address_.halves.high) { + page_crossing_stall_read(); + break; + } + continue; + +#undef page_crossing_stall_read + case CycleAddXToAddressLowRead: next_address_.full = address_.full + x_; address_.halves.low = next_address_.halves.low; @@ -559,21 +570,18 @@ template void Proces throwaway_read(pc_.full - 1); } break; - case CycleAddYToAddressLow: - next_address_.full = address_.full + y_; - address_.halves.low = next_address_.halves.low; - if(address_.halves.high != next_address_.halves.high) { - page_crossing_stall_read(); - break; - } - continue; case CycleAddYToAddressLowRead: next_address_.full = address_.full + y_; address_.halves.low = next_address_.halves.low; - page_crossing_stall_read(); - break; -#undef page_crossing_stall_read + // A similar rule as for above applies; this one adjusts (abs, y) addressing. + + if(!is_65c02(personality) || next_address_.full == address_.full) { + throwaway_read(address_.full); + } else { + throwaway_read(pc_.full - 1); + } + break; case OperationCorrectAddressHigh: // Preserve the uncorrected address in next_address_ (albeit that it's From 24c80060c88c80759a2b0f01a850747f31b8c2a0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Dec 2023 16:04:48 -0500 Subject: [PATCH 4/8] Revise guess on JMP (abs, x). --- Processors/6502/Implementation/6502Storage.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 1bb88d5c9..34aab45b5 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -346,11 +346,14 @@ ProcessorStorage::ProcessorStorage(Personality personality) { } // Correct JMP (abs) and install JMP (abs, x). + // + // Guess: JMP (abs, x) uses the faster abs,x of ASL, LSL, etc rather than the older, slower of INC and DEC. Install(0x6c, Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed)); Install(0x7c, Program( CycleReadAddressHLoadAddressL, // (3) read second byte of (addr) - CycleAddXToAddressLowRead, // (4) calculate addr+x, read from (addr+x) with high byte not yet calculated - OperationCorrectAddressHigh, CycleReadPCLFromAddress, // (5) read from real (addr+x) + CycleAddYToAddressLow, + OperationCorrectAddressHigh, // (4?) read from incorrectly-calculated address + CycleReadPCLFromAddress, // (4/5) read from real (addr+x) CycleReadPCHFromAddressInc // (6) read from addr+x+1 )); From 38c3d302a30c2fd7765b6d5afdc3f5e8d2e7e1f1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Dec 2023 21:55:07 -0500 Subject: [PATCH 5/8] Restore JMP (abs, x) length. --- Processors/6502/Implementation/6502Storage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 34aab45b5..009aa6a85 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -347,13 +347,13 @@ ProcessorStorage::ProcessorStorage(Personality personality) { // Correct JMP (abs) and install JMP (abs, x). // - // Guess: JMP (abs, x) uses the faster abs,x of ASL, LSL, etc rather than the older, slower of INC and DEC. + // Guess: JMP (abs, x), being listed at a fixed 6 cycles, uses the slower abs,x of INC and DEC. Install(0x6c, Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed)); Install(0x7c, Program( CycleReadAddressHLoadAddressL, // (3) read second byte of (addr) - CycleAddYToAddressLow, - OperationCorrectAddressHigh, // (4?) read from incorrectly-calculated address - CycleReadPCLFromAddress, // (4/5) read from real (addr+x) + CycleAddYToAddressLowRead, + OperationCorrectAddressHigh, // (4) read from incorrectly-calculated address + CycleReadPCLFromAddress, // (5) read from real (addr+x) CycleReadPCHFromAddressInc // (6) read from addr+x+1 )); From 60bd81c4cc45d05c0e3c42f04a776082855b3f3f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Dec 2023 21:55:54 -0500 Subject: [PATCH 6/8] Use X. --- Processors/6502/Implementation/6502Storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 009aa6a85..34d49afc7 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -351,7 +351,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) { Install(0x6c, Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed)); Install(0x7c, Program( CycleReadAddressHLoadAddressL, // (3) read second byte of (addr) - CycleAddYToAddressLowRead, + CycleAddXToAddressLowRead, OperationCorrectAddressHigh, // (4) read from incorrectly-calculated address CycleReadPCLFromAddress, // (5) read from real (addr+x) CycleReadPCHFromAddressInc // (6) read from addr+x+1 From 356d8f469aa23f0991565939022bc05ee5dd577f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Dec 2023 21:56:40 -0500 Subject: [PATCH 7/8] Correct various throaway -> throwaway. --- Processors/6502/Implementation/6502Storage.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 4083c0df0..73bd6171f 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -71,15 +71,15 @@ class ProcessorStorage { CycleLoadAddessZeroX, // copies (operand_+x)&0xff to address_, increments the PC, and reads from operand_, throwing away the result CycleLoadAddessZeroY, // copies (operand_+y)&0xff to address_, increments the PC, and reads from operand_, throwing away the result - CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 - CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 - CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 + CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throwaway read from PC-1 + CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throwaway read from PC-1 + CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throwaway read from PC-1 OperationCorrectAddressHigh, // copies next_address_ to address_ // Calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l. // // 6502: schedules a throwaway read from address_. - // 65C02: schedules a throaway read from PC-1 if a page boundary was crossed; + // 65C02: schedules a throwaway read from PC-1 if a page boundary was crossed; // otherwise does as per the 6502. // CycleAddXToAddressLowRead, From 1f2fbccf1f9412db098c7e5c9aa92b9927e18932 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 20 Dec 2023 22:05:45 -0500 Subject: [PATCH 8/8] Update documentation. --- Processors/6502/Implementation/6502Storage.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 73bd6171f..47f5c206c 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -73,7 +73,6 @@ class ProcessorStorage { CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throwaway read from PC-1 CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throwaway read from PC-1 - CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throwaway read from PC-1 OperationCorrectAddressHigh, // copies next_address_ to address_ // Calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l. @@ -83,6 +82,7 @@ class ProcessorStorage { // otherwise does as per the 6502. // CycleAddXToAddressLowRead, + CycleAddYToAddressLowRead, // As per CycleAddXToAddressLowRead, but uses Y rather than X. OperationIncrementPC, // increments the PC CycleFetchOperandFromAddress, // fetches operand_ from address_