From b62f484d93bc9c0f70e178ea31a64e4c00efcc9c Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sat, 18 Jun 2022 13:28:15 -0400 Subject: [PATCH 01/31] Start scaffolding a 65816 test generator. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 + .../65816ComparativeTests.mm | 107 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 5ced38bd7..1744ac269 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; 4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; 4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; }; + 4B04C899285E3DC800AA8FD6 /* 65816ComparativeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B04C898285E3DC800AA8FD6 /* 65816ComparativeTests.mm */; }; 4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; 4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; 4B051C93266D9D6900CA44E8 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; @@ -1110,6 +1111,7 @@ 4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; }; 4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; }; 4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; }; + 4B04C898285E3DC800AA8FD6 /* 65816ComparativeTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = 65816ComparativeTests.mm; sourceTree = "<group>"; }; 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ROMCatalogue.cpp; sourceTree = "<group>"; }; 4B051C5926670A9300CA44E8 /* ROMCatalogue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMCatalogue.hpp; sourceTree = "<group>"; }; 4B051C94266EF50200CA44E8 /* AppleIIController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleIIController.swift; sourceTree = "<group>"; }; @@ -4230,6 +4232,7 @@ children = ( 4B85322922778E4200F26553 /* Comparative68000.hpp */, 4B90467222C6FA31000E2074 /* TestRunner68000.hpp */, + 4B04C898285E3DC800AA8FD6 /* 65816ComparativeTests.mm */, 4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */, 4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */, 4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */, @@ -6053,6 +6056,7 @@ 4B778EF023A5D68C0000D260 /* 68000Storage.cpp in Sources */, 4B7752B228217EAE0073E2C5 /* StaticAnalyser.cpp in Sources */, 4B7752BC28217F1D0073E2C5 /* Disk.cpp in Sources */, + 4B04C899285E3DC800AA8FD6 /* 65816ComparativeTests.mm in Sources */, 4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */, 4B778EFA23A5EB790000D260 /* DMK.cpp in Sources */, 4BEE1EC122B5E2FD000A26A6 /* Encoder.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm new file mode 100644 index 000000000..0af1b0ae9 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -0,0 +1,107 @@ +// +// 65816ComparativeTests.m +// Clock SignalTests +// +// Created by Thomas Harte on 18/06/2022. +// Copyright © 2022 Thomas Harte. All rights reserved. +// + +#include "65816.hpp" + +#import <XCTest/XCTest.h> +#include <array> +#include <vector> +#include <unordered_map> + +namespace { + +struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { + // Use a map to store RAM contents, in order to preserve initialised state. + std::unordered_map<uint32_t, uint8_t> ram; + + Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, uint32_t address, uint8_t *value) { + // Record the basics of the operation. + auto &cycle = cycles.emplace_back(); + cycle.address = address; + cycle.operation = operation; + cycle.value = 0xff; + + // Perform the operation, and fill in the cycle's value. + using BusOperation = CPU::MOS6502Esque::BusOperation; + auto ram_value = ram.find(address); + switch(operation) { + case BusOperation::ReadOpcode: + ++opcodes_fetched_; + case BusOperation::Read: + case BusOperation::ReadProgram: + case BusOperation::ReadVector: + if(ram_value != ram.end()) { + cycle.value = *value = ram_value->second; + } else { + cycle.value = *value = uint8_t(rand() >> 8); + ram[address] = cycle.value; + } + break; + + case BusOperation::Write: + cycle.value = ram[address] = *value; + break; + + default: break; + } + + // Don't occupy any bonus time. + return Cycles(1); + } + + int opcodes_fetched_ = 0; + + struct Cycle { + CPU::MOS6502Esque::BusOperation operation; + uint32_t address; + uint8_t value; + }; + std::vector<Cycle> cycles; +}; + +} + +// MARK: - New test generator. + +@interface TestGenerator : NSObject +@end + +@implementation TestGenerator + +- (void)generate { + BusHandler handler; + CPU::WDC65816::Processor<BusHandler, false> processor(handler); + + for(int operation = 0; operation < 512; operation++) { + const bool is_emulated = operation & 256; + const uint8_t opcode = operation & 255; + + // TODO: set up for opcode and emulation mode. + + // TODO: run for a bit longer than this, of course. + processor.run_for(Cycles(1)); + } + + printf(""); +} + +@end + +// MARK: - Existing test evaluator. + +@interface WDC65816ComparativeTests : XCTestCase +@end + +@implementation WDC65816ComparativeTests + +// A generator for tests; not intended to be a permanent fixture. +- (void)testGenerate { + [[[TestGenerator alloc] init] generate]; +} + +@end From 586ef4810bbe627120fec3ba46e1898443d35cac Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sat, 18 Jun 2022 16:25:57 -0400 Subject: [PATCH 02/31] Add `restart_operation_fetch`, to aid with testing. --- Processors/65816/65816.hpp | 9 ++++++++- Processors/65816/Implementation/65816Implementation.hpp | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index e65b7cd45..d9390113e 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -45,7 +45,8 @@ class ProcessorBase: protected ProcessorStorage { inline bool get_is_resetting() const; /*! - Returns the current state of all lines not ordinarily pushed to the BusHandler. + Returns the current state of all lines not ordinarily pushed to the BusHandler, + as listed in the ExtendedBusOutput enum. */ inline int get_extended_bus_output(); @@ -54,6 +55,12 @@ class ProcessorBase: protected ProcessorStorage { */ inline bool is_jammed() const; + /*! + FOR TESTING PURPOSES ONLY: forces the processor into a state where + the next thing it intends to do is fetch a new opcode. + */ + inline void restart_operation_fetch(); + void set_value_of_register(Register r, uint16_t value); uint16_t get_value_of_register(Register r) const; }; diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index f3128e550..71bf18f2a 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -1065,3 +1065,10 @@ int ProcessorBase::get_extended_bus_output() { (registers_.mx_flags[1] ? ExtendedBusOutput::IndexSize : 0) | (registers_.emulation_flag ? ExtendedBusOutput::Emulation : 0); } + +void ProcessorBase::restart_operation_fetch() { + // Find a OperationMoveToNextProgram, so that the main loop can make + // relevant decisions. + next_op_ = micro_ops_.data(); + while(*next_op_ != OperationMoveToNextProgram) ++next_op_; +} From f8e695473928f2a71f033c67032354c3c85b892f Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sat, 18 Jun 2022 16:26:40 -0400 Subject: [PATCH 03/31] Ensure complete runs of each tested opcode. --- .../65816ComparativeTests.mm | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 0af1b0ae9..7699b368c 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -15,9 +15,12 @@ namespace { +struct StopException {}; + struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { // Use a map to store RAM contents, in order to preserve initialised state. std::unordered_map<uint32_t, uint8_t> ram; + std::unordered_map<uint32_t, uint8_t> inventions; Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, uint32_t address, uint8_t *value) { // Record the basics of the operation. @@ -31,7 +34,11 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { auto ram_value = ram.find(address); switch(operation) { case BusOperation::ReadOpcode: - ++opcodes_fetched_; + --opcodes_remaining; + if(!opcodes_remaining) { + cycles.pop_back(); + throw StopException(); + } case BusOperation::Read: case BusOperation::ReadProgram: case BusOperation::ReadVector: @@ -39,7 +46,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { cycle.value = *value = ram_value->second; } else { cycle.value = *value = uint8_t(rand() >> 8); - ram[address] = cycle.value; + inventions[address] = ram[address] = cycle.value; } break; @@ -54,7 +61,19 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { return Cycles(1); } - int opcodes_fetched_ = 0; + template <typename Processor> void setup(Processor &processor, uint8_t opcode) { + ram.clear(); + inventions.clear(); + cycles.clear(); + + using Register = CPU::MOS6502Esque::Register; + const uint32_t pc = + processor.get_value_of_register(Register::ProgramCounter) | + (processor.get_value_of_register(Register::ProgramBank) << 8); + inventions[pc] = ram[pc] = opcode; + } + + int opcodes_remaining = 0; struct Cycle { CPU::MOS6502Esque::BusOperation operation; @@ -77,17 +96,29 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { BusHandler handler; CPU::WDC65816::Processor<BusHandler, false> processor(handler); + // Never run the official reset procedure. + processor.set_power_on(false); + for(int operation = 0; operation < 512; operation++) { const bool is_emulated = operation & 256; const uint8_t opcode = operation & 255; - // TODO: set up for opcode and emulation mode. + // Ensure processor's next action is an opcode fetch. + processor.restart_operation_fetch(); - // TODO: run for a bit longer than this, of course. - processor.run_for(Cycles(1)); + // Randomise processor state. + using Register = CPU::MOS6502Esque::Register; + processor.set_value_of_register(Register::EmulationFlag, is_emulated); + + // Establish the opcode. + handler.setup(processor, opcode); + + // Run to the second opcode fetch. + handler.opcodes_remaining = 2; + try { + processor.run_for(Cycles(100)); + } catch (const StopException &) {} } - - printf(""); } @end From eb82e06fab52e4ff820299eb5098f0de9928b628 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sat, 18 Jun 2022 19:21:56 -0400 Subject: [PATCH 04/31] Add randomised initial state, fix PC. --- .../65816ComparativeTests.mm | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 7699b368c..605a4db8d 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -69,7 +69,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { using Register = CPU::MOS6502Esque::Register; const uint32_t pc = processor.get_value_of_register(Register::ProgramCounter) | - (processor.get_value_of_register(Register::ProgramBank) << 8); + (processor.get_value_of_register(Register::ProgramBank) << 16); inventions[pc] = ram[pc] = opcode; } @@ -99,6 +99,10 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { // Never run the official reset procedure. processor.set_power_on(false); + // Make tests repeatable, at least for any given instance of + // the runtime. + srand(65816); + for(int operation = 0; operation < 512; operation++) { const bool is_emulated = operation & 256; const uint8_t opcode = operation & 255; @@ -106,18 +110,35 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { // Ensure processor's next action is an opcode fetch. processor.restart_operation_fetch(); - // Randomise processor state. + // Randomise most of the processor state... using Register = CPU::MOS6502Esque::Register; + processor.set_value_of_register(Register::A, rand() >> 8); + processor.set_value_of_register(Register::Flags, rand() >> 8); + processor.set_value_of_register(Register::X, rand() >> 8); + processor.set_value_of_register(Register::Y, rand() >> 8); + processor.set_value_of_register(Register::ProgramCounter, rand() >> 8); + processor.set_value_of_register(Register::StackPointer, rand() >> 8); + processor.set_value_of_register(Register::DataBank, rand() >> 8); + processor.set_value_of_register(Register::ProgramBank, rand() >> 8); + processor.set_value_of_register(Register::Direct, rand() >> 8); + + // ... except for emulation mode, which is a given. + // And is set last to ensure proper internal state is applied. processor.set_value_of_register(Register::EmulationFlag, is_emulated); // Establish the opcode. handler.setup(processor, opcode); + // TODO: dump current state. + // Run to the second opcode fetch. handler.opcodes_remaining = 2; try { processor.run_for(Cycles(100)); } catch (const StopException &) {} + + // TODO: dump initial and final memory contents, and final state. + printf(""); } } From 0c24a27ba61bea59613349efa20bcd03988a3971 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sat, 18 Jun 2022 21:32:50 -0400 Subject: [PATCH 05/31] Completely prints tests. --- .../65816ComparativeTests.mm | 131 ++++++++++++++---- 1 file changed, 105 insertions(+), 26 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 605a4db8d..9ee2b3644 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -83,6 +83,31 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { std::vector<Cycle> cycles; }; +template <typename Processor> void print_registers(const Processor &processor, int pc_offset) { + using Register = CPU::MOS6502Esque::Register; + printf("\"pc\": %d, ", (processor.get_value_of_register(Register::ProgramCounter) + pc_offset) & 65535); + printf("\"s\": %d, ", processor.get_value_of_register(Register::StackPointer)); + printf("\"p\": %d, ", processor.get_value_of_register(Register::Flags)); + printf("\"a\": %d, ", processor.get_value_of_register(Register::A)); + printf("\"x\": %d, ", processor.get_value_of_register(Register::X)); + printf("\"y\": %d, ", processor.get_value_of_register(Register::Y)); + printf("\"dbr\": %d, ", processor.get_value_of_register(Register::DataBank)); + printf("\"d\": %d, ", processor.get_value_of_register(Register::Direct)); + printf("\"pbr\": %d, ", processor.get_value_of_register(Register::ProgramBank)); + printf("\"e\": %d, ", processor.get_value_of_register(Register::EmulationFlag)); +} + +void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) { + printf("\"ram\": ["); + bool is_first = true; + for(const auto &pair: data) { + if(!is_first) printf(", "); + is_first = false; + printf("[%d, %d]", pair.first, pair.second); + } + printf("]"); +} + } // MARK: - New test generator. @@ -107,38 +132,92 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { const bool is_emulated = operation & 256; const uint8_t opcode = operation & 255; - // Ensure processor's next action is an opcode fetch. - processor.restart_operation_fetch(); + for(int test = 0; test < 1; test++) { + // Ensure processor's next action is an opcode fetch. + processor.restart_operation_fetch(); - // Randomise most of the processor state... - using Register = CPU::MOS6502Esque::Register; - processor.set_value_of_register(Register::A, rand() >> 8); - processor.set_value_of_register(Register::Flags, rand() >> 8); - processor.set_value_of_register(Register::X, rand() >> 8); - processor.set_value_of_register(Register::Y, rand() >> 8); - processor.set_value_of_register(Register::ProgramCounter, rand() >> 8); - processor.set_value_of_register(Register::StackPointer, rand() >> 8); - processor.set_value_of_register(Register::DataBank, rand() >> 8); - processor.set_value_of_register(Register::ProgramBank, rand() >> 8); - processor.set_value_of_register(Register::Direct, rand() >> 8); + // Randomise most of the processor state... + using Register = CPU::MOS6502Esque::Register; + processor.set_value_of_register(Register::A, rand() >> 8); + processor.set_value_of_register(Register::Flags, rand() >> 8); + processor.set_value_of_register(Register::X, rand() >> 8); + processor.set_value_of_register(Register::Y, rand() >> 8); + processor.set_value_of_register(Register::ProgramCounter, rand() >> 8); + processor.set_value_of_register(Register::StackPointer, rand() >> 8); + processor.set_value_of_register(Register::DataBank, rand() >> 8); + processor.set_value_of_register(Register::ProgramBank, rand() >> 8); + processor.set_value_of_register(Register::Direct, rand() >> 8); - // ... except for emulation mode, which is a given. - // And is set last to ensure proper internal state is applied. - processor.set_value_of_register(Register::EmulationFlag, is_emulated); + // ... except for emulation mode, which is a given. + // And is set last to ensure proper internal state is applied. + processor.set_value_of_register(Register::EmulationFlag, is_emulated); - // Establish the opcode. - handler.setup(processor, opcode); + // Establish the opcode. + handler.setup(processor, opcode); - // TODO: dump current state. + // Dump initial state. + printf("{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1); + printf("\"initial\": {"); + print_registers(processor, 0); - // Run to the second opcode fetch. - handler.opcodes_remaining = 2; - try { - processor.run_for(Cycles(100)); - } catch (const StopException &) {} + // Run to the second opcode fetch. + handler.opcodes_remaining = 2; + try { + processor.run_for(Cycles(100)); + } catch (const StopException &) {} - // TODO: dump initial and final memory contents, and final state. - printf(""); + // Dump all inventions as initial memory state. + print_ram(handler.inventions); + + // Dump final state. + printf("}, \"final\": {"); + print_registers(processor, -1); + print_ram(handler.ram); + printf("}, "); + + // Append cycles. + printf("\"cycles\": ["); + + bool is_first = true; + for(const auto &cycle: handler.cycles) { + if(!is_first) printf(","); + is_first = false; + + bool vda = false; + bool vpa = false; + bool vpb = false; + bool read = false; + bool wait = false; + using BusOperation = CPU::MOS6502Esque::BusOperation; + switch(cycle.operation) { + case BusOperation::Read: read = vda = true; break; + case BusOperation::ReadOpcode: read = vda = vpa = true; break; + case BusOperation::ReadProgram: read = vpa = true; break; + case BusOperation::ReadVector: read = vpb = true; break; + case BusOperation::InternalOperationRead: read = true; break; + + case BusOperation::Write: vda = true; break; + case BusOperation::InternalOperationWrite: break; + + case BusOperation::None: + case BusOperation::Ready: wait = true; break; + + default: + assert(false); + } + + printf("[%d, %d, %c%c%c%c]", + cycle.address, + cycle.value, + vda ? 'd' : '-', + vpa ? 'p' : '-', + vpb ? 'v' : '-', + wait ? '-' : (read ? 'r' : 'w')); + } + + // Terminate object. + printf("]},\n"); + } } } From 15ac2c3e5a5f979120f860e636b94e7a2e66c59f Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sat, 18 Jun 2022 22:00:50 -0400 Subject: [PATCH 06/31] Output to files, at volume, with extended bus flags. --- .../65816ComparativeTests.mm | 131 +++++++++++------- 1 file changed, 81 insertions(+), 50 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 9ee2b3644..6fc80612c 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -28,6 +28,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { cycle.address = address; cycle.operation = operation; cycle.value = 0xff; + cycle.extended_bus = processor.get_extended_bus_output(); // Perform the operation, and fill in the cycle's value. using BusOperation = CPU::MOS6502Esque::BusOperation; @@ -61,7 +62,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { return Cycles(1); } - template <typename Processor> void setup(Processor &processor, uint8_t opcode) { + void setup(uint8_t opcode) { ram.clear(); inventions.clear(); cycles.clear(); @@ -79,33 +80,42 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { CPU::MOS6502Esque::BusOperation operation; uint32_t address; uint8_t value; + int extended_bus; }; std::vector<Cycle> cycles; + + CPU::WDC65816::Processor<BusHandler, false> processor; + + BusHandler() : processor(*this) { + // Never run the official reset procedure. + processor.set_power_on(false); + + } }; -template <typename Processor> void print_registers(const Processor &processor, int pc_offset) { +template <typename Processor> void print_registers(FILE *file, const Processor &processor, int pc_offset) { using Register = CPU::MOS6502Esque::Register; - printf("\"pc\": %d, ", (processor.get_value_of_register(Register::ProgramCounter) + pc_offset) & 65535); - printf("\"s\": %d, ", processor.get_value_of_register(Register::StackPointer)); - printf("\"p\": %d, ", processor.get_value_of_register(Register::Flags)); - printf("\"a\": %d, ", processor.get_value_of_register(Register::A)); - printf("\"x\": %d, ", processor.get_value_of_register(Register::X)); - printf("\"y\": %d, ", processor.get_value_of_register(Register::Y)); - printf("\"dbr\": %d, ", processor.get_value_of_register(Register::DataBank)); - printf("\"d\": %d, ", processor.get_value_of_register(Register::Direct)); - printf("\"pbr\": %d, ", processor.get_value_of_register(Register::ProgramBank)); - printf("\"e\": %d, ", processor.get_value_of_register(Register::EmulationFlag)); + fprintf(file, "\"pc\": %d, ", (processor.get_value_of_register(Register::ProgramCounter) + pc_offset) & 65535); + fprintf(file, "\"s\": %d, ", processor.get_value_of_register(Register::StackPointer)); + fprintf(file, "\"p\": %d, ", processor.get_value_of_register(Register::Flags)); + fprintf(file, "\"a\": %d, ", processor.get_value_of_register(Register::A)); + fprintf(file, "\"x\": %d, ", processor.get_value_of_register(Register::X)); + fprintf(file, "\"y\": %d, ", processor.get_value_of_register(Register::Y)); + fprintf(file, "\"dbr\": %d, ", processor.get_value_of_register(Register::DataBank)); + fprintf(file, "\"d\": %d, ", processor.get_value_of_register(Register::Direct)); + fprintf(file, "\"pbr\": %d, ", processor.get_value_of_register(Register::ProgramBank)); + fprintf(file, "\"e\": %d, ", processor.get_value_of_register(Register::EmulationFlag)); } -void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) { - printf("\"ram\": ["); +void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { + fprintf(file, "\"ram\": ["); bool is_first = true; for(const auto &pair: data) { - if(!is_first) printf(", "); + if(!is_first) fprintf(file, ", "); is_first = false; - printf("[%d, %d]", pair.first, pair.second); + fprintf(file, "[%d, %d]", pair.first, pair.second); } - printf("]"); + fprintf(file, "]"); } } @@ -119,69 +129,76 @@ void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) { - (void)generate { BusHandler handler; - CPU::WDC65816::Processor<BusHandler, false> processor(handler); - - // Never run the official reset procedure. - processor.set_power_on(false); // Make tests repeatable, at least for any given instance of // the runtime. srand(65816); + NSString *const tempDir = NSTemporaryDirectory(); + NSLog(@"Outputting to %@", tempDir); + for(int operation = 0; operation < 512; operation++) { const bool is_emulated = operation & 256; const uint8_t opcode = operation & 255; - for(int test = 0; test < 1; test++) { + NSString *const targetName = [NSString stringWithFormat:@"%@%02x.%c.json", tempDir, opcode, is_emulated ? 'e' : 'n']; + FILE *const target = fopen(targetName.UTF8String, "wt"); + + bool is_first_test = true; + fprintf(target, "["); + for(int test = 0; test < 10'000; test++) { + if(!is_first_test) fprintf(target, ",\n"); + is_first_test = false; + // Ensure processor's next action is an opcode fetch. - processor.restart_operation_fetch(); + handler.processor.restart_operation_fetch(); // Randomise most of the processor state... using Register = CPU::MOS6502Esque::Register; - processor.set_value_of_register(Register::A, rand() >> 8); - processor.set_value_of_register(Register::Flags, rand() >> 8); - processor.set_value_of_register(Register::X, rand() >> 8); - processor.set_value_of_register(Register::Y, rand() >> 8); - processor.set_value_of_register(Register::ProgramCounter, rand() >> 8); - processor.set_value_of_register(Register::StackPointer, rand() >> 8); - processor.set_value_of_register(Register::DataBank, rand() >> 8); - processor.set_value_of_register(Register::ProgramBank, rand() >> 8); - processor.set_value_of_register(Register::Direct, rand() >> 8); + handler.processor.set_value_of_register(Register::A, rand() >> 8); + handler.processor.set_value_of_register(Register::Flags, rand() >> 8); + handler.processor.set_value_of_register(Register::X, rand() >> 8); + handler.processor.set_value_of_register(Register::Y, rand() >> 8); + handler.processor.set_value_of_register(Register::ProgramCounter, rand() >> 8); + handler.processor.set_value_of_register(Register::StackPointer, rand() >> 8); + handler.processor.set_value_of_register(Register::DataBank, rand() >> 8); + handler.processor.set_value_of_register(Register::ProgramBank, rand() >> 8); + handler.processor.set_value_of_register(Register::Direct, rand() >> 8); // ... except for emulation mode, which is a given. // And is set last to ensure proper internal state is applied. - processor.set_value_of_register(Register::EmulationFlag, is_emulated); + handler.processor.set_value_of_register(Register::EmulationFlag, is_emulated); // Establish the opcode. - handler.setup(processor, opcode); + handler.setup(opcode); // Dump initial state. - printf("{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1); - printf("\"initial\": {"); - print_registers(processor, 0); + fprintf(target, "{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1); + fprintf(target, "\"initial\": {"); + print_registers(target, handler.processor, 0); // Run to the second opcode fetch. handler.opcodes_remaining = 2; try { - processor.run_for(Cycles(100)); + handler.processor.run_for(Cycles(100)); } catch (const StopException &) {} // Dump all inventions as initial memory state. - print_ram(handler.inventions); + print_ram(target, handler.inventions); // Dump final state. - printf("}, \"final\": {"); - print_registers(processor, -1); - print_ram(handler.ram); - printf("}, "); + fprintf(target, "}, \"final\": {"); + print_registers(target, handler.processor, -1); + print_ram(target, handler.ram); + fprintf(target, "}, "); // Append cycles. - printf("\"cycles\": ["); + fprintf(target, "\"cycles\": ["); - bool is_first = true; + bool is_first_cycle = true; for(const auto &cycle: handler.cycles) { - if(!is_first) printf(","); - is_first = false; + if(!is_first_cycle) fprintf(target, ","); + is_first_cycle = false; bool vda = false; bool vpa = false; @@ -206,18 +223,32 @@ void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) { assert(false); } - printf("[%d, %d, %c%c%c%c]", + using ExtendedBusOutput = CPU::WDC65816::ExtendedBusOutput; + const bool emulation = cycle.extended_bus & ExtendedBusOutput::Emulation; + const bool memory_size = cycle.extended_bus & ExtendedBusOutput::MemorySize; + const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize; + const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock; + + fprintf(target, "[%d, %d, \"%c%c%c%c%c%c%c%c\"]", cycle.address, cycle.value, vda ? 'd' : '-', vpa ? 'p' : '-', vpb ? 'v' : '-', - wait ? '-' : (read ? 'r' : 'w')); + wait ? '-' : (read ? 'r' : 'w'), + wait ? '-' : (emulation ? 'e' : '-'), + wait ? '-' : (memory_size ? 'm' : '-'), + wait ? '-' : (index_size ? 'i' : '-'), + wait ? '-' : (memory_lock ? 'l' : '-') + ); } // Terminate object. - printf("]},\n"); + fprintf(target, "]}"); } + + fprintf(target, "]"); + fclose(target); } } From ab0c2904899aef5769684aec58646cee0cfb8fff Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sun, 19 Jun 2022 06:58:23 -0400 Subject: [PATCH 07/31] Use 'x' instead of 'i'. --- OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 6fc80612c..e134cdc1a 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -238,7 +238,7 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { wait ? '-' : (read ? 'r' : 'w'), wait ? '-' : (emulation ? 'e' : '-'), wait ? '-' : (memory_size ? 'm' : '-'), - wait ? '-' : (index_size ? 'i' : '-'), + wait ? '-' : (index_size ? 'x' : '-'), wait ? '-' : (memory_lock ? 'l' : '-') ); } From ec98736bd7993e388707a38a16bcf6e645d24d11 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Tue, 21 Jun 2022 11:41:05 -0400 Subject: [PATCH 08/31] Ensure IO cycles don't produce an address of (PC+1). --- .../Implementation/65816Implementation.hpp | 4 + .../65816/Implementation/65816Storage.cpp | 94 +++++++++---------- .../65816/Implementation/65816Storage.hpp | 2 + 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 71bf18f2a..4d7ccbe61 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -99,6 +99,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, perform_bus(registers_.pc | registers_.program_bank, &bus_throwaway_, MOS6502Esque::InternalOperationRead); break; + case CycleFetchPreviousPCThrowaway: + perform_bus(((registers_.pc - 1) & 0xffff) | registers_.program_bank, &bus_throwaway_, MOS6502Esque::InternalOperationRead); + break; + // // Data fetches and stores. // diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 2978cb2b7..3ebdf76ea 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -387,7 +387,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 8. Accumulator; A. static void accumulator(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. // TODO: seriously consider a-specific versions of all relevant operations; // the cost of interpreting three things here is kind of silly. @@ -416,10 +416,10 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 10a. Direct; d. // (That's zero page in 6502 terms) static void direct(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // DO. + target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -430,7 +430,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. read_modify_write(is8bit, target); } @@ -440,9 +440,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectIndexedIndirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchData); // AAH. @@ -458,7 +458,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchData); // AAH. @@ -473,7 +473,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchData); // AAH. @@ -490,7 +490,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchIncrementData); // AAH. @@ -506,7 +506,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectLong); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchIncrementData); // AAH. @@ -522,9 +522,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectX); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -534,9 +534,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectX); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. read_modify_write(is8bit, target); } @@ -546,9 +546,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectY); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -563,20 +563,20 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void immediate_rep_sep(AccessType, bool, const std::function<void(MicroOp)> &target) { target(CycleFetchIncrementPC); // IDL. - target(CycleFetchPCThrowaway); // "Add 1 cycle for REP and SEP" + target(CycleFetchPreviousPCThrowaway); // "Add 1 cycle for REP and SEP" target(OperationPerform); } // 19a. Implied; i. static void implied(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationPerform); } // 19b. Implied; i; XBA. static void implied_xba(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationPerform); } @@ -584,8 +584,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 19d. Wait for interrupt. static void stp_wai(AccessType, bool, const std::function<void(MicroOp)> &target) { target(OperationPerform); // Establishes the termination condition. - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CycleRepeatingNone); // This will first check whether the STP/WAI exit // condition has occurred; if not then it'll issue // either a BusOperation::None or ::Ready and then @@ -594,34 +594,34 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 20. Relative; r. static void relative(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // Offset. + target(CycleFetchIncrementPC); // Offset. - target(OperationPerform); // The branch instructions will all skip one or three - // of the next cycles, depending on the effect of - // the jump. It'll also calculate the correct target - // address, placing it into the data buffer. + target(OperationPerform); // The branch instructions will all skip one or three + // of the next cycles, depending on the effect of + // the jump. It'll also calculate the correct target + // address, placing it into the data buffer. - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. - target(OperationCopyDataToPC); // Install the address that was calculated above. + target(OperationCopyDataToPC); // Install the address that was calculated above. } // 21. Relative long; rl. static void relative_long(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // Offset low. - target(CycleFetchIncrementPC); // Offset high. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchIncrementPC); // Offset low. + target(CycleFetchIncrementPC); // Offset high. + target(CycleFetchPreviousPCThrowaway); // IO. - target(OperationPerform); // [BRL] + target(OperationPerform); // [BRL] } // 22a. Stack; s, abort/irq/nmi/res. // // Combined here with reset, which is the same sequence but with a different stack access. static void stack_exception_impl(AccessType, bool, const std::function<void(MicroOp)> &target, MicroOp stack_op) { - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationPrepareException); // Populates the data buffer; if the exception is a // reset then switches to the reset tail program. @@ -654,8 +654,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22b. Stack; s, PLx. static void stack_pull(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. if(!is8bit) target(CyclePull); // REG low. target(CyclePull); // REG [high]. @@ -665,7 +665,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22c. Stack; s, PHx. static void stack_push(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationPerform); @@ -689,7 +689,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchData); // AAH. @@ -701,7 +701,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void stack_per(AccessType, bool, const std::function<void(MicroOp)> &target) { target(CycleFetchIncrementPC); // Offset low. target(CycleFetchIncrementPC); // Offset high. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationConstructPER); @@ -711,8 +711,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22g. Stack; s, RTI. static void stack_rti(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CyclePull); // P. target(CyclePull); // New PCL. @@ -724,8 +724,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22h. Stack; s, RTS. static void stack_rts(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(CyclePull); // PCL. target(CyclePull); // PCH. @@ -772,7 +772,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 23. Stack Relative; d, s. static void stack_relative(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) { target(CycleFetchIncrementPC); // SO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationConstructStackRelative); read_write(type, is8bit, target); @@ -781,7 +781,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 24. Stack Relative Indirect Indexed (d, s), y. static void stack_relative_indexed_indirect(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) { target(CycleFetchIncrementPC); // SO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationConstructStackRelative); target(CycleFetchIncrementData); // AAL. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 01bfe57cf..5bece0f45 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -13,6 +13,8 @@ enum MicroOp: uint8_t { CycleFetchPC, /// Fetches a byte from the program counter without incrementing it, and throws it away. CycleFetchPCThrowaway, + /// Fetches a byte from (PC - 1), and throws it away; useful for IO cycles that immediately follow incremented PCs. + CycleFetchPreviousPCThrowaway, /// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address. CycleFetchOpcode, From 7dcfa9eb65e96c504089df3e33e8eb1fd49e180d Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Tue, 21 Jun 2022 14:33:06 -0400 Subject: [PATCH 09/31] 65816: improve decimal calculations, posted IO addresses, read/write during redundant read-modify-write cycle. --- .../Implementation/65816Implementation.hpp | 71 ++++++++++++------- .../65816/Implementation/65816Storage.cpp | 4 +- .../65816/Implementation/65816Storage.hpp | 5 +- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 4d7ccbe61..9f77567c6 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -115,6 +115,13 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, read(data_address_, data_buffer_.next_input()); break; + case CycleStoreOrFetchDataThrowaway: + if(registers_.emulation_flag) { + perform_bus(data_address_, data_buffer_.preview_output(), MOS6502Esque::InternalOperationWrite); + break; + } + + [[fallthrough]]; case CycleFetchDataThrowaway: perform_bus(data_address_, &bus_throwaway_, MOS6502Esque::InternalOperationRead); break; @@ -141,10 +148,6 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, write(data_address_, data_buffer_.next_output()); break; - case CycleStoreDataThrowaway: - perform_bus(data_address_, data_buffer_.preview_output(), MOS6502Esque::InternalOperationWrite); - break; - case CycleStoreIncrementData: write(data_address_, data_buffer_.next_output()); increment_data_address(); @@ -906,35 +909,43 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, case SBC: if(registers_.flags.decimal) { - // I've yet to manage to find a rational way to map this to an ADC, - // hence the yucky repetition of code here. const uint16_t a = registers_.a.full & registers_.m_masks[1]; - unsigned int result = 0; - unsigned int borrow = registers_.flags.carry ^ 1; - const uint16_t decimal_result = uint16_t(a - data_buffer_.value - borrow); + const uint16_t decimal_result = uint16_t(a - data_buffer_.value - (1 ^ registers_.flags.carry)); + data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; -#define nibble(mask, adjustment, carry) \ - result += (a & mask) - (data_buffer_.value & mask) - borrow; \ - if(result > mask) result -= adjustment; \ - borrow = (result > mask) ? carry : 0; \ - result &= (carry - 1); +#define begin_nibble(mask) \ + result += (a & mask) + (data_buffer_.value & mask); +#define end_nibble(adjustment, carry) \ + if(result < carry) result -= adjustment; \ + result &= (carry | (carry - 1)); + +#define nibble(mask, adjustment, carry) \ + begin_nibble(mask); \ + end_nibble(adjustment, carry) + + unsigned int result = registers_.flags.carry; nibble(0x000f, 0x0006, 0x00010); nibble(0x00f0, 0x0060, 0x00100); nibble(0x0f00, 0x0600, 0x01000); - nibble(0xf000, 0x6000, 0x10000); + + begin_nibble(0xf000); + registers_.flags.overflow = uint8_t((( (decimal_result ^ result) & (~decimal_result ^ result) ) >> (1 + registers_.m_shift))&0x40); + end_nibble(0x6000, 0x10000); #undef nibble +#undef begin_nibble +#undef end_nibble - registers_.flags.overflow = (( (decimal_result ^ a) & (~decimal_result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; registers_.flags.set_nz(uint16_t(result), registers_.m_shift); - registers_.flags.carry = ((borrow >> 16)&1)^1; + registers_.flags.carry = (result >> (8 + registers_.m_shift))&1; LDA(result); break; } data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; + [[fallthrough]]; case ADC: { @@ -942,22 +953,28 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, const uint16_t a = registers_.a.full & registers_.m_masks[1]; if(registers_.flags.decimal) { - uint16_t partials = 0; +#define begin_nibble(mask) \ + result += (a & mask) + (data_buffer_.value & mask); + +#define end_nibble(limit, adjustment, carry) \ + if(result >= limit) result = ((result + adjustment) & (carry - 1)) + carry; + +#define nibble(mask, limit, adjustment, carry) \ + begin_nibble(mask); \ + end_nibble(limit, adjustment, carry) + result = registers_.flags.carry; - -#define nibble(mask, limit, adjustment, carry) \ - result += (a & mask) + (data_buffer_.value & mask); \ - partials += result & mask; \ - if(result >= limit) result = ((result + (adjustment)) & (carry - 1)) + carry; - nibble(0x000f, 0x000a, 0x0006, 0x00010); nibble(0x00f0, 0x00a0, 0x0060, 0x00100); nibble(0x0f00, 0x0a00, 0x0600, 0x01000); - nibble(0xf000, 0xa000, 0x6000, 0x10000); + + begin_nibble(0xf000); + registers_.flags.overflow = (( (uint16_t(result) ^ registers_.a.full) & (uint16_t(result) ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; + end_nibble(0xa000, 0x6000, 0x10000); #undef nibble - - registers_.flags.overflow = (( (partials ^ registers_.a.full) & (partials ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; +#undef begin_nibble +#undef end_nibble } else { result = int(a + data_buffer_.value + registers_.flags.carry); diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 3ebdf76ea..1ad7b3ec9 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -195,8 +195,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { if(!is8bit) target(CycleFetchIncrementData); // Data low. target(CycleFetchData); // Data [high]. - if(!is8bit) target(CycleFetchDataThrowaway); // 16-bit: reread final byte of data. - else target(CycleStoreDataThrowaway); // 8-bit rewrite final byte of data. + target(CycleStoreOrFetchDataThrowaway); // Native mode: reread final byte of data. + // Emulated mode: rewrite final byte of data. target(OperationPerform); // Perform operation within the data buffer. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 5bece0f45..d2ead050f 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -39,8 +39,9 @@ enum MicroOp: uint8_t { /// Stores a byte from the data buffer. CycleStoreData, - /// Stores the most recent byte placed into the data buffer without removing it. - CycleStoreDataThrowaway, + /// Emulated mode: stores the most recent byte placed into the data buffer without removing it; + /// Native mode: performs CycleFetchDataThrowaway. + CycleStoreOrFetchDataThrowaway, /// Stores a byte to the data address from the data buffer and increments the data address. CycleStoreIncrementData, /// Stores a byte to the data address from the data buffer and decrements the data address. From 2f684ee66d0e077f1b94723e496314a468374b46 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Tue, 21 Jun 2022 21:47:18 -0400 Subject: [PATCH 10/31] Use null for values that were never loaded. --- .../Clock SignalTests/65816ComparativeTests.mm | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index e134cdc1a..342bf51d9 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -10,6 +10,7 @@ #import <XCTest/XCTest.h> #include <array> +#include <optional> #include <vector> #include <unordered_map> @@ -27,7 +28,6 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { auto &cycle = cycles.emplace_back(); cycle.address = address; cycle.operation = operation; - cycle.value = 0xff; cycle.extended_bus = processor.get_extended_bus_output(); // Perform the operation, and fill in the cycle's value. @@ -47,7 +47,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { cycle.value = *value = ram_value->second; } else { cycle.value = *value = uint8_t(rand() >> 8); - inventions[address] = ram[address] = cycle.value; + inventions[address] = ram[address] = *cycle.value; } break; @@ -79,7 +79,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { struct Cycle { CPU::MOS6502Esque::BusOperation operation; uint32_t address; - uint8_t value; + std::optional<uint8_t> value; int extended_bus; }; std::vector<Cycle> cycles; @@ -229,9 +229,13 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize; const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock; - fprintf(target, "[%d, %d, \"%c%c%c%c%c%c%c%c\"]", - cycle.address, - cycle.value, + fprintf(target, "[%d, ", cycle.address); + if(cycle.value) { + fprintf(target, "%d, ", *cycle.value); + } else { + fprintf(target, "null, "); + } + fprintf(target, "\"%c%c%c%c%c%c%c%c\"]", vda ? 'd' : '-', vpa ? 'p' : '-', vpb ? 'v' : '-', From 76767110b7c49e78dd0b718f642b28a15bc635e6 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 22 Jun 2022 15:12:08 -0400 Subject: [PATCH 11/31] Fix overflow for 8-bit calculations; essentially a revert for ADC. --- .../Implementation/65816Implementation.hpp | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 9f77567c6..d5161e64a 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -910,33 +910,25 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, case SBC: if(registers_.flags.decimal) { const uint16_t a = registers_.a.full & registers_.m_masks[1]; - const uint16_t decimal_result = uint16_t(a - data_buffer_.value - (1 ^ registers_.flags.carry)); data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; -#define begin_nibble(mask) \ - result += (a & mask) + (data_buffer_.value & mask); + unsigned int result = registers_.flags.carry; + uint16_t partials = 0; -#define end_nibble(adjustment, carry) \ - if(result < carry) result -= adjustment; \ +#define nibble(mask, adjustment, carry) \ + result += (a & mask) + (data_buffer_.value & mask); \ + partials += result & mask; \ + if(result < carry) result -= adjustment; \ result &= (carry | (carry - 1)); -#define nibble(mask, adjustment, carry) \ - begin_nibble(mask); \ - end_nibble(adjustment, carry) - - unsigned int result = registers_.flags.carry; nibble(0x000f, 0x0006, 0x00010); nibble(0x00f0, 0x0060, 0x00100); nibble(0x0f00, 0x0600, 0x01000); - - begin_nibble(0xf000); - registers_.flags.overflow = uint8_t((( (decimal_result ^ result) & (~decimal_result ^ result) ) >> (1 + registers_.m_shift))&0x40); - end_nibble(0x6000, 0x10000); + nibble(0xf000, 0x6000, 0x10000); #undef nibble -#undef begin_nibble -#undef end_nibble + registers_.flags.overflow = (( (partials ^ registers_.a.full) & (partials ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; registers_.flags.set_nz(uint16_t(result), registers_.m_shift); registers_.flags.carry = (result >> (8 + registers_.m_shift))&1; LDA(result); @@ -945,7 +937,6 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, } data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; - [[fallthrough]]; case ADC: { @@ -953,29 +944,22 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, const uint16_t a = registers_.a.full & registers_.m_masks[1]; if(registers_.flags.decimal) { -#define begin_nibble(mask) \ - result += (a & mask) + (data_buffer_.value & mask); + uint16_t partials = 0; + result = registers_.flags.carry; -#define end_nibble(limit, adjustment, carry) \ +#define nibble(mask, limit, adjustment, carry) \ + result += (a & mask) + (data_buffer_.value & mask); \ + partials += result & mask; \ if(result >= limit) result = ((result + adjustment) & (carry - 1)) + carry; -#define nibble(mask, limit, adjustment, carry) \ - begin_nibble(mask); \ - end_nibble(limit, adjustment, carry) - - result = registers_.flags.carry; nibble(0x000f, 0x000a, 0x0006, 0x00010); nibble(0x00f0, 0x00a0, 0x0060, 0x00100); nibble(0x0f00, 0x0a00, 0x0600, 0x01000); - - begin_nibble(0xf000); - registers_.flags.overflow = (( (uint16_t(result) ^ registers_.a.full) & (uint16_t(result) ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; - end_nibble(0xa000, 0x6000, 0x10000); + nibble(0xf000, 0xa000, 0x6000, 0x10000); #undef nibble -#undef begin_nibble -#undef end_nibble + registers_.flags.overflow = (( (partials ^ registers_.a.full) & (partials ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; } else { result = int(a + data_buffer_.value + registers_.flags.carry); registers_.flags.overflow = (( (uint16_t(result) ^ registers_.a.full) & (uint16_t(result) ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; From 944e5ebbfa2b144027d63f56b9aabce702cadd9b Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 22 Jun 2022 15:28:11 -0400 Subject: [PATCH 12/31] Take another run at IO addresses. --- .../65816/Implementation/65816Storage.cpp | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 1ad7b3ec9..677fd496b 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -387,7 +387,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 8. Accumulator; A. static void accumulator(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. // TODO: seriously consider a-specific versions of all relevant operations; // the cost of interpreting three things here is kind of silly. @@ -430,7 +430,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. read_modify_write(is8bit, target); } @@ -569,14 +569,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 19a. Implied; i. static void implied(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. target(OperationPerform); } // 19b. Implied; i; XBA. static void implied_xba(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. target(OperationPerform); } @@ -584,8 +584,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 19d. Wait for interrupt. static void stp_wai(AccessType, bool, const std::function<void(MicroOp)> &target) { target(OperationPerform); // Establishes the termination condition. - target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. target(CycleRepeatingNone); // This will first check whether the STP/WAI exit // condition has occurred; if not then it'll issue // either a BusOperation::None or ::Ready and then @@ -654,8 +654,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22b. Stack; s, PLx. static void stack_pull(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) { - target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. if(!is8bit) target(CyclePull); // REG low. target(CyclePull); // REG [high]. @@ -665,7 +665,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22c. Stack; s, PHx. static void stack_push(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) { - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. target(OperationPerform); @@ -686,33 +686,33 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22e. Stack; s, PEI. static void stack_pei(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // DO. + target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchIncrementData); // AAL. - target(CycleFetchData); // AAH. - target(CyclePush); // AAH. - target(CyclePush); // AAL. + target(CycleFetchIncrementData); // AAL. + target(CycleFetchData); // AAH. + target(CyclePush); // AAH. + target(CyclePush); // AAL. } // 22f. Stack; s, PER. static void stack_per(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // Offset low. - target(CycleFetchIncrementPC); // Offset high. - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchIncrementPC); // Offset low. + target(CycleFetchIncrementPC); // Offset high. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationConstructPER); - target(CyclePush); // AAH. - target(CyclePush); // AAL. + target(CyclePush); // AAH. + target(CyclePush); // AAL. } // 22g. Stack; s, RTI. static void stack_rti(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. target(CyclePull); // P. target(CyclePull); // New PCL. @@ -724,8 +724,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22h. Stack; s, RTS. static void stack_rts(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPreviousPCThrowaway); // IO. - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. target(CyclePull); // PCL. target(CyclePull); // PCH. @@ -736,8 +736,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22i. Stack; s, RTL. static void stack_rtl(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // IO. - target(CycleFetchIncrementPC); // IO. + target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. target(CyclePull); // New PCL. target(CyclePull); // New PCH. @@ -771,7 +771,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 23. Stack Relative; d, s. static void stack_relative(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // SO. + target(CycleFetchIncrementPC); // SO. target(CycleFetchPreviousPCThrowaway); // IO. target(OperationConstructStackRelative); @@ -780,13 +780,13 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 24. Stack Relative Indirect Indexed (d, s), y. static void stack_relative_indexed_indirect(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) { - target(CycleFetchIncrementPC); // SO. - target(CycleFetchPreviousPCThrowaway); // IO. + target(CycleFetchIncrementPC); // SO. + target(CycleFetchPreviousPCThrowaway); // IO. target(OperationConstructStackRelative); - target(CycleFetchIncrementData); // AAL. - target(CycleFetchData); // AAH. - target(CycleFetchDataThrowaway); // IO. + target(CycleFetchIncrementData); // AAL. + target(CycleFetchData); // AAH. + target(CycleFetchDataThrowaway); // IO. target(OperationConstructStackRelativeIndexedIndirect); read_write(type, is8bit, target); From a72dd96dc6e090f13fad4d442f9112e4fc6ba278 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 22 Jun 2022 15:31:30 -0400 Subject: [PATCH 13/31] Page boundary crossing is free outside of emulation mode. --- Processors/65816/Implementation/65816Implementation.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index d5161e64a..a96354ed5 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -835,7 +835,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, data_buffer_.value = uint32_t(registers_.pc + int8_t(instruction_buffer_.value)); \ data_buffer_.size = 2; \ \ - if((registers_.pc & 0xff00) == (instruction_buffer_.value & 0xff00)) { \ + if( \ + !registers_.emulation_flag || \ + (registers_.pc & 0xff00) == (instruction_buffer_.value & 0xff00) \ + ) { \ ++next_op_; \ } \ } From ecfd17a259758955f71862754286a0df986b4217 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 22 Jun 2022 15:55:34 -0400 Subject: [PATCH 14/31] Report a 1 in the stack pointer high byte when in emulation mode. It has one internally, it just wasn't previously exposed via this method. --- Processors/65816/Implementation/65816Base.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index f61b62248..5dff89928 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -14,7 +14,10 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { switch (r) { case Register::ProgramCounter: return registers_.pc; case Register::LastOperationAddress: return last_operation_pc_; - case Register::StackPointer: return registers_.s.full & (registers_.emulation_flag ? 0xff : 0xffff); + case Register::StackPointer: + return + (registers_.s.full & (registers_.emulation_flag ? 0xff : 0xffff)) | + (registers_.emulation_flag ? 0x100 : 0x000); case Register::Flags: return get_flags(); case Register::A: return registers_.a.full; case Register::X: return registers_.x.full; From 65140b341d92371a931b5a52572efe51ae58be82 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 22 Jun 2022 16:43:00 -0400 Subject: [PATCH 15/31] Simplify slightly, per new S reporting rule. --- OSBindings/Mac/Clock SignalTests/65816kromTests.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816kromTests.swift b/OSBindings/Mac/Clock SignalTests/65816kromTests.swift index 294e6bca2..154ea68d2 100644 --- a/OSBindings/Mac/Clock SignalTests/65816kromTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816kromTests.swift @@ -84,11 +84,7 @@ class Krom65816Tests: XCTestCase { cpuState += String(format: "A:%04x ", machine.value(for: .A)) cpuState += String(format: "X:%04x ", machine.value(for: .X)) cpuState += String(format: "Y:%04x ", machine.value(for: .Y)) - if emulationFlag { - cpuState += String(format: "S:01%02x ", machine.value(for: .stackPointer)) - } else { - cpuState += String(format: "S:%04x ", machine.value(for: .stackPointer)) - } + cpuState += String(format: "S:%04x ", machine.value(for: .stackPointer)) cpuState += String(format: "D:%04x ", machine.value(for: .direct)) cpuState += String(format: "DB:%02x ", machine.value(for: .dataBank)) From 3112376943861d1a768d7c726dea9210daad0d09 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 11:03:37 -0400 Subject: [PATCH 16/31] Don't include DBR in direct indexed indirect. --- Processors/65816/Implementation/65816Implementation.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index a96354ed5..c503c0a44 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -345,10 +345,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, continue; case OperationConstructDirectIndexedIndirect: - data_address_ = registers_.data_bank + (( + data_address_ = ( ((registers_.direct + registers_.x.full + instruction_buffer_.value) & registers_.e_masks[1]) + (registers_.direct & registers_.e_masks[0]) - ) & 0xffff); + ) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; if(!(registers_.direct&0xff)) { From 5a97c0923882811791d0fea33b8e50b73f2ab021 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 11:23:00 -0400 Subject: [PATCH 17/31] Flip internal presumption on the BRK flag. --- Processors/6502Esque/Implementation/LazyFlags.hpp | 2 +- Processors/65816/Implementation/65816Implementation.hpp | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Processors/6502Esque/Implementation/LazyFlags.hpp b/Processors/6502Esque/Implementation/LazyFlags.hpp index 756d7a779..2169d8c24 100644 --- a/Processors/6502Esque/Implementation/LazyFlags.hpp +++ b/Processors/6502Esque/Implementation/LazyFlags.hpp @@ -64,7 +64,7 @@ struct LazyFlags { } uint8_t get() const { - return carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | decimal; + return carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | Flag::Break | decimal; } LazyFlags() { diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index c503c0a44..3a50f3d72 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -415,7 +415,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, case OperationPrepareException: data_buffer_.value = uint32_t((registers_.pc << 8) | get_flags()); if(registers_.emulation_flag) { - if(!exception_is_interrupt_) data_buffer_.value |= Flag::Break; + if(exception_is_interrupt_) data_buffer_.value &= ~Flag::Break; data_buffer_.size = 3; registers_.data_bank = 0; ++next_op_; @@ -563,11 +563,6 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, case PHP: data_buffer_.value = get_flags(); data_buffer_.size = 1; - - if(registers_.emulation_flag) { - // On the 6502, the break flag is set during a PHP. - data_buffer_.value |= Flag::Break; - } break; case NOP: break; From 2c12a7d968cfdd2f5b7f6fac49879a6dae6513ec Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 12:12:02 -0400 Subject: [PATCH 18/31] Make absolutely sure there's no address bit 24. --- Processors/65816/Implementation/65816Implementation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 3a50f3d72..86e561445 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -9,7 +9,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, uses_ready_line>::run_for(const Cycles cycles) { #define perform_bus(address, value, operation) \ - bus_address_ = address; \ + bus_address_ = address & 0xff'ffff; \ bus_value_ = value; \ bus_operation_ = operation From 66775b2c4ee4a989d07dd07e59106ef73167731e Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 12:46:51 -0400 Subject: [PATCH 19/31] Always consume a second cycle in 16-bit mode. --- .../65816/Implementation/65816Implementation.hpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 86e561445..bc4ae8bc8 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -300,8 +300,12 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, data_address_ = instruction_buffer_.value + registers_.x.full + registers_.data_bank; incorrect_data_address_ = ((data_address_ & 0x00ff) | (instruction_buffer_.value & 0xff00)) + registers_.data_bank; - // If the incorrect address isn't actually incorrect, skip its usage. - if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { + // "Add 1 cycle for indexing across page boundaries, or write, or X=0" + // (i.e. don't add 1 cycle if x = 1 and this is a read, and a page boundary wasn't crossed) + if( + operation == OperationConstructAbsoluteXRead && + data_address_ == incorrect_data_address_ && + registers_.mx_flags[1]) { ++next_op_; } data_address_increment_mask_ = 0xff'ff'ff; @@ -312,8 +316,12 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, data_address_ = instruction_buffer_.value + registers_.y.full + registers_.data_bank; incorrect_data_address_ = (data_address_ & 0xff) + (instruction_buffer_.value & 0xff00) + registers_.data_bank; - // If the incorrect address isn't actually incorrect, skip its usage. - if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) { + // "Add 1 cycle for indexing across page boundaries, or write, or X=0" + // (i.e. don't add 1 cycle if x = 1 and this is a read, and a page boundary wasn't crossed) + if( + operation == OperationConstructAbsoluteYRead && + data_address_ == incorrect_data_address_ && + registers_.mx_flags[1]) { ++next_op_; } data_address_increment_mask_ = 0xff'ff'ff; From 380b5141fbd19f0236119a1b79374ea17b988e34 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 13:03:26 -0400 Subject: [PATCH 20/31] Be overt about conversion wanted here. --- Processors/65816/Implementation/65816Implementation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index bc4ae8bc8..3ed50cb3a 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -423,7 +423,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, case OperationPrepareException: data_buffer_.value = uint32_t((registers_.pc << 8) | get_flags()); if(registers_.emulation_flag) { - if(exception_is_interrupt_) data_buffer_.value &= ~Flag::Break; + if(exception_is_interrupt_) data_buffer_.value &= ~uint32_t(Flag::Break); data_buffer_.size = 3; registers_.data_bank = 0; ++next_op_; From da552abf75e18410fc06341fbd45482b9a547144 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 15:24:51 -0400 Subject: [PATCH 21/31] Fix BIT overflow flag. --- Processors/65816/Implementation/65816Implementation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 3ed50cb3a..ec7c73ba6 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -806,7 +806,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, assert(data_buffer_.size == 2 - m_flag()); registers_.flags.set_n(uint16_t(data_buffer_.value), registers_.m_shift); registers_.flags.set_z(uint16_t(data_buffer_.value & registers_.a.full), registers_.m_shift); - registers_.flags.overflow = data_buffer_.value & Flag::Overflow; + registers_.flags.overflow = (data_buffer_.value >> registers_.m_shift) & Flag::Overflow; break; case BITimm: From a23b0f5122695eb3d836988035851c92f667c296 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 20:57:47 -0400 Subject: [PATCH 22/31] Map `STA (d), y` to correct calculator. --- Processors/65816/Implementation/65816Storage.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 677fd496b..ddd9202ec 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -479,7 +479,11 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchData); // AAH. target(OperationCopyDataToInstruction); - target(OperationConstructAbsoluteYRead); + if(type == AccessType::Read) { + target(OperationConstructAbsoluteYRead); // Calculate data address, potentially skipping the next fetch. + } else { + target(OperationConstructAbsoluteY); // Calculate data address. + } target(CycleFetchIncorrectDataAddress); // IO. read_write(type, is8bit, target); From 2e7afb13c79eae2f66468ba3e37ed5b2b5db9f28 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 21:03:40 -0400 Subject: [PATCH 23/31] Exit gracefully upon a STP or WAI. --- .../Mac/Clock SignalTests/65816ComparativeTests.mm | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 342bf51d9..b6cab8023 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -38,6 +38,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { --opcodes_remaining; if(!opcodes_remaining) { cycles.pop_back(); + pc_overshoot = -1; throw StopException(); } case BusOperation::Read: @@ -55,7 +56,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { cycle.value = ram[address] = *value; break; - default: break; + case BusOperation::Ready: + case BusOperation::None: + throw StopException(); + break; + + default: assert(false); } // Don't occupy any bonus time. @@ -66,6 +72,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { ram.clear(); inventions.clear(); cycles.clear(); + pc_overshoot = 0; using Register = CPU::MOS6502Esque::Register; const uint32_t pc = @@ -75,6 +82,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { } int opcodes_remaining = 0; + int pc_overshoot = 0; struct Cycle { CPU::MOS6502Esque::BusOperation operation; @@ -89,7 +97,6 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { BusHandler() : processor(*this) { // Never run the official reset procedure. processor.set_power_on(false); - } }; @@ -188,7 +195,7 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { // Dump final state. fprintf(target, "}, \"final\": {"); - print_registers(target, handler.processor, -1); + print_registers(target, handler.processor, handler.pc_overshoot); print_ram(target, handler.ram); fprintf(target, "}, "); From 4ed3b21bf37a6b6d319bfcfb048ef020b4c819e3 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 23 Jun 2022 21:58:09 -0400 Subject: [PATCH 24/31] Decimal SBC tweak: negative partial results don't cause carry. --- .../65816/Implementation/65816Implementation.hpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index ec7c73ba6..4e542236f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -918,14 +918,22 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, const uint16_t a = registers_.a.full & registers_.m_masks[1]; data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; - unsigned int result = registers_.flags.carry; + int result = registers_.flags.carry; uint16_t partials = 0; #define nibble(mask, adjustment, carry) \ result += (a & mask) + (data_buffer_.value & mask); \ partials += result & mask; \ - if(result < carry) result -= adjustment; \ - result &= (carry | (carry - 1)); + result -= ((result - carry) >> 16) & adjustment; \ + result &= (carry & ~(result >> 31)) | (carry - 1); + + // i.e. add the next nibble to that in the accumulator, with carry, and + // store it to result. Keep a copy for the partials. + // + // If result is less than carry, subtract adjustment. + // + // Allow onward carry if the bit immediately above this nibble is 1, and + // the current partial result is positive. nibble(0x000f, 0x0006, 0x00010); nibble(0x00f0, 0x0060, 0x00100); From 069a057a945ae0a16ed938c900ea90ac5fb35e8b Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 24 Jun 2022 07:26:07 -0400 Subject: [PATCH 25/31] Resolve assumption of arithmetic shifts. --- Processors/65816/Implementation/65816Implementation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 4e542236f..7ac380c08 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -925,7 +925,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, result += (a & mask) + (data_buffer_.value & mask); \ partials += result & mask; \ result -= ((result - carry) >> 16) & adjustment; \ - result &= (carry & ~(result >> 31)) | (carry - 1); + result &= (carry & ~(result >> 1)) | (carry - 1); // i.e. add the next nibble to that in the accumulator, with carry, and // store it to result. Keep a copy for the partials. From 6c638712f3a1eeacbaa2dfabd95cc259a4c67791 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 24 Jun 2022 07:39:58 -0400 Subject: [PATCH 26/31] Attempt to capture MVP and MVN in their entirety. --- .../Mac/Clock SignalTests/65816ComparativeTests.mm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index b6cab8023..ab29c21ff 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -35,12 +35,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { auto ram_value = ram.find(address); switch(operation) { case BusOperation::ReadOpcode: - --opcodes_remaining; - if(!opcodes_remaining) { + if(initial_pc && *initial_pc != address) { cycles.pop_back(); pc_overshoot = -1; throw StopException(); } + initial_pc = address; case BusOperation::Read: case BusOperation::ReadProgram: case BusOperation::ReadVector: @@ -61,6 +61,10 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { throw StopException(); break; + case BusOperation::InternalOperationRead: + case BusOperation::InternalOperationWrite: + break; + default: assert(false); } @@ -73,6 +77,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { inventions.clear(); cycles.clear(); pc_overshoot = 0; + initial_pc = std::nullopt; using Register = CPU::MOS6502Esque::Register; const uint32_t pc = @@ -81,8 +86,8 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { inventions[pc] = ram[pc] = opcode; } - int opcodes_remaining = 0; int pc_overshoot = 0; + std::optional<uint32_t> initial_pc; struct Cycle { CPU::MOS6502Esque::BusOperation operation; @@ -185,7 +190,6 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { print_registers(target, handler.processor, 0); // Run to the second opcode fetch. - handler.opcodes_remaining = 2; try { handler.processor.run_for(Cycles(100)); } catch (const StopException &) {} From a442077eac8faafde5270ea3821a7fae5e3d3d24 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 24 Jun 2022 10:34:43 -0400 Subject: [PATCH 27/31] Allow repetition for MVN and MVP only. --- OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index ab29c21ff..dd0d82e25 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -35,7 +35,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { auto ram_value = ram.find(address); switch(operation) { case BusOperation::ReadOpcode: - if(initial_pc && *initial_pc != address) { + if(initial_pc && (*initial_pc != address || !allow_pc_repetition)) { cycles.pop_back(); pc_overshoot = -1; throw StopException(); @@ -79,6 +79,11 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { pc_overshoot = 0; initial_pc = std::nullopt; + // For MVP or MVN, keep tracking fetches via the same location. + // For other instructions, don't. That's to avoid endless loops + // for flow control that happens to jump back to where it began. + allow_pc_repetition = opcode == 0x54 || opcode == 0x44; + using Register = CPU::MOS6502Esque::Register; const uint32_t pc = processor.get_value_of_register(Register::ProgramCounter) | @@ -88,6 +93,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { int pc_overshoot = 0; std::optional<uint32_t> initial_pc; + bool allow_pc_repetition = false; struct Cycle { CPU::MOS6502Esque::BusOperation operation; From 1c1ce625a767203443e193c6d28f1bed8e8a8f45 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 24 Jun 2022 10:37:39 -0400 Subject: [PATCH 28/31] Vector reads signal VDA. --- OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 2 +- Processors/6502Esque/6502Esque.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index dd0d82e25..f05b7013f 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -227,7 +227,7 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { case BusOperation::Read: read = vda = true; break; case BusOperation::ReadOpcode: read = vda = vpa = true; break; case BusOperation::ReadProgram: read = vpa = true; break; - case BusOperation::ReadVector: read = vpb = true; break; + case BusOperation::ReadVector: read = vpb = vda = true; break; case BusOperation::InternalOperationRead: read = true; break; case BusOperation::Write: vda = true; break; diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index 8f5c75589..84edc52d2 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -77,7 +77,7 @@ enum BusOperation { /// 65816: indicates that a read was signalled with VPA. ReadProgram, /// 6502: never signalled. - /// 65816: indicates that a read was signalled with VPB. + /// 65816: indicates that a read was signalled with VPB and VDA. ReadVector, /// 6502: never signalled. /// 65816: indicates that a read was signalled, but neither VDA nor VPA were active. From ef5ac1442fb06927819f69b95175990b519d8805 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 24 Jun 2022 13:05:32 -0400 Subject: [PATCH 29/31] Don't invent an address for STP and WAI. --- .../65816ComparativeTests.mm | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index f05b7013f..277c00c9b 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -26,7 +26,6 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, uint32_t address, uint8_t *value) { // Record the basics of the operation. auto &cycle = cycles.emplace_back(); - cycle.address = address; cycle.operation = operation; cycle.extended_bus = processor.get_extended_bus_output(); @@ -41,9 +40,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { throw StopException(); } initial_pc = address; + [[fallthrough]]; + case BusOperation::Read: case BusOperation::ReadProgram: case BusOperation::ReadVector: + cycle.address = address; if(ram_value != ram.end()) { cycle.value = *value = ram_value->second; } else { @@ -53,6 +55,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { break; case BusOperation::Write: + cycle.address = address; cycle.value = ram[address] = *value; break; @@ -61,8 +64,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { throw StopException(); break; - case BusOperation::InternalOperationRead: case BusOperation::InternalOperationWrite: + cycle.value = *value = ram_value->second; + [[fallthrough]]; + + case BusOperation::InternalOperationRead: + cycle.address = address; break; default: assert(false); @@ -97,7 +104,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t> { struct Cycle { CPU::MOS6502Esque::BusOperation operation; - uint32_t address; + std::optional<uint32_t> address; std::optional<uint8_t> value; int extended_bus; }; @@ -246,7 +253,12 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize; const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock; - fprintf(target, "[%d, ", cycle.address); + fprintf(target, "["); + if(cycle.address) { + fprintf(target, "%d, ", *cycle.address); + } else { + fprintf(target, "null, "); + } if(cycle.value) { fprintf(target, "%d, ", *cycle.value); } else { From 4467eb1c41ba447868da6e53e19634ddb8b6f1e4 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 24 Jun 2022 14:00:03 -0400 Subject: [PATCH 30/31] Ensure relevant throwaway stack reads use the previous stack address. TODO: can CycleFetchPreviousThrowaway be used more widely? --- .../65816/Implementation/65816Implementation.hpp | 4 ++++ Processors/65816/Implementation/65816Storage.cpp | 14 +++++++------- Processors/65816/Implementation/65816Storage.hpp | 4 +++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 7ac380c08..ea07667fd 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -103,6 +103,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, perform_bus(((registers_.pc - 1) & 0xffff) | registers_.program_bank, &bus_throwaway_, MOS6502Esque::InternalOperationRead); break; + case CycleFetchPreviousThrowaway: + perform_bus(bus_address_, &bus_throwaway_, MOS6502Esque::InternalOperationRead); + break; + // // Data fetches and stores. // diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index ddd9202ec..5249be667 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -320,7 +320,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationCopyPBRToData); // Copy PBR to the data register. target(CyclePush); // PBR. - target(CycleAccessStack); // IO. + target(CycleFetchPreviousThrowaway); // IO. target(CycleFetchPC); // New PBR. @@ -728,14 +728,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22h. Stack; s, RTS. static void stack_rts(AccessType, bool, const std::function<void(MicroOp)> &target) { - target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. - target(CyclePull); // PCL. - target(CyclePull); // PCH. - target(CycleAccessStack); // IO. + target(CyclePull); // PCL. + target(CyclePull); // PCH. + target(CycleFetchPreviousThrowaway); // IO. - target(OperationPerform); // [RTS] + target(OperationPerform); // [RTS] } // 22i. Stack; s, RTL. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index d2ead050f..bac7fda8b 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -15,6 +15,8 @@ enum MicroOp: uint8_t { CycleFetchPCThrowaway, /// Fetches a byte from (PC - 1), and throws it away; useful for IO cycles that immediately follow incremented PCs. CycleFetchPreviousPCThrowaway, + /// Fetches from whichever address was used in the last bus cycle, and throws away the result. + CycleFetchPreviousThrowaway, /// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address. CycleFetchOpcode, @@ -274,7 +276,7 @@ struct ProcessorStorage { // Registers. RegisterPair16 a; RegisterPair16 x, y; - RegisterPair16 s; + RegisterPair16 s; uint16_t pc; // Flags aplenty. From f2c2027a8c50f5555570fbc1d2fc02f8a5cee6ee Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 24 Jun 2022 16:50:23 -0400 Subject: [PATCH 31/31] Disable test generation for commit. --- OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 277c00c9b..8f1d49be4 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -295,8 +295,8 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) { @implementation WDC65816ComparativeTests // A generator for tests; not intended to be a permanent fixture. -- (void)testGenerate { - [[[TestGenerator alloc] init] generate]; -} +//- (void)testGenerate { +// [[[TestGenerator alloc] init] generate]; +//} @end