From f87fe92bc8c4f1f9e78dc1cb5fd0809966181b9c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 23 Sep 2020 22:14:42 -0400 Subject: [PATCH 001/150] Begins a meandering road towards the 65816. --- .../Clock Signal.xcodeproj/project.pbxproj | 26 ++ Processors/65816/65816.hpp | 32 ++ .../65816/Implementation/65816Storage.cpp | 317 ++++++++++++++++++ .../65816/Implementation/65816Storage.hpp | 65 ++++ 4 files changed, 440 insertions(+) create mode 100644 Processors/65816/65816.hpp create mode 100644 Processors/65816/Implementation/65816Storage.cpp create mode 100644 Processors/65816/Implementation/65816Storage.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b86a62c7d..94d7fb34e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -861,6 +861,8 @@ 4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; }; 4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; }; 4BF8D4C82516E27A00BBE21B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BB8617024E22F4900A00E03 /* Accelerate.framework */; }; + 4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */; }; + 4BF8D4D6251C11DD00BBE21B /* 65816Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */; }; 4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; }; 4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; }; 4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; }; @@ -1788,6 +1790,9 @@ 4BF4A2D91F534DB300B171F4 /* TargetPlatforms.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TargetPlatforms.hpp; sourceTree = ""; }; 4BF52672218E752E00313227 /* ScanTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ScanTarget.hpp; path = ../../Outputs/ScanTarget.hpp; sourceTree = ""; }; 4BF6606A1F281573002CB053 /* ClockReceiver.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClockReceiver.hpp; sourceTree = ""; }; + 4BF8D4CD251C0C9C00BBE21B /* 65816.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816.hpp; sourceTree = ""; }; + 4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Storage.hpp; sourceTree = ""; }; + 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = 65816Storage.cpp; sourceTree = ""; }; 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AllRAMProcessor.cpp; sourceTree = ""; }; 4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AllRAMProcessor.hpp; sourceTree = ""; }; 4BFCA1251ECBE33200AC40C1 /* TestMachineZ80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachineZ80.h; sourceTree = ""; }; @@ -3485,6 +3490,7 @@ 4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */, 4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */, 4B1414561B58879D00E04248 /* 6502 */, + 4BF8D4CC251C0C9C00BBE21B /* 65816 */, 4BFF1D332233778C00838EA1 /* 68000 */, 4B77069E1EC9045B0053B588 /* Z80 */, ); @@ -3892,6 +3898,24 @@ path = ../../ClockReceiver; sourceTree = ""; }; + 4BF8D4CC251C0C9C00BBE21B /* 65816 */ = { + isa = PBXGroup; + children = ( + 4BF8D4CD251C0C9C00BBE21B /* 65816.hpp */, + 4BF8D4D2251C0D9F00BBE21B /* Implementation */, + ); + path = 65816; + sourceTree = ""; + }; + 4BF8D4D2251C0D9F00BBE21B /* Implementation */ = { + isa = PBXGroup; + children = ( + 4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */, + 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */, + ); + path = Implementation; + sourceTree = ""; + }; 4BFDD7891F7F2DB4008579B9 /* Utility */ = { isa = PBXGroup; children = ( @@ -4489,6 +4513,7 @@ 4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */, 4B055AA61FAE85EF0060FFFF /* Parser.cpp in Sources */, 4B055AE91FAE9B990060FFFF /* 6502Base.cpp in Sources */, + 4BF8D4D6251C11DD00BBE21B /* 65816Storage.cpp in Sources */, 4B055AEF1FAE9BF00060FFFF /* Typer.cpp in Sources */, 4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */, @@ -4771,6 +4796,7 @@ 4B55DD8320DF06680043F2E5 /* MachinePicker.swift in Sources */, 4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */, 4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, + 4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */, 4B0ACC2823775819008902D0 /* DMAController.cpp in Sources */, 4BC131702346DE5000E4FF3D /* StaticAnalyser.cpp in Sources */, 4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */, diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp new file mode 100644 index 000000000..acb76ccf3 --- /dev/null +++ b/Processors/65816/65816.hpp @@ -0,0 +1,32 @@ +// +// 65816.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/09/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef WDC65816_hpp +#define WDC65816_hpp + +#include +#include + +namespace CPU { +namespace WDC65816 { + +enum class Personality { + WDC65816, + WDC65802 +}; + +#include "Implementation/65816Storage.hpp" + +template class Processor: public ProcessorStorage { + +}; + +} +} + +#endif /* WDC65816_hpp */ diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp new file mode 100644 index 000000000..f3cde1803 --- /dev/null +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -0,0 +1,317 @@ +// +// 65816Storage.cpp +// Clock Signal +// +// Created by Thomas Harte on 23/09/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#include +#include + +#include "65816Storage.hpp" + +ProcessorStorage::ProcessorStorage() { + // 1a. Absolute a [read and write]. + const auto absolute_read = + install_ops({ + CycleFetchIncrementPC, // OpCode. + CycleFetchIncrementPC, // AAL. + CycleFetchIncrementPC, // AAH. + OperationConstructAbsolute, // (copy AAL and AAH fetch to address) + CycleFetchIncrementData, // Data low. + OperationSkipIf8, // (don't do the next fetch if in emulation mode) + CycleFetchIncrementData, // Data high. + OperationPerform, // (whatever the operation is) + OperationMoveToNextProgram + }); + + const auto absolute_write = + install_ops({ + CycleFetchIncrementPC, // OpCode. + CycleFetchIncrementPC, // AAL. + CycleFetchIncrementPC, // AAH. + OperationConstructAbsolute, // (copy AAL and AAH fetch to address) + OperationPerform, // (whatever the operation is) + CycleStoreIncrementData, // Data low. + OperationSkipIf8, // (don't do the next fetch if in emulation mode) + CycleStoreIncrementData, // Data high. + OperationMoveToNextProgram + }); + + // Install the instructions. +#define op set_instruction + /* 0x00 BRK s */ + /* 0x01 ORA (d, x) */ + /* 0x02 COP s */ + /* 0x03 ORA d, s */ + /* 0x04 TSB d */ + /* 0x05 ORA d */ + /* 0x06 ASL d */ + /* 0x07 ORA [d] */ + /* 0x08 PHP s */ + /* 0x09 ORA # */ + /* 0x0a ASL a */ + /* 0x0b PHD s */ + /* 0x0c TSB a */ + /* 0x0d ORA a */ op(0x0d, absolute_read, ORA); + /* 0x0e ASL a */ + /* 0x0f ORA al */ + + /* 0x10 BPL r */ + /* 0x11 ORA (d), y */ + /* 0x12 ORA (d) */ + /* 0x13 ORA (d, s), y */ + /* 0x14 TRB d */ + /* 0x15 ORA d,x */ + /* 0x16 ASL d, x */ + /* 0x17 ORA [d], y */ + /* 0x18 CLC i */ + /* 0x19 ORA a, y */ + /* 0x1a INC A */ + /* 0x1b TCS i */ + /* 0x1c TRB a */ + /* 0x1d ORA a, x */ + /* 0x1e ASL a, x */ + /* 0x1f ORA al, x */ + + /* 0x20 JSR a */ + /* 0x21 ORA (d), y */ + /* 0x22 AND (d, x) */ + /* 0x23 JSL al */ + /* 0x24 BIT d */ + /* 0x25 AND d */ + /* 0x26 ROL d */ + /* 0x27 AND [d] */ + /* 0x28 PLP s */ + /* 0x29 AND # */ + /* 0x2a ROL A */ + /* 0x2b PLD s */ + /* 0x2c BIT a */ op(0x2c, absolute_read, BIT); + /* 0x2d AND a */ op(0x2d, absolute_read, AND); + /* 0x2e ROL a */ + /* 0x2f AND al */ + + /* 0x30 BMI R */ + /* 0x31 AND (d), y */ + /* 0x32 AND (d) */ + /* 0x33 AND (d, s), y */ + /* 0x34 BIT d, x */ + /* 0x35 AND d, x */ + /* 0x36 TOL d, x */ + /* 0x37 AND [d], y */ + /* 0x38 SEC i */ + /* 0x39 AND a, y */ + /* 0x3a DEC A */ + /* 0x3b TSC i */ + /* 0x3c BIT a, x */ + /* 0x3d AND a, x */ + /* 0x3e TLD a, x */ + /* 0x3f AND al, x */ + + /* 0x40 RTI s */ + /* 0x41 EOR (d, x) */ + /* 0x42 WDM i */ + /* 0x43 EOR d, s */ + /* 0x44 MVP xyc */ + /* 0x45 EOR d */ + /* 0x46 LSR d */ + /* 0x47 EOR [d] */ + /* 0x48 PHA s */ + /* 0x49 EOR # */ + /* 0x4a LSR A */ + /* 0x4b PHK s */ + /* 0x4c JMP a */ + /* 0x4d EOR a */ op(0x4d, absolute_read, EOR); + /* 0x4e LSR a */ + /* 0x4f EOR Al */ + + /* 0x50 BVC r */ + /* 0x51 EOR (d), y */ + /* 0x52 EOR (d) */ + /* 0x53 EOR (d, s), y */ + /* 0x54 MVN xyc */ + /* 0x55 EOR d, x */ + /* 0x56 LSR d, x */ + /* 0x57 EOR [d],y */ + /* 0x58 CLI i */ + /* 0x59 EOR a, y */ + /* 0x5a PHY s */ + /* 0x5b TCD i */ + /* 0x5c JMP al */ + /* 0x5d EOR a, x */ + /* 0x5e LSR a, x */ + /* 0x5f EOR al, x */ + + /* 0x60 RTS s */ + /* 0x61 ADC (d, x) */ + /* 0x62 PER s */ + /* 0x63 ADC d, s */ + /* 0x64 STZ d */ + /* 0x65 ADC d */ + /* 0x66 ROR d */ + /* 0x67 ADC [d] */ + /* 0x68 PLA s */ + /* 0x69 ADC # */ + /* 0x6a ROR A */ + /* 0x6b RTL s */ + /* 0x6c JMP (a) */ + /* 0x6d ADC a */ op(0x6d, absolute_read, ADC); + /* 0x6e ROR a */ + /* 0x6f ADC al */ + + /* 0x70 BVS r */ + /* 0x71 ADC (d), y */ + /* 0x72 ADC (d) */ + /* 0x73 ADC (d, s), y */ + /* 0x74 STZ d, x */ + /* 0x75 ADC d, x */ + /* 0x76 ROR d, x */ + /* 0x77 ADC [d], y */ + /* 0x78 SEI i */ + /* 0x79 ADC a, y */ + /* 0x7a PLY s */ + /* 0x7b TDC i */ + /* 0x7c JMP (a, x) */ + /* 0x7d ADC a, x */ + /* 0x7e ROR a, x */ + /* 0x7f ADC al, x */ + + /* 0x80 BRA r */ + /* 0x81 STA (d, x) */ + /* 0x82 BRL rl */ + /* 0x83 STA d, s */ + /* 0x84 STY d */ + /* 0x85 STA d */ + /* 0x86 STX d */ + /* 0x87 STA [d] */ + /* 0x88 DEY i */ + /* 0x89 BIT # */ + /* 0x8a TXA i */ + /* 0x8b PHB s */ + /* 0x8c STY a */ op(0x8c, absolute_write, STY); + /* 0x8d STA a */ op(0x8d, absolute_write, STA); + /* 0x8e STX a */ op(0x8e, absolute_write, STX); + /* 0x8f STA al */ + + /* 0x90 BCC r */ + /* 0x91 STA (d), y */ + /* 0x92 STA (d) */ + /* 0x93 STA (d, x), y */ + /* 0x94 STY d, x */ + /* 0x95 STA d, x */ + /* 0x96 STX d, y */ + /* 0x97 STA [d], y */ + /* 0x98 TYA i */ + /* 0x99 STA a, y */ + /* 0x9a TXS i */ + /* 0x9b TXY i */ + /* 0x9c STZ a */ op(0x9c, absolute_write, STZ); + /* 0x9d STA a, x */ + /* 0x9e STZ a, x */ + /* 0x9f STA al, x */ + + /* 0xa0 LDY # */ + /* 0xa1 LDA (d, x) */ + /* 0xa2 LDX # */ + /* 0xa3 LDA d, s */ + /* 0xa4 LDY d */ + /* 0xa5 LDA d */ + /* 0xa6 LDX d */ + /* 0xa7 LDA [d] */ + /* 0xa8 TAY i */ + /* 0xa9 LDA # */ + /* 0xaa TAX i */ + /* 0xab PLB s */ + /* 0xac LDY a */ op(0xac, absolute_read, LDY); + /* 0xad LDA a */ op(0xad, absolute_read, LDA); + /* 0xae LDX a */ op(0xae, absolute_read, LDX); + /* 0xaf LDA al */ + + /* 0xb0 BCS r */ + /* 0xb1 LDA (d), y */ + /* 0xb2 LDA (d) */ + /* 0xb3 LDA (d, s), y */ + /* 0xb4 LDY d, x */ + /* 0xb5 LDA d, x */ + /* 0xb6 LDX d, y */ + /* 0xb7 LDA [d], y */ + /* 0xb8 CLV i */ + /* 0xb9 LDA a, y */ + /* 0xba TSX i */ + /* 0xbb TYX i */ + /* 0xbc LDY a, x */ + /* 0xbd LDA a, x */ + /* 0xbe LDX a, y */ + /* 0xbf LDA al, x */ + + /* 0xc0 CPY # */ + /* 0xc1 CMP (d, x) */ + /* 0xc2 REP # */ + /* 0xc3 CMP d, s */ + /* 0xc4 CPY d */ + /* 0xc5 CMP d */ + /* 0xc6 DEC d */ + /* 0xc7 CMP [d] */ + /* 0xc8 INY i */ + /* 0xc9 CMP # */ + /* 0xca DEX i */ + /* 0xcb WAI i */ + /* 0xcc CPY a */ op(0xcd, absolute_read, CPY); + /* 0xcd CMP a */ op(0xcd, absolute_read, CMP); + /* 0xce DEC a */ + /* 0xcf CMP al */ + + /* 0xd0 BNE r */ + /* 0xd1 CMP (d), y */ + /* 0xd2 CMP (d) */ + /* 0xd3 CMP (d, s), y */ + /* 0xd4 PEI s */ + /* 0xd5 CMP d, x */ + /* 0xd6 DEC d, x */ + /* 0xd7 CMP [d], y */ + /* 0xd8 CLD i */ + /* 0xd9 CMP a, y */ + /* 0xda PHX s */ + /* 0xdb STP i */ + /* 0xdc JMP (a) */ + /* 0xdd CMP a, x */ + /* 0xde DEC a, x */ + /* 0xdf CMP al, x */ + + /* 0xe0 CPX # */ + /* 0xe1 SBC (d, x) */ + /* 0xe2 SEP # */ + /* 0xe3 SBC d, s */ + /* 0xe4 CPX d */ + /* 0xe5 SBC d */ + /* 0xe6 INC d */ + /* 0xe7 SBC [d] */ + /* 0xe8 INX i */ + /* 0xe9 SBC # */ + /* 0xea NOP i */ + /* 0xeb XBA i */ + /* 0xec CPX a */ op(0xec, absolute_read, CPX); + /* 0xed SBC a */ op(0xed, absolute_read, SBC); + /* 0xee INC a */ + /* 0xef SBC al */ + + /* 0xf0 BEQ r */ + /* 0xf1 SBC (d), y */ + /* 0xf2 SBC (d) */ + /* 0xf3 SBC (d, s), y */ + /* 0xf4 PEA s */ + /* 0xf5 SBC d, x */ + /* 0xf6 INC d, x */ + /* 0xf7 SBC [d], y */ + /* 0xf8 SED i */ + /* 0xf9 SBC a, y */ + /* 0xfa PLX s */ + /* 0xfb XCE i */ + /* 0xfc JSR (a, x) */ + /* 0xfd SBC a, x */ + /* 0xfe INC a, x */ + /* 0xff SBC al, x */ + +#undef op +} diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp new file mode 100644 index 000000000..8ecef7ae6 --- /dev/null +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -0,0 +1,65 @@ +// +// 65816Implementation.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/09/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef WDC65816Implementation_h +#define WDC65816Implementation_h + +class ProcessorStorage { + public: + ProcessorStorage(); + + enum MicroOp: uint8_t { + /// Fetches a byte from the program counter to the instruction buffer and increments the program counter. + CycleFetchIncrementPC, + /// Fetches a byte from the data address to the data buffer and increments the data address. + CycleFetchIncrementData, + /// Stores a byte to the data address from the data buffer and increments the data address. + CycleStoreIncrementData, + + /// Skips the next micro-op if in emulation mode. + OperationSkipIf8, + + /// Sets the data address by copying the final two bytes of the instruction buffer. + OperationConstructAbsolute, + + /// Performs whatever operation goes with this program. + OperationPerform, + + /// Complete this set of micr-ops. + OperationMoveToNextProgram + }; + + enum Operation: uint8_t { + ADC, AND, BIT, CMP, CPX, CPY, EOR, LDA, LDX, LDY, ORA, SBC, + + STA, STX, STY, STZ, + }; + + struct Instruction { + size_t program_offset; + Operation operation; + }; + Instruction instructions[256]; + + private: + std::vector micro_ops_; + + size_t install_ops(std::initializer_list ops) { + // Just copy into place and return the index at which copying began. + const size_t index = micro_ops_.size(); + micro_ops_.insert(micro_ops_.end(), ops.begin(), ops.end()); + return index; + } + + void set_instruction(uint8_t opcode, size_t micro_ops, Operation operation) { + instructions[opcode].program_offset = micro_ops; + instructions[opcode].operation = operation; + } +}; + +#endif /* WDC65816Implementation_h */ From f9045b5352cbe5e094380df9776b6d4044720ce8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 23 Sep 2020 22:23:23 -0400 Subject: [PATCH 002/150] Rounds out declaration of the absolutes. --- .../65816/Implementation/65816Storage.cpp | 27 +++++++++++++++++-- .../65816/Implementation/65816Storage.hpp | 20 +++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index f3cde1803..5edd529df 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -39,6 +39,29 @@ ProcessorStorage::ProcessorStorage() { OperationMoveToNextProgram }); + // 1b. Absolute a, JMP. + const auto absolute_jmp = + install_ops({ + CycleFetchIncrementPC, // OpCode. + CycleFetchIncrementPC, // New PCL. + CycleFetchIncrementPC, // New PCH. + OperationPerform, // [JMP] + OperationMoveToNextProgram + }); + + // 1c. Absolute a, JSR. + const auto absolute_jsr = + install_ops({ + CycleFetchIncrementPC, // OpCode. + CycleFetchIncrementPC, // New PCL. + CycleFetchIncrementPC, // New PCH. + CycleFetchPreviousPC, // IO. + OperationPerform, // [JSR] + CyclePush, // PCH + CyclePush, // PCL + OperationMoveToNextProgram + }); + // Install the instructions. #define op set_instruction /* 0x00 BRK s */ @@ -75,7 +98,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x1e ASL a, x */ /* 0x1f ORA al, x */ - /* 0x20 JSR a */ + /* 0x20 JSR a */ op(0x20, absolute_jsr, JSR); /* 0x21 ORA (d), y */ /* 0x22 AND (d, x) */ /* 0x23 JSL al */ @@ -121,7 +144,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x49 EOR # */ /* 0x4a LSR A */ /* 0x4b PHK s */ - /* 0x4c JMP a */ + /* 0x4c JMP a */ op(0x4c, absolute_jmp, JMP); /* 0x4d EOR a */ op(0x4d, absolute_read, EOR); /* 0x4e LSR a */ /* 0x4f EOR Al */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 8ecef7ae6..53f767e51 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -16,11 +16,17 @@ class ProcessorStorage { enum MicroOp: uint8_t { /// Fetches a byte from the program counter to the instruction buffer and increments the program counter. CycleFetchIncrementPC, + /// Does a no-effect fetch from PC-1. + CycleFetchPreviousPC, + /// Fetches a byte from the data address to the data buffer and increments the data address. CycleFetchIncrementData, /// Stores a byte to the data address from the data buffer and increments the data address. CycleStoreIncrementData, + /// Pushes a single byte from the data buffer to the stack. + CyclePush, + /// Skips the next micro-op if in emulation mode. OperationSkipIf8, @@ -35,9 +41,21 @@ class ProcessorStorage { }; enum Operation: uint8_t { - ADC, AND, BIT, CMP, CPX, CPY, EOR, LDA, LDX, LDY, ORA, SBC, + // These perform the named operation using the value in the data buffer. + ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, + // These load the respective register from the data buffer. + LDA, LDX, LDY, + + // These move the respective register (or value) to the data buffer. STA, STX, STY, STZ, + + /// Loads the PC with the operand from the instruction buffer. + JMP, + + /// Loads the PC with the operand from the instruction buffer, placing + /// the old PC into the data buffer. + JSR, }; struct Instruction { From 72b5584042022a76cba06b7994b1da54c1d34c34 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 23 Sep 2020 22:28:15 -0400 Subject: [PATCH 003/150] Immediately runs afoul of a read/write difference in the specs between 8/16-bit mode that suggests maybe this isn't a good structure. Perhaps generators of some sort? --- Processors/65816/Implementation/65816Storage.cpp | 13 +++++++++++++ Processors/65816/Implementation/65816Storage.hpp | 2 ++ 2 files changed, 15 insertions(+) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 5edd529df..fd06007a0 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -62,6 +62,19 @@ ProcessorStorage::ProcessorStorage() { OperationMoveToNextProgram }); + // 1d. Absolute read-modify-write. +// const auto absolute_rmw = +// install_ops({ +// CycleFetchIncrementPC, // OpCode. +// CycleFetchIncrementPC, // AAL. +// CycleFetchIncrementPC, // AAH. +// CycleFetchIncrementData, // Data low. +// OperationSkipIf8, // (don't do the next fetch if in emulation mode) +// CycleFetchIncrementData, // Data high. +// OperationPerform, // (whatever the operation is) +// OperationMoveToNextProgram +// }); + // Install the instructions. #define op set_instruction /* 0x00 BRK s */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 53f767e51..533c356a7 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -23,6 +23,8 @@ class ProcessorStorage { CycleFetchIncrementData, /// Stores a byte to the data address from the data buffer and increments the data address. CycleStoreIncrementData, + /// Decrements the data address and writes a byte to it. + CycleDecrementStoreData, /// Pushes a single byte from the data buffer to the stack. CyclePush, From 5c9192e5e682fa1d03ff223100a7c805c815fb2b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 24 Sep 2020 17:36:11 -0400 Subject: [PATCH 004/150] Switches to generators for spitting out micro-ops. Hopefully with a lot of parts to factor out naturally. --- .../65816/Implementation/65816Storage.cpp | 163 ++++++++++-------- .../65816/Implementation/65816Storage.hpp | 16 +- 2 files changed, 98 insertions(+), 81 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index fd06007a0..410ceb746 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -11,69 +11,84 @@ #include "65816Storage.hpp" -ProcessorStorage::ProcessorStorage() { - // 1a. Absolute a [read and write]. - const auto absolute_read = - install_ops({ - CycleFetchIncrementPC, // OpCode. - CycleFetchIncrementPC, // AAL. - CycleFetchIncrementPC, // AAH. - OperationConstructAbsolute, // (copy AAL and AAH fetch to address) - CycleFetchIncrementData, // Data low. - OperationSkipIf8, // (don't do the next fetch if in emulation mode) - CycleFetchIncrementData, // Data high. - OperationPerform, // (whatever the operation is) - OperationMoveToNextProgram - }); +namespace { - const auto absolute_write = - install_ops({ - CycleFetchIncrementPC, // OpCode. - CycleFetchIncrementPC, // AAL. - CycleFetchIncrementPC, // AAH. - OperationConstructAbsolute, // (copy AAL and AAH fetch to address) - OperationPerform, // (whatever the operation is) - CycleStoreIncrementData, // Data low. - OperationSkipIf8, // (don't do the next fetch if in emulation mode) - CycleStoreIncrementData, // Data high. - OperationMoveToNextProgram - }); +enum class AccessType { + Read, Write, ReadModifyWrite +}; + +} + +ProcessorStorage::ProcessorStorage() { + /* + Code below is structured to ease translation from Table 5-7 of the 2018 + edition of the WDC 65816 datasheet. + + In each case the relevant addressing mode is described here via a generator + function that will spit out the correct MicroOps based on access type + (i.e. read, write or read-modify-write) and data size (8- or 16-bit). + + That leads up to being able to declare the opcode map by addressing mode + and operation alone. + + Things the generators can assume before they start: + + 1) the opcode has already been fetched and decoded, and the program counter incremented; + 2) the data buffer is empty; and + 3) the data address is undefined. + */ + + // 1a. Absolute a. + auto absolute = [] (AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(OperationConstructAbsolute); // Calculate data address. + + if(type == AccessType::Write) { + target(OperationPerform); // Perform operation to fill the data buffer. + target(CycleStoreIncrementData); // Data low. + if(is8bit) target(CycleStoreIncrementData); // Data high. + } else { + target(CycleFetchIncrementData); // Data low. + if(is8bit) target(CycleFetchIncrementData); // Data high. + target(OperationPerform); // Perform operation from the data buffer. + } + }; // 1b. Absolute a, JMP. - const auto absolute_jmp = - install_ops({ - CycleFetchIncrementPC, // OpCode. - CycleFetchIncrementPC, // New PCL. - CycleFetchIncrementPC, // New PCH. - OperationPerform, // [JMP] - OperationMoveToNextProgram - }); + auto absolute_jmp = [] (AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // New PCL. + target(CycleFetchIncrementPC); // New PCH. + target(OperationPerform); // [JMP] + }; // 1c. Absolute a, JSR. - const auto absolute_jsr = - install_ops({ - CycleFetchIncrementPC, // OpCode. - CycleFetchIncrementPC, // New PCL. - CycleFetchIncrementPC, // New PCH. - CycleFetchPreviousPC, // IO. - OperationPerform, // [JSR] - CyclePush, // PCH - CyclePush, // PCL - OperationMoveToNextProgram - }); + auto absolute_jsr = [] (AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // New PCL. + target(CycleFetchIncrementPC); // New PCH. + target(CycleFetchPCDiscard); // IO + target(OperationPerform); // [JSR] + target(CyclePush); // PCH + target(CyclePush); // PCL + }; // 1d. Absolute read-modify-write. -// const auto absolute_rmw = -// install_ops({ -// CycleFetchIncrementPC, // OpCode. -// CycleFetchIncrementPC, // AAL. -// CycleFetchIncrementPC, // AAH. -// CycleFetchIncrementData, // Data low. -// OperationSkipIf8, // (don't do the next fetch if in emulation mode) -// CycleFetchIncrementData, // Data high. -// OperationPerform, // (whatever the operation is) -// OperationMoveToNextProgram -// }); + auto absolute_rmw = [] (AccessType, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(OperationConstructAbsolute); // Calculate data address. + + if(!is8bit) target(CycleFetchIncrementData); // Data low. + target(CycleFetchData); // Data [high]. + + if(!is8bit) target(CycleFetchData); // 16-bit: reread final byte of data. + else target(CycleStoreData); // 8-bit rewrite final byte of data. + + target(OperationPerform); // Perform operation within the data buffer. + + if(!is8bit) target(CycleStoreDecrementData); // Data high. + target(CycleStoreData); // Data [low]. + }; // Install the instructions. #define op set_instruction @@ -90,7 +105,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x0a ASL a */ /* 0x0b PHD s */ /* 0x0c TSB a */ - /* 0x0d ORA a */ op(0x0d, absolute_read, ORA); + /* 0x0d ORA a */ // op(0x0d, absolute_read, ORA); /* 0x0e ASL a */ /* 0x0f ORA al */ @@ -111,7 +126,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x1e ASL a, x */ /* 0x1f ORA al, x */ - /* 0x20 JSR a */ op(0x20, absolute_jsr, JSR); + /* 0x20 JSR a */ // op(0x20, absolute_jsr, JSR); /* 0x21 ORA (d), y */ /* 0x22 AND (d, x) */ /* 0x23 JSL al */ @@ -123,8 +138,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x29 AND # */ /* 0x2a ROL A */ /* 0x2b PLD s */ - /* 0x2c BIT a */ op(0x2c, absolute_read, BIT); - /* 0x2d AND a */ op(0x2d, absolute_read, AND); + /* 0x2c BIT a */ // op(0x2c, absolute_read, BIT); + /* 0x2d AND a */ // op(0x2d, absolute_read, AND); /* 0x2e ROL a */ /* 0x2f AND al */ @@ -157,8 +172,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x49 EOR # */ /* 0x4a LSR A */ /* 0x4b PHK s */ - /* 0x4c JMP a */ op(0x4c, absolute_jmp, JMP); - /* 0x4d EOR a */ op(0x4d, absolute_read, EOR); + /* 0x4c JMP a */ // op(0x4c, absolute_jmp, JMP); + /* 0x4d EOR a */ // op(0x4d, absolute_read, EOR); /* 0x4e LSR a */ /* 0x4f EOR Al */ @@ -192,7 +207,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x6a ROR A */ /* 0x6b RTL s */ /* 0x6c JMP (a) */ - /* 0x6d ADC a */ op(0x6d, absolute_read, ADC); + /* 0x6d ADC a */ // op(0x6d, absolute_read, ADC); /* 0x6e ROR a */ /* 0x6f ADC al */ @@ -225,9 +240,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x89 BIT # */ /* 0x8a TXA i */ /* 0x8b PHB s */ - /* 0x8c STY a */ op(0x8c, absolute_write, STY); - /* 0x8d STA a */ op(0x8d, absolute_write, STA); - /* 0x8e STX a */ op(0x8e, absolute_write, STX); + /* 0x8c STY a */ // op(0x8c, absolute_write, STY); + /* 0x8d STA a */ // op(0x8d, absolute_write, STA); + /* 0x8e STX a */ // op(0x8e, absolute_write, STX); /* 0x8f STA al */ /* 0x90 BCC r */ @@ -242,7 +257,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x99 STA a, y */ /* 0x9a TXS i */ /* 0x9b TXY i */ - /* 0x9c STZ a */ op(0x9c, absolute_write, STZ); + /* 0x9c STZ a */ // op(0x9c, absolute_write, STZ); /* 0x9d STA a, x */ /* 0x9e STZ a, x */ /* 0x9f STA al, x */ @@ -259,9 +274,9 @@ ProcessorStorage::ProcessorStorage() { /* 0xa9 LDA # */ /* 0xaa TAX i */ /* 0xab PLB s */ - /* 0xac LDY a */ op(0xac, absolute_read, LDY); - /* 0xad LDA a */ op(0xad, absolute_read, LDA); - /* 0xae LDX a */ op(0xae, absolute_read, LDX); + /* 0xac LDY a */ // op(0xac, absolute_read, LDY); + /* 0xad LDA a */ // op(0xad, absolute_read, LDA); + /* 0xae LDX a */ // op(0xae, absolute_read, LDX); /* 0xaf LDA al */ /* 0xb0 BCS r */ @@ -293,8 +308,8 @@ ProcessorStorage::ProcessorStorage() { /* 0xc9 CMP # */ /* 0xca DEX i */ /* 0xcb WAI i */ - /* 0xcc CPY a */ op(0xcd, absolute_read, CPY); - /* 0xcd CMP a */ op(0xcd, absolute_read, CMP); + /* 0xcc CPY a */ // op(0xcd, absolute_read, CPY); + /* 0xcd CMP a */ // op(0xcd, absolute_read, CMP); /* 0xce DEC a */ /* 0xcf CMP al */ @@ -327,8 +342,8 @@ ProcessorStorage::ProcessorStorage() { /* 0xe9 SBC # */ /* 0xea NOP i */ /* 0xeb XBA i */ - /* 0xec CPX a */ op(0xec, absolute_read, CPX); - /* 0xed SBC a */ op(0xed, absolute_read, SBC); + /* 0xec CPX a */ // op(0xec, absolute_read, CPX); + /* 0xed SBC a */ // op(0xed, absolute_read, SBC); /* 0xee INC a */ /* 0xef SBC al */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 533c356a7..8a8ccf014 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -16,22 +16,24 @@ class ProcessorStorage { enum MicroOp: uint8_t { /// Fetches a byte from the program counter to the instruction buffer and increments the program counter. CycleFetchIncrementPC, - /// Does a no-effect fetch from PC-1. - CycleFetchPreviousPC, + /// Fetches a byte from the program counter without incrementing it, and throws it away. + CycleFetchPCDiscard, + /// Fetches a byte from the data address to the data buffer. + CycleFetchData, /// Fetches a byte from the data address to the data buffer and increments the data address. CycleFetchIncrementData, + + /// Stores a byte from the data buffer. + CycleStoreData, /// Stores a byte to the data address from the data buffer and increments the data address. CycleStoreIncrementData, - /// Decrements the data address and writes a byte to it. - CycleDecrementStoreData, + /// Stores a byte to the data address from the data buffer and decrements the data address. + CycleStoreDecrementData, /// Pushes a single byte from the data buffer to the stack. CyclePush, - /// Skips the next micro-op if in emulation mode. - OperationSkipIf8, - /// Sets the data address by copying the final two bytes of the instruction buffer. OperationConstructAbsolute, From d707c5ac952fbae327244c1d467ad96d04ca18a3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 24 Sep 2020 22:27:20 -0400 Subject: [PATCH 005/150] Switches to generators with stable pointers; adds 2a. --- .../65816/Implementation/65816Storage.cpp | 132 ++++++++++++------ .../65816/Implementation/65816Storage.hpp | 98 ++++++------- 2 files changed, 137 insertions(+), 93 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 410ceb746..0cef9c14d 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -6,20 +6,11 @@ // Copyright © 2020 Thomas Harte. All rights reserved. // -#include -#include +#include "../65816.hpp" -#include "65816Storage.hpp" +using namespace CPU::WDC65816; -namespace { - -enum class AccessType { - Read, Write, ReadModifyWrite -}; - -} - -ProcessorStorage::ProcessorStorage() { +struct CPU::WDC65816::ProcessorStorageConstructor { /* Code below is structured to ease translation from Table 5-7 of the 2018 edition of the WDC 65816 datasheet. @@ -39,10 +30,10 @@ ProcessorStorage::ProcessorStorage() { */ // 1a. Absolute a. - auto absolute = [] (AccessType type, bool is8bit, const std::function &target) { - target(CycleFetchIncrementPC); // AAL. - target(CycleFetchIncrementPC); // AAH. - target(OperationConstructAbsolute); // Calculate data address. + static void absolute(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(OperationConstructAbsolute); // Calculate data address. if(type == AccessType::Write) { target(OperationPerform); // Perform operation to fill the data buffer. @@ -56,27 +47,29 @@ ProcessorStorage::ProcessorStorage() { }; // 1b. Absolute a, JMP. - auto absolute_jmp = [] (AccessType, bool, const std::function &target) { + static void absolute_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. - target(CycleFetchIncrementPC); // New PCH. + target(CycleFetchPC); // New PCH. + target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // [JMP] }; // 1c. Absolute a, JSR. - auto absolute_jsr = [] (AccessType, bool, const std::function &target) { + static void absolute_jsr(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. - target(CycleFetchIncrementPC); // New PCH. - target(CycleFetchPCDiscard); // IO + target(CycleFetchPC); // New PCH. + target(CycleFetchPC); // IO + target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // [JSR] target(CyclePush); // PCH target(CyclePush); // PCL }; // 1d. Absolute read-modify-write. - auto absolute_rmw = [] (AccessType, bool is8bit, const std::function &target) { - target(CycleFetchIncrementPC); // AAL. - target(CycleFetchIncrementPC); // AAH. - target(OperationConstructAbsolute); // Calculate data address. + static void absolute_rmw(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(OperationConstructAbsolute); // Calculate data address. if(!is8bit) target(CycleFetchIncrementData); // Data low. target(CycleFetchData); // Data [high]. @@ -90,8 +83,57 @@ ProcessorStorage::ProcessorStorage() { target(CycleStoreData); // Data [low]. }; + // 2a. Absolute Indexed Indirect (a, x), JMP. + static void absolute_indexed_indirect_jmp(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchPC); // AAH. + target(CycleFetchPC); // IO. + target(OperationConstructAbsoluteIndexedIndirect); // Calculate data address. + target(CycleFetchIncrementData); // New PCL + target(CycleFetchData); // New PCH. + target(OperationPerform); // [JMP] + }; + + // 2b. Absolute Indexed Indirect (a, x), JSR. + +}; + +AccessType ProcessorStorage::access_type_for_operation(Operation operation) { + switch(operation) { + case ADC: case AND: case BIT: case CMP: + case CPX: case CPY: case EOR: case ORA: + case SBC: + + case LDA: case LDX: case LDY: + + case JMP: case JSR: + return AccessType::Read; + + case STA: case STX: case STY: case STZ: + return AccessType::Write; + } +} + +void ProcessorStorage::install(void (* generator)(AccessType, bool, const std::function&), Operation operation, ProcessorStorageConstructor &) { + const AccessType access_type = access_type_for_operation(operation); + (*generator)(access_type, true, [] (MicroOp) {}); + + // TODO: + // (1) use [hash of] pointer to generator to determine whether operation tables have already been built; + // (2) if not, build them in both 8- and 16-bit variations; + // (3) insert appropriate entries into an instruction table for the current instruction; and + // (4) increment the instruction counter. + // + // Additional observation: temporary storage would be helpful, so load that into ProcessorStorageConstructor. +} + + +ProcessorStorage::ProcessorStorage() { + ProcessorStorageConstructor constructor; + // Install the instructions. -#define op set_instruction +#define op(x, y) install(&ProcessorStorageConstructor::x, y, constructor) + /* 0x00 BRK s */ /* 0x01 ORA (d, x) */ /* 0x02 COP s */ @@ -105,7 +147,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x0a ASL a */ /* 0x0b PHD s */ /* 0x0c TSB a */ - /* 0x0d ORA a */ // op(0x0d, absolute_read, ORA); + /* 0x0d ORA a */ op(absolute, ORA); /* 0x0e ASL a */ /* 0x0f ORA al */ @@ -126,7 +168,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x1e ASL a, x */ /* 0x1f ORA al, x */ - /* 0x20 JSR a */ // op(0x20, absolute_jsr, JSR); + /* 0x20 JSR a */ op(absolute_jsr, JSR); /* 0x21 ORA (d), y */ /* 0x22 AND (d, x) */ /* 0x23 JSL al */ @@ -138,8 +180,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x29 AND # */ /* 0x2a ROL A */ /* 0x2b PLD s */ - /* 0x2c BIT a */ // op(0x2c, absolute_read, BIT); - /* 0x2d AND a */ // op(0x2d, absolute_read, AND); + /* 0x2c BIT a */ op(absolute, BIT); + /* 0x2d AND a */ op(absolute, AND); /* 0x2e ROL a */ /* 0x2f AND al */ @@ -172,8 +214,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x49 EOR # */ /* 0x4a LSR A */ /* 0x4b PHK s */ - /* 0x4c JMP a */ // op(0x4c, absolute_jmp, JMP); - /* 0x4d EOR a */ // op(0x4d, absolute_read, EOR); + /* 0x4c JMP a */ op(absolute, JMP); + /* 0x4d EOR a */ op(absolute, EOR); /* 0x4e LSR a */ /* 0x4f EOR Al */ @@ -207,7 +249,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x6a ROR A */ /* 0x6b RTL s */ /* 0x6c JMP (a) */ - /* 0x6d ADC a */ // op(0x6d, absolute_read, ADC); + /* 0x6d ADC a */ op(absolute, ADC); /* 0x6e ROR a */ /* 0x6f ADC al */ @@ -223,7 +265,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x79 ADC a, y */ /* 0x7a PLY s */ /* 0x7b TDC i */ - /* 0x7c JMP (a, x) */ + /* 0x7c JMP (a, x) */ op(absolute_indexed_indirect_jmp, JMP); /* 0x7d ADC a, x */ /* 0x7e ROR a, x */ /* 0x7f ADC al, x */ @@ -240,9 +282,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x89 BIT # */ /* 0x8a TXA i */ /* 0x8b PHB s */ - /* 0x8c STY a */ // op(0x8c, absolute_write, STY); - /* 0x8d STA a */ // op(0x8d, absolute_write, STA); - /* 0x8e STX a */ // op(0x8e, absolute_write, STX); + /* 0x8c STY a */ op(absolute, STY); + /* 0x8d STA a */ op(absolute, STA); + /* 0x8e STX a */ op(absolute, STX); /* 0x8f STA al */ /* 0x90 BCC r */ @@ -257,7 +299,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x99 STA a, y */ /* 0x9a TXS i */ /* 0x9b TXY i */ - /* 0x9c STZ a */ // op(0x9c, absolute_write, STZ); + /* 0x9c STZ a */ op(absolute, STZ); /* 0x9d STA a, x */ /* 0x9e STZ a, x */ /* 0x9f STA al, x */ @@ -274,9 +316,9 @@ ProcessorStorage::ProcessorStorage() { /* 0xa9 LDA # */ /* 0xaa TAX i */ /* 0xab PLB s */ - /* 0xac LDY a */ // op(0xac, absolute_read, LDY); - /* 0xad LDA a */ // op(0xad, absolute_read, LDA); - /* 0xae LDX a */ // op(0xae, absolute_read, LDX); + /* 0xac LDY a */ op(absolute, LDY); + /* 0xad LDA a */ op(absolute, LDA); + /* 0xae LDX a */ op(absolute, LDX); /* 0xaf LDA al */ /* 0xb0 BCS r */ @@ -308,8 +350,8 @@ ProcessorStorage::ProcessorStorage() { /* 0xc9 CMP # */ /* 0xca DEX i */ /* 0xcb WAI i */ - /* 0xcc CPY a */ // op(0xcd, absolute_read, CPY); - /* 0xcd CMP a */ // op(0xcd, absolute_read, CMP); + /* 0xcc CPY a */ op(absolute, CPY); + /* 0xcd CMP a */ op(absolute, CMP); /* 0xce DEC a */ /* 0xcf CMP al */ @@ -342,8 +384,8 @@ ProcessorStorage::ProcessorStorage() { /* 0xe9 SBC # */ /* 0xea NOP i */ /* 0xeb XBA i */ - /* 0xec CPX a */ // op(0xec, absolute_read, CPX); - /* 0xed SBC a */ // op(0xed, absolute_read, SBC); + /* 0xec CPX a */ op(absolute, CPX); + /* 0xed SBC a */ op(absolute, SBC); /* 0xee INC a */ /* 0xef SBC al */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 8a8ccf014..e8a635e61 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -9,56 +9,67 @@ #ifndef WDC65816Implementation_h #define WDC65816Implementation_h +enum MicroOp: uint8_t { + /// Fetches a byte from the program counter to the instruction buffer and increments the program counter. + CycleFetchIncrementPC, + /// Fetches a byte from the program counter without incrementing it, and throws it away. + CycleFetchPC, + + /// Fetches a byte from the data address to the data buffer. + CycleFetchData, + /// Fetches a byte from the data address to the data buffer and increments the data address. + CycleFetchIncrementData, + + /// Stores a byte from the data buffer. + CycleStoreData, + /// 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. + CycleStoreDecrementData, + + /// Pushes a single byte from the data buffer to the stack. + CyclePush, + + /// Sets the data address by copying the final two bytes of the instruction buffer. + OperationConstructAbsolute, + /// Sets the data address + OperationConstructAbsoluteIndexedIndirect, + + /// Performs whatever operation goes with this program. + OperationPerform, + + /// Complete this set of micr-ops. + OperationMoveToNextProgram +}; + +enum class AccessType { + Read, Write +}; + +class ProcessorStorageConstructor; + class ProcessorStorage { public: ProcessorStorage(); - enum MicroOp: uint8_t { - /// Fetches a byte from the program counter to the instruction buffer and increments the program counter. - CycleFetchIncrementPC, - /// Fetches a byte from the program counter without incrementing it, and throws it away. - CycleFetchPCDiscard, - - /// Fetches a byte from the data address to the data buffer. - CycleFetchData, - /// Fetches a byte from the data address to the data buffer and increments the data address. - CycleFetchIncrementData, - - /// Stores a byte from the data buffer. - CycleStoreData, - /// 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. - CycleStoreDecrementData, - - /// Pushes a single byte from the data buffer to the stack. - CyclePush, - - /// Sets the data address by copying the final two bytes of the instruction buffer. - OperationConstructAbsolute, - - /// Performs whatever operation goes with this program. - OperationPerform, - - /// Complete this set of micr-ops. - OperationMoveToNextProgram - }; - enum Operation: uint8_t { - // These perform the named operation using the value in the data buffer. + // These perform the named operation using the value in the data buffer; + // they are implicitly AccessType::Read. ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, - // These load the respective register from the data buffer. + // These load the respective register from the data buffer; + // they are implicitly AccessType::Read. LDA, LDX, LDY, - // These move the respective register (or value) to the data buffer. + // These move the respective register (or value) to the data buffer; + // they are implicitly AccessType::Write. STA, STX, STY, STZ, - /// Loads the PC with the operand from the instruction buffer. + /// Loads the PC with the operand from the data buffer. JMP, - /// Loads the PC with the operand from the instruction buffer, placing - /// the old PC into the data buffer. + /// Loads the PC with the operand from the daa buffer, replacing + /// it with the old PC. JSR, }; @@ -71,17 +82,8 @@ class ProcessorStorage { private: std::vector micro_ops_; - size_t install_ops(std::initializer_list ops) { - // Just copy into place and return the index at which copying began. - const size_t index = micro_ops_.size(); - micro_ops_.insert(micro_ops_.end(), ops.begin(), ops.end()); - return index; - } - - void set_instruction(uint8_t opcode, size_t micro_ops, Operation operation) { - instructions[opcode].program_offset = micro_ops; - instructions[opcode].operation = operation; - } + AccessType access_type_for_operation(Operation); + void install(void (* generator)(AccessType, bool, const std::function&), Operation, ProcessorStorageConstructor &); }; #endif /* WDC65816Implementation_h */ From 95af1815c8eb1c4c0dcaaef952f5c013af2f341e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 24 Sep 2020 22:37:31 -0400 Subject: [PATCH 006/150] Completes absolute indexed indirect micro-ops. For the record: this is just six out of forty-seven codes complete. Or about two-thirds of six pages. Plenty to do even before I start trying to interpret these things. --- Processors/65816/Implementation/65816Storage.cpp | 16 +++++++++++++++- Processors/65816/Implementation/65816Storage.hpp | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 0cef9c14d..163b0bb76 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -95,7 +95,21 @@ struct CPU::WDC65816::ProcessorStorageConstructor { }; // 2b. Absolute Indexed Indirect (a, x), JSR. + static void absolute_indexed_indirect_jsr(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(OperationCopyPCToData); // Prepare to push. + target(CyclePush); // PCH + target(CyclePush); // PCL + + target(CycleFetchPC); // AAH. + target(CycleFetchPC); // IO. + + target(OperationConstructAbsoluteIndexedIndirect); // Calculate data address. + target(CycleFetchIncrementData); // New PCL + target(CycleFetchData); // New PCH. + target(OperationPerform); // [JSR] + } }; AccessType ProcessorStorage::access_type_for_operation(Operation operation) { @@ -401,7 +415,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf9 SBC a, y */ /* 0xfa PLX s */ /* 0xfb XCE i */ - /* 0xfc JSR (a, x) */ + /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JSR); /* 0xfd SBC a, x */ /* 0xfe INC a, x */ /* 0xff SBC al, x */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index e8a635e61..e69005239 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -38,6 +38,9 @@ enum MicroOp: uint8_t { /// Performs whatever operation goes with this program. OperationPerform, + /// Copies the current program counter to the data buffer. + OperationCopyPCToData, + /// Complete this set of micr-ops. OperationMoveToNextProgram }; From 22c792dc46956b5d84e3e6492cede6e0764024db Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 17:18:25 -0400 Subject: [PATCH 007/150] Adds enough logic to start serialising instructions to somewhere. Possibly extraneous for now, but it means I can start stepping and testing. --- .../65816/Implementation/65816Storage.cpp | 106 +++++++++++++----- .../65816/Implementation/65816Storage.hpp | 54 ++++----- 2 files changed, 102 insertions(+), 58 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 163b0bb76..1e2506f7c 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -7,10 +7,83 @@ // #include "../65816.hpp" +#include using namespace CPU::WDC65816; struct CPU::WDC65816::ProcessorStorageConstructor { + // Establish that a storage constructor needs access to ProcessorStorage. + ProcessorStorage &storage_; + ProcessorStorageConstructor(ProcessorStorage &storage) : storage_(storage) {} + + enum class AccessType { + Read, Write + }; + + constexpr AccessType access_type_for_operation(Operation operation) { + switch(operation) { + case ADC: case AND: case BIT: case CMP: + case CPX: case CPY: case EOR: case ORA: + case SBC: + + case LDA: case LDX: case LDY: + + case JMP: case JSR: + return AccessType::Read; + + case STA: case STX: case STY: case STZ: + return AccessType::Write; + } + } + + typedef void (* Generator)(AccessType, bool, const std::function&); + using GeneratorKey = std::tuple; + std::map> installed_patterns; + + uint8_t opcode = 0; + void install(Generator generator, Operation operation) { + // Determine the access type implied by this operation. + const AccessType access_type = access_type_for_operation(operation); + + // Check whether this access type + addressing mode generator has already been generated. + const auto key = std::make_pair(access_type, generator); + const auto map_entry = installed_patterns.find(key); + size_t micro_op_location_8, micro_op_location_16; + + // If it wasn't found, generate it now in both 8- and 16-bit variants. + // Otherwise, get the location of the existing entries. + if(map_entry == installed_patterns.end()) { + // Generate 8-bit steps. + micro_op_location_8 = storage_.micro_ops_.size(); + (*generator)(access_type, true, [this] (MicroOp op) { + this->storage_.micro_ops_.push_back(op); + }); + storage_.micro_ops_.push_back(OperationMoveToNextProgram); + + // Generate 16-bit steps. + micro_op_location_16 = storage_.micro_ops_.size(); + (*generator)(access_type, false, [this] (MicroOp op) { + this->storage_.micro_ops_.push_back(op); + }); + storage_.micro_ops_.push_back(OperationMoveToNextProgram); + + // Insert into the map. + installed_patterns[key] = std::make_pair(micro_op_location_8, micro_op_location_16); + } else { + micro_op_location_8 = map_entry->second.first; + micro_op_location_16 = map_entry->second.second; + } + + // Fill in the proper table entries and increment the opcode pointer. + storage_.instructions[opcode].program_offset = micro_op_location_8; + storage_.instructions[opcode].operation = operation; + + storage_.instructions[opcode + 256].program_offset = micro_op_location_16; + storage_.instructions[opcode + 256].operation = operation; + + ++opcode; + } + /* Code below is structured to ease translation from Table 5-7 of the 2018 edition of the WDC 65816 datasheet. @@ -112,41 +185,12 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } }; -AccessType ProcessorStorage::access_type_for_operation(Operation operation) { - switch(operation) { - case ADC: case AND: case BIT: case CMP: - case CPX: case CPY: case EOR: case ORA: - case SBC: - - case LDA: case LDX: case LDY: - - case JMP: case JSR: - return AccessType::Read; - - case STA: case STX: case STY: case STZ: - return AccessType::Write; - } -} - -void ProcessorStorage::install(void (* generator)(AccessType, bool, const std::function&), Operation operation, ProcessorStorageConstructor &) { - const AccessType access_type = access_type_for_operation(operation); - (*generator)(access_type, true, [] (MicroOp) {}); - - // TODO: - // (1) use [hash of] pointer to generator to determine whether operation tables have already been built; - // (2) if not, build them in both 8- and 16-bit variations; - // (3) insert appropriate entries into an instruction table for the current instruction; and - // (4) increment the instruction counter. - // - // Additional observation: temporary storage would be helpful, so load that into ProcessorStorageConstructor. -} - ProcessorStorage::ProcessorStorage() { - ProcessorStorageConstructor constructor; + ProcessorStorageConstructor constructor(*this); // Install the instructions. -#define op(x, y) install(&ProcessorStorageConstructor::x, y, constructor) +#define op(x, y) constructor.install(&ProcessorStorageConstructor::x, y) /* 0x00 BRK s */ /* 0x01 ORA (d, x) */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index e69005239..d1f3dcf96 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -45,8 +45,25 @@ enum MicroOp: uint8_t { OperationMoveToNextProgram }; -enum class AccessType { - Read, Write +enum Operation: uint8_t { + // These perform the named operation using the value in the data buffer; + // they are implicitly AccessType::Read. + ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, + + // These load the respective register from the data buffer; + // they are implicitly AccessType::Read. + LDA, LDX, LDY, + + // These move the respective register (or value) to the data buffer; + // they are implicitly AccessType::Write. + STA, STX, STY, STZ, + + /// Loads the PC with the operand from the data buffer. + JMP, + + /// Loads the PC with the operand from the daa buffer, replacing + /// it with the old PC. + JSR, }; class ProcessorStorageConstructor; @@ -55,38 +72,21 @@ class ProcessorStorage { public: ProcessorStorage(); - enum Operation: uint8_t { - // These perform the named operation using the value in the data buffer; - // they are implicitly AccessType::Read. - ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, - - // These load the respective register from the data buffer; - // they are implicitly AccessType::Read. - LDA, LDX, LDY, - - // These move the respective register (or value) to the data buffer; - // they are implicitly AccessType::Write. - STA, STX, STY, STZ, - - /// Loads the PC with the operand from the data buffer. - JMP, - - /// Loads the PC with the operand from the daa buffer, replacing - /// it with the old PC. - JSR, - }; - struct Instruction { size_t program_offset; Operation operation; }; - Instruction instructions[256]; + Instruction instructions[512 + 3]; // Arranged as: + // 256 entries: emulation-mode instructions; + // 256 entries: 16-bit instructions; + // reset + // NMI + // IRQ private: - std::vector micro_ops_; + friend ProcessorStorageConstructor; - AccessType access_type_for_operation(Operation); - void install(void (* generator)(AccessType, bool, const std::function&), Operation, ProcessorStorageConstructor &); + std::vector micro_ops_; }; #endif /* WDC65816Implementation_h */ From 636e92960708fcd4bda2a217b448e773359a505d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 17:42:42 -0400 Subject: [PATCH 008/150] Adds a check for 8/16-bit redundancy. --- .../65816/Implementation/65816Storage.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 1e2506f7c..8a1eea9e5 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -67,6 +67,23 @@ struct CPU::WDC65816::ProcessorStorageConstructor { }); storage_.micro_ops_.push_back(OperationMoveToNextProgram); + // Minor optimisation: elide the steps if 8- and 16-bit steps are equal. + bool are_equal = true; + size_t c = 0; + while(true) { + if(storage_.micro_ops_[micro_op_location_8 + c] != storage_.micro_ops_[micro_op_location_16 + c]) { + are_equal = false; + break; + } + if(storage_.micro_ops_[micro_op_location_8 + c] == OperationMoveToNextProgram) break; + ++c; + } + + if(are_equal) { + storage_.micro_ops_.resize(micro_op_location_16); + micro_op_location_16 = micro_op_location_8; + } + // Insert into the map. installed_patterns[key] = std::make_pair(micro_op_location_8, micro_op_location_16); } else { @@ -185,6 +202,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } }; +ProcessorStorage TEMPORARY_test_instance; ProcessorStorage::ProcessorStorage() { ProcessorStorageConstructor constructor(*this); From 125ddfa513df9315c41ca043d6847deeb6d5fb91 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 18:00:02 -0400 Subject: [PATCH 009/150] Pays a little attention to runtime storage; completes the first page of bus patterns. --- Processors/65816/65816.hpp | 2 ++ .../65816/Implementation/65816Storage.cpp | 32 +++++++++++++++++-- .../65816/Implementation/65816Storage.hpp | 17 ++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index acb76ccf3..25837e6db 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -12,6 +12,8 @@ #include #include +#include "../RegisterSizes.hpp" + namespace CPU { namespace WDC65816 { diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 8a1eea9e5..022e576a8 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -28,7 +28,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { case LDA: case LDX: case LDY: - case JMP: case JSR: + // The access type for these is arbitrary, though consistency is beneficial. + case JMP: case JSR: case JML: return AccessType::Read; case STA: case STX: case STY: case STZ: @@ -200,6 +201,31 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchData); // New PCH. target(OperationPerform); // [JSR] } + + // 3a. Absolute Indirect (a), JML. + static void absolute_indirect_jml(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // New AAL. + target(CycleFetchPC); // New AAH. + + target(OperationConstructAbsolute); // Calculate data address. + target(CycleFetchIncrementData); // New PCL + target(CycleFetchIncrementData); // New PCH + target(CycleFetchData); // New PBR + + target(OperationPerform); // [JML] + }; + + // 3b. Absolute Indirect (a), JMP. + static void absolute_indirect_jmp(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // New AAL. + target(CycleFetchPC); // New AAH. + + target(OperationConstructAbsolute); // Calculate data address. + target(CycleFetchIncrementData); // New PCL + target(CycleFetchData); // New PCH + + target(OperationPerform); // [JMP] + }; }; ProcessorStorage TEMPORARY_test_instance; @@ -324,7 +350,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x69 ADC # */ /* 0x6a ROR A */ /* 0x6b RTL s */ - /* 0x6c JMP (a) */ + /* 0x6c JMP (a) */ op(absolute_indirect_jmp, JMP); /* 0x6d ADC a */ op(absolute, ADC); /* 0x6e ROR a */ /* 0x6f ADC al */ @@ -443,7 +469,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd9 CMP a, y */ /* 0xda PHX s */ /* 0xdb STP i */ - /* 0xdc JMP (a) */ + /* 0xdc JML (a) */ op(absolute_indirect_jml, JML); /* 0xdd CMP a, x */ /* 0xde DEC a, x */ /* 0xdf CMP al, x */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index d1f3dcf96..599689a6d 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -61,6 +61,9 @@ enum Operation: uint8_t { /// Loads the PC with the operand from the data buffer. JMP, + /// Loads the PC and PBR with the operand from the data buffer. + JML, + /// Loads the PC with the operand from the daa buffer, replacing /// it with the old PC. JSR, @@ -86,6 +89,20 @@ class ProcessorStorage { private: friend ProcessorStorageConstructor; + // Registers. + RegisterPair16 a_; + RegisterPair16 x_, y_; + uint16_t pc_, s_; + + // Not + uint16_t direct_; + + // Banking registers are all stored with the relevant byte + // shifted up bits 16–23. + uint32_t data_bank_; // i.e. DBR. + uint32_t program_bank_; // i.e. PBR. + + std::vector micro_ops_; }; From 7980a9033eb196467e2ce105db44b97684e99379 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 18:16:49 -0400 Subject: [PATCH 010/150] Adds two-thirds of absolute long. Working total: 31 opcodes covered; 10/47ths of bus patterns. Next is JSL, which I think will require additional operations. --- .../65816/Implementation/65816Storage.cpp | 62 ++++++++++++++----- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 022e576a8..540877b44 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -127,13 +127,13 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsolute); // Calculate data address. if(type == AccessType::Write) { - target(OperationPerform); // Perform operation to fill the data buffer. - target(CycleStoreIncrementData); // Data low. - if(is8bit) target(CycleStoreIncrementData); // Data high. + target(OperationPerform); // Perform operation to fill the data buffer. + if(!is8bit) target(CycleStoreIncrementData); // Data low. + target(CycleStoreData); // Data [high]. } else { - target(CycleFetchIncrementData); // Data low. - if(is8bit) target(CycleFetchIncrementData); // Data high. - target(OperationPerform); // Perform operation from the data buffer. + if(!is8bit) target(CycleFetchIncrementData); // Data low. + target(CycleFetchIncrementData); // Data [high]. + target(OperationPerform); // Perform operation from the data buffer. } }; @@ -226,6 +226,35 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // [JMP] }; + + // 4a. Absolute long al. + static void absolute_long(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(CycleFetchPC); // AAB. + + target(OperationConstructAbsolute); // Calculate data address. + + if(type == AccessType::Write) { + target(OperationPerform); // Perform operation to fill the data buffer. + if(!is8bit) target(CycleStoreIncrementData); // Data low. + target(CycleStoreData); // Data [high]. + } else { + if(!is8bit) target(CycleFetchIncrementData); // Data low. + target(CycleFetchData); // Data [high]. + target(OperationPerform); // Perform operation from the data buffer. + } + }; + + // 4a. Absolute long al, JMP. + static void absolute_long_jmp(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // New PCL. + target(CycleFetchIncrementPC); // New PCH. + target(CycleFetchPC); // New PBR. + + target(OperationConstructAbsolute); // Calculate data address. + target(OperationPerform); // ['JMP' (though it's JML in internal terms)] + }; }; ProcessorStorage TEMPORARY_test_instance; @@ -251,7 +280,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x0c TSB a */ /* 0x0d ORA a */ op(absolute, ORA); /* 0x0e ASL a */ - /* 0x0f ORA al */ + /* 0x0f ORA al */ op(absolute_long, ORA); /* 0x10 BPL r */ /* 0x11 ORA (d), y */ @@ -285,7 +314,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x2c BIT a */ op(absolute, BIT); /* 0x2d AND a */ op(absolute, AND); /* 0x2e ROL a */ - /* 0x2f AND al */ + /* 0x2f AND al */ op(absolute_long, AND); /* 0x30 BMI R */ /* 0x31 AND (d), y */ @@ -319,7 +348,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x4c JMP a */ op(absolute, JMP); /* 0x4d EOR a */ op(absolute, EOR); /* 0x4e LSR a */ - /* 0x4f EOR Al */ + /* 0x4f EOR al */ op(absolute_long, EOR); /* 0x50 BVC r */ /* 0x51 EOR (d), y */ @@ -333,7 +362,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x59 EOR a, y */ /* 0x5a PHY s */ /* 0x5b TCD i */ - /* 0x5c JMP al */ + /* 0x5c JMP al */ op(absolute_long_jmp, JML); // [sic]; this updates PBR so it's JML. /* 0x5d EOR a, x */ /* 0x5e LSR a, x */ /* 0x5f EOR al, x */ @@ -353,7 +382,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x6c JMP (a) */ op(absolute_indirect_jmp, JMP); /* 0x6d ADC a */ op(absolute, ADC); /* 0x6e ROR a */ - /* 0x6f ADC al */ + /* 0x6f ADC al */ op(absolute_long, ADC); /* 0x70 BVS r */ /* 0x71 ADC (d), y */ @@ -387,7 +416,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x8c STY a */ op(absolute, STY); /* 0x8d STA a */ op(absolute, STA); /* 0x8e STX a */ op(absolute, STX); - /* 0x8f STA al */ + /* 0x8f STA al */ op(absolute_long, STA); /* 0x90 BCC r */ /* 0x91 STA (d), y */ @@ -421,7 +450,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xac LDY a */ op(absolute, LDY); /* 0xad LDA a */ op(absolute, LDA); /* 0xae LDX a */ op(absolute, LDX); - /* 0xaf LDA al */ + /* 0xaf LDA al */ op(absolute_long, LDA); /* 0xb0 BCS r */ /* 0xb1 LDA (d), y */ @@ -455,7 +484,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xcc CPY a */ op(absolute, CPY); /* 0xcd CMP a */ op(absolute, CMP); /* 0xce DEC a */ - /* 0xcf CMP al */ + /* 0xcf CMP al */ op(absolute_long, CMP); /* 0xd0 BNE r */ /* 0xd1 CMP (d), y */ @@ -489,7 +518,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xec CPX a */ op(absolute, CPX); /* 0xed SBC a */ op(absolute, SBC); /* 0xee INC a */ - /* 0xef SBC al */ + /* 0xef SBC al */ op(absolute_long, SBC); /* 0xf0 BEQ r */ /* 0xf1 SBC (d), y */ @@ -509,4 +538,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xff SBC al, x */ #undef op + + // TEMPORARY: for my interest. To be removed. + printf("Generated %zd micro-ops in total; covered %d opcodes\n", micro_ops_.size(), constructor.opcode); } From 2b7ffcd48ff65c683e82e82b0f85959f949ea59c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 18:35:00 -0400 Subject: [PATCH 011/150] Takes a run at JSL al. --- .../65816/Implementation/65816Storage.cpp | 32 +++++++++++++++---- .../65816/Implementation/65816Storage.hpp | 14 ++++++-- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 540877b44..db4ca2917 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -20,7 +20,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { Read, Write }; - constexpr AccessType access_type_for_operation(Operation operation) { + constexpr static AccessType access_type_for_operation(Operation operation) { switch(operation) { case ADC: case AND: case BIT: case CMP: case CPX: case CPY: case EOR: case ORA: @@ -29,7 +29,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { case LDA: case LDX: case LDY: // The access type for these is arbitrary, though consistency is beneficial. - case JMP: case JSR: case JML: + case JMP: case JSR: case JML: case JSL: return AccessType::Read; case STA: case STX: case STY: case STZ: @@ -199,7 +199,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsoluteIndexedIndirect); // Calculate data address. target(CycleFetchIncrementData); // New PCL target(CycleFetchData); // New PCH. - target(OperationPerform); // [JSR] + target(OperationPerform); // ['JSR' (actually: JMP will do)] } // 3a. Absolute Indirect (a), JML. @@ -246,8 +246,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } }; - // 4a. Absolute long al, JMP. - static void absolute_long_jmp(AccessType type, bool is8bit, const std::function &target) { + // 4b. Absolute long al, JMP. + static void absolute_long_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. target(CycleFetchIncrementPC); // New PCH. target(CycleFetchPC); // New PBR. @@ -255,6 +255,24 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // ['JMP' (though it's JML in internal terms)] }; + + // 4c. Absolute long al, JSL. + static void absolute_long_jsl(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // New PCL. + target(CycleFetchIncrementPC); // New PCH. + + target(OperationCopyPBRToData); // Copy PBR to the data register. + target(CyclePush); // PBR. + target(CycleAccessStack); // IO. + + target(CycleFetchIncrementPC); // New PBR. + + target(OperationConstructAbsolute); // Calculate data address. + target(OperationPerform); // [JSL] + + target(CyclePush); // PCH + target(CyclePush); // PCL + }; }; ProcessorStorage TEMPORARY_test_instance; @@ -302,7 +320,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x20 JSR a */ op(absolute_jsr, JSR); /* 0x21 ORA (d), y */ /* 0x22 AND (d, x) */ - /* 0x23 JSL al */ + /* 0x23 JSL al */ op(absolute_long_jsl, JSL); /* 0x24 BIT d */ /* 0x25 AND d */ /* 0x26 ROL d */ @@ -532,7 +550,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf9 SBC a, y */ /* 0xfa PLX s */ /* 0xfb XCE i */ - /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JSR); + /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JMP); // [sic] /* 0xfd SBC a, x */ /* 0xfe INC a, x */ /* 0xff SBC al, x */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 599689a6d..d78f3b736 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -29,10 +29,13 @@ enum MicroOp: uint8_t { /// Pushes a single byte from the data buffer to the stack. CyclePush, + /// Fetches from the current stack location and throws the result away. + CycleAccessStack, /// Sets the data address by copying the final two bytes of the instruction buffer. OperationConstructAbsolute, - /// Sets the data address + /// Sets the data address to the result of (a, x). + /// TODO: explain better once implemented. OperationConstructAbsoluteIndexedIndirect, /// Performs whatever operation goes with this program. @@ -41,6 +44,9 @@ enum MicroOp: uint8_t { /// Copies the current program counter to the data buffer. OperationCopyPCToData, + /// Copies the current PBR to the data buffer. + OperationCopyPBRToData, + /// Complete this set of micr-ops. OperationMoveToNextProgram }; @@ -64,9 +70,13 @@ enum Operation: uint8_t { /// Loads the PC and PBR with the operand from the data buffer. JML, - /// Loads the PC with the operand from the daa buffer, replacing + /// Loads the PC with the operand from the data buffer, replacing /// it with the old PC. JSR, + + /// Loads the PC and the PBR with the operand from the data buffer, + /// replacing it with the old PC (and only the PC; PBR not included). + JSL, }; class ProcessorStorageConstructor; From 8c11df52bfec741113772532d5ac8b673bc92dfa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 19:27:17 -0400 Subject: [PATCH 012/150] Adds absolute long, x. Factors out the commonality of a closing read/write while I'm here. --- .../65816/Implementation/65816Storage.cpp | 75 +++++++++++-------- .../65816/Implementation/65816Storage.hpp | 1 + 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index db4ca2917..54beddf0d 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -120,24 +120,29 @@ struct CPU::WDC65816::ProcessorStorageConstructor { 3) the data address is undefined. */ - // 1a. Absolute a. - static void absolute(AccessType type, bool is8bit, const std::function &target) { - target(CycleFetchIncrementPC); // AAL. - target(CycleFetchIncrementPC); // AAH. - target(OperationConstructAbsolute); // Calculate data address. - + // Performs the closing 8- or 16-bit read or write common to many modes below. + static void read_write(AccessType type, bool is8bit, const std::function &target) { if(type == AccessType::Write) { target(OperationPerform); // Perform operation to fill the data buffer. if(!is8bit) target(CycleStoreIncrementData); // Data low. target(CycleStoreData); // Data [high]. } else { if(!is8bit) target(CycleFetchIncrementData); // Data low. - target(CycleFetchIncrementData); // Data [high]. + target(CycleFetchData); // Data [high]. target(OperationPerform); // Perform operation from the data buffer. } + } + + // 1a. Absolute; a. + static void absolute(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(OperationConstructAbsolute); // Calculate data address. + + read_write(type, is8bit, target); }; - // 1b. Absolute a, JMP. + // 1b. Absolute; a, JMP. static void absolute_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. target(CycleFetchPC); // New PCH. @@ -145,7 +150,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // [JMP] }; - // 1c. Absolute a, JSR. + // 1c. Absolute; a, JSR. static void absolute_jsr(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. target(CycleFetchPC); // New PCH. @@ -156,7 +161,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePush); // PCL }; - // 1d. Absolute read-modify-write. + // 1d. Absolute; a, read-modify-write. static void absolute_rmw(AccessType, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // AAL. target(CycleFetchIncrementPC); // AAH. @@ -174,7 +179,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleStoreData); // Data [low]. }; - // 2a. Absolute Indexed Indirect (a, x), JMP. + // 2a. Absolute Indexed Indirect; (a, x), JMP. static void absolute_indexed_indirect_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // AAL. target(CycleFetchPC); // AAH. @@ -185,7 +190,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // [JMP] }; - // 2b. Absolute Indexed Indirect (a, x), JSR. + // 2b. Absolute Indexed Indirect; (a, x), JSR. static void absolute_indexed_indirect_jsr(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // AAL. @@ -202,7 +207,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // ['JSR' (actually: JMP will do)] } - // 3a. Absolute Indirect (a), JML. + // 3a. Absolute Indirect; (a), JML. static void absolute_indirect_jml(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New AAL. target(CycleFetchPC); // New AAH. @@ -215,7 +220,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // [JML] }; - // 3b. Absolute Indirect (a), JMP. + // 3b. Absolute Indirect; (a), JMP. static void absolute_indirect_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New AAL. target(CycleFetchPC); // New AAH. @@ -227,7 +232,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // [JMP] }; - // 4a. Absolute long al. + // 4a. Absolute long; al. static void absolute_long(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // AAL. target(CycleFetchIncrementPC); // AAH. @@ -235,18 +240,10 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsolute); // Calculate data address. - if(type == AccessType::Write) { - target(OperationPerform); // Perform operation to fill the data buffer. - if(!is8bit) target(CycleStoreIncrementData); // Data low. - target(CycleStoreData); // Data [high]. - } else { - if(!is8bit) target(CycleFetchIncrementData); // Data low. - target(CycleFetchData); // Data [high]. - target(OperationPerform); // Perform operation from the data buffer. - } + read_write(type, is8bit, target); }; - // 4b. Absolute long al, JMP. + // 4b. Absolute long; al, JMP. static void absolute_long_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. target(CycleFetchIncrementPC); // New PCH. @@ -273,8 +270,20 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePush); // PCH target(CyclePush); // PCL }; + + // 5. Absolute long, X; al, x. + static void absolute_long_x(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(CycleFetchIncrementPC); // AAB. + + target(OperationConstructAbsoluteLongX); // Calculate data address. + + read_write(type, is8bit, target); + } }; +// TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. ProcessorStorage TEMPORARY_test_instance; ProcessorStorage::ProcessorStorage() { @@ -315,7 +324,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x1c TRB a */ /* 0x1d ORA a, x */ /* 0x1e ASL a, x */ - /* 0x1f ORA al, x */ + /* 0x1f ORA al, x */ op(absolute_long_x, ORA); /* 0x20 JSR a */ op(absolute_jsr, JSR); /* 0x21 ORA (d), y */ @@ -349,7 +358,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x3c BIT a, x */ /* 0x3d AND a, x */ /* 0x3e TLD a, x */ - /* 0x3f AND al, x */ + /* 0x3f AND al, x */ op(absolute_long_x, AND); /* 0x40 RTI s */ /* 0x41 EOR (d, x) */ @@ -383,7 +392,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x5c JMP al */ op(absolute_long_jmp, JML); // [sic]; this updates PBR so it's JML. /* 0x5d EOR a, x */ /* 0x5e LSR a, x */ - /* 0x5f EOR al, x */ + /* 0x5f EOR al, x */ op(absolute_long_x, EOR); /* 0x60 RTS s */ /* 0x61 ADC (d, x) */ @@ -417,7 +426,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x7c JMP (a, x) */ op(absolute_indexed_indirect_jmp, JMP); /* 0x7d ADC a, x */ /* 0x7e ROR a, x */ - /* 0x7f ADC al, x */ + /* 0x7f ADC al, x */ op(absolute_long_x, ADC); /* 0x80 BRA r */ /* 0x81 STA (d, x) */ @@ -451,7 +460,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x9c STZ a */ op(absolute, STZ); /* 0x9d STA a, x */ /* 0x9e STZ a, x */ - /* 0x9f STA al, x */ + /* 0x9f STA al, x */ op(absolute_long_x, STA); /* 0xa0 LDY # */ /* 0xa1 LDA (d, x) */ @@ -485,7 +494,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xbc LDY a, x */ /* 0xbd LDA a, x */ /* 0xbe LDX a, y */ - /* 0xbf LDA al, x */ + /* 0xbf LDA al, x */ op(absolute_long_x, LDA); /* 0xc0 CPY # */ /* 0xc1 CMP (d, x) */ @@ -519,7 +528,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xdc JML (a) */ op(absolute_indirect_jml, JML); /* 0xdd CMP a, x */ /* 0xde DEC a, x */ - /* 0xdf CMP al, x */ + /* 0xdf CMP al, x */ op(absolute_long_x, CMP); /* 0xe0 CPX # */ /* 0xe1 SBC (d, x) */ @@ -553,7 +562,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JMP); // [sic] /* 0xfd SBC a, x */ /* 0xfe INC a, x */ - /* 0xff SBC al, x */ + /* 0xff SBC al, x */ op(absolute_long_x, SBC); #undef op diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index d78f3b736..e0b6671c3 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -37,6 +37,7 @@ enum MicroOp: uint8_t { /// Sets the data address to the result of (a, x). /// TODO: explain better once implemented. OperationConstructAbsoluteIndexedIndirect, + OperationConstructAbsoluteLongX, /// Performs whatever operation goes with this program. OperationPerform, From 2957a31f4038368f503fb2ee8bb5a27a61dd00dc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 21:16:36 -0400 Subject: [PATCH 013/150] Adds absolute, x; absolute,y; and accumulator addressing modes. Now covered: 80/256 opcodes, from 2/6 pages of the data sheet; or 16/47 bus programs. --- .../65816/Implementation/65816Storage.cpp | 164 ++++++++++++------ .../65816/Implementation/65816Storage.hpp | 22 +++ 2 files changed, 135 insertions(+), 51 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 54beddf0d..5281a1aa8 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -28,8 +28,12 @@ struct CPU::WDC65816::ProcessorStorageConstructor { case LDA: case LDX: case LDY: - // The access type for these is arbitrary, though consistency is beneficial. + // The access type for the rest of these ::Reads is arbitrary. case JMP: case JSR: case JML: case JSL: + + case ASL: case DEC: case INC: case LSR: + case ROL: case ROR: case TRB: case TSB: + return AccessType::Read; case STA: case STX: case STY: case STZ: @@ -133,6 +137,19 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } } + static void read_modify_write(bool is8bit, const std::function &target) { + if(!is8bit) target(CycleFetchIncrementData); // Data low. + target(CycleFetchData); // Data [high]. + + if(!is8bit) target(CycleFetchData); // 16-bit: reread final byte of data. + else target(CycleStoreData); // 8-bit rewrite final byte of data. + + target(OperationPerform); // Perform operation within the data buffer. + + if(!is8bit) target(CycleStoreDecrementData); // Data high. + target(CycleStoreData); // Data [low]. + } + // 1a. Absolute; a. static void absolute(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // AAL. @@ -167,16 +184,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // AAH. target(OperationConstructAbsolute); // Calculate data address. - if(!is8bit) target(CycleFetchIncrementData); // Data low. - target(CycleFetchData); // Data [high]. - - if(!is8bit) target(CycleFetchData); // 16-bit: reread final byte of data. - else target(CycleStoreData); // 8-bit rewrite final byte of data. - - target(OperationPerform); // Perform operation within the data buffer. - - if(!is8bit) target(CycleStoreDecrementData); // Data high. - target(CycleStoreData); // Data [low]. + read_modify_write(is8bit, target); }; // 2a. Absolute Indexed Indirect; (a, x), JMP. @@ -281,6 +289,60 @@ struct CPU::WDC65816::ProcessorStorageConstructor { read_write(type, is8bit, target); } + + // 6a. Absolute, X; a, x. + static void absolute_x(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + + if(type == AccessType::Read) { + target(OperationConstructAbsoluteXRead); // Calculate data address, potentially skipping the next fetch. + } else { + target(OperationConstructAbsoluteX); // Calculate data address. + } + target(CycleFetchIncorrectDataAddress); // Do the dummy read if necessary; OperationConstructAbsoluteX + // will skip this if it isn't required. + + read_write(type, is8bit, target); + } + + // 6b. Absolute, X; a, x, read-modify-write. + static void absolute_x_rmw(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + + target(OperationConstructAbsoluteX); // Calculate data address. + target(CycleFetchIncorrectDataAddress); // Perform dummy read. + + read_modify_write(is8bit, target); + } + + // 6a. Absolute, Y; a, y. + static void absolute_y(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + + if(type == AccessType::Read) { + target(OperationConstructAbsoluteYRead); // Calculate data address, potentially skipping the next fetch. + } else { + target(OperationConstructAbsoluteY); // Calculate data address. + } + target(CycleFetchIncorrectDataAddress); // Do the dummy read if necessary; OperationConstructAbsoluteX + // will skip this if it isn't required. + + read_write(type, is8bit, target); + } + + // 7. Accumulator; A. + static void accumulator(AccessType, bool, const std::function &target) { + target(CycleFetchPC); // IO. + + // TODO: seriously consider a-specific versions of all relevant operations; + // the cost of interpreting three things here is kind of silly. + target(OperationCopyAToData); + target(OperationPerform); + target(OperationCopyDataToA); + } }; // TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. @@ -302,11 +364,11 @@ ProcessorStorage::ProcessorStorage() { /* 0x07 ORA [d] */ /* 0x08 PHP s */ /* 0x09 ORA # */ - /* 0x0a ASL a */ + /* 0x0a ASL A */ op(accumulator, ASL); /* 0x0b PHD s */ - /* 0x0c TSB a */ + /* 0x0c TSB a */ op(absolute_rmw, TSB); /* 0x0d ORA a */ op(absolute, ORA); - /* 0x0e ASL a */ + /* 0x0e ASL a */ op(absolute_rmw, ASL); /* 0x0f ORA al */ op(absolute_long, ORA); /* 0x10 BPL r */ @@ -318,12 +380,12 @@ ProcessorStorage::ProcessorStorage() { /* 0x16 ASL d, x */ /* 0x17 ORA [d], y */ /* 0x18 CLC i */ - /* 0x19 ORA a, y */ - /* 0x1a INC A */ + /* 0x19 ORA a, y */ op(absolute_y, ORA); + /* 0x1a INC A */ op(accumulator, INC); /* 0x1b TCS i */ - /* 0x1c TRB a */ - /* 0x1d ORA a, x */ - /* 0x1e ASL a, x */ + /* 0x1c TRB a */ op(absolute_rmw, TRB); + /* 0x1d ORA a, x */ op(absolute_x, ORA); + /* 0x1e ASL a, x */ op(absolute_x_rmw, ASL); /* 0x1f ORA al, x */ op(absolute_long_x, ORA); /* 0x20 JSR a */ op(absolute_jsr, JSR); @@ -332,15 +394,15 @@ ProcessorStorage::ProcessorStorage() { /* 0x23 JSL al */ op(absolute_long_jsl, JSL); /* 0x24 BIT d */ /* 0x25 AND d */ - /* 0x26 ROL d */ + /* 0x26 ROL d */ op(absolute_x_rmw, ROL); /* 0x27 AND [d] */ /* 0x28 PLP s */ /* 0x29 AND # */ - /* 0x2a ROL A */ + /* 0x2a ROL A */ op(accumulator, ROL); /* 0x2b PLD s */ /* 0x2c BIT a */ op(absolute, BIT); /* 0x2d AND a */ op(absolute, AND); - /* 0x2e ROL a */ + /* 0x2e ROL a */ op(absolute_rmw, ROL); /* 0x2f AND al */ op(absolute_long, AND); /* 0x30 BMI R */ @@ -352,11 +414,11 @@ ProcessorStorage::ProcessorStorage() { /* 0x36 TOL d, x */ /* 0x37 AND [d], y */ /* 0x38 SEC i */ - /* 0x39 AND a, y */ - /* 0x3a DEC A */ + /* 0x39 AND a, y */ op(absolute_y, AND); + /* 0x3a DEC A */ op(accumulator, DEC); /* 0x3b TSC i */ - /* 0x3c BIT a, x */ - /* 0x3d AND a, x */ + /* 0x3c BIT a, x */ op(absolute_x, BIT); + /* 0x3d AND a, x */ op(absolute_x, AND); /* 0x3e TLD a, x */ /* 0x3f AND al, x */ op(absolute_long_x, AND); @@ -370,11 +432,11 @@ ProcessorStorage::ProcessorStorage() { /* 0x47 EOR [d] */ /* 0x48 PHA s */ /* 0x49 EOR # */ - /* 0x4a LSR A */ + /* 0x4a LSR A */ op(accumulator, LSR); /* 0x4b PHK s */ /* 0x4c JMP a */ op(absolute, JMP); /* 0x4d EOR a */ op(absolute, EOR); - /* 0x4e LSR a */ + /* 0x4e LSR a */ op(absolute_rmw, LSR); /* 0x4f EOR al */ op(absolute_long, EOR); /* 0x50 BVC r */ @@ -386,12 +448,12 @@ ProcessorStorage::ProcessorStorage() { /* 0x56 LSR d, x */ /* 0x57 EOR [d],y */ /* 0x58 CLI i */ - /* 0x59 EOR a, y */ + /* 0x59 EOR a, y */ op(absolute_y, EOR); /* 0x5a PHY s */ /* 0x5b TCD i */ /* 0x5c JMP al */ op(absolute_long_jmp, JML); // [sic]; this updates PBR so it's JML. - /* 0x5d EOR a, x */ - /* 0x5e LSR a, x */ + /* 0x5d EOR a, x */ op(absolute_x, EOR); + /* 0x5e LSR a, x */ op(absolute_x_rmw, LSR); /* 0x5f EOR al, x */ op(absolute_long_x, EOR); /* 0x60 RTS s */ @@ -404,11 +466,11 @@ ProcessorStorage::ProcessorStorage() { /* 0x67 ADC [d] */ /* 0x68 PLA s */ /* 0x69 ADC # */ - /* 0x6a ROR A */ + /* 0x6a ROR A */ op(accumulator, ROR); /* 0x6b RTL s */ /* 0x6c JMP (a) */ op(absolute_indirect_jmp, JMP); /* 0x6d ADC a */ op(absolute, ADC); - /* 0x6e ROR a */ + /* 0x6e ROR a */ op(absolute_rmw, ROR); /* 0x6f ADC al */ op(absolute_long, ADC); /* 0x70 BVS r */ @@ -420,12 +482,12 @@ ProcessorStorage::ProcessorStorage() { /* 0x76 ROR d, x */ /* 0x77 ADC [d], y */ /* 0x78 SEI i */ - /* 0x79 ADC a, y */ + /* 0x79 ADC a, y */ op(absolute_y, ADC); /* 0x7a PLY s */ /* 0x7b TDC i */ /* 0x7c JMP (a, x) */ op(absolute_indexed_indirect_jmp, JMP); - /* 0x7d ADC a, x */ - /* 0x7e ROR a, x */ + /* 0x7d ADC a, x */ op(absolute_x, ADC); + /* 0x7e ROR a, x */ op(absolute_x_rmw, ROR); /* 0x7f ADC al, x */ op(absolute_long_x, ADC); /* 0x80 BRA r */ @@ -454,12 +516,12 @@ ProcessorStorage::ProcessorStorage() { /* 0x96 STX d, y */ /* 0x97 STA [d], y */ /* 0x98 TYA i */ - /* 0x99 STA a, y */ + /* 0x99 STA a, y */ op(absolute_y, STA); /* 0x9a TXS i */ /* 0x9b TXY i */ /* 0x9c STZ a */ op(absolute, STZ); - /* 0x9d STA a, x */ - /* 0x9e STZ a, x */ + /* 0x9d STA a, x */ op(absolute_x, STA); + /* 0x9e STZ a, x */ op(absolute_x, STZ); /* 0x9f STA al, x */ op(absolute_long_x, STA); /* 0xa0 LDY # */ @@ -488,12 +550,12 @@ ProcessorStorage::ProcessorStorage() { /* 0xb6 LDX d, y */ /* 0xb7 LDA [d], y */ /* 0xb8 CLV i */ - /* 0xb9 LDA a, y */ + /* 0xb9 LDA a, y */ op(absolute_y, LDA); /* 0xba TSX i */ /* 0xbb TYX i */ - /* 0xbc LDY a, x */ - /* 0xbd LDA a, x */ - /* 0xbe LDX a, y */ + /* 0xbc LDY a, x */ op(absolute_x, LDY); + /* 0xbd LDA a, x */ op(absolute_x, LDA); + /* 0xbe LDX a, y */ op(absolute_y, LDX); /* 0xbf LDA al, x */ op(absolute_long_x, LDA); /* 0xc0 CPY # */ @@ -510,7 +572,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xcb WAI i */ /* 0xcc CPY a */ op(absolute, CPY); /* 0xcd CMP a */ op(absolute, CMP); - /* 0xce DEC a */ + /* 0xce DEC a */ op(absolute_rmw, DEC); /* 0xcf CMP al */ op(absolute_long, CMP); /* 0xd0 BNE r */ @@ -522,12 +584,12 @@ ProcessorStorage::ProcessorStorage() { /* 0xd6 DEC d, x */ /* 0xd7 CMP [d], y */ /* 0xd8 CLD i */ - /* 0xd9 CMP a, y */ + /* 0xd9 CMP a, y */ op(absolute_y, CMP); /* 0xda PHX s */ /* 0xdb STP i */ /* 0xdc JML (a) */ op(absolute_indirect_jml, JML); - /* 0xdd CMP a, x */ - /* 0xde DEC a, x */ + /* 0xdd CMP a, x */ op(absolute_x, CMP); + /* 0xde DEC a, x */ op(absolute_x_rmw, DEC); /* 0xdf CMP al, x */ op(absolute_long_x, CMP); /* 0xe0 CPX # */ @@ -544,7 +606,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xeb XBA i */ /* 0xec CPX a */ op(absolute, CPX); /* 0xed SBC a */ op(absolute, SBC); - /* 0xee INC a */ + /* 0xee INC a */ op(absolute_rmw, INC); /* 0xef SBC al */ op(absolute_long, SBC); /* 0xf0 BEQ r */ @@ -556,12 +618,12 @@ ProcessorStorage::ProcessorStorage() { /* 0xf6 INC d, x */ /* 0xf7 SBC [d], y */ /* 0xf8 SED i */ - /* 0xf9 SBC a, y */ + /* 0xf9 SBC a, y */ op(absolute_y, SBC); /* 0xfa PLX s */ /* 0xfb XCE i */ /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JMP); // [sic] - /* 0xfd SBC a, x */ - /* 0xfe INC a, x */ + /* 0xfd SBC a, x */ op(absolute_x, SBC); + /* 0xfe INC a, x */ op(absolute_x_rmw, INC); /* 0xff SBC al, x */ op(absolute_long_x, SBC); #undef op diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index e0b6671c3..bb1840d78 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -19,6 +19,9 @@ enum MicroOp: uint8_t { CycleFetchData, /// Fetches a byte from the data address to the data buffer and increments the data address. CycleFetchIncrementData, + /// Fetches from the address formed by the low byte of the data address and the high byte + /// of the instruction buffer, throwing the result away. + CycleFetchIncorrectDataAddress, /// Stores a byte from the data buffer. CycleStoreData, @@ -39,6 +42,19 @@ enum MicroOp: uint8_t { OperationConstructAbsoluteIndexedIndirect, OperationConstructAbsoluteLongX, + /// Calculates an a, x address; if: + /// there was no carry into the top byte of the address; and + /// the process or in emulation or 8-bit index mode; + /// then it also skips the next micro-op. + OperationConstructAbsoluteXRead, + + /// Calculates an a, x address. + OperationConstructAbsoluteX, + + // These are analogous to the X versions above. + OperationConstructAbsoluteY, + OperationConstructAbsoluteYRead, + /// Performs whatever operation goes with this program. OperationPerform, @@ -48,6 +64,9 @@ enum MicroOp: uint8_t { /// Copies the current PBR to the data buffer. OperationCopyPBRToData, + OperationCopyAToData, + OperationCopyDataToA, + /// Complete this set of micr-ops. OperationMoveToNextProgram }; @@ -65,6 +84,9 @@ enum Operation: uint8_t { // they are implicitly AccessType::Write. STA, STX, STY, STZ, + // These modify the value in the data buffer as part of a read-modify-write. + ASL, DEC, INC, LSR, ROL, ROR, TRB, TSB, + /// Loads the PC with the operand from the data buffer. JMP, From 5360a7b4ce5f4505495f0a78b0fb12b2c1f6c76c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 21:49:03 -0400 Subject: [PATCH 014/150] Adds block moves. These are fairly specialised, dealing in two data addresses simultaneously. --- .../65816/Implementation/65816Storage.cpp | 30 +++++++++++++++---- .../65816/Implementation/65816Storage.hpp | 9 ++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 5281a1aa8..754918146 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -28,12 +28,15 @@ struct CPU::WDC65816::ProcessorStorageConstructor { case LDA: case LDX: case LDY: - // The access type for the rest of these ::Reads is arbitrary. + // The access type for the rest of these ::Reads is arbitrary; they're + // not relevantly either read or write. case JMP: case JSR: case JML: case JSL: case ASL: case DEC: case INC: case LSR: case ROL: case ROR: case TRB: case TSB: + case MVN: case MVP: + return AccessType::Read; case STA: case STX: case STY: case STZ: @@ -317,7 +320,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { read_modify_write(is8bit, target); } - // 6a. Absolute, Y; a, y. + // 7. Absolute, Y; a, y. static void absolute_y(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // AAL. target(CycleFetchIncrementPC); // AAH. @@ -333,7 +336,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { read_write(type, is8bit, target); } - // 7. Accumulator; A. + // 8. Accumulator; A. static void accumulator(AccessType, bool, const std::function &target) { target(CycleFetchPC); // IO. @@ -343,6 +346,23 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); target(OperationCopyDataToA); } + + // 9a. Block Move Negative [and] + // 9b. Block Move Positive. + // + // These don't fit the general model very well at all, hence the specialised fetch and store cycles. + static void block_move(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // DBA. + target(CycleFetchIncrementPC); // SBA. + + target(CycleFetchBlockX); // SRC Data. + target(CycleStoreBlockY); // Dest Data. + + target(CycleFetchBlockY); // IO. + target(CycleFetchBlockY); // IO. + + target(OperationPerform); // [MVN or MVP] + } }; // TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. @@ -426,7 +446,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x41 EOR (d, x) */ /* 0x42 WDM i */ /* 0x43 EOR d, s */ - /* 0x44 MVP xyc */ + /* 0x44 MVP xyc */ op(block_move, MVP); /* 0x45 EOR d */ /* 0x46 LSR d */ /* 0x47 EOR [d] */ @@ -443,7 +463,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x51 EOR (d), y */ /* 0x52 EOR (d) */ /* 0x53 EOR (d, s), y */ - /* 0x54 MVN xyc */ + /* 0x54 MVN xyc */ op(block_move, MVN); /* 0x55 EOR d, x */ /* 0x56 LSR d, x */ /* 0x57 EOR [d],y */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index bb1840d78..dc7d73ae5 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -23,6 +23,11 @@ enum MicroOp: uint8_t { /// of the instruction buffer, throwing the result away. CycleFetchIncorrectDataAddress, + // Dedicated block-move cycles; these use the data buffer as an intermediary. + CycleFetchBlockX, + CycleFetchBlockY, + CycleStoreBlockY, + /// Stores a byte from the data buffer. CycleStoreData, /// Stores a byte to the data address from the data buffer and increments the data address. @@ -87,6 +92,10 @@ enum Operation: uint8_t { // These modify the value in the data buffer as part of a read-modify-write. ASL, DEC, INC, LSR, ROL, ROR, TRB, TSB, + // These merely decrement A, increment or decrement X and Y, and regress + // the program counter only if appropriate. + MVN, MVP, + /// Loads the PC with the operand from the data buffer. JMP, From 5039cc7bb26526b02169d2d378b33f180d77cc8e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 22:01:36 -0400 Subject: [PATCH 015/150] Adds direct page. ... to cover 106 opcodes. --- .../65816/Implementation/65816Storage.cpp | 74 ++++++++++++------- .../65816/Implementation/65816Storage.hpp | 4 + 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 754918146..87d1196b5 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -363,6 +363,28 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // [MVN or MVP] } + + // 10a. Direct; d. + // (That's zero page in 6502 terms) + static void direct(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirect); + target(CycleFetchPC); // IO. + + read_write(type, is8bit, target); + }; + + // 10b. Direct; d, read-modify-write. + // (That's zero page in 6502 terms) + static void direct_rmw(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirect); + target(CycleFetchPC); // IO. + + read_modify_write(is8bit, target); + }; }; // TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. @@ -378,9 +400,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x01 ORA (d, x) */ /* 0x02 COP s */ /* 0x03 ORA d, s */ - /* 0x04 TSB d */ - /* 0x05 ORA d */ - /* 0x06 ASL d */ + /* 0x04 TSB d */ op(direct_rmw, TSB); + /* 0x05 ORA d */ op(direct, ORA); + /* 0x06 ASL d */ op(direct_rmw, ASL); /* 0x07 ORA [d] */ /* 0x08 PHP s */ /* 0x09 ORA # */ @@ -395,8 +417,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x11 ORA (d), y */ /* 0x12 ORA (d) */ /* 0x13 ORA (d, s), y */ - /* 0x14 TRB d */ - /* 0x15 ORA d,x */ + /* 0x14 TRB d */ op(absolute_rmw, TRB); + /* 0x15 ORA d, x */ /* 0x16 ASL d, x */ /* 0x17 ORA [d], y */ /* 0x18 CLC i */ @@ -412,9 +434,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x21 ORA (d), y */ /* 0x22 AND (d, x) */ /* 0x23 JSL al */ op(absolute_long_jsl, JSL); - /* 0x24 BIT d */ - /* 0x25 AND d */ - /* 0x26 ROL d */ op(absolute_x_rmw, ROL); + /* 0x24 BIT d */ op(direct, BIT); + /* 0x25 AND d */ op(direct, AND); + /* 0x26 ROL d */ op(absolute_rmw, ROL); /* 0x27 AND [d] */ /* 0x28 PLP s */ /* 0x29 AND # */ @@ -431,7 +453,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x33 AND (d, s), y */ /* 0x34 BIT d, x */ /* 0x35 AND d, x */ - /* 0x36 TOL d, x */ + /* 0x36 ROL d, x */ op(absolute_x_rmw, ROL); /* 0x37 AND [d], y */ /* 0x38 SEC i */ /* 0x39 AND a, y */ op(absolute_y, AND); @@ -447,8 +469,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x42 WDM i */ /* 0x43 EOR d, s */ /* 0x44 MVP xyc */ op(block_move, MVP); - /* 0x45 EOR d */ - /* 0x46 LSR d */ + /* 0x45 EOR d */ op(direct, EOR); + /* 0x46 LSR d */ op(direct_rmw, LSR); /* 0x47 EOR [d] */ /* 0x48 PHA s */ /* 0x49 EOR # */ @@ -480,9 +502,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x61 ADC (d, x) */ /* 0x62 PER s */ /* 0x63 ADC d, s */ - /* 0x64 STZ d */ - /* 0x65 ADC d */ - /* 0x66 ROR d */ + /* 0x64 STZ d */ op(direct, STZ); + /* 0x65 ADC d */ op(direct, ADC); + /* 0x66 ROR d */ op(direct_rmw, ROR); /* 0x67 ADC [d] */ /* 0x68 PLA s */ /* 0x69 ADC # */ @@ -514,9 +536,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x81 STA (d, x) */ /* 0x82 BRL rl */ /* 0x83 STA d, s */ - /* 0x84 STY d */ - /* 0x85 STA d */ - /* 0x86 STX d */ + /* 0x84 STY d */ op(direct, STY); + /* 0x85 STA d */ op(direct, STA); + /* 0x86 STX d */ op(direct, STX); /* 0x87 STA [d] */ /* 0x88 DEY i */ /* 0x89 BIT # */ @@ -548,9 +570,9 @@ ProcessorStorage::ProcessorStorage() { /* 0xa1 LDA (d, x) */ /* 0xa2 LDX # */ /* 0xa3 LDA d, s */ - /* 0xa4 LDY d */ - /* 0xa5 LDA d */ - /* 0xa6 LDX d */ + /* 0xa4 LDY d */ op(direct, LDY); + /* 0xa5 LDA d */ op(direct, LDA); + /* 0xa6 LDX d */ op(direct, LDX); /* 0xa7 LDA [d] */ /* 0xa8 TAY i */ /* 0xa9 LDA # */ @@ -582,9 +604,9 @@ ProcessorStorage::ProcessorStorage() { /* 0xc1 CMP (d, x) */ /* 0xc2 REP # */ /* 0xc3 CMP d, s */ - /* 0xc4 CPY d */ - /* 0xc5 CMP d */ - /* 0xc6 DEC d */ + /* 0xc4 CPY d */ op(direct, CPY); + /* 0xc5 CMP d */ op(direct, CMP); + /* 0xc6 DEC d */ op(direct_rmw, DEC); /* 0xc7 CMP [d] */ /* 0xc8 INY i */ /* 0xc9 CMP # */ @@ -616,9 +638,9 @@ ProcessorStorage::ProcessorStorage() { /* 0xe1 SBC (d, x) */ /* 0xe2 SEP # */ /* 0xe3 SBC d, s */ - /* 0xe4 CPX d */ - /* 0xe5 SBC d */ - /* 0xe6 INC d */ + /* 0xe4 CPX d */ op(direct, CPX); + /* 0xe5 SBC d */ op(direct, SBC); + /* 0xe6 INC d */ op(direct_rmw, INC); /* 0xe7 SBC [d] */ /* 0xe8 INX i */ /* 0xe9 SBC # */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index dc7d73ae5..aeeb99bfb 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -60,6 +60,10 @@ enum MicroOp: uint8_t { OperationConstructAbsoluteY, OperationConstructAbsoluteYRead, + /// Constructs the current direct address using the value in the instruction buffer. + /// Skips the next micro-op if the low byte of the direct register is 0. + OperationConstructDirect, + /// Performs whatever operation goes with this program. OperationPerform, From 1512ac11da5597bd21e8da23298354bb4813db0a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 22:22:30 -0400 Subject: [PATCH 016/150] Adds (d, x) and (d) modes. Albeit by deferring the hard work. That's: 122/256 opcodes; 22/47 bus programs, ~3.5/7 pages transcribed. Maybe I'll be able to get to the runtime stuff sooner rather than later? --- .../65816/Implementation/65816Storage.cpp | 58 ++++++++++++++----- .../65816/Implementation/65816Storage.hpp | 4 ++ 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 87d1196b5..02e4768cb 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -385,6 +385,32 @@ struct CPU::WDC65816::ProcessorStorageConstructor { read_modify_write(is8bit, target); }; + + // 11. Direct Indexed Indirect; (d, x). + static void direct_indexed_indirect(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectIndexedIndirect); + target(CycleFetchPC); // IO. + + target(CycleFetchPC); // IO. + + read_write(type, is8bit, target); + }; + + // 12. Direct Indirect; (d). + static void direct_indirect(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectIndirect); + target(CycleFetchPC); // IO. + + read_write(type, is8bit, target); + }; + + // 13. Direct Indirect Indexed; (d), y. + // 14. Direct Indirect Indexed Long; [d], y. + // 15. Direct Indirect Long; [d]. }; // TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. @@ -397,7 +423,7 @@ ProcessorStorage::ProcessorStorage() { #define op(x, y) constructor.install(&ProcessorStorageConstructor::x, y) /* 0x00 BRK s */ - /* 0x01 ORA (d, x) */ + /* 0x01 ORA (d, x) */ op(direct_indexed_indirect, ORA); /* 0x02 COP s */ /* 0x03 ORA d, s */ /* 0x04 TSB d */ op(direct_rmw, TSB); @@ -415,7 +441,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x10 BPL r */ /* 0x11 ORA (d), y */ - /* 0x12 ORA (d) */ + /* 0x12 ORA (d) */ op(direct_indirect, ORA); /* 0x13 ORA (d, s), y */ /* 0x14 TRB d */ op(absolute_rmw, TRB); /* 0x15 ORA d, x */ @@ -432,7 +458,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x20 JSR a */ op(absolute_jsr, JSR); /* 0x21 ORA (d), y */ - /* 0x22 AND (d, x) */ + /* 0x22 AND (d, x) */ op(direct_indexed_indirect, AND); /* 0x23 JSL al */ op(absolute_long_jsl, JSL); /* 0x24 BIT d */ op(direct, BIT); /* 0x25 AND d */ op(direct, AND); @@ -449,7 +475,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x30 BMI R */ /* 0x31 AND (d), y */ - /* 0x32 AND (d) */ + /* 0x32 AND (d) */ op(direct_indirect, AND); /* 0x33 AND (d, s), y */ /* 0x34 BIT d, x */ /* 0x35 AND d, x */ @@ -465,7 +491,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x3f AND al, x */ op(absolute_long_x, AND); /* 0x40 RTI s */ - /* 0x41 EOR (d, x) */ + /* 0x41 EOR (d, x) */ op(direct_indexed_indirect, EOR); /* 0x42 WDM i */ /* 0x43 EOR d, s */ /* 0x44 MVP xyc */ op(block_move, MVP); @@ -483,7 +509,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x50 BVC r */ /* 0x51 EOR (d), y */ - /* 0x52 EOR (d) */ + /* 0x52 EOR (d) */ op(direct_indirect, EOR); /* 0x53 EOR (d, s), y */ /* 0x54 MVN xyc */ op(block_move, MVN); /* 0x55 EOR d, x */ @@ -499,7 +525,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x5f EOR al, x */ op(absolute_long_x, EOR); /* 0x60 RTS s */ - /* 0x61 ADC (d, x) */ + /* 0x61 ADC (d, x) */ op(direct_indexed_indirect, ADC); /* 0x62 PER s */ /* 0x63 ADC d, s */ /* 0x64 STZ d */ op(direct, STZ); @@ -517,7 +543,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x70 BVS r */ /* 0x71 ADC (d), y */ - /* 0x72 ADC (d) */ + /* 0x72 ADC (d) */ op(direct_indirect, ADC); /* 0x73 ADC (d, s), y */ /* 0x74 STZ d, x */ /* 0x75 ADC d, x */ @@ -533,7 +559,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x7f ADC al, x */ op(absolute_long_x, ADC); /* 0x80 BRA r */ - /* 0x81 STA (d, x) */ + /* 0x81 STA (d, x) */ op(direct_indexed_indirect, STA); /* 0x82 BRL rl */ /* 0x83 STA d, s */ /* 0x84 STY d */ op(direct, STY); @@ -551,7 +577,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x90 BCC r */ /* 0x91 STA (d), y */ - /* 0x92 STA (d) */ + /* 0x92 STA (d) */ op(direct_indirect, STA); /* 0x93 STA (d, x), y */ /* 0x94 STY d, x */ /* 0x95 STA d, x */ @@ -567,7 +593,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x9f STA al, x */ op(absolute_long_x, STA); /* 0xa0 LDY # */ - /* 0xa1 LDA (d, x) */ + /* 0xa1 LDA (d, x) */ op(direct_indexed_indirect, LDA); /* 0xa2 LDX # */ /* 0xa3 LDA d, s */ /* 0xa4 LDY d */ op(direct, LDY); @@ -585,7 +611,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xb0 BCS r */ /* 0xb1 LDA (d), y */ - /* 0xb2 LDA (d) */ + /* 0xb2 LDA (d) */ op(direct_indirect, LDA); /* 0xb3 LDA (d, s), y */ /* 0xb4 LDY d, x */ /* 0xb5 LDA d, x */ @@ -601,7 +627,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xbf LDA al, x */ op(absolute_long_x, LDA); /* 0xc0 CPY # */ - /* 0xc1 CMP (d, x) */ + /* 0xc1 CMP (d, x) */ op(direct_indexed_indirect, CMP); /* 0xc2 REP # */ /* 0xc3 CMP d, s */ /* 0xc4 CPY d */ op(direct, CPY); @@ -619,7 +645,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd0 BNE r */ /* 0xd1 CMP (d), y */ - /* 0xd2 CMP (d) */ + /* 0xd2 CMP (d) */ op(direct_indirect, CMP); /* 0xd3 CMP (d, s), y */ /* 0xd4 PEI s */ /* 0xd5 CMP d, x */ @@ -635,7 +661,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xdf CMP al, x */ op(absolute_long_x, CMP); /* 0xe0 CPX # */ - /* 0xe1 SBC (d, x) */ + /* 0xe1 SBC (d, x) */ op(direct_indexed_indirect, SBC); /* 0xe2 SEP # */ /* 0xe3 SBC d, s */ /* 0xe4 CPX d */ op(direct, CPX); @@ -653,7 +679,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf0 BEQ r */ /* 0xf1 SBC (d), y */ - /* 0xf2 SBC (d) */ + /* 0xf2 SBC (d) */ op(direct_indirect, SBC); /* 0xf3 SBC (d, s), y */ /* 0xf4 PEA s */ /* 0xf5 SBC d, x */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index aeeb99bfb..e5abf9fc0 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -64,6 +64,10 @@ enum MicroOp: uint8_t { /// Skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirect, + // These follow similar skip-one-if-possible logic to OperationConstructDirect. + OperationConstructDirectIndexedIndirect, + OperationConstructDirectIndirect, + /// Performs whatever operation goes with this program. OperationPerform, From 3fc649359ab4c82431e6ae81828a27eee15aca04 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 22:29:19 -0400 Subject: [PATCH 017/150] Transcribes the titles of all remaining bus programs. Thereby frames the distance yet to travel. --- .../65816/Implementation/65816Storage.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 02e4768cb..5b71eb29e 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -411,6 +411,28 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 13. Direct Indirect Indexed; (d), y. // 14. Direct Indirect Indexed Long; [d], y. // 15. Direct Indirect Long; [d]. + // 16a. Direct, X; d, x. + // 16b. Direct, X; d, x, read-modify-write. + // 17. Direct, Y; d, y. + // 18. Immediate; #. + // 19a. Implied; i. + // 19b. Implied; i; XBA. + // 19c. Stop the Clock. + // 19d. Wait for interrupt. + // 20. Relative; r. + // 21. Relative long; rl. + // 22a. Stack; s, abort/irq/nmi/res. + // 22b. Stack; s, PLx. + // 22c. Stack; s, PHx. + // 22d. Stack; s, PEA. + // 22e. Stack; s, PEI. + // 22f. Stack; s, PER. + // 22g. Stack; s, RTI. + // 22h. Stack; s, RTS. + // 22i. Stack; s, RTL. + // 22j. Stack; s, BRK/COP. + // 23. Stack Relative; d, s. + // 24. Stack Relative Indirect Indexed (d, s), y. }; // TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. From d2e868ea2ba8983dce3eec64bf3cfd2add3100e5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 16:55:58 -0400 Subject: [PATCH 018/150] Adds (d), y; [d], y; and [d]. Now covered: 146/256 opcodes, 4/7 pages, 25/47 bus programs. --- .../65816/Implementation/65816Storage.cpp | 131 ++++++++++++------ .../65816/Implementation/65816Storage.hpp | 3 + 2 files changed, 93 insertions(+), 41 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 5b71eb29e..343f81371 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -160,7 +160,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsolute); // Calculate data address. read_write(type, is8bit, target); - }; + } // 1b. Absolute; a, JMP. static void absolute_jmp(AccessType, bool, const std::function &target) { @@ -168,7 +168,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPC); // New PCH. target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // [JMP] - }; + } // 1c. Absolute; a, JSR. static void absolute_jsr(AccessType, bool, const std::function &target) { @@ -179,7 +179,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // [JSR] target(CyclePush); // PCH target(CyclePush); // PCL - }; + } // 1d. Absolute; a, read-modify-write. static void absolute_rmw(AccessType, bool is8bit, const std::function &target) { @@ -188,7 +188,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsolute); // Calculate data address. read_modify_write(is8bit, target); - }; + } // 2a. Absolute Indexed Indirect; (a, x), JMP. static void absolute_indexed_indirect_jmp(AccessType, bool, const std::function &target) { @@ -199,7 +199,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementData); // New PCL target(CycleFetchData); // New PCH. target(OperationPerform); // [JMP] - }; + } // 2b. Absolute Indexed Indirect; (a, x), JSR. static void absolute_indexed_indirect_jsr(AccessType, bool, const std::function &target) { @@ -229,7 +229,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchData); // New PBR target(OperationPerform); // [JML] - }; + } // 3b. Absolute Indirect; (a), JMP. static void absolute_indirect_jmp(AccessType, bool, const std::function &target) { @@ -241,7 +241,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchData); // New PCH target(OperationPerform); // [JMP] - }; + } // 4a. Absolute long; al. static void absolute_long(AccessType type, bool is8bit, const std::function &target) { @@ -252,7 +252,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsolute); // Calculate data address. read_write(type, is8bit, target); - }; + } // 4b. Absolute long; al, JMP. static void absolute_long_jmp(AccessType, bool, const std::function &target) { @@ -262,7 +262,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // ['JMP' (though it's JML in internal terms)] - }; + } // 4c. Absolute long al, JSL. static void absolute_long_jsl(AccessType, bool, const std::function &target) { @@ -280,7 +280,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePush); // PCH target(CyclePush); // PCL - }; + } // 5. Absolute long, X; al, x. static void absolute_long_x(AccessType type, bool is8bit, const std::function &target) { @@ -373,7 +373,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPC); // IO. read_write(type, is8bit, target); - }; + } // 10b. Direct; d, read-modify-write. // (That's zero page in 6502 terms) @@ -384,7 +384,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPC); // IO. read_modify_write(is8bit, target); - }; + } // 11. Direct Indexed Indirect; (d, x). static void direct_indexed_indirect(AccessType type, bool is8bit, const std::function &target) { @@ -396,7 +396,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPC); // IO. read_write(type, is8bit, target); - }; + } // 12. Direct Indirect; (d). static void direct_indirect(AccessType type, bool is8bit, const std::function &target) { @@ -406,11 +406,60 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPC); // IO. read_write(type, is8bit, target); - }; + } // 13. Direct Indirect Indexed; (d), y. + static void direct_indirect_indexed(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectIndirect); + target(CycleFetchPC); // IO. + + target(CycleFetchIncrementData); // AAL. + target(CycleFetchData); // AAH. + + target(OperationConstructDirectIndirectIndexed); + target(CycleFetchIncorrectDataAddress); // IO. + + read_write(type, is8bit, target); + } + // TODO: verify, especially re: false addresses. + // i.e. it currently looks to me superficially as though I can reuse + // OperationConstructDirectIndirect here, but writing the proper + // interpreter will help to clarify. + // 14. Direct Indirect Indexed Long; [d], y. + static void direct_indirect_indexed_long(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectIndirect); + target(CycleFetchPC); // IO. + + target(CycleFetchIncrementData); // AAL. + target(CycleFetchIncrementData); // AAH. + target(CycleFetchData); // AAB. + + target(OperationConstructDirectIndirectIndexedLong); + + read_write(type, is8bit, target); + } + // 15. Direct Indirect Long; [d]. + static void direct_indirect_long(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectIndirect); + target(CycleFetchPC); // IO. + + target(CycleFetchIncrementData); // AAL. + target(CycleFetchIncrementData); // AAH. + target(CycleFetchData); // AAB. + + target(OperationConstructDirectIndirectLong); + + read_write(type, is8bit, target); + } + // 16a. Direct, X; d, x. // 16b. Direct, X; d, x, read-modify-write. // 17. Direct, Y; d, y. @@ -451,7 +500,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x04 TSB d */ op(direct_rmw, TSB); /* 0x05 ORA d */ op(direct, ORA); /* 0x06 ASL d */ op(direct_rmw, ASL); - /* 0x07 ORA [d] */ + /* 0x07 ORA [d] */ op(direct_indirect_long, ORA); /* 0x08 PHP s */ /* 0x09 ORA # */ /* 0x0a ASL A */ op(accumulator, ASL); @@ -462,13 +511,13 @@ ProcessorStorage::ProcessorStorage() { /* 0x0f ORA al */ op(absolute_long, ORA); /* 0x10 BPL r */ - /* 0x11 ORA (d), y */ + /* 0x11 ORA (d), y */ op(direct_indirect_indexed, ORA); /* 0x12 ORA (d) */ op(direct_indirect, ORA); /* 0x13 ORA (d, s), y */ /* 0x14 TRB d */ op(absolute_rmw, TRB); /* 0x15 ORA d, x */ /* 0x16 ASL d, x */ - /* 0x17 ORA [d], y */ + /* 0x17 ORA [d], y */ op(direct_indirect_indexed_long, ORA); /* 0x18 CLC i */ /* 0x19 ORA a, y */ op(absolute_y, ORA); /* 0x1a INC A */ op(accumulator, INC); @@ -479,13 +528,13 @@ ProcessorStorage::ProcessorStorage() { /* 0x1f ORA al, x */ op(absolute_long_x, ORA); /* 0x20 JSR a */ op(absolute_jsr, JSR); - /* 0x21 ORA (d), y */ - /* 0x22 AND (d, x) */ op(direct_indexed_indirect, AND); - /* 0x23 JSL al */ op(absolute_long_jsl, JSL); + /* 0x21 AND (d, x) */ op(direct_indexed_indirect, AND); + /* 0x22 JSL al */ op(absolute_long_jsl, JSL); + /* 0x23 AND d, s */ /* 0x24 BIT d */ op(direct, BIT); /* 0x25 AND d */ op(direct, AND); /* 0x26 ROL d */ op(absolute_rmw, ROL); - /* 0x27 AND [d] */ + /* 0x27 AND [d] */ op(direct_indirect_long, AND); /* 0x28 PLP s */ /* 0x29 AND # */ /* 0x2a ROL A */ op(accumulator, ROL); @@ -496,13 +545,13 @@ ProcessorStorage::ProcessorStorage() { /* 0x2f AND al */ op(absolute_long, AND); /* 0x30 BMI R */ - /* 0x31 AND (d), y */ + /* 0x31 AND (d), y */ op(direct_indirect_indexed, AND); /* 0x32 AND (d) */ op(direct_indirect, AND); /* 0x33 AND (d, s), y */ /* 0x34 BIT d, x */ /* 0x35 AND d, x */ /* 0x36 ROL d, x */ op(absolute_x_rmw, ROL); - /* 0x37 AND [d], y */ + /* 0x37 AND [d], y */ op(direct_indirect_indexed_long, AND); /* 0x38 SEC i */ /* 0x39 AND a, y */ op(absolute_y, AND); /* 0x3a DEC A */ op(accumulator, DEC); @@ -519,7 +568,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x44 MVP xyc */ op(block_move, MVP); /* 0x45 EOR d */ op(direct, EOR); /* 0x46 LSR d */ op(direct_rmw, LSR); - /* 0x47 EOR [d] */ + /* 0x47 EOR [d] */ op(direct_indirect_long, EOR); /* 0x48 PHA s */ /* 0x49 EOR # */ /* 0x4a LSR A */ op(accumulator, LSR); @@ -530,13 +579,13 @@ ProcessorStorage::ProcessorStorage() { /* 0x4f EOR al */ op(absolute_long, EOR); /* 0x50 BVC r */ - /* 0x51 EOR (d), y */ + /* 0x51 EOR (d), y */ op(direct_indirect_indexed, EOR); /* 0x52 EOR (d) */ op(direct_indirect, EOR); /* 0x53 EOR (d, s), y */ /* 0x54 MVN xyc */ op(block_move, MVN); /* 0x55 EOR d, x */ /* 0x56 LSR d, x */ - /* 0x57 EOR [d],y */ + /* 0x57 EOR [d], y */ op(direct_indirect_indexed_long, EOR); /* 0x58 CLI i */ /* 0x59 EOR a, y */ op(absolute_y, EOR); /* 0x5a PHY s */ @@ -553,7 +602,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x64 STZ d */ op(direct, STZ); /* 0x65 ADC d */ op(direct, ADC); /* 0x66 ROR d */ op(direct_rmw, ROR); - /* 0x67 ADC [d] */ + /* 0x67 ADC [d] */ op(direct_indirect_long, ADC); /* 0x68 PLA s */ /* 0x69 ADC # */ /* 0x6a ROR A */ op(accumulator, ROR); @@ -564,13 +613,13 @@ ProcessorStorage::ProcessorStorage() { /* 0x6f ADC al */ op(absolute_long, ADC); /* 0x70 BVS r */ - /* 0x71 ADC (d), y */ + /* 0x71 ADC (d), y */ op(direct_indirect_indexed, ADC); /* 0x72 ADC (d) */ op(direct_indirect, ADC); /* 0x73 ADC (d, s), y */ /* 0x74 STZ d, x */ /* 0x75 ADC d, x */ /* 0x76 ROR d, x */ - /* 0x77 ADC [d], y */ + /* 0x77 ADC [d], y */ op(direct_indirect_indexed_long, ADC); /* 0x78 SEI i */ /* 0x79 ADC a, y */ op(absolute_y, ADC); /* 0x7a PLY s */ @@ -587,7 +636,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x84 STY d */ op(direct, STY); /* 0x85 STA d */ op(direct, STA); /* 0x86 STX d */ op(direct, STX); - /* 0x87 STA [d] */ + /* 0x87 STA [d] */ op(direct_indirect_long, STA); /* 0x88 DEY i */ /* 0x89 BIT # */ /* 0x8a TXA i */ @@ -598,13 +647,13 @@ ProcessorStorage::ProcessorStorage() { /* 0x8f STA al */ op(absolute_long, STA); /* 0x90 BCC r */ - /* 0x91 STA (d), y */ + /* 0x91 STA (d), y */ op(direct_indirect_indexed, STA); /* 0x92 STA (d) */ op(direct_indirect, STA); /* 0x93 STA (d, x), y */ /* 0x94 STY d, x */ /* 0x95 STA d, x */ /* 0x96 STX d, y */ - /* 0x97 STA [d], y */ + /* 0x97 STA [d], y */ op(direct_indirect_indexed_long, STA); /* 0x98 TYA i */ /* 0x99 STA a, y */ op(absolute_y, STA); /* 0x9a TXS i */ @@ -621,7 +670,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xa4 LDY d */ op(direct, LDY); /* 0xa5 LDA d */ op(direct, LDA); /* 0xa6 LDX d */ op(direct, LDX); - /* 0xa7 LDA [d] */ + /* 0xa7 LDA [d] */ op(direct_indirect_long, LDA); /* 0xa8 TAY i */ /* 0xa9 LDA # */ /* 0xaa TAX i */ @@ -632,13 +681,13 @@ ProcessorStorage::ProcessorStorage() { /* 0xaf LDA al */ op(absolute_long, LDA); /* 0xb0 BCS r */ - /* 0xb1 LDA (d), y */ + /* 0xb1 LDA (d), y */ op(direct_indirect_indexed, LDA); /* 0xb2 LDA (d) */ op(direct_indirect, LDA); /* 0xb3 LDA (d, s), y */ /* 0xb4 LDY d, x */ /* 0xb5 LDA d, x */ /* 0xb6 LDX d, y */ - /* 0xb7 LDA [d], y */ + /* 0xb7 LDA [d], y */ op(direct_indirect_indexed_long, LDA); /* 0xb8 CLV i */ /* 0xb9 LDA a, y */ op(absolute_y, LDA); /* 0xba TSX i */ @@ -655,7 +704,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xc4 CPY d */ op(direct, CPY); /* 0xc5 CMP d */ op(direct, CMP); /* 0xc6 DEC d */ op(direct_rmw, DEC); - /* 0xc7 CMP [d] */ + /* 0xc7 CMP [d] */ op(direct_indirect_long, CMP); /* 0xc8 INY i */ /* 0xc9 CMP # */ /* 0xca DEX i */ @@ -666,13 +715,13 @@ ProcessorStorage::ProcessorStorage() { /* 0xcf CMP al */ op(absolute_long, CMP); /* 0xd0 BNE r */ - /* 0xd1 CMP (d), y */ + /* 0xd1 CMP (d), y */ op(direct_indirect_indexed, CMP); /* 0xd2 CMP (d) */ op(direct_indirect, CMP); /* 0xd3 CMP (d, s), y */ /* 0xd4 PEI s */ /* 0xd5 CMP d, x */ /* 0xd6 DEC d, x */ - /* 0xd7 CMP [d], y */ + /* 0xd7 CMP [d], y */ op(direct_indirect_indexed_long, CMP); /* 0xd8 CLD i */ /* 0xd9 CMP a, y */ op(absolute_y, CMP); /* 0xda PHX s */ @@ -689,7 +738,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xe4 CPX d */ op(direct, CPX); /* 0xe5 SBC d */ op(direct, SBC); /* 0xe6 INC d */ op(direct_rmw, INC); - /* 0xe7 SBC [d] */ + /* 0xe7 SBC [d] */ op(direct_indirect_long, SBC); /* 0xe8 INX i */ /* 0xe9 SBC # */ /* 0xea NOP i */ @@ -700,13 +749,13 @@ ProcessorStorage::ProcessorStorage() { /* 0xef SBC al */ op(absolute_long, SBC); /* 0xf0 BEQ r */ - /* 0xf1 SBC (d), y */ + /* 0xf1 SBC (d), y */ op(direct_indirect_indexed, SBC); /* 0xf2 SBC (d) */ op(direct_indirect, SBC); /* 0xf3 SBC (d, s), y */ /* 0xf4 PEA s */ /* 0xf5 SBC d, x */ /* 0xf6 INC d, x */ - /* 0xf7 SBC [d], y */ + /* 0xf7 SBC [d], y */ op(direct_indirect_indexed_long, SBC); /* 0xf8 SED i */ /* 0xf9 SBC a, y */ op(absolute_y, SBC); /* 0xfa PLX s */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index e5abf9fc0..41b15f98f 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -67,6 +67,9 @@ enum MicroOp: uint8_t { // These follow similar skip-one-if-possible logic to OperationConstructDirect. OperationConstructDirectIndexedIndirect, OperationConstructDirectIndirect, + OperationConstructDirectIndirectIndexed, + OperationConstructDirectIndirectIndexedLong, + OperationConstructDirectIndirectLong, /// Performs whatever operation goes with this program. OperationPerform, From f54b655606295afdcb9c0e56f7079f07fda62c8e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 17:26:17 -0400 Subject: [PATCH 019/150] Adds `d, x` and `d, y`. --- .../65816/Implementation/65816Storage.cpp | 71 ++++++++++++++----- .../65816/Implementation/65816Storage.hpp | 2 + 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 343f81371..d2e5a9c90 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -461,8 +461,41 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 16a. Direct, X; d, x. + static void direct_x(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectX); + target(CycleFetchPC); // IO. + + target(CycleFetchPC); // IO. + + read_write(type, is8bit, target); + } + // 16b. Direct, X; d, x, read-modify-write. + static void direct_x_rmw(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectX); + target(CycleFetchPC); // IO. + + target(CycleFetchPC); // IO. + + read_modify_write(is8bit, target); + } + // 17. Direct, Y; d, y. + static void direct_y(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // DO. + + target(OperationConstructDirectY); + target(CycleFetchPC); // IO. + + target(CycleFetchPC); // IO. + + read_write(type, is8bit, target); + } + // 18. Immediate; #. // 19a. Implied; i. // 19b. Implied; i; XBA. @@ -515,8 +548,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x12 ORA (d) */ op(direct_indirect, ORA); /* 0x13 ORA (d, s), y */ /* 0x14 TRB d */ op(absolute_rmw, TRB); - /* 0x15 ORA d, x */ - /* 0x16 ASL d, x */ + /* 0x15 ORA d, x */ op(direct_x, ORA); + /* 0x16 ASL d, x */ op(direct_x_rmw, ASL); /* 0x17 ORA [d], y */ op(direct_indirect_indexed_long, ORA); /* 0x18 CLC i */ /* 0x19 ORA a, y */ op(absolute_y, ORA); @@ -548,8 +581,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x31 AND (d), y */ op(direct_indirect_indexed, AND); /* 0x32 AND (d) */ op(direct_indirect, AND); /* 0x33 AND (d, s), y */ - /* 0x34 BIT d, x */ - /* 0x35 AND d, x */ + /* 0x34 BIT d, x */ op(direct_x, BIT); + /* 0x35 AND d, x */ op(direct_x, AND); /* 0x36 ROL d, x */ op(absolute_x_rmw, ROL); /* 0x37 AND [d], y */ op(direct_indirect_indexed_long, AND); /* 0x38 SEC i */ @@ -583,8 +616,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x52 EOR (d) */ op(direct_indirect, EOR); /* 0x53 EOR (d, s), y */ /* 0x54 MVN xyc */ op(block_move, MVN); - /* 0x55 EOR d, x */ - /* 0x56 LSR d, x */ + /* 0x55 EOR d, x */ op(direct_x, EOR); + /* 0x56 LSR d, x */ op(direct_x_rmw, LSR); /* 0x57 EOR [d], y */ op(direct_indirect_indexed_long, EOR); /* 0x58 CLI i */ /* 0x59 EOR a, y */ op(absolute_y, EOR); @@ -616,9 +649,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x71 ADC (d), y */ op(direct_indirect_indexed, ADC); /* 0x72 ADC (d) */ op(direct_indirect, ADC); /* 0x73 ADC (d, s), y */ - /* 0x74 STZ d, x */ - /* 0x75 ADC d, x */ - /* 0x76 ROR d, x */ + /* 0x74 STZ d, x */ op(direct_x, STZ); + /* 0x75 ADC d, x */ op(direct_x, ADC); + /* 0x76 ROR d, x */ op(direct_x_rmw, ROR); /* 0x77 ADC [d], y */ op(direct_indirect_indexed_long, ADC); /* 0x78 SEI i */ /* 0x79 ADC a, y */ op(absolute_y, ADC); @@ -650,9 +683,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x91 STA (d), y */ op(direct_indirect_indexed, STA); /* 0x92 STA (d) */ op(direct_indirect, STA); /* 0x93 STA (d, x), y */ - /* 0x94 STY d, x */ - /* 0x95 STA d, x */ - /* 0x96 STX d, y */ + /* 0x94 STY d, x */ op(direct_x, STY); + /* 0x95 STA d, x */ op(direct_x, STA); + /* 0x96 STX d, y */ op(direct_y, STX); /* 0x97 STA [d], y */ op(direct_indirect_indexed_long, STA); /* 0x98 TYA i */ /* 0x99 STA a, y */ op(absolute_y, STA); @@ -684,9 +717,9 @@ ProcessorStorage::ProcessorStorage() { /* 0xb1 LDA (d), y */ op(direct_indirect_indexed, LDA); /* 0xb2 LDA (d) */ op(direct_indirect, LDA); /* 0xb3 LDA (d, s), y */ - /* 0xb4 LDY d, x */ - /* 0xb5 LDA d, x */ - /* 0xb6 LDX d, y */ + /* 0xb4 LDY d, x */ op(direct_x, LDY); + /* 0xb5 LDA d, x */ op(direct_x, LDA); + /* 0xb6 LDX d, y */ op(direct_y, LDX); /* 0xb7 LDA [d], y */ op(direct_indirect_indexed_long, LDA); /* 0xb8 CLV i */ /* 0xb9 LDA a, y */ op(absolute_y, LDA); @@ -719,8 +752,8 @@ ProcessorStorage::ProcessorStorage() { /* 0xd2 CMP (d) */ op(direct_indirect, CMP); /* 0xd3 CMP (d, s), y */ /* 0xd4 PEI s */ - /* 0xd5 CMP d, x */ - /* 0xd6 DEC d, x */ + /* 0xd5 CMP d, x */ op(direct_x, CMP); + /* 0xd6 DEC d, x */ op(direct_x_rmw, DEC); /* 0xd7 CMP [d], y */ op(direct_indirect_indexed_long, CMP); /* 0xd8 CLD i */ /* 0xd9 CMP a, y */ op(absolute_y, CMP); @@ -753,8 +786,8 @@ ProcessorStorage::ProcessorStorage() { /* 0xf2 SBC (d) */ op(direct_indirect, SBC); /* 0xf3 SBC (d, s), y */ /* 0xf4 PEA s */ - /* 0xf5 SBC d, x */ - /* 0xf6 INC d, x */ + /* 0xf5 SBC d, x */ op(direct_x, SBC); + /* 0xf6 INC d, x */ op(direct_x_rmw, INC); /* 0xf7 SBC [d], y */ op(direct_indirect_indexed_long, SBC); /* 0xf8 SED i */ /* 0xf9 SBC a, y */ op(absolute_y, SBC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 41b15f98f..be1aed30b 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -70,6 +70,8 @@ enum MicroOp: uint8_t { OperationConstructDirectIndirectIndexed, OperationConstructDirectIndirectIndexedLong, OperationConstructDirectIndirectLong, + OperationConstructDirectX, + OperationConstructDirectY, /// Performs whatever operation goes with this program. OperationPerform, From 3dc22a9fd5e841d720c610148a5488e39250c0a6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 17:42:42 -0400 Subject: [PATCH 020/150] Adds implied and immediate modes. ... for 204/256 opcodes covered. --- .../65816/Implementation/65816Storage.cpp | 112 ++++++++++-------- .../65816/Implementation/65816Storage.hpp | 9 ++ 2 files changed, 74 insertions(+), 47 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index d2e5a9c90..c19a5f829 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -28,15 +28,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { case LDA: case LDX: case LDY: - // The access type for the rest of these ::Reads is arbitrary; they're + // The access type for everything else is arbitrary; they're // not relevantly either read or write. - case JMP: case JSR: case JML: case JSL: - - case ASL: case DEC: case INC: case LSR: - case ROL: case ROR: case TRB: case TSB: - - case MVN: case MVP: - + default: return AccessType::Read; case STA: case STX: case STY: case STZ: @@ -497,8 +491,32 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 18. Immediate; #. + static void immediate(AccessType, bool is8bit, const std::function &target) { + if(!is8bit) target(CycleFetchIncrementPC); // IDL. + target(CycleFetchIncrementPC); // ID [H]. + target(OperationCopyInstructionToData); + target(OperationPerform); + } + + static void immediate_rep_sep(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // IDL. + target(CycleFetchPC); // "Add 1 cycle for REP and SEP" + target(OperationPerform); + } + // 19a. Implied; i. + static void implied(AccessType, bool, const std::function &target) { + target(CycleFetchPC); // IO + target(OperationPerform); + } + // 19b. Implied; i; XBA. + static void implied_xba(AccessType, bool, const std::function &target) { + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO + target(OperationPerform); + } + // 19c. Stop the Clock. // 19d. Wait for interrupt. // 20. Relative; r. @@ -535,7 +553,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x06 ASL d */ op(direct_rmw, ASL); /* 0x07 ORA [d] */ op(direct_indirect_long, ORA); /* 0x08 PHP s */ - /* 0x09 ORA # */ + /* 0x09 ORA # */ op(immediate, ORA); /* 0x0a ASL A */ op(accumulator, ASL); /* 0x0b PHD s */ /* 0x0c TSB a */ op(absolute_rmw, TSB); @@ -551,10 +569,10 @@ ProcessorStorage::ProcessorStorage() { /* 0x15 ORA d, x */ op(direct_x, ORA); /* 0x16 ASL d, x */ op(direct_x_rmw, ASL); /* 0x17 ORA [d], y */ op(direct_indirect_indexed_long, ORA); - /* 0x18 CLC i */ + /* 0x18 CLC i */ op(implied, CLC); /* 0x19 ORA a, y */ op(absolute_y, ORA); /* 0x1a INC A */ op(accumulator, INC); - /* 0x1b TCS i */ + /* 0x1b TCS i */ op(implied, TCS); /* 0x1c TRB a */ op(absolute_rmw, TRB); /* 0x1d ORA a, x */ op(absolute_x, ORA); /* 0x1e ASL a, x */ op(absolute_x_rmw, ASL); @@ -569,7 +587,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x26 ROL d */ op(absolute_rmw, ROL); /* 0x27 AND [d] */ op(direct_indirect_long, AND); /* 0x28 PLP s */ - /* 0x29 AND # */ + /* 0x29 AND # */ op(immediate, AND); /* 0x2a ROL A */ op(accumulator, ROL); /* 0x2b PLD s */ /* 0x2c BIT a */ op(absolute, BIT); @@ -585,10 +603,10 @@ ProcessorStorage::ProcessorStorage() { /* 0x35 AND d, x */ op(direct_x, AND); /* 0x36 ROL d, x */ op(absolute_x_rmw, ROL); /* 0x37 AND [d], y */ op(direct_indirect_indexed_long, AND); - /* 0x38 SEC i */ + /* 0x38 SEC i */ op(implied, SEC); /* 0x39 AND a, y */ op(absolute_y, AND); /* 0x3a DEC A */ op(accumulator, DEC); - /* 0x3b TSC i */ + /* 0x3b TSC i */ op(implied, TSC); /* 0x3c BIT a, x */ op(absolute_x, BIT); /* 0x3d AND a, x */ op(absolute_x, AND); /* 0x3e TLD a, x */ @@ -603,7 +621,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x46 LSR d */ op(direct_rmw, LSR); /* 0x47 EOR [d] */ op(direct_indirect_long, EOR); /* 0x48 PHA s */ - /* 0x49 EOR # */ + /* 0x49 EOR # */ op(immediate, EOR); /* 0x4a LSR A */ op(accumulator, LSR); /* 0x4b PHK s */ /* 0x4c JMP a */ op(absolute, JMP); @@ -619,10 +637,10 @@ ProcessorStorage::ProcessorStorage() { /* 0x55 EOR d, x */ op(direct_x, EOR); /* 0x56 LSR d, x */ op(direct_x_rmw, LSR); /* 0x57 EOR [d], y */ op(direct_indirect_indexed_long, EOR); - /* 0x58 CLI i */ + /* 0x58 CLI i */ op(implied, CLI); /* 0x59 EOR a, y */ op(absolute_y, EOR); /* 0x5a PHY s */ - /* 0x5b TCD i */ + /* 0x5b TCD i */ op(implied, TCD); /* 0x5c JMP al */ op(absolute_long_jmp, JML); // [sic]; this updates PBR so it's JML. /* 0x5d EOR a, x */ op(absolute_x, EOR); /* 0x5e LSR a, x */ op(absolute_x_rmw, LSR); @@ -637,7 +655,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x66 ROR d */ op(direct_rmw, ROR); /* 0x67 ADC [d] */ op(direct_indirect_long, ADC); /* 0x68 PLA s */ - /* 0x69 ADC # */ + /* 0x69 ADC # */ op(immediate, ADC); /* 0x6a ROR A */ op(accumulator, ROR); /* 0x6b RTL s */ /* 0x6c JMP (a) */ op(absolute_indirect_jmp, JMP); @@ -653,10 +671,10 @@ ProcessorStorage::ProcessorStorage() { /* 0x75 ADC d, x */ op(direct_x, ADC); /* 0x76 ROR d, x */ op(direct_x_rmw, ROR); /* 0x77 ADC [d], y */ op(direct_indirect_indexed_long, ADC); - /* 0x78 SEI i */ + /* 0x78 SEI i */ op(implied, SEI); /* 0x79 ADC a, y */ op(absolute_y, ADC); /* 0x7a PLY s */ - /* 0x7b TDC i */ + /* 0x7b TDC i */ op(implied, TDC); /* 0x7c JMP (a, x) */ op(absolute_indexed_indirect_jmp, JMP); /* 0x7d ADC a, x */ op(absolute_x, ADC); /* 0x7e ROR a, x */ op(absolute_x_rmw, ROR); @@ -670,9 +688,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x85 STA d */ op(direct, STA); /* 0x86 STX d */ op(direct, STX); /* 0x87 STA [d] */ op(direct_indirect_long, STA); - /* 0x88 DEY i */ - /* 0x89 BIT # */ - /* 0x8a TXA i */ + /* 0x88 DEY i */ op(implied, DEY); + /* 0x89 BIT # */ op(immediate, BIT); + /* 0x8a TXA i */ op(implied, TXA); /* 0x8b PHB s */ /* 0x8c STY a */ op(absolute, STY); /* 0x8d STA a */ op(absolute, STA); @@ -687,26 +705,26 @@ ProcessorStorage::ProcessorStorage() { /* 0x95 STA d, x */ op(direct_x, STA); /* 0x96 STX d, y */ op(direct_y, STX); /* 0x97 STA [d], y */ op(direct_indirect_indexed_long, STA); - /* 0x98 TYA i */ + /* 0x98 TYA i */ op(implied, TYA); /* 0x99 STA a, y */ op(absolute_y, STA); - /* 0x9a TXS i */ - /* 0x9b TXY i */ + /* 0x9a TXS i */ op(implied, TXS); + /* 0x9b TXY i */ op(implied, TXY); /* 0x9c STZ a */ op(absolute, STZ); /* 0x9d STA a, x */ op(absolute_x, STA); /* 0x9e STZ a, x */ op(absolute_x, STZ); /* 0x9f STA al, x */ op(absolute_long_x, STA); - /* 0xa0 LDY # */ + /* 0xa0 LDY # */ op(immediate, LDY); /* 0xa1 LDA (d, x) */ op(direct_indexed_indirect, LDA); - /* 0xa2 LDX # */ + /* 0xa2 LDX # */ op(immediate, LDX); /* 0xa3 LDA d, s */ /* 0xa4 LDY d */ op(direct, LDY); /* 0xa5 LDA d */ op(direct, LDA); /* 0xa6 LDX d */ op(direct, LDX); /* 0xa7 LDA [d] */ op(direct_indirect_long, LDA); - /* 0xa8 TAY i */ - /* 0xa9 LDA # */ - /* 0xaa TAX i */ + /* 0xa8 TAY i */ op(implied, TAY); + /* 0xa9 LDA # */ op(immediate, LDA); + /* 0xaa TAX i */ op(implied, TAX); /* 0xab PLB s */ /* 0xac LDY a */ op(absolute, LDY); /* 0xad LDA a */ op(absolute, LDA); @@ -721,26 +739,26 @@ ProcessorStorage::ProcessorStorage() { /* 0xb5 LDA d, x */ op(direct_x, LDA); /* 0xb6 LDX d, y */ op(direct_y, LDX); /* 0xb7 LDA [d], y */ op(direct_indirect_indexed_long, LDA); - /* 0xb8 CLV i */ + /* 0xb8 CLV i */ op(implied, CLV); /* 0xb9 LDA a, y */ op(absolute_y, LDA); - /* 0xba TSX i */ - /* 0xbb TYX i */ + /* 0xba TSX i */ op(implied, TSX); + /* 0xbb TYX i */ op(implied, TYX); /* 0xbc LDY a, x */ op(absolute_x, LDY); /* 0xbd LDA a, x */ op(absolute_x, LDA); /* 0xbe LDX a, y */ op(absolute_y, LDX); /* 0xbf LDA al, x */ op(absolute_long_x, LDA); - /* 0xc0 CPY # */ + /* 0xc0 CPY # */ op(immediate, CPY); /* 0xc1 CMP (d, x) */ op(direct_indexed_indirect, CMP); - /* 0xc2 REP # */ + /* 0xc2 REP # */ op(immediate_rep_sep, REP); /* 0xc3 CMP d, s */ /* 0xc4 CPY d */ op(direct, CPY); /* 0xc5 CMP d */ op(direct, CMP); /* 0xc6 DEC d */ op(direct_rmw, DEC); /* 0xc7 CMP [d] */ op(direct_indirect_long, CMP); - /* 0xc8 INY i */ - /* 0xc9 CMP # */ - /* 0xca DEX i */ + /* 0xc8 INY i */ op(implied, INY); + /* 0xc9 CMP # */ op(immediate, CMP); + /* 0xca DEX i */ op(implied, DEC); /* 0xcb WAI i */ /* 0xcc CPY a */ op(absolute, CPY); /* 0xcd CMP a */ op(absolute, CMP); @@ -755,7 +773,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd5 CMP d, x */ op(direct_x, CMP); /* 0xd6 DEC d, x */ op(direct_x_rmw, DEC); /* 0xd7 CMP [d], y */ op(direct_indirect_indexed_long, CMP); - /* 0xd8 CLD i */ + /* 0xd8 CLD i */ op(implied, CLD); /* 0xd9 CMP a, y */ op(absolute_y, CMP); /* 0xda PHX s */ /* 0xdb STP i */ @@ -764,18 +782,18 @@ ProcessorStorage::ProcessorStorage() { /* 0xde DEC a, x */ op(absolute_x_rmw, DEC); /* 0xdf CMP al, x */ op(absolute_long_x, CMP); - /* 0xe0 CPX # */ + /* 0xe0 CPX # */ op(immediate, CPX); /* 0xe1 SBC (d, x) */ op(direct_indexed_indirect, SBC); - /* 0xe2 SEP # */ + /* 0xe2 SEP # */ op(immediate_rep_sep, SEP); /* 0xe3 SBC d, s */ /* 0xe4 CPX d */ op(direct, CPX); /* 0xe5 SBC d */ op(direct, SBC); /* 0xe6 INC d */ op(direct_rmw, INC); /* 0xe7 SBC [d] */ op(direct_indirect_long, SBC); - /* 0xe8 INX i */ + /* 0xe8 INX i */ op(implied, INX); /* 0xe9 SBC # */ - /* 0xea NOP i */ - /* 0xeb XBA i */ + /* 0xea NOP i */ op(implied, NOP); + /* 0xeb XBA i */ op(implied_xba, XBA); /* 0xec CPX a */ op(absolute, CPX); /* 0xed SBC a */ op(absolute, SBC); /* 0xee INC a */ op(absolute_rmw, INC); @@ -789,10 +807,10 @@ ProcessorStorage::ProcessorStorage() { /* 0xf5 SBC d, x */ op(direct_x, SBC); /* 0xf6 INC d, x */ op(direct_x_rmw, INC); /* 0xf7 SBC [d], y */ op(direct_indirect_indexed_long, SBC); - /* 0xf8 SED i */ + /* 0xf8 SED i */ op(implied, SED); /* 0xf9 SBC a, y */ op(absolute_y, SBC); /* 0xfa PLX s */ - /* 0xfb XCE i */ + /* 0xfb XCE i */ op(implied, XCE); /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JMP); // [sic] /* 0xfd SBC a, x */ op(absolute_x, SBC); /* 0xfe INC a, x */ op(absolute_x_rmw, INC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index be1aed30b..641c517ed 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -78,6 +78,7 @@ enum MicroOp: uint8_t { /// Copies the current program counter to the data buffer. OperationCopyPCToData, + OperationCopyInstructionToData, /// Copies the current PBR to the data buffer. OperationCopyPBRToData, @@ -109,6 +110,14 @@ enum Operation: uint8_t { // the program counter only if appropriate. MVN, MVP, + // These use a value straight from the instruction buffer. + REP, SEP, + + // These are all implicit. + CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, SEC, SED, SEI, + TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, + XCE, XBA, + /// Loads the PC with the operand from the data buffer. JMP, From 473799cb62241b474f64548480406458e3f703c6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 20:18:30 -0400 Subject: [PATCH 021/150] There's not a lot to STP and WAI from a bus program point of view. --- Processors/65816/Implementation/65816Storage.cpp | 16 ++++++++++++++-- Processors/65816/Implementation/65816Storage.hpp | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index c19a5f829..00af1b764 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -518,7 +518,19 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 19c. Stop the Clock. + static void stp(AccessType, bool, const std::function &target) { + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO + target(OperationPerform); + } + // 19d. Wait for interrupt. + static void wai(AccessType, bool, const std::function &target) { + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO + target(OperationPerform); + } + // 20. Relative; r. // 21. Relative long; rl. // 22a. Stack; s, abort/irq/nmi/res. @@ -759,7 +771,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xc8 INY i */ op(implied, INY); /* 0xc9 CMP # */ op(immediate, CMP); /* 0xca DEX i */ op(implied, DEC); - /* 0xcb WAI i */ + /* 0xcb WAI i */ op(wai, WAI); /* 0xcc CPY a */ op(absolute, CPY); /* 0xcd CMP a */ op(absolute, CMP); /* 0xce DEC a */ op(absolute_rmw, DEC); @@ -776,7 +788,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd8 CLD i */ op(implied, CLD); /* 0xd9 CMP a, y */ op(absolute_y, CMP); /* 0xda PHX s */ - /* 0xdb STP i */ + /* 0xdb STP i */ op(stp, STP); /* 0xdc JML (a) */ op(absolute_indirect_jml, JML); /* 0xdd CMP a, x */ op(absolute_x, CMP); /* 0xde DEC a, x */ op(absolute_x_rmw, DEC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 641c517ed..03042c79e 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -118,6 +118,8 @@ enum Operation: uint8_t { TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, XCE, XBA, + STP, WAI, + /// Loads the PC with the operand from the data buffer. JMP, From 687f4bb3bb5219a57144929da55a2fbcd238a907 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 20:24:50 -0400 Subject: [PATCH 022/150] Adds relative and relative long bus patterns. Many of the rest cover only one or two opcodes so this puts me at 216/256 opcodes covered; 35/47 bus programs; just more than 5/7 pages. --- .../65816/Implementation/65816Storage.cpp | 38 ++++++++++++++----- .../65816/Implementation/65816Storage.hpp | 2 + 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 00af1b764..ec0176133 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -532,7 +532,25 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 20. Relative; r. + static void relative(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // Offset + + target(OperationPerform); // The branch instructions will all skip one or two + // of the next cycles, depending on the effect of + // the jump. + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO + } + // 21. Relative long; rl. + static void relative_long(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // Offset low. + target(CycleFetchIncrementPC); // Offset high. + target(CycleFetchPC); // IO + + target(OperationPerform); // [BRL] + } + // 22a. Stack; s, abort/irq/nmi/res. // 22b. Stack; s, PLx. // 22c. Stack; s, PHx. @@ -573,7 +591,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x0e ASL a */ op(absolute_rmw, ASL); /* 0x0f ORA al */ op(absolute_long, ORA); - /* 0x10 BPL r */ + /* 0x10 BPL r */ op(relative, BPL); /* 0x11 ORA (d), y */ op(direct_indirect_indexed, ORA); /* 0x12 ORA (d) */ op(direct_indirect, ORA); /* 0x13 ORA (d, s), y */ @@ -607,7 +625,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x2e ROL a */ op(absolute_rmw, ROL); /* 0x2f AND al */ op(absolute_long, AND); - /* 0x30 BMI R */ + /* 0x30 BMI r */ op(relative, BMI); /* 0x31 AND (d), y */ op(direct_indirect_indexed, AND); /* 0x32 AND (d) */ op(direct_indirect, AND); /* 0x33 AND (d, s), y */ @@ -641,7 +659,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x4e LSR a */ op(absolute_rmw, LSR); /* 0x4f EOR al */ op(absolute_long, EOR); - /* 0x50 BVC r */ + /* 0x50 BVC r */ op(relative, BVC); /* 0x51 EOR (d), y */ op(direct_indirect_indexed, EOR); /* 0x52 EOR (d) */ op(direct_indirect, EOR); /* 0x53 EOR (d, s), y */ @@ -675,7 +693,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x6e ROR a */ op(absolute_rmw, ROR); /* 0x6f ADC al */ op(absolute_long, ADC); - /* 0x70 BVS r */ + /* 0x70 BVS r */ op(relative, BVS); /* 0x71 ADC (d), y */ op(direct_indirect_indexed, ADC); /* 0x72 ADC (d) */ op(direct_indirect, ADC); /* 0x73 ADC (d, s), y */ @@ -692,9 +710,9 @@ ProcessorStorage::ProcessorStorage() { /* 0x7e ROR a, x */ op(absolute_x_rmw, ROR); /* 0x7f ADC al, x */ op(absolute_long_x, ADC); - /* 0x80 BRA r */ + /* 0x80 BRA r */ op(relative, BRA); /* 0x81 STA (d, x) */ op(direct_indexed_indirect, STA); - /* 0x82 BRL rl */ + /* 0x82 BRL rl */ op(relative_long, BRL); /* 0x83 STA d, s */ /* 0x84 STY d */ op(direct, STY); /* 0x85 STA d */ op(direct, STA); @@ -709,7 +727,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x8e STX a */ op(absolute, STX); /* 0x8f STA al */ op(absolute_long, STA); - /* 0x90 BCC r */ + /* 0x90 BCC r */ op(relative, BCC); /* 0x91 STA (d), y */ op(direct_indirect_indexed, STA); /* 0x92 STA (d) */ op(direct_indirect, STA); /* 0x93 STA (d, x), y */ @@ -743,7 +761,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xae LDX a */ op(absolute, LDX); /* 0xaf LDA al */ op(absolute_long, LDA); - /* 0xb0 BCS r */ + /* 0xb0 BCS r */ op(relative, BCS); /* 0xb1 LDA (d), y */ op(direct_indirect_indexed, LDA); /* 0xb2 LDA (d) */ op(direct_indirect, LDA); /* 0xb3 LDA (d, s), y */ @@ -777,7 +795,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xce DEC a */ op(absolute_rmw, DEC); /* 0xcf CMP al */ op(absolute_long, CMP); - /* 0xd0 BNE r */ + /* 0xd0 BNE r */ op(relative, BNE); /* 0xd1 CMP (d), y */ op(direct_indirect_indexed, CMP); /* 0xd2 CMP (d) */ op(direct_indirect, CMP); /* 0xd3 CMP (d, s), y */ @@ -811,7 +829,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xee INC a */ op(absolute_rmw, INC); /* 0xef SBC al */ op(absolute_long, SBC); - /* 0xf0 BEQ r */ + /* 0xf0 BEQ r */ op(relative, BEQ); /* 0xf1 SBC (d), y */ op(direct_indirect_indexed, SBC); /* 0xf2 SBC (d) */ op(direct_indirect, SBC); /* 0xf3 SBC (d, s), y */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 03042c79e..0167d0e10 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -113,6 +113,8 @@ enum Operation: uint8_t { // These use a value straight from the instruction buffer. REP, SEP, + BCC, BCS, BEQ, BMI, BNE, BPL, BRA, BVC, BVS, BRL, + // These are all implicit. CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, SEC, SED, SEI, TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, From 4a53b6e5384387f8143a7389798124b3e54aab70 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 20:38:29 -0400 Subject: [PATCH 023/150] Adds push and pull, reaching 229/256 opcodes. --- .../65816/Implementation/65816Storage.cpp | 75 ++++++++++++++----- .../65816/Implementation/65816Storage.hpp | 12 +++ 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index ec0176133..d92e6efbd 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -533,13 +533,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 20. Relative; r. static void relative(AccessType, bool, const std::function &target) { - target(CycleFetchIncrementPC); // Offset + target(CycleFetchIncrementPC); // Offset - target(OperationPerform); // The branch instructions will all skip one or two - // of the next cycles, depending on the effect of - // the jump. - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(OperationPerform); // The branch instructions will all skip one or two + // of the next cycles, depending on the effect of + // the jump. + + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO } // 21. Relative long; rl. @@ -552,8 +553,44 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 22a. Stack; s, abort/irq/nmi/res. + static void stack_exception(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO + + target(OperationPrepareException); // Populates the data buffer. + + if(!is8bit) target(CyclePush); // PBR + target(CyclePush); // PCH + target(CyclePush); // PCL + target(CyclePush); // P + + target(CycleFetchIncrementData); // AAVL + target(CycleFetchIncrementData); // AAVH + + target(OperationPerform); // Jumps to the vector address. + } + // 22b. Stack; s, PLx. + static void stack_pull(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO + + if(!is8bit) target(CyclePull); // REG low. + target(CyclePull); // REG [high]. + + target(OperationPerform); + } + // 22c. Stack; s, PHx. + static void stack_push(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchPC); // IO + + target(OperationPerform); + + if(!is8bit) target(CyclePush); // REG high. + target(CyclePush); // REG [low]. + } + // 22d. Stack; s, PEA. // 22e. Stack; s, PEI. // 22f. Stack; s, PER. @@ -582,10 +619,10 @@ ProcessorStorage::ProcessorStorage() { /* 0x05 ORA d */ op(direct, ORA); /* 0x06 ASL d */ op(direct_rmw, ASL); /* 0x07 ORA [d] */ op(direct_indirect_long, ORA); - /* 0x08 PHP s */ + /* 0x08 PHP s */ op(stack_push, PHP); /* 0x09 ORA # */ op(immediate, ORA); /* 0x0a ASL A */ op(accumulator, ASL); - /* 0x0b PHD s */ + /* 0x0b PHD s */ op(stack_push, PHD); /* 0x0c TSB a */ op(absolute_rmw, TSB); /* 0x0d ORA a */ op(absolute, ORA); /* 0x0e ASL a */ op(absolute_rmw, ASL); @@ -616,10 +653,10 @@ ProcessorStorage::ProcessorStorage() { /* 0x25 AND d */ op(direct, AND); /* 0x26 ROL d */ op(absolute_rmw, ROL); /* 0x27 AND [d] */ op(direct_indirect_long, AND); - /* 0x28 PLP s */ + /* 0x28 PLP s */ op(stack_pull, PLP); /* 0x29 AND # */ op(immediate, AND); /* 0x2a ROL A */ op(accumulator, ROL); - /* 0x2b PLD s */ + /* 0x2b PLD s */ op(stack_pull, PLD); /* 0x2c BIT a */ op(absolute, BIT); /* 0x2d AND a */ op(absolute, AND); /* 0x2e ROL a */ op(absolute_rmw, ROL); @@ -650,10 +687,10 @@ ProcessorStorage::ProcessorStorage() { /* 0x45 EOR d */ op(direct, EOR); /* 0x46 LSR d */ op(direct_rmw, LSR); /* 0x47 EOR [d] */ op(direct_indirect_long, EOR); - /* 0x48 PHA s */ + /* 0x48 PHA s */ op(stack_push, STA); /* 0x49 EOR # */ op(immediate, EOR); /* 0x4a LSR A */ op(accumulator, LSR); - /* 0x4b PHK s */ + /* 0x4b PHK s */ op(stack_push, PHK); /* 0x4c JMP a */ op(absolute, JMP); /* 0x4d EOR a */ op(absolute, EOR); /* 0x4e LSR a */ op(absolute_rmw, LSR); @@ -669,7 +706,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x57 EOR [d], y */ op(direct_indirect_indexed_long, EOR); /* 0x58 CLI i */ op(implied, CLI); /* 0x59 EOR a, y */ op(absolute_y, EOR); - /* 0x5a PHY s */ + /* 0x5a PHY s */ op(stack_push, STY); /* 0x5b TCD i */ op(implied, TCD); /* 0x5c JMP al */ op(absolute_long_jmp, JML); // [sic]; this updates PBR so it's JML. /* 0x5d EOR a, x */ op(absolute_x, EOR); @@ -684,7 +721,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x65 ADC d */ op(direct, ADC); /* 0x66 ROR d */ op(direct_rmw, ROR); /* 0x67 ADC [d] */ op(direct_indirect_long, ADC); - /* 0x68 PLA s */ + /* 0x68 PLA s */ op(stack_pull, LDA); /* 0x69 ADC # */ op(immediate, ADC); /* 0x6a ROR A */ op(accumulator, ROR); /* 0x6b RTL s */ @@ -703,7 +740,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x77 ADC [d], y */ op(direct_indirect_indexed_long, ADC); /* 0x78 SEI i */ op(implied, SEI); /* 0x79 ADC a, y */ op(absolute_y, ADC); - /* 0x7a PLY s */ + /* 0x7a PLY s */ op(stack_pull, LDY); /* 0x7b TDC i */ op(implied, TDC); /* 0x7c JMP (a, x) */ op(absolute_indexed_indirect_jmp, JMP); /* 0x7d ADC a, x */ op(absolute_x, ADC); @@ -721,7 +758,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x88 DEY i */ op(implied, DEY); /* 0x89 BIT # */ op(immediate, BIT); /* 0x8a TXA i */ op(implied, TXA); - /* 0x8b PHB s */ + /* 0x8b PHB s */ op(stack_push, PHB); /* 0x8c STY a */ op(absolute, STY); /* 0x8d STA a */ op(absolute, STA); /* 0x8e STX a */ op(absolute, STX); @@ -755,7 +792,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xa8 TAY i */ op(implied, TAY); /* 0xa9 LDA # */ op(immediate, LDA); /* 0xaa TAX i */ op(implied, TAX); - /* 0xab PLB s */ + /* 0xab PLB s */ op(stack_pull, PLB); /* 0xac LDY a */ op(absolute, LDY); /* 0xad LDA a */ op(absolute, LDA); /* 0xae LDX a */ op(absolute, LDX); @@ -805,7 +842,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd7 CMP [d], y */ op(direct_indirect_indexed_long, CMP); /* 0xd8 CLD i */ op(implied, CLD); /* 0xd9 CMP a, y */ op(absolute_y, CMP); - /* 0xda PHX s */ + /* 0xda PHX s */ op(stack_push, STX); /* 0xdb STP i */ op(stp, STP); /* 0xdc JML (a) */ op(absolute_indirect_jml, JML); /* 0xdd CMP a, x */ op(absolute_x, CMP); @@ -839,7 +876,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf7 SBC [d], y */ op(direct_indirect_indexed_long, SBC); /* 0xf8 SED i */ op(implied, SED); /* 0xf9 SBC a, y */ op(absolute_y, SBC); - /* 0xfa PLX s */ + /* 0xfa PLX s */ op(stack_pull, LDX); /* 0xfb XCE i */ op(implied, XCE); /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JMP); // [sic] /* 0xfd SBC a, x */ op(absolute_x, SBC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 0167d0e10..0492f0852 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -23,6 +23,9 @@ enum MicroOp: uint8_t { /// of the instruction buffer, throwing the result away. CycleFetchIncorrectDataAddress, + /// Fetches a vector (i.e. IRQ, NMI, etc) into the data buffer. + CycleFetchVector, + // Dedicated block-move cycles; these use the data buffer as an intermediary. CycleFetchBlockX, CycleFetchBlockY, @@ -39,6 +42,8 @@ enum MicroOp: uint8_t { CyclePush, /// Fetches from the current stack location and throws the result away. CycleAccessStack, + /// Pulls a single byte to the data buffer from the stack. + CyclePull, /// Sets the data address by copying the final two bytes of the instruction buffer. OperationConstructAbsolute, @@ -86,6 +91,11 @@ enum MicroOp: uint8_t { OperationCopyAToData, OperationCopyDataToA, + /// Fills the data buffer with three or four bytes, depending on emulation mode, containing the program + /// counter, flags and possibly the program bank. Also puts the appropriate vector address into the + /// address register. + OperationPrepareException, + /// Complete this set of micr-ops. OperationMoveToNextProgram }; @@ -98,10 +108,12 @@ enum Operation: uint8_t { // These load the respective register from the data buffer; // they are implicitly AccessType::Read. LDA, LDX, LDY, + PLB, PLD, PLP, // LDA, LDX and LDY can be used for PLA, PLX, PLY. // These move the respective register (or value) to the data buffer; // they are implicitly AccessType::Write. STA, STX, STY, STZ, + PHB, PHP, PHD, PHK, // These modify the value in the data buffer as part of a read-modify-write. ASL, DEC, INC, LSR, ROL, ROR, TRB, TSB, From 596e700b60a1a39d669887a4abf956f549929b7a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 20:57:24 -0400 Subject: [PATCH 024/150] Drags myself onto the final page of bus programs. 233 opcodes now complete; six bus programs to go. --- .../65816/Implementation/65816Storage.cpp | 38 +++++++++++++++++-- .../65816/Implementation/65816Storage.hpp | 2 + 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index d92e6efbd..2977db29e 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -592,8 +592,38 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 22d. Stack; s, PEA. + static void stack_pea(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // AAL + target(CycleFetchIncrementPC); // AAH + target(CyclePush); // AAH + target(CyclePush); // AAL + } + // 22e. Stack; s, PEI. + static void stack_pei(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // DO + + target(OperationConstructDirect); + target(CycleFetchPC); // IO + + 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 &target) { + target(CycleFetchIncrementPC); // Offset low. + target(CycleFetchIncrementPC); // Offset high. + target(CycleFetchPC); // IO + + target(OperationConstructPER); + + target(CyclePush); // AAH + target(CyclePush); // AAL + } + // 22g. Stack; s, RTI. // 22h. Stack; s, RTS. // 22i. Stack; s, RTL. @@ -676,7 +706,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x3b TSC i */ op(implied, TSC); /* 0x3c BIT a, x */ op(absolute_x, BIT); /* 0x3d AND a, x */ op(absolute_x, AND); - /* 0x3e TLD a, x */ + /* 0x3e ROL a, x */ op(absolute_x_rmw, ROL); /* 0x3f AND al, x */ op(absolute_long_x, AND); /* 0x40 RTI s */ @@ -715,7 +745,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x60 RTS s */ /* 0x61 ADC (d, x) */ op(direct_indexed_indirect, ADC); - /* 0x62 PER s */ + /* 0x62 PER s */ op(stack_per, NOP); /* 0x63 ADC d, s */ /* 0x64 STZ d */ op(direct, STZ); /* 0x65 ADC d */ op(direct, ADC); @@ -836,7 +866,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd1 CMP (d), y */ op(direct_indirect_indexed, CMP); /* 0xd2 CMP (d) */ op(direct_indirect, CMP); /* 0xd3 CMP (d, s), y */ - /* 0xd4 PEI s */ + /* 0xd4 PEI s */ op(stack_pei, NOP); /* 0xd5 CMP d, x */ op(direct_x, CMP); /* 0xd6 DEC d, x */ op(direct_x_rmw, DEC); /* 0xd7 CMP [d], y */ op(direct_indirect_indexed_long, CMP); @@ -870,7 +900,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf1 SBC (d), y */ op(direct_indirect_indexed, SBC); /* 0xf2 SBC (d) */ op(direct_indirect, SBC); /* 0xf3 SBC (d, s), y */ - /* 0xf4 PEA s */ + /* 0xf4 PEA s */ op(stack_pea, NOP); /* 0xf5 SBC d, x */ op(direct_x, SBC); /* 0xf6 INC d, x */ op(direct_x_rmw, INC); /* 0xf7 SBC [d], y */ op(direct_indirect_indexed_long, SBC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 0492f0852..16bfbabbd 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -78,6 +78,8 @@ enum MicroOp: uint8_t { OperationConstructDirectX, OperationConstructDirectY, + OperationConstructPER, + /// Performs whatever operation goes with this program. OperationPerform, From 67c2ce2174d5928cd1c72eb7c9f15782ed2a5e96 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 21:20:01 -0400 Subject: [PATCH 025/150] Takes a run at completing the stack section. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm not really sure about BRK though — does it gain a signature on the 65816? --- .../65816/Implementation/65816Storage.cpp | 56 ++++++++++++++++--- .../65816/Implementation/65816Storage.hpp | 9 +++ 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 2977db29e..8497b9065 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -553,19 +553,20 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 22a. Stack; s, abort/irq/nmi/res. - static void stack_exception(AccessType, bool is8bit, const std::function &target) { + static void stack_exception(AccessType, bool, const std::function &target) { target(CycleFetchPC); // IO target(CycleFetchPC); // IO - target(OperationPrepareException); // Populates the data buffer. + target(OperationPrepareException); // Populates the data buffer; this skips a micro-op if + // in emulation mode. - if(!is8bit) target(CyclePush); // PBR + target(CyclePush); // PBR [skipped in emulation mode] target(CyclePush); // PCH target(CyclePush); // PCL target(CyclePush); // P target(CycleFetchIncrementData); // AAVL - target(CycleFetchIncrementData); // AAVH + target(CycleFetchData); // AAVH target(OperationPerform); // Jumps to the vector address. } @@ -625,9 +626,46 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 22g. Stack; s, RTI. + static void stack_rti(AccessType, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // IO + target(CycleFetchIncrementPC); // IO + + target(CyclePull); // P + target(CyclePull); // New PCL + target(CyclePull); // New PCH + if(!is8bit) target(CyclePull); // PBR + + target(OperationPerform); // [RTI] — to unpack the fields above. + } + // 22h. Stack; s, RTS. + static void stack_rts(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // IO + target(CycleFetchIncrementPC); // IO + + target(CyclePull); // PCL + target(CyclePull); // PCH + target(CycleAccessStack); // IO + + target(OperationPerform); // [JMP, to perform the RTS] + } + // 22i. Stack; s, RTL. + static void stack_rtl(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // IO + target(CycleFetchIncrementPC); // IO + + target(CyclePull); // New PCL + target(CyclePull); // New PCH + target(CyclePull); // New PBR + + target(OperationPerform); // [JML, to perform the RTL] + } + // 22j. Stack; s, BRK/COP. + + // Covered by stack_exception. + // 23. Stack Relative; d, s. // 24. Stack Relative Indirect Indexed (d, s), y. }; @@ -641,9 +679,9 @@ ProcessorStorage::ProcessorStorage() { // Install the instructions. #define op(x, y) constructor.install(&ProcessorStorageConstructor::x, y) - /* 0x00 BRK s */ + /* 0x00 BRK s */ op(stack_exception, BRK); /* 0x01 ORA (d, x) */ op(direct_indexed_indirect, ORA); - /* 0x02 COP s */ + /* 0x02 COP s */ op(stack_exception, BRK); /* 0x03 ORA d, s */ /* 0x04 TSB d */ op(direct_rmw, TSB); /* 0x05 ORA d */ op(direct, ORA); @@ -709,7 +747,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x3e ROL a, x */ op(absolute_x_rmw, ROL); /* 0x3f AND al, x */ op(absolute_long_x, AND); - /* 0x40 RTI s */ + /* 0x40 RTI s */ op(stack_rti, RTI); /* 0x41 EOR (d, x) */ op(direct_indexed_indirect, EOR); /* 0x42 WDM i */ /* 0x43 EOR d, s */ @@ -743,7 +781,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x5e LSR a, x */ op(absolute_x_rmw, LSR); /* 0x5f EOR al, x */ op(absolute_long_x, EOR); - /* 0x60 RTS s */ + /* 0x60 RTS s */ op(stack_rts, JMP); // [sic]; loads the PC from data as per an RTS. /* 0x61 ADC (d, x) */ op(direct_indexed_indirect, ADC); /* 0x62 PER s */ op(stack_per, NOP); /* 0x63 ADC d, s */ @@ -754,7 +792,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x68 PLA s */ op(stack_pull, LDA); /* 0x69 ADC # */ op(immediate, ADC); /* 0x6a ROR A */ op(accumulator, ROR); - /* 0x6b RTL s */ + /* 0x6b RTL s */ op(stack_rtl, JML); /* 0x6c JMP (a) */ op(absolute_indirect_jmp, JMP); /* 0x6d ADC a */ op(absolute, ADC); /* 0x6e ROR a */ op(absolute_rmw, ROR); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 16bfbabbd..d2c7f8839 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -79,6 +79,7 @@ enum MicroOp: uint8_t { OperationConstructDirectY, OperationConstructPER, + OperationConstructBRK, /// Performs whatever operation goes with this program. OperationPerform, @@ -136,6 +137,10 @@ enum Operation: uint8_t { STP, WAI, + // These unpack values from the data buffer, which has been filled + // from the stack. + RTI, RTL, + /// Loads the PC with the operand from the data buffer. JMP, @@ -149,6 +154,10 @@ enum Operation: uint8_t { /// Loads the PC and the PBR with the operand from the data buffer, /// replacing it with the old PC (and only the PC; PBR not included). JSL, + + /// i.e. jump to vector. TODO: is this really distinct from JMP? I'm assuming so for now, + /// as I assume the PBR is implicitly modified. We'll see. + BRK, }; class ProcessorStorageConstructor; From e680022b1f4262998c60c32336259384c383960a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 21:35:31 -0400 Subject: [PATCH 026/150] Completes the opcode set. A million bugs yet to find. --- .../65816/Implementation/65816Storage.cpp | 74 ++++++++++++------- .../65816/Implementation/65816Storage.hpp | 3 + 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 8497b9065..9fc540cb8 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -42,7 +42,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { using GeneratorKey = std::tuple; std::map> installed_patterns; - uint8_t opcode = 0; + int opcode = 0; void install(Generator generator, Operation operation) { // Determine the access type implied by this operation. const AccessType access_type = access_type_for_operation(operation); @@ -627,8 +627,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22g. Stack; s, RTI. static void stack_rti(AccessType, bool is8bit, const std::function &target) { - target(CycleFetchIncrementPC); // IO - target(CycleFetchIncrementPC); // IO + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO target(CyclePull); // P target(CyclePull); // New PCL @@ -640,14 +640,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22h. Stack; s, RTS. static void stack_rts(AccessType, bool, const std::function &target) { - target(CycleFetchIncrementPC); // IO - target(CycleFetchIncrementPC); // IO + target(CycleFetchPC); // IO + target(CycleFetchPC); // IO - target(CyclePull); // PCL - target(CyclePull); // PCH - target(CycleAccessStack); // IO + target(CyclePull); // PCL + target(CyclePull); // PCH + target(CycleAccessStack); // IO - target(OperationPerform); // [JMP, to perform the RTS] + target(OperationPerform); // [JMP, to perform the RTS] } // 22i. Stack; s, RTL. @@ -667,7 +667,27 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // Covered by stack_exception. // 23. Stack Relative; d, s. + static void stack_relative(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // SO + target(CycleFetchPC); // IO + + target(OperationConstructStackRelative); + read_write(type, is8bit, target); + } + // 24. Stack Relative Indirect Indexed (d, s), y. + static void stack_relative_indexed_indirect(AccessType type, bool is8bit, const std::function &target) { + target(CycleFetchIncrementPC); // SO + target(CycleFetchPC); // IO + + target(OperationConstructStackRelative); + target(CycleFetchIncrementData); // AAL + target(CycleFetchData); // AAH + target(CycleFetchData); // IO. + + target(OperationConstructStackRelativeIndexedIndirect); + read_write(type, is8bit, target); + } }; // TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. @@ -682,7 +702,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x00 BRK s */ op(stack_exception, BRK); /* 0x01 ORA (d, x) */ op(direct_indexed_indirect, ORA); /* 0x02 COP s */ op(stack_exception, BRK); - /* 0x03 ORA d, s */ + /* 0x03 ORA d, s */ op(stack_relative, ORA); /* 0x04 TSB d */ op(direct_rmw, TSB); /* 0x05 ORA d */ op(direct, ORA); /* 0x06 ASL d */ op(direct_rmw, ASL); @@ -699,7 +719,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x10 BPL r */ op(relative, BPL); /* 0x11 ORA (d), y */ op(direct_indirect_indexed, ORA); /* 0x12 ORA (d) */ op(direct_indirect, ORA); - /* 0x13 ORA (d, s), y */ + /* 0x13 ORA (d, s), y */ op(stack_relative_indexed_indirect, ORA); /* 0x14 TRB d */ op(absolute_rmw, TRB); /* 0x15 ORA d, x */ op(direct_x, ORA); /* 0x16 ASL d, x */ op(direct_x_rmw, ASL); @@ -716,7 +736,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x20 JSR a */ op(absolute_jsr, JSR); /* 0x21 AND (d, x) */ op(direct_indexed_indirect, AND); /* 0x22 JSL al */ op(absolute_long_jsl, JSL); - /* 0x23 AND d, s */ + /* 0x23 AND d, s */ op(stack_relative, AND); /* 0x24 BIT d */ op(direct, BIT); /* 0x25 AND d */ op(direct, AND); /* 0x26 ROL d */ op(absolute_rmw, ROL); @@ -733,7 +753,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x30 BMI r */ op(relative, BMI); /* 0x31 AND (d), y */ op(direct_indirect_indexed, AND); /* 0x32 AND (d) */ op(direct_indirect, AND); - /* 0x33 AND (d, s), y */ + /* 0x33 AND (d, s), y */ op(stack_relative_indexed_indirect, AND); /* 0x34 BIT d, x */ op(direct_x, BIT); /* 0x35 AND d, x */ op(direct_x, AND); /* 0x36 ROL d, x */ op(absolute_x_rmw, ROL); @@ -749,8 +769,8 @@ ProcessorStorage::ProcessorStorage() { /* 0x40 RTI s */ op(stack_rti, RTI); /* 0x41 EOR (d, x) */ op(direct_indexed_indirect, EOR); - /* 0x42 WDM i */ - /* 0x43 EOR d, s */ + /* 0x42 WDM i */ op(implied, NOP); + /* 0x43 EOR d, s */ op(stack_relative, EOR); /* 0x44 MVP xyc */ op(block_move, MVP); /* 0x45 EOR d */ op(direct, EOR); /* 0x46 LSR d */ op(direct_rmw, LSR); @@ -767,7 +787,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x50 BVC r */ op(relative, BVC); /* 0x51 EOR (d), y */ op(direct_indirect_indexed, EOR); /* 0x52 EOR (d) */ op(direct_indirect, EOR); - /* 0x53 EOR (d, s), y */ + /* 0x53 EOR (d, s), y */ op(stack_relative_indexed_indirect, EOR); /* 0x54 MVN xyc */ op(block_move, MVN); /* 0x55 EOR d, x */ op(direct_x, EOR); /* 0x56 LSR d, x */ op(direct_x_rmw, LSR); @@ -784,7 +804,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x60 RTS s */ op(stack_rts, JMP); // [sic]; loads the PC from data as per an RTS. /* 0x61 ADC (d, x) */ op(direct_indexed_indirect, ADC); /* 0x62 PER s */ op(stack_per, NOP); - /* 0x63 ADC d, s */ + /* 0x63 ADC d, s */ op(stack_relative, ADC); /* 0x64 STZ d */ op(direct, STZ); /* 0x65 ADC d */ op(direct, ADC); /* 0x66 ROR d */ op(direct_rmw, ROR); @@ -801,7 +821,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x70 BVS r */ op(relative, BVS); /* 0x71 ADC (d), y */ op(direct_indirect_indexed, ADC); /* 0x72 ADC (d) */ op(direct_indirect, ADC); - /* 0x73 ADC (d, s), y */ + /* 0x73 ADC (d, s), y */ op(stack_relative_indexed_indirect, ADC); /* 0x74 STZ d, x */ op(direct_x, STZ); /* 0x75 ADC d, x */ op(direct_x, ADC); /* 0x76 ROR d, x */ op(direct_x_rmw, ROR); @@ -818,7 +838,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x80 BRA r */ op(relative, BRA); /* 0x81 STA (d, x) */ op(direct_indexed_indirect, STA); /* 0x82 BRL rl */ op(relative_long, BRL); - /* 0x83 STA d, s */ + /* 0x83 STA d, s */ op(stack_relative, STA); /* 0x84 STY d */ op(direct, STY); /* 0x85 STA d */ op(direct, STA); /* 0x86 STX d */ op(direct, STX); @@ -835,7 +855,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x90 BCC r */ op(relative, BCC); /* 0x91 STA (d), y */ op(direct_indirect_indexed, STA); /* 0x92 STA (d) */ op(direct_indirect, STA); - /* 0x93 STA (d, x), y */ + /* 0x93 STA (d, s), y */ op(stack_relative_indexed_indirect, STA); /* 0x94 STY d, x */ op(direct_x, STY); /* 0x95 STA d, x */ op(direct_x, STA); /* 0x96 STX d, y */ op(direct_y, STX); @@ -852,7 +872,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xa0 LDY # */ op(immediate, LDY); /* 0xa1 LDA (d, x) */ op(direct_indexed_indirect, LDA); /* 0xa2 LDX # */ op(immediate, LDX); - /* 0xa3 LDA d, s */ + /* 0xa3 LDA d, s */ op(stack_relative, LDA); /* 0xa4 LDY d */ op(direct, LDY); /* 0xa5 LDA d */ op(direct, LDA); /* 0xa6 LDX d */ op(direct, LDX); @@ -869,7 +889,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xb0 BCS r */ op(relative, BCS); /* 0xb1 LDA (d), y */ op(direct_indirect_indexed, LDA); /* 0xb2 LDA (d) */ op(direct_indirect, LDA); - /* 0xb3 LDA (d, s), y */ + /* 0xb3 LDA (d, s), y */ op(stack_relative_indexed_indirect, LDA); /* 0xb4 LDY d, x */ op(direct_x, LDY); /* 0xb5 LDA d, x */ op(direct_x, LDA); /* 0xb6 LDX d, y */ op(direct_y, LDX); @@ -886,7 +906,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xc0 CPY # */ op(immediate, CPY); /* 0xc1 CMP (d, x) */ op(direct_indexed_indirect, CMP); /* 0xc2 REP # */ op(immediate_rep_sep, REP); - /* 0xc3 CMP d, s */ + /* 0xc3 CMP d, s */ op(stack_relative, CMP); /* 0xc4 CPY d */ op(direct, CPY); /* 0xc5 CMP d */ op(direct, CMP); /* 0xc6 DEC d */ op(direct_rmw, DEC); @@ -903,7 +923,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd0 BNE r */ op(relative, BNE); /* 0xd1 CMP (d), y */ op(direct_indirect_indexed, CMP); /* 0xd2 CMP (d) */ op(direct_indirect, CMP); - /* 0xd3 CMP (d, s), y */ + /* 0xd3 CMP (d, s), y */ op(stack_relative_indexed_indirect, CMP); /* 0xd4 PEI s */ op(stack_pei, NOP); /* 0xd5 CMP d, x */ op(direct_x, CMP); /* 0xd6 DEC d, x */ op(direct_x_rmw, DEC); @@ -920,13 +940,13 @@ ProcessorStorage::ProcessorStorage() { /* 0xe0 CPX # */ op(immediate, CPX); /* 0xe1 SBC (d, x) */ op(direct_indexed_indirect, SBC); /* 0xe2 SEP # */ op(immediate_rep_sep, SEP); - /* 0xe3 SBC d, s */ + /* 0xe3 SBC d, s */ op(stack_relative, SBC); /* 0xe4 CPX d */ op(direct, CPX); /* 0xe5 SBC d */ op(direct, SBC); /* 0xe6 INC d */ op(direct_rmw, INC); /* 0xe7 SBC [d] */ op(direct_indirect_long, SBC); /* 0xe8 INX i */ op(implied, INX); - /* 0xe9 SBC # */ + /* 0xe9 SBC # */ op(immediate, SBC); /* 0xea NOP i */ op(implied, NOP); /* 0xeb XBA i */ op(implied_xba, XBA); /* 0xec CPX a */ op(absolute, CPX); @@ -937,7 +957,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf0 BEQ r */ op(relative, BEQ); /* 0xf1 SBC (d), y */ op(direct_indirect_indexed, SBC); /* 0xf2 SBC (d) */ op(direct_indirect, SBC); - /* 0xf3 SBC (d, s), y */ + /* 0xf3 SBC (d, s), y */ op(stack_relative_indexed_indirect, SBC); /* 0xf4 PEA s */ op(stack_pea, NOP); /* 0xf5 SBC d, x */ op(direct_x, SBC); /* 0xf6 INC d, x */ op(direct_x_rmw, INC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index d2c7f8839..1d8a9009f 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -81,6 +81,9 @@ enum MicroOp: uint8_t { OperationConstructPER, OperationConstructBRK, + OperationConstructStackRelative, + OperationConstructStackRelativeIndexedIndirect, + /// Performs whatever operation goes with this program. OperationPerform, From 1cd664ad857cf0beed7464ec70e2c65a59cb337d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 21:43:26 -0400 Subject: [PATCH 027/150] Adds a sanity check. --- .../65816/Implementation/65816Storage.cpp | 20 +++++++++++++++---- .../65816/Implementation/65816Storage.hpp | 17 ++++++++-------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 9fc540cb8..45b49a07d 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -7,6 +7,8 @@ // #include "../65816.hpp" + +#include #include using namespace CPU::WDC65816; @@ -94,15 +96,22 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // Fill in the proper table entries and increment the opcode pointer. - storage_.instructions[opcode].program_offset = micro_op_location_8; + storage_.instructions[opcode].program_offset = uint16_t(micro_op_location_8); storage_.instructions[opcode].operation = operation; - storage_.instructions[opcode + 256].program_offset = micro_op_location_16; + storage_.instructions[opcode + 256].program_offset = uint16_t(micro_op_location_16); storage_.instructions[opcode + 256].operation = operation; ++opcode; } + void set_exception_generator(Generator generator) { + const auto key = std::make_pair(AccessType::Read, generator); + const auto map_entry = installed_patterns.find(key); + storage_.instructions[512].program_offset = uint16_t(map_entry->second.first); + storage_.instructions[512].operation = BRK; + } + /* Code below is structured to ease translation from Table 5-7 of the 2018 edition of the WDC 65816 datasheet. @@ -970,9 +979,12 @@ ProcessorStorage::ProcessorStorage() { /* 0xfd SBC a, x */ op(absolute_x, SBC); /* 0xfe INC a, x */ op(absolute_x_rmw, INC); /* 0xff SBC al, x */ op(absolute_long_x, SBC); - #undef op - // TEMPORARY: for my interest. To be removed. + constructor.set_exception_generator(&ProcessorStorageConstructor::stack_exception); + +#ifndef NDEBUG + assert(micro_ops_.size() < 65536); printf("Generated %zd micro-ops in total; covered %d opcodes\n", micro_ops_.size(), constructor.opcode); +#endif } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 1d8a9009f..d3f430a5a 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -169,16 +169,17 @@ class ProcessorStorage { public: ProcessorStorage(); + // Frustratingly, there is not quite enough space in 16 bits to store both + // the program offset and the operation as currently defined. struct Instruction { - size_t program_offset; + uint16_t program_offset; Operation operation; }; - Instruction instructions[512 + 3]; // Arranged as: - // 256 entries: emulation-mode instructions; - // 256 entries: 16-bit instructions; - // reset - // NMI - // IRQ + Instruction instructions[513]; // Arranged as: + // 256 entries: emulation-mode instructions; + // 256 entries: 16-bit instructions; and + // the entry for 'exceptions' (i.e. reset, irq, nmi). + private: friend ProcessorStorageConstructor; @@ -188,7 +189,7 @@ class ProcessorStorage { RegisterPair16 x_, y_; uint16_t pc_, s_; - // Not + // I.e. the offset for direct addressing (outside of emulation mode). uint16_t direct_; // Banking registers are all stored with the relevant byte From 5449e90b34f4181aed0d8648f73351dd38402429 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 26 Sep 2020 22:31:50 -0400 Subject: [PATCH 028/150] Edges towards offering the 65816 as another type of 6502 for testing. --- .../Clock SignalTests/6502InterruptTests.swift | 2 +- .../Mac/Clock SignalTests/6502TimingTests.swift | 2 +- .../Mac/Clock SignalTests/AllSuiteATests.swift | 2 +- OSBindings/Mac/Clock SignalTests/BCDTest.swift | 2 +- .../Clock SignalTests/Bridges/TestMachine6502.h | 8 +++++++- .../Clock SignalTests/Bridges/TestMachine6502.mm | 16 +++++++++++++--- .../Clock SignalTests/Bridges/TestMachineZ80.mm | 4 ++-- .../Mac/Clock SignalTests/Comparative68000.hpp | 4 ++-- .../Clock SignalTests/KlausDormannTests.swift | 2 +- .../Clock SignalTests/WolfgangLorenzTests.swift | 2 +- Processors/65816/Implementation/65816Storage.cpp | 3 --- 11 files changed, 30 insertions(+), 17 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift b/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift index 691ef4613..23edefea8 100644 --- a/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift +++ b/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift @@ -15,7 +15,7 @@ class MOS6502InterruptTests: XCTestCase { super.setUp() // create a machine full of NOPs - machine = CSTestMachine6502(is65C02: false) + machine = CSTestMachine6502(processor: .processor6502) for c in 0...65535 { machine.setValue(0xea, forAddress: UInt16(c)) } diff --git a/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift b/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift index 514b41413..078266de9 100644 --- a/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift @@ -12,7 +12,7 @@ import XCTest class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { private var endTime: UInt32 = 0 - private let machine = CSTestMachine6502(is65C02: false) + private let machine = CSTestMachine6502(processor: .processor6502) func testImplied() { let code: [UInt8] = [ diff --git a/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift b/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift index 5c0a13607..a058a039e 100644 --- a/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift +++ b/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift @@ -13,7 +13,7 @@ class AllSuiteATests: XCTestCase { func testAllSuiteA() { if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") { if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) { - let machine = CSTestMachine6502(is65C02: false) + let machine = CSTestMachine6502(processor: .processor6502) machine.setData(allSuiteA, atAddress: 0x4000) machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end diff --git a/OSBindings/Mac/Clock SignalTests/BCDTest.swift b/OSBindings/Mac/Clock SignalTests/BCDTest.swift index 6eb9850b0..72a7025ee 100644 --- a/OSBindings/Mac/Clock SignalTests/BCDTest.swift +++ b/OSBindings/Mac/Clock SignalTests/BCDTest.swift @@ -14,7 +14,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler { func testBCD() { if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) { if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) { - let machine = CSTestMachine6502(is65C02: false) + let machine = CSTestMachine6502(processor: .processor6502) machine.trapHandler = self machine.setData(bcdTest, atAddress: 0x2900) diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h index 59b5f0aed..ffb2a9e1a 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h @@ -19,13 +19,19 @@ typedef NS_ENUM(NSInteger, CSTestMachine6502Register) { CSTestMachine6502RegisterY, }; +typedef NS_ENUM(NSInteger, CSTestMachine6502Processor) { + CSTestMachine6502Processor6502, + CSTestMachine6502Processor65C02, + CSTestMachine6502Processor65816 +}; + extern const uint8_t CSTestMachine6502JamOpcode; @interface CSTestMachine6502 : CSTestMachine - (nonnull instancetype)init NS_UNAVAILABLE; -- (nonnull instancetype)initIs65C02:(BOOL)is65C02; +- (nonnull instancetype)initWithProcessor:(CSTestMachine6502Processor)processor; - (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress; - (void)runForNumberOfCycles:(int)cycles; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm index 635005613..af097cea5 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm @@ -9,6 +9,7 @@ #import "TestMachine6502.h" #include #include "../../../../Processors/6502/AllRAM/6502AllRAM.hpp" +//#include "../../../../Processors/65816/AllRAM/65816AllRAM.hpp" #import "TestMachine+ForSubclassEyesOnly.h" const uint8_t CSTestMachine6502JamOpcode = CPU::MOS6502::JamOpcode; @@ -35,12 +36,21 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg) #pragma mark - Lifecycle -- (instancetype)initIs65C02:(BOOL)is65C02 { +- (instancetype)initWithProcessor:(CSTestMachine6502Processor)processor { self = [super init]; if(self) { - _processor = CPU::MOS6502::AllRAMProcessor::Processor( - is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502); + switch(processor) { + case CSTestMachine6502Processor6502: + _processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502::Personality::P6502); + break; + case CSTestMachine6502Processor65C02: + _processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502::Personality::PWDC65C02); + break; + default: + assert(false); // TODO + + } } return self; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm index 65ec87bc7..9ca773e84 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm @@ -23,7 +23,7 @@ class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegat public: BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {} - void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) { + void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) { [target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp]; } @@ -77,7 +77,7 @@ struct PortAccessDelegateTopByte: public CPU::Z80::AllRAMProcessor::PortAccessDe }; struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelegate { - uint8_t z80_all_ram_processor_input(uint16_t port) final { return 191; } + uint8_t z80_all_ram_processor_input(uint16_t) final { return 191; } }; #pragma mark - Capture class diff --git a/OSBindings/Mac/Clock SignalTests/Comparative68000.hpp b/OSBindings/Mac/Clock SignalTests/Comparative68000.hpp index b64ec0965..6c8c5472d 100644 --- a/OSBindings/Mac/Clock SignalTests/Comparative68000.hpp +++ b/OSBindings/Mac/Clock SignalTests/Comparative68000.hpp @@ -23,7 +23,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler { gzclose(trace); } - void will_perform(uint32_t address, uint16_t opcode) { + void will_perform(uint32_t address, uint16_t) { // Obtain the next line from the trace file. char correct_state[300] = "\n"; gzgets(trace, correct_state, sizeof(correct_state)); @@ -45,7 +45,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler { fprintf(stderr, "Diverges at line %d\n", line_count); fprintf(stderr, "Good: %s", correct_state); fprintf(stderr, "Bad: %s", local_state); - assert(false); + throw std::exception(); } } diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index d04e74e1e..2de0b6005 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -14,7 +14,7 @@ class KlausDormannTests: XCTestCase { fileprivate func runTest(resource: String, is65C02: Bool) -> UInt16 { if let filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") { if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) { - let machine = CSTestMachine6502(is65C02: is65C02) + let machine = CSTestMachine6502(processor: is65C02 ? .processor65C02 : .processor6502) machine.setData(functionalTest, atAddress: 0) machine.setValue(0x400, for: .programCounter) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 4ec9be87b..604e12e02 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -200,7 +200,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) { if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) { - machine = CSTestMachine6502(is65C02: false) + machine = CSTestMachine6502(processor: .processor6502) machine.trapHandler = self // machine.logActivity = true output = "" diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 45b49a07d..82fd8b112 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -699,9 +699,6 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } }; -// TEMPORARY. Kneejerk way to get a step debug of 65816 storage construction. -ProcessorStorage TEMPORARY_test_instance; - ProcessorStorage::ProcessorStorage() { ProcessorStorageConstructor constructor(*this); From 8641494809df43855d4f8f81b454ef45df2416d6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 27 Sep 2020 15:10:29 -0400 Subject: [PATCH 029/150] Resolve various test-case warnings. --- OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj | 4 +++- .../xcschemes/Clock Signal Kiosk.xcscheme | 2 +- .../xcshareddata/xcschemes/Clock Signal.xcscheme | 2 +- .../xcshareddata/xcschemes/Clock SignalTests.xcscheme | 11 +---------- .../Mac/Clock SignalTests/6502TimingTests.swift | 2 +- .../Mac/Clock SignalTests/68000ComparativeTests.mm | 4 ++-- OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm | 2 +- OSBindings/Mac/Clock SignalTests/QLTests.mm | 2 +- OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp | 4 ++-- Processors/Z80/AllRAM/Z80AllRAM.hpp | 2 +- 10 files changed, 14 insertions(+), 21 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 94d7fb34e..ee46d66e1 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -4025,7 +4025,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 1130; + LastUpgradeCheck = 1200; ORGANIZATIONNAME = "Thomas Harte"; TargetAttributes = { 4B055A691FAE763F0060FFFF = { @@ -5170,6 +5170,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -5228,6 +5229,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index a98a6ccfd..9891a6c49 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -1,6 +1,6 @@ - - - - diff --git a/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift b/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift index 078266de9..3ca39bc35 100644 --- a/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift @@ -200,7 +200,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { func runTest(_ code: [UInt8], expectedRunLength: UInt32) { machine.trapHandler = self - let immediateCode = Data(bytes: UnsafePointer(code), count: code.count) + let immediateCode = Data(code) machine.setData(immediateCode, atAddress: 0x200) machine.addTrapAddress(UInt16(0x200 + code.count)) machine.setValue(0x00, forAddress: 0x0000) diff --git a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm index 38aa41718..2f555f34c 100644 --- a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm @@ -91,12 +91,12 @@ Test68000() : processor(*this) { } - void will_perform(uint32_t address, uint16_t opcode) { + void will_perform(uint32_t, uint16_t) { --instructions_remaining_; if(!instructions_remaining_) comparitor(); } - HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) { + HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) { using Microcycle = CPU::MC68000::Microcycle; if(cycle.data_select_active()) { cycle.apply(&ram[cycle.host_endian_byte_address()]); diff --git a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm index 785da74c6..ad5dbb163 100644 --- a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm +++ b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm @@ -36,7 +36,7 @@ class EmuTOS: public ComparativeBusHandler { return m68000_.get_state(); } - HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) { + HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) { const uint32_t address = cycle.word_address(); uint32_t word_address = address; diff --git a/OSBindings/Mac/Clock SignalTests/QLTests.mm b/OSBindings/Mac/Clock SignalTests/QLTests.mm index 505016cef..3df640dc2 100644 --- a/OSBindings/Mac/Clock SignalTests/QLTests.mm +++ b/OSBindings/Mac/Clock SignalTests/QLTests.mm @@ -39,7 +39,7 @@ class QL: public ComparativeBusHandler { return m68000_.get_state(); } - HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) { + HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) { const uint32_t address = cycle.word_address(); uint32_t word_address = address; diff --git a/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp b/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp index 0a912b3dc..b02798428 100644 --- a/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp +++ b/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp @@ -48,7 +48,7 @@ class RAM68000: public CPU::MC68000::BusHandler { ram_[1] = sp & 0xffff; } - void will_perform(uint32_t address, uint16_t opcode) { + void will_perform(uint32_t, uint16_t) { --instructions_remaining_; } @@ -80,7 +80,7 @@ class RAM68000: public CPU::MC68000::BusHandler { return &ram_[(address >> 1) % ram_.size()]; } - HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) { + HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) { const uint32_t word_address = cycle.word_address(); if(instructions_remaining_) duration_ += cycle.length; diff --git a/Processors/Z80/AllRAM/Z80AllRAM.hpp b/Processors/Z80/AllRAM/Z80AllRAM.hpp index c8f160a20..85f4f04dc 100644 --- a/Processors/Z80/AllRAM/Z80AllRAM.hpp +++ b/Processors/Z80/AllRAM/Z80AllRAM.hpp @@ -29,7 +29,7 @@ class AllRAMProcessor: } struct PortAccessDelegate { - virtual uint8_t z80_all_ram_processor_input(uint16_t port) { return 0xff; } + virtual uint8_t z80_all_ram_processor_input(uint16_t) { return 0xff; } }; inline void set_port_access_delegate(PortAccessDelegate *delegate) { port_delegate_ = delegate; From ad8a2e2cb9a0013c964ad70ea8474de4a70f6a72 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 27 Sep 2020 22:19:42 -0400 Subject: [PATCH 030/150] Corrects a long-standing naming obscurity. --- Processors/6502/6502.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index d91b5a010..d540ec223 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -205,12 +205,12 @@ class ProcessorBase: public ProcessorStorage { can also nominate whether the processor includes support for the ready line. Declining to support the ready line can produce a minor runtime performance improvement. */ -template class Processor: public ProcessorBase { +template class Processor: public ProcessorBase { public: /*! Constructs an instance of the 6502 that will use @c bus_handler for all bus communications. */ - Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {} + Processor(BusHandler &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {} /*! Runs the 6502 for a supplied number of cycles. @@ -227,7 +227,7 @@ template class Proce void set_ready_line(bool active); private: - T &bus_handler_; + BusHandler &bus_handler_; }; #include "Implementation/6502Implementation.hpp" From b96cd4d18bf3dbb8f46078e555cd28daa53fd3be Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 27 Sep 2020 22:20:13 -0400 Subject: [PATCH 031/150] Resolves another unsafe pointer assumption. --- OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 604e12e02..93829747a 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -219,11 +219,11 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { machine.setValue(0x48, forAddress: 0xfffe) machine.setValue(0xff, forAddress: 0xffff) - let irqHandler = Data(bytes: UnsafePointer([ + let irqHandler: [UInt8] = [ 0x48, 0x8a, 0x48, 0x98, 0x48, 0xba, 0xbd, 0x04, 0x01, 0x29, 0x10, 0xf0, 0x03, 0x6c, 0x16, 0x03, 0x6c, 0x14, 0x03 - ] as [UInt8]), count: 19) - machine.setData( irqHandler, atAddress: 0xff48) + ] + machine.setData(Data(irqHandler), atAddress: 0xff48) machine.addTrapAddress(0xffd2) // print character machine.addTrapAddress(0xffe4) // scan keyboard From 5645f90abe7c7e8c8c78a1796141d464dc1c18ec Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 27 Sep 2020 22:20:58 -0400 Subject: [PATCH 032/150] Takes a minor first step towards actually performing 65816 instructions. --- .../Clock Signal.xcodeproj/project.pbxproj | 2 + Processors/65816/65816.hpp | 16 ++-- .../Implementation/65816Implementation.hpp | 27 +++++++ .../65816/Implementation/65816Storage.cpp | 12 ++- .../65816/Implementation/65816Storage.hpp | 76 ++++++++++++------- 5 files changed, 98 insertions(+), 35 deletions(-) create mode 100644 Processors/65816/Implementation/65816Implementation.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index ee46d66e1..1ba9d7732 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1342,6 +1342,7 @@ 4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = ""; }; 4BAF2B4C2004580C00480230 /* DMK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DMK.cpp; sourceTree = ""; }; 4BAF2B4D2004580C00480230 /* DMK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMK.hpp; sourceTree = ""; }; + 4BB023FF25212888009F8D90 /* 65816Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 65816Implementation.hpp; path = ../../Processors/65816/Implementation/65816Implementation.hpp; sourceTree = ""; }; 4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = ""; }; 4BB0A6592044FD3000FB3688 /* SN76489.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SN76489.cpp; sourceTree = ""; }; 4BB0A65A2044FD3000FB3688 /* SN76489.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SN76489.hpp; sourceTree = ""; }; @@ -3330,6 +3331,7 @@ 4BB73E951B587A5100552FC2 = { isa = PBXGroup; children = ( + 4BB023FF25212888009F8D90 /* 65816Implementation.hpp */, 4B51F70820A521D700AFA2C1 /* Activity */, 4B8944E2201967B4007DE474 /* Analyser */, 4BB73EA01B587A5100552FC2 /* Clock Signal */, diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index 25837e6db..24b4344e2 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -13,21 +13,25 @@ #include #include "../RegisterSizes.hpp" +#include "../../ClockReceiver/ClockReceiver.hpp" namespace CPU { namespace WDC65816 { -enum class Personality { - WDC65816, - WDC65802 -}; - #include "Implementation/65816Storage.hpp" -template class Processor: public ProcessorStorage { +template class Processor: private ProcessorStorage { + public: + Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {} + void run_for(const Cycles cycles); + + private: + BusHandler &bus_handler_; }; +#include "Implementation/65816Implementation.hpp" + } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp new file mode 100644 index 000000000..3555ca4eb --- /dev/null +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -0,0 +1,27 @@ +// +// 65816Implementation.hpp +// Clock Signal +// +// Created by Thomas Harte on 27/09/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +template void Processor::run_for(const Cycles cycles) { + auto int_cycles = cycles.as_integral(); + while(int_cycles--) { + const MicroOp operation = *next_op_; + ++next_op_; + + switch(operation) { + case OperationMoveToNextProgram: + // The exception program will determine the appropriate way to respond + // based on the pending exception if one exists; otherwise just do a + // standard fetch-decode-execute. + next_op_ = &instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)]; + continue; + + default: + assert(false); + } + } +} diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 82fd8b112..31e5642ad 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -108,8 +108,15 @@ struct CPU::WDC65816::ProcessorStorageConstructor { void set_exception_generator(Generator generator) { const auto key = std::make_pair(AccessType::Read, generator); const auto map_entry = installed_patterns.find(key); - storage_.instructions[512].program_offset = uint16_t(map_entry->second.first); - storage_.instructions[512].operation = BRK; + storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offset = uint16_t(map_entry->second.first); + storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].operation = BRK; + } + + void install_fetch_decode_execute() { + storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].program_offset = uint16_t(storage_.micro_ops_.size()); + storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].operation = NOP; + storage_.micro_ops_.push_back(CycleFetchIncrementPC); + storage_.micro_ops_.push_back(OperationDecode); } /* @@ -979,6 +986,7 @@ ProcessorStorage::ProcessorStorage() { #undef op constructor.set_exception_generator(&ProcessorStorageConstructor::stack_exception); + constructor.install_fetch_decode_execute(); #ifndef NDEBUG assert(micro_ops_.size() < 65536); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index d3f430a5a..cad67ee3b 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -103,7 +103,10 @@ enum MicroOp: uint8_t { OperationPrepareException, /// Complete this set of micr-ops. - OperationMoveToNextProgram + OperationMoveToNextProgram, + + /// Inspects the instruction buffer and thereby selects the next set of micro-ops to schedule. + OperationDecode, }; enum Operation: uint8_t { @@ -165,40 +168,59 @@ enum Operation: uint8_t { class ProcessorStorageConstructor; -class ProcessorStorage { - public: - ProcessorStorage(); +struct ProcessorStorage { + ProcessorStorage(); - // Frustratingly, there is not quite enough space in 16 bits to store both - // the program offset and the operation as currently defined. - struct Instruction { - uint16_t program_offset; - Operation operation; - }; - Instruction instructions[513]; // Arranged as: - // 256 entries: emulation-mode instructions; - // 256 entries: 16-bit instructions; and - // the entry for 'exceptions' (i.e. reset, irq, nmi). + // Frustratingly, there is not quite enough space in 16 bits to store both + // the program offset and the operation as currently defined. + struct Instruction { + uint16_t program_offset; + Operation operation; + }; + Instruction instructions[514]; // Arranged as: + // 256 entries: emulation-mode instructions; + // 256 entries: 16-bit instructions; + // the entry for 'exceptions' (i.e. reset, irq, nmi); and + // the entry for fetch-decode-execute. + enum class OperationSlot { + Exception = 512, + FetchDecodeExecute + }; - private: - friend ProcessorStorageConstructor; + void set_power_on_state() { + // Set next_op_ to any instance of OperationMoveToNextProgram. + for(size_t c = 0; c < micro_ops_.size(); ++c) { + if(micro_ops_[c] == OperationMoveToNextProgram) { + next_op_ = µ_ops_[c]; + break; + } + } - // Registers. - RegisterPair16 a_; - RegisterPair16 x_, y_; - uint16_t pc_, s_; + pending_exceptions_ = PowerOn; + } - // I.e. the offset for direct addressing (outside of emulation mode). - uint16_t direct_; + // Registers. + RegisterPair16 a_; + RegisterPair16 x_, y_; + uint16_t pc_, s_; - // Banking registers are all stored with the relevant byte - // shifted up bits 16–23. - uint32_t data_bank_; // i.e. DBR. - uint32_t program_bank_; // i.e. PBR. + // I.e. the offset for direct addressing (outside of emulation mode). + uint16_t direct_; + // Banking registers are all stored with the relevant byte + // shifted up bits 16–23. + uint32_t data_bank_; // i.e. DBR. + uint32_t program_bank_; // i.e. PBR. - std::vector micro_ops_; + static constexpr int PowerOn = 1 << 0; + static constexpr int Reset = 1 << 1; + static constexpr int IRQ = 1 << 2; + static constexpr int NMI = 1 << 3; + int pending_exceptions_ = 0; + + std::vector micro_ops_; + MicroOp *next_op_ = nullptr; }; #endif /* WDC65816Implementation_h */ From 6635876e7e1f27aad1a8dd73ed543e2172b6ab1f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 28 Sep 2020 18:43:53 -0400 Subject: [PATCH 033/150] Performs a bare factoring out of the 6502 bus handler. --- .../Clock Signal.xcodeproj/project.pbxproj | 2 + Processors/6502/6502.hpp | 54 +------------ .../6502/Implementation/6502Storage.hpp | 5 -- Processors/6502Esque.hpp | 78 +++++++++++++++++++ .../Implementation/65816Implementation.hpp | 4 +- .../65816/Implementation/65816Storage.hpp | 39 ++++++---- 6 files changed, 113 insertions(+), 69 deletions(-) create mode 100644 Processors/6502Esque.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 1ba9d7732..b1c5c3d70 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1343,6 +1343,7 @@ 4BAF2B4C2004580C00480230 /* DMK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DMK.cpp; sourceTree = ""; }; 4BAF2B4D2004580C00480230 /* DMK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMK.hpp; sourceTree = ""; }; 4BB023FF25212888009F8D90 /* 65816Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 65816Implementation.hpp; path = ../../Processors/65816/Implementation/65816Implementation.hpp; sourceTree = ""; }; + 4BB0240425229C6E009F8D90 /* 6502Esque.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = ""; }; 4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = ""; }; 4BB0A6592044FD3000FB3688 /* SN76489.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SN76489.cpp; sourceTree = ""; }; 4BB0A65A2044FD3000FB3688 /* SN76489.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SN76489.hpp; sourceTree = ""; }; @@ -3489,6 +3490,7 @@ isa = PBXGroup; children = ( 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */, + 4BB0240425229C6E009F8D90 /* 6502Esque.hpp */, 4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */, 4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */, 4B1414561B58879D00E04248 /* 6502 */, diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index d540ec223..652ac606d 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -15,10 +15,14 @@ #include "../RegisterSizes.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" +#include "../6502Esque.hpp" namespace CPU { namespace MOS6502 { +using BusOperation = CPU::MOS6502Esque::BusOperation; +using BusHandler = CPU::MOS6502Esque::BusHandler; + /* The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register. */ @@ -63,61 +67,11 @@ enum Flag: uint8_t { Carry = 0x01 }; -/*! - Subclasses will be given the task of performing bus operations, allowing them to provide whatever interface they like - between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested. - - @c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the - isReadOperation macro to make a binary choice between reading and writing. -*/ -enum BusOperation { - Read, ReadOpcode, Write, Ready, None -}; - -/*! - Evaluates to `true` if the operation is a read; `false` if it is a write. -*/ -#define isReadOperation(v) (v == CPU::MOS6502::BusOperation::Read || v == CPU::MOS6502::BusOperation::ReadOpcode) - /*! An opcode that is guaranteed to cause the CPU to jam. */ extern const uint8_t JamOpcode; -/*! - A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus, - machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus - handler. -*/ -class BusHandler { - public: - /*! - Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502, - all bus cycles take one clock cycle so the amoutn of time advanced is implicit. - - @param operation The type of bus cycle: read, read opcode (i.e. read, with sync active), - write or ready. - @param address The value of the address bus during this bus cycle. - @param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is - a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during - a read cycle will produce undefined behaviour. - - @returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing. - On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502 - during some periods; one way to simulate that is to have the bus handler return a number other than - Cycles(1) to describe lengthened bus cycles. - */ - Cycles perform_bus_operation([[maybe_unused]] CPU::MOS6502::BusOperation operation, [[maybe_unused]] uint16_t address, [[maybe_unused]] uint8_t *value) { - return Cycles(1); - } - - /*! - Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow - bus handlers to perform any deferred output work. - */ - void flush() {} -}; - #include "Implementation/6502Storage.hpp" /*! diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 037943b70..a5ae557ee 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -6,9 +6,6 @@ // Copyright 2017 Thomas Harte. All rights reserved. // -#ifndef MOS6502Storage_h -#define MOS6502Storage_h - /*! A repository for all the internal state of a CPU::MOS6502::Processor; extracted into a separate base class in order to remove it from visibility within the main 6502.hpp. @@ -288,5 +285,3 @@ class ProcessorStorage { // Allow state objects to capture and apply state. friend struct State; }; - -#endif /* _502Storage_h */ diff --git a/Processors/6502Esque.hpp b/Processors/6502Esque.hpp new file mode 100644 index 000000000..07f83aa12 --- /dev/null +++ b/Processors/6502Esque.hpp @@ -0,0 +1,78 @@ +// +// 6502Esque.hpp +// Clock Signal +// +// Created by Thomas Harte on 28/09/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef m6502Esque_h +#define m6502Esque_h + +/* + This file defines how the CPU-controlled part of a bus looks for the 6502 and + for other processors with a sufficiently-similar bus. + + I'm not yet a big fan of the name I've used here, and I'm still on the fence + about what to do when eventually I get around to the 6800 and/or 6809, which have + very similar bus characteristics. + + So: this is _very_ provisional stuff. +*/ +namespace CPU { +namespace MOS6502Esque { + +/*! + Bus handlers will be given the task of performing bus operations, allowing them to provide whatever interface they like + between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested. + + @c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the + isReadOperation macro to make a binary choice between reading and writing. +*/ +enum BusOperation { + Read, ReadOpcode, Write, Ready, None +}; + +/*! + Evaluates to `true` if the operation is a read; `false` if it is a write. +*/ +#define isReadOperation(v) (v == CPU::MOS6502Esque::BusOperation::Read || v == CPU::MOS6502Esque::BusOperation::ReadOpcode) + +/*! + A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus, + machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus + handler. +*/ +template class BusHandler { + public: + /*! + Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502, + all bus cycles take one clock cycle so the amoutn of time advanced is implicit. + + @param operation The type of bus cycle: read, read opcode (i.e. read, with sync active), + write or ready. + @param address The value of the address bus during this bus cycle. + @param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is + a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during + a read cycle will produce undefined behaviour. + + @returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing. + On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502 + during some periods; one way to simulate that is to have the bus handler return a number other than + Cycles(1) to describe lengthened bus cycles. + */ + Cycles perform_bus_operation([[maybe_unused]] BusOperation operation, [[maybe_unused]] AddressType address, [[maybe_unused]] uint8_t *value) { + return Cycles(1); + } + + /*! + Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow + bus handlers to perform any deferred output work. + */ + void flush() {} +}; + +} +} + +#endif /* m6502Esque_h */ diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 3555ca4eb..ae462743d 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -17,7 +17,9 @@ template void Processor::run_for(const Cycles // The exception program will determine the appropriate way to respond // based on the pending exception if one exists; otherwise just do a // standard fetch-decode-execute. - next_op_ = &instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)]; + next_op_ = µ_ops_[&instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset]; + + // TODO: reset instruction buffer. continue; default: diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index cad67ee3b..51860fe25 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -6,9 +6,6 @@ // Copyright © 2020 Thomas Harte. All rights reserved. // -#ifndef WDC65816Implementation_h -#define WDC65816Implementation_h - enum MicroOp: uint8_t { /// Fetches a byte from the program counter to the instruction buffer and increments the program counter. CycleFetchIncrementPC, @@ -189,14 +186,8 @@ struct ProcessorStorage { }; void set_power_on_state() { - // Set next_op_ to any instance of OperationMoveToNextProgram. - for(size_t c = 0; c < micro_ops_.size(); ++c) { - if(micro_ops_[c] == OperationMoveToNextProgram) { - next_op_ = µ_ops_[c]; - break; - } - } - + // Set next_op_ to start the exception program. + next_op_ = µ_ops_[instructions[size_t(OperationSlot::Exception)].program_offset]; pending_exceptions_ = PowerOn; } @@ -219,8 +210,30 @@ struct ProcessorStorage { static constexpr int NMI = 1 << 3; int pending_exceptions_ = 0; + /// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte + /// to most significant. + struct Buffer { + uint32_t value = 0; + int size = 0; + + void clear() { + value = 0; + size = 0; + } + + uint8_t *next() { + #if TARGET_RT_BIG_ENDIAN + uint8_t *const target = reinterpret_cast(&value) + (3 ^ size); + #else + uint8_t *const target = reinterpret_cast(&value) + size; + #endif + + ++size; + return target; + } + }; + Buffer instruction_buffer_, data_buffer_; + std::vector micro_ops_; MicroOp *next_op_ = nullptr; }; - -#endif /* WDC65816Implementation_h */ From ef1a514785ea4bcc0bae9fef171a22c63af7ab73 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 28 Sep 2020 21:35:46 -0400 Subject: [PATCH 034/150] Introduces 6502Selector, for picking either a 6502 or a 65816 based on a single template parameter. --- Machines/Oric/Oric.cpp | 4 +- .../Clock Signal.xcodeproj/project.pbxproj | 10 ++++ .../Bridges/TestMachine6502.mm | 9 ++-- Processors/6502/6502.hpp | 14 +----- Processors/6502/AllRAM/6502AllRAM.cpp | 21 +++++---- Processors/6502/AllRAM/6502AllRAM.hpp | 5 +- Processors/6502/Implementation/6502Base.cpp | 2 - Processors/6502Esque.hpp | 13 ++++++ Processors/6502Selector.hpp | 46 +++++++++++++++++++ Processors/65816/65816.hpp | 18 +++++++- Processors/65816/Implementation/65816Base.cpp | 36 +++++++++++++++ .../Implementation/65816Implementation.hpp | 7 ++- .../65816/Implementation/65816Storage.hpp | 12 +++-- 13 files changed, 156 insertions(+), 41 deletions(-) create mode 100644 Processors/6502Selector.hpp create mode 100644 Processors/65816/Implementation/65816Base.cpp diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index ed8df1aa5..fa7b2a6b8 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -424,8 +424,8 @@ template class Co !tape_player_.get_tape()->is_at_end()) { uint8_t next_byte = tape_player_.get_next_byte(!ram_[tape_speed_address_]); - m6502_.set_value_of_register(CPU::MOS6502::A, next_byte); - m6502_.set_value_of_register(CPU::MOS6502::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero); + m6502_.set_value_of_register(CPU::MOS6502Esque::A, next_byte); + m6502_.set_value_of_register(CPU::MOS6502Esque::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero); *value = 0x60; // i.e. RTS } } else { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b1c5c3d70..b78dc0fda 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -210,6 +210,9 @@ 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; }; 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; }; + 4B4DEBED2522C03F004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */; }; + 4B4DEBEE2522C03F004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */; }; + 4B4DEBEF2522C03F004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */; }; 4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; }; 4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; }; 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; }; @@ -1119,6 +1122,7 @@ 4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = ""; }; 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = ""; }; 4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = ""; }; + 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = 65816Base.cpp; path = ../../Processors/65816/Implementation/65816Base.cpp; sourceTree = ""; }; 4B4F2B7024DF99D4000DA6B0 /* CSScanTarget+CppScanTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSScanTarget+CppScanTarget.h"; sourceTree = ""; }; 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = ""; }; @@ -1344,6 +1348,7 @@ 4BAF2B4D2004580C00480230 /* DMK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMK.hpp; sourceTree = ""; }; 4BB023FF25212888009F8D90 /* 65816Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 65816Implementation.hpp; path = ../../Processors/65816/Implementation/65816Implementation.hpp; sourceTree = ""; }; 4BB0240425229C6E009F8D90 /* 6502Esque.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = ""; }; + 4BB024092522B7BE009F8D90 /* 6502Selector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Selector.hpp; sourceTree = ""; }; 4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = ""; }; 4BB0A6592044FD3000FB3688 /* SN76489.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SN76489.cpp; sourceTree = ""; }; 4BB0A65A2044FD3000FB3688 /* SN76489.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SN76489.hpp; sourceTree = ""; }; @@ -3332,6 +3337,7 @@ 4BB73E951B587A5100552FC2 = { isa = PBXGroup; children = ( + 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */, 4BB023FF25212888009F8D90 /* 65816Implementation.hpp */, 4B51F70820A521D700AFA2C1 /* Activity */, 4B8944E2201967B4007DE474 /* Analyser */, @@ -3491,6 +3497,7 @@ children = ( 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */, 4BB0240425229C6E009F8D90 /* 6502Esque.hpp */, + 4BB024092522B7BE009F8D90 /* 6502Selector.hpp */, 4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */, 4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */, 4B1414561B58879D00E04248 /* 6502 */, @@ -4496,6 +4503,7 @@ 4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */, 4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */, 4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */, + 4B4DEBEF2522C03F004583AC /* 65816Base.cpp in Sources */, 4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */, 4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */, @@ -4778,6 +4786,7 @@ 4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */, 4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */, 4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */, + 4B4DEBED2522C03F004583AC /* 65816Base.cpp in Sources */, 4B894524201967B4007DE474 /* Tape.cpp in Sources */, 4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */, 4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */, @@ -4925,6 +4934,7 @@ 4B778F4123A5F19A0000D260 /* MemoryPacker.cpp in Sources */, 4B778F4423A5F1BE0000D260 /* CommodoreGCR.cpp in Sources */, 4B778EF923A5EB740000D260 /* MSA.cpp in Sources */, + 4B4DEBEE2522C03F004583AC /* 65816Base.cpp in Sources */, 4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */, 4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */, 4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm index af097cea5..a941f4d82 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm @@ -42,14 +42,13 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg) if(self) { switch(processor) { case CSTestMachine6502Processor6502: - _processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502::Personality::P6502); + _processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::T6502); break; case CSTestMachine6502Processor65C02: - _processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502::Personality::PWDC65C02); + _processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65C02); break; - default: - assert(false); // TODO - + case CSTestMachine6502Processor65816: + _processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65816); } } diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 652ac606d..fe69490e9 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -22,19 +22,7 @@ namespace MOS6502 { using BusOperation = CPU::MOS6502Esque::BusOperation; using BusHandler = CPU::MOS6502Esque::BusHandler; - -/* - The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register. -*/ -enum Register { - LastOperationAddress, - ProgramCounter, - StackPointer, - Flags, - A, - X, - Y -}; +using Register = CPU::MOS6502Esque::Register; /* The list of 6502 variants supported by this implementation. diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 43e202efe..0381384b4 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -15,7 +15,9 @@ using namespace CPU::MOS6502; namespace { -template class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { +using Type = CPU::MOS6502Esque::Type; + +template class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { public: ConcreteAllRAMProcessor() : mos6502_(*this) { @@ -63,20 +65,21 @@ template class ConcreteAllRAMProcessor: public AllRAMP } private: - CPU::MOS6502::Processor mos6502_; + CPU::MOS6502Esque::Processor mos6502_; }; } -AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) { +AllRAMProcessor *AllRAMProcessor::Processor(Type type) { #define Bind(p) case p: return new ConcreteAllRAMProcessor

(); - switch(personality) { + switch(type) { default: - Bind(Personality::P6502) - Bind(Personality::PNES6502) - Bind(Personality::PSynertek65C02) - Bind(Personality::PWDC65C02) - Bind(Personality::PRockwell65C02) + Bind(Type::T6502) + Bind(Type::TNES6502) + Bind(Type::TSynertek65C02) + Bind(Type::TWDC65C02) + Bind(Type::TRockwell65C02) + Bind(Type::TWDC65816) } #undef Bind } diff --git a/Processors/6502/AllRAM/6502AllRAM.hpp b/Processors/6502/AllRAM/6502AllRAM.hpp index 41dc61245..9d7a6cd1d 100644 --- a/Processors/6502/AllRAM/6502AllRAM.hpp +++ b/Processors/6502/AllRAM/6502AllRAM.hpp @@ -9,7 +9,7 @@ #ifndef MOS6502AllRAM_cpp #define MOS6502AllRAM_cpp -#include "../6502.hpp" +#include "../../6502Selector.hpp" #include "../../AllRAMProcessor.hpp" namespace CPU { @@ -17,9 +17,8 @@ namespace MOS6502 { class AllRAMProcessor: public ::CPU::AllRAMProcessor { - public: - static AllRAMProcessor *Processor(Personality personality); + static AllRAMProcessor *Processor(CPU::MOS6502Esque::Type type); virtual ~AllRAMProcessor() {} virtual void run_for(const Cycles cycles) = 0; diff --git a/Processors/6502/Implementation/6502Base.cpp b/Processors/6502/Implementation/6502Base.cpp index bc80e6db0..ef50dc040 100644 --- a/Processors/6502/Implementation/6502Base.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -8,8 +8,6 @@ #include "../6502.hpp" -#include - using namespace CPU::MOS6502; const uint8_t CPU::MOS6502::JamOpcode = 0xf2; diff --git a/Processors/6502Esque.hpp b/Processors/6502Esque.hpp index 07f83aa12..c124b390d 100644 --- a/Processors/6502Esque.hpp +++ b/Processors/6502Esque.hpp @@ -22,6 +22,19 @@ namespace CPU { namespace MOS6502Esque { +/* + The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register. +*/ +enum Register { + LastOperationAddress, + ProgramCounter, + StackPointer, + Flags, + A, + X, + Y +}; + /*! Bus handlers will be given the task of performing bus operations, allowing them to provide whatever interface they like between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested. diff --git a/Processors/6502Selector.hpp b/Processors/6502Selector.hpp new file mode 100644 index 000000000..6045d6a2e --- /dev/null +++ b/Processors/6502Selector.hpp @@ -0,0 +1,46 @@ +// +// 6502Selector.hpp +// Clock Signal +// +// Created by Thomas Harte on 28/09/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef _502Selector_h +#define _502Selector_h + +#include "6502/6502.hpp" +#include "65816/65816.hpp" + +namespace CPU { +namespace MOS6502Esque { + +enum class Type { + TNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag) + T6502, // the original [NMOS] 6502, replete with various undocumented instructions + TSynertek65C02, // a 6502 extended with BRA, P[H/L][X/Y], STZ, TRB, TSB and the (zp) addressing mode and a few other additions + TRockwell65C02, // like the Synertek, but with BBR, BBS, RMB and SMB + TWDC65C02, // like the Rockwell, but with STP and WAI + TWDC65816, // the slightly 16-bit follow-up to the 6502 +}; + +/* + Machines that can use either a 6502 or a 65816 can use CPU::MOS6502Esque::Processor in order to select the proper + class in much the same way that a raw user of CPU::MOS6502::Processor would set the personality. Just provide one + of the type enums as above as the appropriate template parameter. +*/ + +template class Processor: + public CPU::MOS6502::Processor { + using CPU::MOS6502::Processor::Processor; +}; + +template class Processor: + public CPU::WDC65816::Processor { + using CPU::WDC65816::Processor::Processor; +}; + +} +} + +#endif /* _502Selector_h */ diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index 24b4344e2..327855251 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -14,16 +14,30 @@ #include "../RegisterSizes.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" +#include "../6502Esque.hpp" namespace CPU { namespace WDC65816 { +using BusOperation = CPU::MOS6502Esque::BusOperation; +using Register = CPU::MOS6502Esque::Register; + #include "Implementation/65816Storage.hpp" -template class Processor: private ProcessorStorage { +class ProcessorBase: protected ProcessorStorage { + public: + inline void set_power_on(bool); + inline void set_irq_line(bool); + inline void set_nmi_line(bool); + void set_value_of_register(Register r, uint16_t value); + + inline bool is_jammed() const; + uint16_t get_value_of_register(Register r) const; +}; + +template class Processor: public ProcessorBase { public: Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {} - void run_for(const Cycles cycles); private: diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp new file mode 100644 index 000000000..f0165281d --- /dev/null +++ b/Processors/65816/Implementation/65816Base.cpp @@ -0,0 +1,36 @@ +// +// 65816Base.cpp +// Clock Signal +// +// Created by Thomas Harte on 28/09/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#include "../65816.hpp" + +using namespace CPU::WDC65816; + +uint16_t ProcessorBase::get_value_of_register(Register r) const { + switch (r) { +// case Register::ProgramCounter: return pc_.full; +// case Register::LastOperationAddress: return last_operation_pc_.full; +// case Register::StackPointer: return s_; +// case Register::Flags: return get_flags(); +// case Register::A: return a_; +// case Register::X: return x_; +// case Register::Y: return y_; + default: return 0; + } +} + +void ProcessorBase::set_value_of_register(Register r, uint16_t value) { + switch (r) { +// case Register::ProgramCounter: pc_.full = value; break; +// case Register::StackPointer: s_ = uint8_t(value); break; +// case Register::Flags: set_flags(uint8_t(value)); break; +// case Register::A: a_ = uint8_t(value); break; +// case Register::X: x_ = uint8_t(value); break; +// case Register::Y: y_ = uint8_t(value); break; + default: break; + } +} diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index ae462743d..ae6d8189a 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -17,7 +17,7 @@ template void Processor::run_for(const Cycles // The exception program will determine the appropriate way to respond // based on the pending exception if one exists; otherwise just do a // standard fetch-decode-execute. - next_op_ = µ_ops_[&instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset]; + next_op_ = µ_ops_[instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset]; // TODO: reset instruction buffer. continue; @@ -27,3 +27,8 @@ template void Processor::run_for(const Cycles } } } + +void ProcessorBase::set_power_on(bool) {} +void ProcessorBase::set_irq_line(bool) {} +void ProcessorBase::set_nmi_line(bool) {} +bool ProcessorBase::is_jammed() const { return false; } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 51860fe25..388a6493e 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -185,10 +185,14 @@ struct ProcessorStorage { FetchDecodeExecute }; - void set_power_on_state() { - // Set next_op_ to start the exception program. - next_op_ = µ_ops_[instructions[size_t(OperationSlot::Exception)].program_offset]; - pending_exceptions_ = PowerOn; + void set_power_on(bool power_on) { + if(power_on) { + // Set next_op_ to start the exception program. + next_op_ = µ_ops_[instructions[size_t(OperationSlot::Exception)].program_offset]; + pending_exceptions_ = PowerOn; + } else { + pending_exceptions_ &= ~PowerOn; + } } // Registers. From 78b3ec4b1040ea05ba4304e8dfa5f434485c8b31 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 29 Sep 2020 18:42:07 -0400 Subject: [PATCH 035/150] The actual work begins: starts implementing 65816 micro-ops. --- Processors/65816/65816.hpp | 1 + Processors/65816/Implementation/65816Base.cpp | 22 ++-- .../Implementation/65816Implementation.hpp | 116 ++++++++++++++++-- .../65816/Implementation/65816Storage.cpp | 4 + .../65816/Implementation/65816Storage.hpp | 23 ++-- 5 files changed, 134 insertions(+), 32 deletions(-) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index 327855251..f47d9711b 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -29,6 +29,7 @@ class ProcessorBase: protected ProcessorStorage { inline void set_power_on(bool); inline void set_irq_line(bool); inline void set_nmi_line(bool); + inline void set_reset_line(bool); void set_value_of_register(Register r, uint16_t value); inline bool is_jammed() const; diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index f0165281d..ab1e699ca 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -12,25 +12,25 @@ using namespace CPU::WDC65816; uint16_t ProcessorBase::get_value_of_register(Register r) const { switch (r) { -// case Register::ProgramCounter: return pc_.full; -// case Register::LastOperationAddress: return last_operation_pc_.full; -// case Register::StackPointer: return s_; + case Register::ProgramCounter: return pc_; + case Register::LastOperationAddress: return last_operation_pc_; + case Register::StackPointer: return s_; // case Register::Flags: return get_flags(); -// case Register::A: return a_; -// case Register::X: return x_; -// case Register::Y: return y_; + case Register::A: return a_.full; + case Register::X: return x_.full; + case Register::Y: return y_.full; default: return 0; } } void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { -// case Register::ProgramCounter: pc_.full = value; break; -// case Register::StackPointer: s_ = uint8_t(value); break; + case Register::ProgramCounter: pc_ = value; break; + case Register::StackPointer: s_ = value; break; // case Register::Flags: set_flags(uint8_t(value)); break; -// case Register::A: a_ = uint8_t(value); break; -// case Register::X: x_ = uint8_t(value); break; -// case Register::Y: y_ = uint8_t(value); break; + case Register::A: a_ = value; break; + case Register::X: x_ = value; break; + case Register::Y: y_ = value; break; default: break; } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index ae6d8189a..0a8602254 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -7,28 +7,130 @@ // template void Processor::run_for(const Cycles cycles) { - auto int_cycles = cycles.as_integral(); - while(int_cycles--) { + // Temporary storage for the next bus cycle. + uint32_t bus_address = 0; + uint8_t *bus_value = nullptr; + uint8_t throwaway = 0; + BusOperation bus_operation = BusOperation::None; + + Cycles number_of_cycles = cycles + cycles_left_to_run_; + while(number_of_cycles > Cycles(0)) { const MicroOp operation = *next_op_; ++next_op_; switch(operation) { + + // + // Scheduling. + // + case OperationMoveToNextProgram: // The exception program will determine the appropriate way to respond // based on the pending exception if one exists; otherwise just do a // standard fetch-decode-execute. next_op_ = µ_ops_[instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset]; - - // TODO: reset instruction buffer. + instruction_buffer_.clear(); + data_buffer_.clear(); + last_operation_pc_ = pc_; continue; + case OperationDecode: + // A VERY TEMPORARY piece of logging. + printf("%02x\n", instruction_buffer_.value); + active_instruction_ = &instructions[instruction_buffer_.value]; + next_op_ = µ_ops_[active_instruction_->program_offset]; + instruction_buffer_.clear(); + continue; + + // + // PC fetches. + // + + case CycleFetchIncrementPC: + bus_address = pc_ | program_bank_; + bus_value = instruction_buffer_.next(); + bus_operation = MOS6502Esque::Read; // TODO: indicate ReadOpcode when appropriate. + ++pc_; + break; + + case CycleFetchPC: + bus_address = pc_ | program_bank_; + bus_value = &throwaway; + bus_operation = MOS6502Esque::Read; + break; + + // + // Data movement. + // + + case OperationCopyPCToData: + data_buffer_.size = 2; + data_buffer_.value = pc_; + break; + + case OperationCopyInstructionToData: + data_buffer_ = instruction_buffer_; + break; + + // + // Performance. + // + + case OperationPerform: + switch(active_instruction_->operation) { + case CLD: + // TODO. + break; + + case LDX: + // TODO. + break; + + default: + assert(false); + } + break; + default: assert(false); } + + number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); + } + + cycles_left_to_run_ = number_of_cycles; +} + +void ProcessorBase::set_power_on(bool active) { + if(active) { + pending_exceptions_ |= PowerOn; + } else { + pending_exceptions_ &= ~PowerOn; } } -void ProcessorBase::set_power_on(bool) {} -void ProcessorBase::set_irq_line(bool) {} -void ProcessorBase::set_nmi_line(bool) {} +void ProcessorBase::set_irq_line(bool active) { + if(active) { + pending_exceptions_ |= IRQ; + } else { + pending_exceptions_ &= ~IRQ; + } +} + +void ProcessorBase::set_reset_line(bool active) { + if(active) { + pending_exceptions_ |= Reset; + } else { + pending_exceptions_ &= ~Reset; + } +} + +void ProcessorBase::set_nmi_line(bool active) { + // This is edge triggered. + if(active) { + pending_exceptions_ |= NMI; + } +} + +// The 65816 can't jam. bool ProcessorBase::is_jammed() const { return false; } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 31e5642ad..889eea8bc 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -988,6 +988,10 @@ ProcessorStorage::ProcessorStorage() { constructor.set_exception_generator(&ProcessorStorageConstructor::stack_exception); constructor.install_fetch_decode_execute(); + // Find any OperationMoveToNextProgram. + next_op_ = micro_ops_.data(); + while(*next_op_ != OperationMoveToNextProgram) ++next_op_; + #ifndef NDEBUG assert(micro_ops_.size() < 65536); printf("Generated %zd micro-ops in total; covered %d opcodes\n", micro_ops_.size(), constructor.opcode); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 388a6493e..79cda40b7 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -185,34 +185,29 @@ struct ProcessorStorage { FetchDecodeExecute }; - void set_power_on(bool power_on) { - if(power_on) { - // Set next_op_ to start the exception program. - next_op_ = µ_ops_[instructions[size_t(OperationSlot::Exception)].program_offset]; - pending_exceptions_ = PowerOn; - } else { - pending_exceptions_ &= ~PowerOn; - } - } - // Registers. RegisterPair16 a_; RegisterPair16 x_, y_; uint16_t pc_, s_; + // A helper for testing. + uint16_t last_operation_pc_; + Instruction *active_instruction_; + Cycles cycles_left_to_run_; + // I.e. the offset for direct addressing (outside of emulation mode). - uint16_t direct_; + uint16_t direct_ = 0; // Banking registers are all stored with the relevant byte // shifted up bits 16–23. - uint32_t data_bank_; // i.e. DBR. - uint32_t program_bank_; // i.e. PBR. + uint32_t data_bank_ = 0; // i.e. DBR. + uint32_t program_bank_ = 0; // i.e. PBR. static constexpr int PowerOn = 1 << 0; static constexpr int Reset = 1 << 1; static constexpr int IRQ = 1 << 2; static constexpr int NMI = 1 << 3; - int pending_exceptions_ = 0; + int pending_exceptions_ = PowerOn; // By default. /// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte /// to most significant. From 4f03bf754df4fee890ce3e2d5aa75d85ac27c763 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 29 Sep 2020 18:43:39 -0400 Subject: [PATCH 036/150] Adds the 65816 to SConstruct. --- OSBindings/SDL/SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/OSBindings/SDL/SConstruct b/OSBindings/SDL/SConstruct index 9dcfe4a70..c21979fa8 100644 --- a/OSBindings/SDL/SConstruct +++ b/OSBindings/SDL/SConstruct @@ -85,6 +85,7 @@ SOURCES += glob.glob('../../Outputs/OpenGL/Primitives/*.cpp') SOURCES += glob.glob('../../Processors/6502/Implementation/*.cpp') SOURCES += glob.glob('../../Processors/6502/State/*.cpp') +SOURCES += glob.glob('../../Processors/65816/Implementation/*.cpp') SOURCES += glob.glob('../../Processors/68000/Implementation/*.cpp') SOURCES += glob.glob('../../Processors/68000/State/*.cpp') SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp') From a72ac8294ca3f17e091a2b5357ae2c128ed87ca9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 29 Sep 2020 18:49:58 -0400 Subject: [PATCH 037/150] Adds 65816 alternates to Klaus Dormann's tests. While also correcting a couple of misspellings of his name. Apologies, Klaus! --- .../Clock SignalTests/KlausDormannTests.swift | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index 2de0b6005..94b45a3c4 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -11,10 +11,10 @@ import XCTest class KlausDormannTests: XCTestCase { - fileprivate func runTest(resource: String, is65C02: Bool) -> UInt16 { + fileprivate func runTest(resource: String, processor: CSTestMachine6502Processor) -> UInt16 { if let filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") { if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) { - let machine = CSTestMachine6502(processor: is65C02 ? .processor65C02 : .processor6502) + let machine = CSTestMachine6502(processor: processor) machine.setData(functionalTest, atAddress: 0) machine.setValue(0x400, for: .programCounter) @@ -39,8 +39,7 @@ class KlausDormannTests: XCTestCase { return 0 } - /// Runs Klaus Dorman's 6502 tests. - func test6502() { + func runTest6502(processor: CSTestMachine6502Processor) { func errorForTrapAddress(_ address: UInt16) -> String? { switch address { case 0x3399: return nil // success! @@ -61,13 +60,12 @@ class KlausDormannTests: XCTestCase { } } - let destination = runTest(resource: "6502_functional_test", is65C02: false) + let destination = runTest(resource: "6502_functional_test", processor: processor) let error = errorForTrapAddress(destination) XCTAssert(error == nil, "Failed with error \(error!)") } - /// Runs Klaus Dorman's 65C02 tests. - func test65C02() { + func runTest65C02(processor: CSTestMachine6502Processor) { func errorForTrapAddress(_ address: UInt16) -> String? { switch address { case 0x24f1: return nil // success! @@ -110,8 +108,29 @@ class KlausDormannTests: XCTestCase { } } - let destination = runTest(resource: "65C02_extended_opcodes_test", is65C02: true) + let destination = runTest(resource: "65C02_extended_opcodes_test", processor: processor) let error = errorForTrapAddress(destination) XCTAssert(error == nil, "Failed with error \(error!)") } + + + /// Runs Klaus Dormann's 6502 tests. + func test6502() { + runTest6502(processor: .processor6502) + } + + /// Runs Klaus Dormann's standard 6502 tests on a 65816. + func test65816As6502() { + runTest6502(processor: .processor65816) + } + + /// Runs Klaus Dormann's 65C02 tests. + func test65C02() { + runTest65C02(processor: .processor65C02) + } + + /// Runs Klaus Dormann's 65C02 tests on a 65816. + func test65816As65C02() { + runTest65C02(processor: .processor65816) + } } From 00923eac7c4bf08063b93f46be71b7a30a5e3aaa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 29 Sep 2020 18:52:25 -0400 Subject: [PATCH 038/150] Ensure `assert` is visible to 65816Implementation.hpp. --- Processors/65816/65816.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index f47d9711b..3843520c0 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -9,6 +9,7 @@ #ifndef WDC65816_hpp #define WDC65816_hpp +#include // TEMPORARILY. #include #include From 15c87e02e94fedd2eda5db9cc922a6be482f27d0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 29 Sep 2020 18:53:02 -0400 Subject: [PATCH 039/150] Ditto for `printf`. --- Processors/65816/65816.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index 3843520c0..a08fd59c6 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -11,6 +11,7 @@ #include // TEMPORARILY. #include +#include // TEMPORARILY. #include #include "../RegisterSizes.hpp" From 36f843bc6e1827f969de8ba5b54d7663db0572a2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 29 Sep 2020 19:23:38 -0400 Subject: [PATCH 040/150] Ensure std::function is visible to 65816Storage.cpp. --- Processors/65816/Implementation/65816Storage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 889eea8bc..f6f10deda 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -9,6 +9,7 @@ #include "../65816.hpp" #include +#include #include using namespace CPU::WDC65816; From b83d93abc2e7f92c4ec4c59dcf2d4f849c782c9b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 2 Oct 2020 17:08:30 -0400 Subject: [PATCH 041/150] Accepts that whether instructions do 8- or 16-bit bus accesses depends on either M or X depending on the operation. --- Processors/65816/Implementation/65816Base.cpp | 10 ++-- .../Implementation/65816Implementation.hpp | 52 +++++++++++++++---- .../65816/Implementation/65816Storage.cpp | 15 +++--- .../65816/Implementation/65816Storage.hpp | 32 ++++++++---- 4 files changed, 78 insertions(+), 31 deletions(-) diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index ab1e699ca..736ed2268 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -14,7 +14,7 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { switch (r) { case Register::ProgramCounter: return pc_; case Register::LastOperationAddress: return last_operation_pc_; - case Register::StackPointer: return s_; + case Register::StackPointer: return s_.full; // case Register::Flags: return get_flags(); case Register::A: return a_.full; case Register::X: return x_.full; @@ -26,11 +26,11 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { case Register::ProgramCounter: pc_ = value; break; - case Register::StackPointer: s_ = value; break; + case Register::StackPointer: s_.full = value; break; // case Register::Flags: set_flags(uint8_t(value)); break; - case Register::A: a_ = value; break; - case Register::X: x_ = value; break; - case Register::Y: y_ = value; break; + case Register::A: a_.full = value; break; + case Register::X: x_.full = value; break; + case Register::Y: y_.full = value; break; default: break; } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 0a8602254..b85ac3779 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -24,32 +24,38 @@ template void Processor::run_for(const Cycles // Scheduling. // - case OperationMoveToNextProgram: + case OperationMoveToNextProgram: { // The exception program will determine the appropriate way to respond // based on the pending exception if one exists; otherwise just do a // standard fetch-decode-execute. - next_op_ = µ_ops_[instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset]; + const auto offset = instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offsets[0]; + next_op_ = µ_ops_[offset]; instruction_buffer_.clear(); data_buffer_.clear(); last_operation_pc_ = pc_; - continue; + } continue; - case OperationDecode: + case OperationDecode: { // A VERY TEMPORARY piece of logging. printf("%02x\n", instruction_buffer_.value); - active_instruction_ = &instructions[instruction_buffer_.value]; - next_op_ = µ_ops_[active_instruction_->program_offset]; + active_instruction_ = &instructions[instruction_offset_ + instruction_buffer_.value]; + + const auto size_flag = mx_flags_[active_instruction_->size_field]; + next_op_ = µ_ops_[active_instruction_->program_offsets[size_flag]]; instruction_buffer_.clear(); - continue; + } continue; // // PC fetches. // case CycleFetchIncrementPC: + case CycleFetchOpcode: bus_address = pc_ | program_bank_; bus_value = instruction_buffer_.next(); - bus_operation = MOS6502Esque::Read; // TODO: indicate ReadOpcode when appropriate. + bus_operation = (operation == CycleFetchOpcode) ? MOS6502Esque::ReadOpcode : MOS6502Esque::Read; + // TODO: split this action when I'm happy that my route to bus accesses is settled, to avoid repeating the conditional + // embedded into the `switch`. ++pc_; break; @@ -78,14 +84,40 @@ template void Processor::run_for(const Cycles case OperationPerform: switch(active_instruction_->operation) { + + // + // Flag manipulation. + // + case CLD: - // TODO. + decimal_flag_ = 0; + break; + + // + // Loads, stores and transfers + // + +#define LD(dest, src, masks) dest.full = (dest.full & masks[0]) | (src & masks[1]) + + case LDA: + LD(a_, data_buffer_.value, m_masks_); break; case LDX: - // TODO. + LD(x_, data_buffer_.value, x_masks_); break; + case LDY: + LD(y_, data_buffer_.value, x_masks_); + break; + + case TXS: + // TODO: does this transfer in full when in 8-bit index mode? + LD(s_, x_.full, x_masks_); + break; + +#undef LD + default: assert(false); } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index f6f10deda..c57ca6ee1 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -97,11 +97,11 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // Fill in the proper table entries and increment the opcode pointer. - storage_.instructions[opcode].program_offset = uint16_t(micro_op_location_8); + storage_.instructions[opcode].program_offsets[0] = uint16_t(micro_op_location_16); + storage_.instructions[opcode].program_offsets[1] = uint16_t(micro_op_location_8); storage_.instructions[opcode].operation = operation; - storage_.instructions[opcode + 256].program_offset = uint16_t(micro_op_location_16); - storage_.instructions[opcode + 256].operation = operation; + // TODO: fill in size_field. ++opcode; } @@ -109,14 +109,15 @@ struct CPU::WDC65816::ProcessorStorageConstructor { void set_exception_generator(Generator generator) { const auto key = std::make_pair(AccessType::Read, generator); const auto map_entry = installed_patterns.find(key); - storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offset = uint16_t(map_entry->second.first); + storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[0] = + storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[1] = uint16_t(map_entry->second.first); storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].operation = BRK; } void install_fetch_decode_execute() { - storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].program_offset = uint16_t(storage_.micro_ops_.size()); - storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].operation = NOP; - storage_.micro_ops_.push_back(CycleFetchIncrementPC); + storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].program_offsets[0] = + storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].program_offsets[1] = uint16_t(storage_.micro_ops_.size()); + storage_.micro_ops_.push_back(CycleFetchOpcode); storage_.micro_ops_.push_back(OperationDecode); } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 79cda40b7..680f42d18 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -11,6 +11,8 @@ enum MicroOp: uint8_t { CycleFetchIncrementPC, /// Fetches a byte from the program counter without incrementing it, and throws it away. CycleFetchPC, + /// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address. + CycleFetchOpcode, /// Fetches a byte from the data address to the data buffer. CycleFetchData, @@ -171,30 +173,42 @@ struct ProcessorStorage { // Frustratingly, there is not quite enough space in 16 bits to store both // the program offset and the operation as currently defined. struct Instruction { - uint16_t program_offset; - Operation operation; + /// Pointers into micro_ops_ for: [0] = 16-bit operation; [1] = 8-bit operation. + uint16_t program_offsets[2] = {0xffff, 0xffff}; + /// The operation to perform upon an OperationPerform. + Operation operation = NOP; + /// An index into the mx field indicating which of M or X affects whether this is an 8-bit or 16-bit field. + /// So the program to perform is that at @c program_offsets[mx_flags[size_field]] + uint8_t size_field = 0; }; - Instruction instructions[514]; // Arranged as: - // 256 entries: emulation-mode instructions; - // 256 entries: 16-bit instructions; - // the entry for 'exceptions' (i.e. reset, irq, nmi); and - // the entry for fetch-decode-execute. + Instruction instructions[256 + 2]; // Arranged as: + // 256 entries: instructions; + // the entry for 'exceptions' (i.e. reset, irq, nmi); and + // the entry for fetch-decode-execute. enum class OperationSlot { - Exception = 512, + Exception = 256, FetchDecodeExecute }; // Registers. RegisterPair16 a_; RegisterPair16 x_, y_; - uint16_t pc_, s_; + RegisterPair16 s_; + uint16_t pc_; // A helper for testing. uint16_t last_operation_pc_; Instruction *active_instruction_; Cycles cycles_left_to_run_; + // Flags aplenty. + uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0; + uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`. + uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + int instruction_offset_ = 0; + // I.e. the offset for direct addressing (outside of emulation mode). uint16_t direct_ = 0; From bdc1136b965aeff227eb8ef8ccfb74f4158c1fe3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 3 Oct 2020 21:30:24 -0400 Subject: [PATCH 042/150] Edges towards working short absolute addressing mode. --- .../Implementation/65816Implementation.hpp | 52 ++++++++++++++++++- .../65816/Implementation/65816Storage.hpp | 36 +++++++++---- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index b85ac3779..e9ca6dee9 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -52,7 +52,7 @@ template void Processor::run_for(const Cycles case CycleFetchIncrementPC: case CycleFetchOpcode: bus_address = pc_ | program_bank_; - bus_value = instruction_buffer_.next(); + bus_value = instruction_buffer_.next_input(); bus_operation = (operation == CycleFetchOpcode) ? MOS6502Esque::ReadOpcode : MOS6502Esque::Read; // TODO: split this action when I'm happy that my route to bus accesses is settled, to avoid repeating the conditional // embedded into the `switch`. @@ -65,6 +65,43 @@ template void Processor::run_for(const Cycles bus_operation = MOS6502Esque::Read; break; + // + // Data fetches and stores. + // + + case CycleFetchData: + bus_address = data_address_; + bus_value = data_buffer_.next_input(); + bus_operation = MOS6502Esque::Read; + break; + + case CycleFetchIncrementData: + bus_address = data_address_; + bus_value = data_buffer_.next_input(); + bus_operation = MOS6502Esque::Read; + ++data_address_; + break; + + case CycleStoreData: + bus_address = data_address_; + bus_value = data_buffer_.next_output(); + bus_operation = MOS6502Esque::Read; + break; + + case CycleStoreIncrementData: + bus_address = data_address_; + bus_value = data_buffer_.next_output(); + bus_operation = MOS6502Esque::Read; + ++data_address_; + break; + + case CycleStoreDecrementData: + bus_address = data_address_; + bus_value = data_buffer_.next_output(); + bus_operation = MOS6502Esque::Read; + --data_address_; + break; + // // Data movement. // @@ -78,6 +115,14 @@ template void Processor::run_for(const Cycles data_buffer_ = instruction_buffer_; break; + // + // Address construction. + // + + case OperationConstructAbsolute: + data_address_ = instruction_buffer_.value | data_bank_; + break; + // // Performance. // @@ -118,6 +163,11 @@ template void Processor::run_for(const Cycles #undef LD + case STA: + data_buffer_.value = a_.full & m_masks_[1]; + data_buffer_.size = 2 - mx_flags_[0]; + break; + default: assert(false); } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 680f42d18..514447606 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -204,7 +204,7 @@ struct ProcessorStorage { // Flags aplenty. uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0; - uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`. + uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. int instruction_offset_ = 0; @@ -228,24 +228,42 @@ struct ProcessorStorage { struct Buffer { uint32_t value = 0; int size = 0; + int read = 0; void clear() { value = 0; size = 0; + read = 0; } - uint8_t *next() { - #if TARGET_RT_BIG_ENDIAN - uint8_t *const target = reinterpret_cast(&value) + (3 ^ size); - #else - uint8_t *const target = reinterpret_cast(&value) + size; - #endif - + uint8_t *next_input() { + uint8_t *const next = byte(size); ++size; - return target; + return next; } + + uint8_t *next_output() { + uint8_t *const next = byte(read); + ++read; + return next; + } + + uint8_t *next_stack() { + --size; + return byte(size); + } + + private: + uint8_t *byte(int pointer) { + #if TARGET_RT_BIG_ENDIAN + return reinterpret_cast(&value) + (3 ^ pointer); + #else + return reinterpret_cast(&value) + pointer; + #endif + } }; Buffer instruction_buffer_, data_buffer_; + uint32_t data_address_; std::vector micro_ops_; MicroOp *next_op_ = nullptr; From 8a83024962bf3ed8c606b52b3f0611257a09d8c3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Oct 2020 18:52:46 -0400 Subject: [PATCH 043/150] Starts a dash towards just completing the addressing modes for now. This brings me up to the end of absolute long (i.e. 4a on the datasheet). --- .../Implementation/65816Implementation.hpp | 74 +++++++++++++++++-- .../65816/Implementation/65816Storage.hpp | 2 +- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index e9ca6dee9..6cd2269cb 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -37,8 +37,8 @@ template void Processor::run_for(const Cycles case OperationDecode: { // A VERY TEMPORARY piece of logging. - printf("%02x\n", instruction_buffer_.value); - active_instruction_ = &instructions[instruction_offset_ + instruction_buffer_.value]; + printf("[%04x] %02x\n", pc_ - 1, instruction_buffer_.value); + active_instruction_ = &instructions[instruction_buffer_.value]; const auto size_flag = mx_flags_[active_instruction_->size_field]; next_op_ = µ_ops_[active_instruction_->program_offsets[size_flag]]; @@ -69,6 +69,10 @@ template void Processor::run_for(const Cycles // Data fetches and stores. // +#define increment_data_address() data_address_ = (data_address_ & 0xff0000) + ((data_address_ + 1) & 0xffff) +#define decrement_data_address() data_address_ = (data_address_ & 0xff0000) + ((data_address_ - 1) & 0xffff) + + case CycleFetchData: bus_address = data_address_; bus_value = data_buffer_.next_input(); @@ -79,7 +83,7 @@ template void Processor::run_for(const Cycles bus_address = data_address_; bus_value = data_buffer_.next_input(); bus_operation = MOS6502Esque::Read; - ++data_address_; + increment_data_address(); break; case CycleStoreData: @@ -92,16 +96,48 @@ template void Processor::run_for(const Cycles bus_address = data_address_; bus_value = data_buffer_.next_output(); bus_operation = MOS6502Esque::Read; - ++data_address_; + increment_data_address(); break; case CycleStoreDecrementData: bus_address = data_address_; bus_value = data_buffer_.next_output(); bus_operation = MOS6502Esque::Read; - --data_address_; + decrement_data_address(); break; +#undef increment_data_address +#undef decrement_data_address + + // + // Stack accesses. + // + +#define stack_access(value, operation) \ + if(emulation_flag_) { \ + bus_address = s_.halves.low | 0x100; \ + } else { \ + bus_address = s_.full; \ + } \ + bus_value = value; \ + bus_operation = operation; + + case CyclePush: + stack_access(data_buffer_.next_stack(), MOS6502Esque::Write); + --s_.full; + break; + + case CyclePull: + ++s_.full; + stack_access(data_buffer_.next_input(), MOS6502Esque::Read); + break; + + case CycleAccessStack: + stack_access(&throwaway, MOS6502Esque::Read); + break; + +#undef stack_access + // // Data movement. // @@ -123,6 +159,10 @@ template void Processor::run_for(const Cycles data_address_ = instruction_buffer_.value | data_bank_; break; + case OperationConstructAbsoluteIndexedIndirect: + data_address_ = (instruction_buffer_.value + (x_.full & x_masks_[1])) & 0xffff; + break; + // // Performance. // @@ -168,6 +208,30 @@ template void Processor::run_for(const Cycles data_buffer_.size = 2 - mx_flags_[0]; break; + // + // Jumps. + // + + case JML: + program_bank_ = instruction_buffer_.value & 0xff0000; + pc_ = instruction_buffer_.value & 0xffff; + break; + + case JSL: + program_bank_ = instruction_buffer_.value & 0xff0000; + instruction_buffer_.size = 2; + [[fallthrough]]; + + case JSR: { + const uint16_t old_pc = pc_; + pc_ = instruction_buffer_.value; + instruction_buffer_.value = old_pc; + } break; + + case JSL: { + + } break; + default: assert(false); } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 514447606..f184e8b9d 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -207,7 +207,7 @@ struct ProcessorStorage { uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. - int instruction_offset_ = 0; + bool emulation_flag_ = true; // I.e. the offset for direct addressing (outside of emulation mode). uint16_t direct_ = 0; From 4ebf594b3b7fed819136348ff73e15d8ba72b614 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Oct 2020 19:02:47 -0400 Subject: [PATCH 044/150] This should bring me up to absolute, y. i.e. next is datasheet program 7. --- .../Implementation/65816Implementation.hpp | 21 +++++++++++++++++++ .../65816/Implementation/65816Storage.hpp | 1 + 2 files changed, 22 insertions(+) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 6cd2269cb..54097f428 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -79,6 +79,12 @@ template void Processor::run_for(const Cycles bus_operation = MOS6502Esque::Read; break; + case CycleFetchIncorrectDataAddress: + bus_address = incorrect_data_address_; + bus_value = &throwaway; + bus_operation = MOS6502Esque::Read; + break; + case CycleFetchIncrementData: bus_address = data_address_; bus_value = data_buffer_.next_input(); @@ -163,6 +169,21 @@ template void Processor::run_for(const Cycles data_address_ = (instruction_buffer_.value + (x_.full & x_masks_[1])) & 0xffff; break; + case OperationConstructAbsoluteLongX: + data_address_ = instruction_buffer_.value + (x_.full & x_masks_[1]); + break; + + case OperationConstructAbsoluteXRead: + case OperationConstructAbsoluteX: + data_address_ = ((instruction_buffer_.value + (x_.full & x_masks_[1])) & 0xffff) | data_bank_; + incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_; + + // If the incorrect address isn't actually incorrect, skip its usage. + if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { + ++next_op_; + } + break; + // // Performance. // diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index f184e8b9d..b8dd19ff6 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -264,6 +264,7 @@ struct ProcessorStorage { }; Buffer instruction_buffer_, data_buffer_; uint32_t data_address_; + uint32_t incorrect_data_address_; std::vector micro_ops_; MicroOp *next_op_ = nullptr; From b416aa640f7151c55802d3a1a580157350733c7f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Oct 2020 19:12:04 -0400 Subject: [PATCH 045/150] Slightly tidies up, eliminating some store bugs. --- .../Implementation/65816Implementation.hpp | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 54097f428..abc3adb12 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -13,6 +13,14 @@ template void Processor::run_for(const Cycles uint8_t throwaway = 0; BusOperation bus_operation = BusOperation::None; +#define perform_bus(address, value, operation) \ + bus_address = address; \ + bus_value = value; \ + bus_operation = operation + +#define read(address, value) perform_bus(address, value, MOS6502Esque::Read) +#define write(address, value) perform_bus(address, value, MOS6502Esque::Write) + Cycles number_of_cycles = cycles + cycles_left_to_run_; while(number_of_cycles > Cycles(0)) { const MicroOp operation = *next_op_; @@ -50,19 +58,17 @@ template void Processor::run_for(const Cycles // case CycleFetchIncrementPC: + read(pc_ | program_bank_, instruction_buffer_.next_input()); + ++pc_; + break; + case CycleFetchOpcode: - bus_address = pc_ | program_bank_; - bus_value = instruction_buffer_.next_input(); - bus_operation = (operation == CycleFetchOpcode) ? MOS6502Esque::ReadOpcode : MOS6502Esque::Read; - // TODO: split this action when I'm happy that my route to bus accesses is settled, to avoid repeating the conditional - // embedded into the `switch`. + perform_bus(pc_ | program_bank_, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode); ++pc_; break; case CycleFetchPC: - bus_address = pc_ | program_bank_; - bus_value = &throwaway; - bus_operation = MOS6502Esque::Read; + read(pc_ | program_bank_, &throwaway); break; // @@ -74,41 +80,29 @@ template void Processor::run_for(const Cycles case CycleFetchData: - bus_address = data_address_; - bus_value = data_buffer_.next_input(); - bus_operation = MOS6502Esque::Read; + read(data_address_, data_buffer_.next_input()); break; case CycleFetchIncorrectDataAddress: - bus_address = incorrect_data_address_; - bus_value = &throwaway; - bus_operation = MOS6502Esque::Read; + read(incorrect_data_address_, &throwaway); break; case CycleFetchIncrementData: - bus_address = data_address_; - bus_value = data_buffer_.next_input(); - bus_operation = MOS6502Esque::Read; + read(data_address_, data_buffer_.next_input()); increment_data_address(); break; case CycleStoreData: - bus_address = data_address_; - bus_value = data_buffer_.next_output(); - bus_operation = MOS6502Esque::Read; + write(data_address_, data_buffer_.next_output()); break; case CycleStoreIncrementData: - bus_address = data_address_; - bus_value = data_buffer_.next_output(); - bus_operation = MOS6502Esque::Read; + write(data_address_, data_buffer_.next_output()); increment_data_address(); break; case CycleStoreDecrementData: - bus_address = data_address_; - bus_value = data_buffer_.next_output(); - bus_operation = MOS6502Esque::Read; + write(data_address_, data_buffer_.next_output()); decrement_data_address(); break; @@ -184,6 +178,17 @@ template void Processor::run_for(const Cycles } break; + case OperationConstructAbsoluteYRead: + case OperationConstructAbsoluteY: + data_address_ = ((instruction_buffer_.value + (y_.full & x_masks_[1])) & 0xffff) | data_bank_; + incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_; + + // If the incorrect address isn't actually incorrect, skip its usage. + if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) { + ++next_op_; + } + break; + // // Performance. // @@ -265,6 +270,10 @@ template void Processor::run_for(const Cycles number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); } +#undef read +#undef write +#undef bus_operation + cycles_left_to_run_ = number_of_cycles; } From d8dccf250048de37292d083569af16d1bbdf481e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Oct 2020 19:21:04 -0400 Subject: [PATCH 046/150] Attempts a full implementation of MVN and MVP. --- .../Implementation/65816Implementation.hpp | 32 +++++++++++++++++-- .../65816/Implementation/65816Storage.hpp | 4 +++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index abc3adb12..f7f0666aa 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -106,6 +106,18 @@ template void Processor::run_for(const Cycles decrement_data_address(); break; + case CycleFetchBlockX: + read(((instruction_buffer_.value & 0xff00) << 8) | (x_.full & x_masks_[1]), data_buffer_.any_byte()); + break; + + case CycleFetchBlockY: + read(((instruction_buffer_.value & 0xff00) << 8) | (y_.full & x_masks_[1]), &throwaway); + break; + + case CycleStoreBlockY: + write(((instruction_buffer_.value & 0xff00) << 8) | (y_.full & x_masks_[1]), data_buffer_.any_byte()); + break; + #undef increment_data_address #undef decrement_data_address @@ -254,9 +266,25 @@ template void Processor::run_for(const Cycles instruction_buffer_.value = old_pc; } break; - case JSL: { + // + // Block moves. + // - } break; + case MVP: + data_bank_ = (instruction_buffer_.value & 0xff) << 16; + --x_.full; + --y_.full; + --a_.full; + if(a_.full) pc_ -= 3; + break; + + case MVN: + data_bank_ = (instruction_buffer_.value & 0xff) << 16; + ++x_.full; + ++y_.full; + --a_.full; + if(a_.full) pc_ -= 3; + break; default: assert(false); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index b8dd19ff6..0baa015ba 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -253,6 +253,10 @@ struct ProcessorStorage { return byte(size); } + uint8_t *any_byte() { + return reinterpret_cast(&value); + } + private: uint8_t *byte(int pointer) { #if TARGET_RT_BIG_ENDIAN From 9a05c68ce78c62655c289011588d6abd90a5aa52 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Oct 2020 22:06:25 -0400 Subject: [PATCH 047/150] Attempts direct and direct indexed indirect. --- .../Implementation/65816Implementation.hpp | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index f7f0666aa..81fab2f8f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -21,6 +21,9 @@ template void Processor::run_for(const Cycles #define read(address, value) perform_bus(address, value, MOS6502Esque::Read) #define write(address, value) perform_bus(address, value, MOS6502Esque::Write) +#define x() (x_.full & x_masks_[1]) +#define y() (y_.full & x_masks_[1]) + Cycles number_of_cycles = cycles + cycles_left_to_run_; while(number_of_cycles > Cycles(0)) { const MicroOp operation = *next_op_; @@ -107,15 +110,15 @@ template void Processor::run_for(const Cycles break; case CycleFetchBlockX: - read(((instruction_buffer_.value & 0xff00) << 8) | (x_.full & x_masks_[1]), data_buffer_.any_byte()); + read(((instruction_buffer_.value & 0xff00) << 8) | x(), data_buffer_.any_byte()); break; case CycleFetchBlockY: - read(((instruction_buffer_.value & 0xff00) << 8) | (y_.full & x_masks_[1]), &throwaway); + read(((instruction_buffer_.value & 0xff00) << 8) | y(), &throwaway); break; case CycleStoreBlockY: - write(((instruction_buffer_.value & 0xff00) << 8) | (y_.full & x_masks_[1]), data_buffer_.any_byte()); + write(((instruction_buffer_.value & 0xff00) << 8) | x(), data_buffer_.any_byte()); break; #undef increment_data_address @@ -172,16 +175,16 @@ template void Processor::run_for(const Cycles break; case OperationConstructAbsoluteIndexedIndirect: - data_address_ = (instruction_buffer_.value + (x_.full & x_masks_[1])) & 0xffff; + data_address_ = (instruction_buffer_.value + x()) & 0xffff; break; case OperationConstructAbsoluteLongX: - data_address_ = instruction_buffer_.value + (x_.full & x_masks_[1]); + data_address_ = instruction_buffer_.value + x(); break; case OperationConstructAbsoluteXRead: case OperationConstructAbsoluteX: - data_address_ = ((instruction_buffer_.value + (x_.full & x_masks_[1])) & 0xffff) | data_bank_; + data_address_ = ((instruction_buffer_.value + x()) & 0xffff) | data_bank_; incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_; // If the incorrect address isn't actually incorrect, skip its usage. @@ -192,7 +195,7 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteYRead: case OperationConstructAbsoluteY: - data_address_ = ((instruction_buffer_.value + (y_.full & x_masks_[1])) & 0xffff) | data_bank_; + data_address_ = ((instruction_buffer_.value + y()) & 0xffff) | data_bank_; incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_; // If the incorrect address isn't actually incorrect, skip its usage. @@ -201,6 +204,20 @@ template void Processor::run_for(const Cycles } break; + case OperationConstructDirect: + data_address_ = direct_ + instruction_buffer_.value; + if(!(direct_&0xff)) { + ++next_op_; + } + break; + + case OperationConstructDirectIndexedIndirect: + data_address_ = direct_ + x() + instruction_buffer_.value; + if(!(direct_&0xff)) { + ++next_op_; + } + break; + // // Performance. // @@ -301,6 +318,8 @@ template void Processor::run_for(const Cycles #undef read #undef write #undef bus_operation +#undef x +#undef y cycles_left_to_run_ = number_of_cycles; } From 825201f4f2acd83b32f8b72c4096409814e40202 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Oct 2020 22:11:41 -0400 Subject: [PATCH 048/150] Adds direct indirect. --- .../65816/Implementation/65816Implementation.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 81fab2f8f..866748c82 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -205,14 +205,21 @@ template void Processor::run_for(const Cycles break; case OperationConstructDirect: - data_address_ = direct_ + instruction_buffer_.value; + data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; if(!(direct_&0xff)) { ++next_op_; } break; case OperationConstructDirectIndexedIndirect: - data_address_ = direct_ + x() + instruction_buffer_.value; + data_address_ = data_bank_ + (direct_ + x() + instruction_buffer_.value) & 0xffff; + if(!(direct_&0xff)) { + ++next_op_; + } + break; + + case OperationConstructDirectIndirect: + data_address_ = data_bank_ + (direct_ + instruction_buffer_.value) & 0xffff; if(!(direct_&0xff)) { ++next_op_; } From b7ba0d43277d50154ad56b9d1ffbbda350d8ec22 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Oct 2020 17:04:57 -0400 Subject: [PATCH 049/150] Attempts to complete all addressing modes. So, if bugs didn't exist, it'd just be members of the Operation enum to go. --- .../Implementation/65816Implementation.hpp | 73 +++++++++++++++++-- .../65816/Implementation/65816Storage.cpp | 8 +- .../65816/Implementation/65816Storage.hpp | 43 ++++++++--- 3 files changed, 101 insertions(+), 23 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 866748c82..50844b09c 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -166,12 +166,30 @@ template void Processor::run_for(const Cycles data_buffer_ = instruction_buffer_; break; + case OperationCopyAToData: + if(mx_flags_[0]) { + data_buffer_.size = 1; + data_buffer_.value = a_.halves.high; + } else { + data_buffer_.size = 2; + data_buffer_.value = a_.full; + } + break; + + case OperationCopyDataToA: + if(mx_flags_[0]) { + a_.halves.high = data_buffer_.value; + } else { + a_.full = data_buffer_.value; + } + break; + // // Address construction. // case OperationConstructAbsolute: - data_address_ = instruction_buffer_.value | data_bank_; + data_address_ = instruction_buffer_.value + data_bank_; break; case OperationConstructAbsoluteIndexedIndirect: @@ -179,13 +197,13 @@ template void Processor::run_for(const Cycles break; case OperationConstructAbsoluteLongX: - data_address_ = instruction_buffer_.value + x(); + data_address_ = (instruction_buffer_.value + x()) & 0xffff + instruction_buffer_.value & 0xff0000; break; case OperationConstructAbsoluteXRead: case OperationConstructAbsoluteX: - data_address_ = ((instruction_buffer_.value + x()) & 0xffff) | data_bank_; - incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_; + data_address_ = ((instruction_buffer_.value + x()) & 0xffff) + data_bank_; + incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) + data_bank_; // If the incorrect address isn't actually incorrect, skip its usage. if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { @@ -195,8 +213,8 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteYRead: case OperationConstructAbsoluteY: - data_address_ = ((instruction_buffer_.value + y()) & 0xffff) | data_bank_; - incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_; + data_address_ = ((instruction_buffer_.value + y()) & 0xffff) + data_bank_; + incorrect_data_address_ = (data_address_ & 0xff) + (instruction_buffer_.value & 0xff00) + data_bank_; // If the incorrect address isn't actually incorrect, skip its usage. if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) { @@ -225,6 +243,45 @@ template void Processor::run_for(const Cycles } break; + case OperationConstructDirectIndirectIndexedLong: + // TODO: assumed here is that the low 16-bit calculation can't carry into + // the high byte. Test this! + data_address_ = (y() + instruction_buffer_.value) & 0xffff + instruction_buffer_.value & 0xff0000; + break; + + case OperationConstructDirectIndirectLong: + data_address_ = instruction_buffer_.value; + break; + + case OperationConstructDirectX: + data_address_ = (direct_ + x()) & 0xffff; + incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); + if(!(direct_&0xff)) { + ++next_op_; + } + break; + + case OperationConstructDirectY: + data_address_ = (direct_ + y()) & 0xffff; + incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); + if(!(direct_&0xff)) { + ++next_op_; + } + break; + + case OperationConstructPER: + data_buffer_.value = instruction_buffer_.value + pc_; + data_buffer_.size = 2; + break; + + case OperationConstructStackRelative: + data_address_ = (s_.full + instruction_buffer_.value) & 0xffff; + break; + + case OperationConstructStackRelativeIndexedIndirect: + data_address_ = data_bank_ + (instruction_buffer_.value + y()) & 0xffff; + break; + // // Performance. // @@ -315,8 +372,8 @@ template void Processor::run_for(const Cycles } break; - default: - assert(false); +// default: +// assert(false); } number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index c57ca6ee1..2439f2189 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -424,21 +424,17 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void direct_indirect_indexed(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // DO. - target(OperationConstructDirectIndirect); + target(OperationConstructDirect); target(CycleFetchPC); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchData); // AAH. - target(OperationConstructDirectIndirectIndexed); + target(OperationConstructAbsoluteYRead); target(CycleFetchIncorrectDataAddress); // IO. read_write(type, is8bit, target); } - // TODO: verify, especially re: false addresses. - // i.e. it currently looks to me superficially as though I can reuse - // OperationConstructDirectIndirect here, but writing the proper - // interpreter will help to clarify. // 14. Direct Indirect Indexed Long; [d], y. static void direct_indirect_indexed_long(AccessType type, bool is8bit, const std::function &target) { diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 0baa015ba..b0e12ccdd 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -22,9 +22,6 @@ enum MicroOp: uint8_t { /// of the instruction buffer, throwing the result away. CycleFetchIncorrectDataAddress, - /// Fetches a vector (i.e. IRQ, NMI, etc) into the data buffer. - CycleFetchVector, - // Dedicated block-move cycles; these use the data buffer as an intermediary. CycleFetchBlockX, CycleFetchBlockY, @@ -44,11 +41,15 @@ enum MicroOp: uint8_t { /// Pulls a single byte to the data buffer from the stack. CyclePull, - /// Sets the data address by copying the final two bytes of the instruction buffer. + /// Sets the data address by copying the final two bytes of the instruction buffer and + /// using the data register as a high byte. OperationConstructAbsolute, - /// Sets the data address to the result of (a, x). - /// TODO: explain better once implemented. + + /// Sets the data address to the 16-bit result of adding x to the value in the instruction buffer. OperationConstructAbsoluteIndexedIndirect, + + /// Sets the data address to the 24-bit result of adding x to the low 16-bits of the value in the + /// instruction buffer and retaining the highest 8-bits as specified. OperationConstructAbsoluteLongX, /// Calculates an a, x address; if: @@ -68,19 +69,40 @@ enum MicroOp: uint8_t { /// Skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirect, - // These follow similar skip-one-if-possible logic to OperationConstructDirect. + /// Constructs the current direct indexed indirect address using the data bank, + /// direct and x registers plus the value currently in the instruction buffer. + /// Skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirectIndexedIndirect, + + /// Constructs the current direct indexed indirect address using the data bank and + /// direct registers plus the value currently in the instruction buffer. + /// Skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirectIndirect, - OperationConstructDirectIndirectIndexed, + + /// Adds y to the low 16-bits currently in the instruction buffer and appends a high 8-bits + /// also from the instruction buffer. OperationConstructDirectIndirectIndexedLong, + + /// Uses the 24-bit address currently in the instruction buffer. OperationConstructDirectIndirectLong, + + /// Adds the x register to the direct register to produce a 16-bit address; + /// skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirectX, + + /// Adds the y register to the direct register to produce a 16-bit address; + /// skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirectY, + /// Adds the instruction buffer to the program counter, making a 16-bit result, + /// *and stores it into the data buffer*. OperationConstructPER, - OperationConstructBRK, + /// Adds the stack pointer to the instruction buffer to produce a 16-bit address. OperationConstructStackRelative, + + /// Adds y to the value in the instruction buffer to produce a 16-bit result and + /// prefixes the current data bank. OperationConstructStackRelativeIndexedIndirect, /// Performs whatever operation goes with this program. @@ -93,7 +115,10 @@ enum MicroOp: uint8_t { /// Copies the current PBR to the data buffer. OperationCopyPBRToData, + /// Copies A to the data buffer. OperationCopyAToData, + + /// Copies the data buffer to A. OperationCopyDataToA, /// Fills the data buffer with three or four bytes, depending on emulation mode, containing the program From 18e8d6ce06237f2be158e0419535f32f2d905a89 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Oct 2020 22:23:33 -0400 Subject: [PATCH 050/150] Makes an effort to factor out the 6502's [lazy] flags. This is preparatory to deciding which instructions, if any, are worth factoring out. --- .../Clock Signal.xcodeproj/project.pbxproj | 48 +++- Processors/6502/6502.hpp | 20 +- Processors/6502/AllRAM/6502AllRAM.hpp | 2 +- .../Implementation/6502Implementation.hpp | 263 +++++++++--------- .../6502/Implementation/6502Storage.cpp | 6 - .../6502/Implementation/6502Storage.hpp | 4 +- Processors/{ => 6502Esque}/6502Esque.hpp | 17 ++ Processors/{ => 6502Esque}/6502Selector.hpp | 4 +- .../6502Esque/Implementation/LazyFlags.hpp | 66 +++++ Processors/6502Esque/README.md | 3 + Processors/65816/65816.hpp | 2 +- .../Implementation/65816Implementation.hpp | 6 +- 12 files changed, 262 insertions(+), 179 deletions(-) rename Processors/{ => 6502Esque}/6502Esque.hpp (89%) rename Processors/{ => 6502Esque}/6502Selector.hpp (96%) create mode 100644 Processors/6502Esque/Implementation/LazyFlags.hpp create mode 100644 Processors/6502Esque/README.md diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b78dc0fda..30c4d4bd2 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -210,9 +210,9 @@ 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; }; 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; }; - 4B4DEBED2522C03F004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */; }; - 4B4DEBEE2522C03F004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */; }; - 4B4DEBEF2522C03F004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */; }; + 4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; + 4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; + 4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; 4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; }; 4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; }; 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; }; @@ -1122,7 +1122,11 @@ 4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = ""; }; 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = ""; }; 4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = ""; }; - 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = 65816Base.cpp; path = ../../Processors/65816/Implementation/65816Base.cpp; sourceTree = ""; }; + 4B4DEC04252BFA56004583AC /* 65816Implementation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Implementation.hpp; sourceTree = ""; }; + 4B4DEC05252BFA56004583AC /* 65816Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 65816Base.cpp; sourceTree = ""; }; + 4B4DEC16252BFA9C004583AC /* 6502Selector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502Selector.hpp; sourceTree = ""; }; + 4B4DEC18252BFA9C004583AC /* 6502Esque.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = ""; }; + 4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LazyFlags.hpp; sourceTree = ""; }; 4B4F2B7024DF99D4000DA6B0 /* CSScanTarget+CppScanTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSScanTarget+CppScanTarget.h"; sourceTree = ""; }; 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = ""; }; @@ -1346,9 +1350,6 @@ 4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = ""; }; 4BAF2B4C2004580C00480230 /* DMK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DMK.cpp; sourceTree = ""; }; 4BAF2B4D2004580C00480230 /* DMK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMK.hpp; sourceTree = ""; }; - 4BB023FF25212888009F8D90 /* 65816Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 65816Implementation.hpp; path = ../../Processors/65816/Implementation/65816Implementation.hpp; sourceTree = ""; }; - 4BB0240425229C6E009F8D90 /* 6502Esque.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = ""; }; - 4BB024092522B7BE009F8D90 /* 6502Selector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Selector.hpp; sourceTree = ""; }; 4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = ""; }; 4BB0A6592044FD3000FB3688 /* SN76489.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SN76489.cpp; sourceTree = ""; }; 4BB0A65A2044FD3000FB3688 /* SN76489.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SN76489.hpp; sourceTree = ""; }; @@ -2463,6 +2464,24 @@ path = 1540; sourceTree = ""; }; + 4B4DEC15252BFA9C004583AC /* 6502Esque */ = { + isa = PBXGroup; + children = ( + 4B4DEC18252BFA9C004583AC /* 6502Esque.hpp */, + 4B4DEC16252BFA9C004583AC /* 6502Selector.hpp */, + 4B4DEC17252BFA9C004583AC /* Implementation */, + ); + path = 6502Esque; + sourceTree = ""; + }; + 4B4DEC17252BFA9C004583AC /* Implementation */ = { + isa = PBXGroup; + children = ( + 4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */, + ); + path = Implementation; + sourceTree = ""; + }; 4B51F70820A521D700AFA2C1 /* Activity */ = { isa = PBXGroup; children = ( @@ -3337,8 +3356,6 @@ 4BB73E951B587A5100552FC2 = { isa = PBXGroup; children = ( - 4B4DEBEC2522C03F004583AC /* 65816Base.cpp */, - 4BB023FF25212888009F8D90 /* 65816Implementation.hpp */, 4B51F70820A521D700AFA2C1 /* Activity */, 4B8944E2201967B4007DE474 /* Analyser */, 4BB73EA01B587A5100552FC2 /* Clock Signal */, @@ -3496,11 +3513,10 @@ isa = PBXGroup; children = ( 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */, - 4BB0240425229C6E009F8D90 /* 6502Esque.hpp */, - 4BB024092522B7BE009F8D90 /* 6502Selector.hpp */, 4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */, 4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */, 4B1414561B58879D00E04248 /* 6502 */, + 4B4DEC15252BFA9C004583AC /* 6502Esque */, 4BF8D4CC251C0C9C00BBE21B /* 65816 */, 4BFF1D332233778C00838EA1 /* 68000 */, 4B77069E1EC9045B0053B588 /* Z80 */, @@ -3921,8 +3937,10 @@ 4BF8D4D2251C0D9F00BBE21B /* Implementation */ = { isa = PBXGroup; children = ( - 4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */, + 4B4DEC05252BFA56004583AC /* 65816Base.cpp */, 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */, + 4B4DEC04252BFA56004583AC /* 65816Implementation.hpp */, + 4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */, ); path = Implementation; sourceTree = ""; @@ -4503,7 +4521,7 @@ 4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */, 4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */, 4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */, - 4B4DEBEF2522C03F004583AC /* 65816Base.cpp in Sources */, + 4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */, 4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */, 4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */, @@ -4786,7 +4804,7 @@ 4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */, 4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */, 4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */, - 4B4DEBED2522C03F004583AC /* 65816Base.cpp in Sources */, + 4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */, 4B894524201967B4007DE474 /* Tape.cpp in Sources */, 4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */, 4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */, @@ -4934,7 +4952,7 @@ 4B778F4123A5F19A0000D260 /* MemoryPacker.cpp in Sources */, 4B778F4423A5F1BE0000D260 /* CommodoreGCR.cpp in Sources */, 4B778EF923A5EB740000D260 /* MSA.cpp in Sources */, - 4B4DEBEE2522C03F004583AC /* 65816Base.cpp in Sources */, + 4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */, 4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */, 4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */, 4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */, diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index fe69490e9..7b4eb55a8 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -13,16 +13,19 @@ #include #include +#include "../6502Esque/6502Esque.hpp" +#include "../6502Esque/Implementation/LazyFlags.hpp" #include "../RegisterSizes.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" -#include "../6502Esque.hpp" namespace CPU { namespace MOS6502 { +// Adopt a bunch of things from MOS6502Esque. using BusOperation = CPU::MOS6502Esque::BusOperation; using BusHandler = CPU::MOS6502Esque::BusHandler; using Register = CPU::MOS6502Esque::Register; +using Flag = CPU::MOS6502Esque::Flag; /* The list of 6502 variants supported by this implementation. @@ -40,21 +43,6 @@ enum Personality { #define has_bbrbbsrmbsmb(p) ((p) >= Personality::PRockwell65C02) #define has_stpwai(p) ((p) >= Personality::PWDC65C02) -/* - Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for - the corresponding set. -*/ -enum Flag: uint8_t { - Sign = 0x80, - Overflow = 0x40, - Always = 0x20, - Break = 0x10, - Decimal = 0x08, - Interrupt = 0x04, - Zero = 0x02, - Carry = 0x01 -}; - /*! An opcode that is guaranteed to cause the CPU to jam. */ diff --git a/Processors/6502/AllRAM/6502AllRAM.hpp b/Processors/6502/AllRAM/6502AllRAM.hpp index 9d7a6cd1d..f6a354f78 100644 --- a/Processors/6502/AllRAM/6502AllRAM.hpp +++ b/Processors/6502/AllRAM/6502AllRAM.hpp @@ -9,7 +9,7 @@ #ifndef MOS6502AllRAM_cpp #define MOS6502AllRAM_cpp -#include "../../6502Selector.hpp" +#include "../../6502Esque/6502Selector.hpp" #include "../../AllRAMProcessor.hpp" namespace CPU { diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 773edbb00..673970e31 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -13,7 +13,7 @@ */ template void Processor::run_for(const Cycles cycles) { - static uint8_t throwaway_target; + uint8_t throwaway_target; // These plus program below act to give the compiler permission to update these values // without touching the class storage (i.e. it explicitly says they need be completely up @@ -42,7 +42,7 @@ template void Proces #define bus_access() \ interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \ - irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \ + irq_request_history_ = irq_line_ & flags_.inverse_interrupt; \ number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \ nextBusOperation = BusOperation::None; \ if(number_of_cycles <= Cycles(0)) break; @@ -70,7 +70,7 @@ template void Proces // Deal with a potential WAI state, if this 6502 implements WAI. while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) { number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue); - interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_); + interrupt_requests_ |= (irq_line_ & flags_.inverse_interrupt); if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) { wait_is_active_ = false; checkSchedule(); @@ -167,25 +167,25 @@ template void Proces case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break; case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break; case OperationSetIRQFlags: - inverse_interrupt_flag_ = 0; - if(is_65c02(personality)) decimal_flag_ = false; + flags_.inverse_interrupt = 0; + if(is_65c02(personality)) flags_.decimal = 0; continue; case OperationSetNMIRSTFlags: - if(is_65c02(personality)) decimal_flag_ = false; + if(is_65c02(personality)) flags_.decimal = 0; continue; - case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break; - case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break; - case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break; - case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break; - case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break; - case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break; - case OperationSetFlagsFromOperand: set_flags(operand_); continue; - case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue; - case OperationSetOperandFromFlags: operand_ = get_flags(); continue; - case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue; - case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue; - case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue; + case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break; + case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break; + case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break; + case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break; + case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break; + case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break; + case OperationSetFlagsFromOperand: set_flags(operand_); continue; + case OperationSetOperandFromFlagsWithBRKSet: operand_ = flags_.get() | Flag::Break; continue; + case OperationSetOperandFromFlags: operand_ = flags_.get(); continue; + case OperationSetFlagsFromA: flags_.set_nz(a_); continue; + case OperationSetFlagsFromX: flags_.set_nz(x_); continue; + case OperationSetFlagsFromY: flags_.set_nz(y_); continue; case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break; case CycleReadPCLFromAddress: read_mem(pc_.halves.low, address_.full); break; @@ -216,17 +216,17 @@ template void Proces // MARK: - Bitwise - case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue; - case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue; - case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue; + case OperationORA: a_ |= operand_; flags_.set_nz(a_); continue; + case OperationAND: a_ &= operand_; flags_.set_nz(a_); continue; + case OperationEOR: a_ ^= operand_; flags_.set_nz(a_); continue; // MARK: - Load and Store - case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue; - case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue; - case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue; - case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue; - case OperationCopyOperandToA: a_ = operand_; continue; + case OperationLDA: flags_.set_nz(a_ = operand_); continue; + case OperationLDX: flags_.set_nz(x_ = operand_); continue; + case OperationLDY: flags_.set_nz(y_ = operand_); continue; + case OperationLAX: flags_.set_nz(a_ = x_ = operand_); continue; + case OperationCopyOperandToA: a_ = operand_; continue; case OperationSTA: operand_ = a_; continue; case OperationSTX: operand_ = x_; continue; @@ -240,43 +240,43 @@ template void Proces case OperationLXA: a_ = x_ = (a_ | 0xee) & operand_; - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); continue; // MARK: - Compare case OperationCMP: { const uint16_t temp16 = a_ - operand_; - negative_result_ = zero_result_ = uint8_t(temp16); - carry_flag_ = ((~temp16) >> 8)&1; + flags_.set_nz(uint8_t(temp16)); + flags_.carry = ((~temp16) >> 8)&1; } continue; case OperationCPX: { const uint16_t temp16 = x_ - operand_; - negative_result_ = zero_result_ = uint8_t(temp16); - carry_flag_ = ((~temp16) >> 8)&1; + flags_.set_nz(uint8_t(temp16)); + flags_.carry = ((~temp16) >> 8)&1; } continue; case OperationCPY: { const uint16_t temp16 = y_ - operand_; - negative_result_ = zero_result_ = uint8_t(temp16); - carry_flag_ = ((~temp16) >> 8)&1; + flags_.set_nz(uint8_t(temp16)); + flags_.carry = ((~temp16) >> 8)&1; } continue; // MARK: - BIT, TSB, TRB case OperationBIT: - zero_result_ = operand_ & a_; - negative_result_ = operand_; - overflow_flag_ = operand_&Flag::Overflow; + flags_.zero_result = operand_ & a_; + flags_.negative_result = operand_; + flags_.overflow = operand_ & Flag::Overflow; continue; case OperationBITNoNV: - zero_result_ = operand_ & a_; + flags_.zero_result = operand_ & a_; continue; case OperationTRB: - zero_result_ = operand_ & a_; + flags_.zero_result = operand_ & a_; operand_ &= ~a_; continue; case OperationTSB: - zero_result_ = operand_ & a_; + flags_.zero_result = operand_ & a_; operand_ |= a_; continue; @@ -295,8 +295,8 @@ template void Proces operand_++; [[fallthrough]]; case OperationSBC: - if(decimal_flag_ && has_decimal_mode(personality)) { - const uint16_t notCarry = carry_flag_ ^ 0x1; + if(flags_.decimal && has_decimal_mode(personality)) { + const uint16_t notCarry = flags_.carry ^ 0x1; const uint16_t decimalResult = uint16_t(a_) - uint16_t(operand_) - notCarry; uint16_t temp16; @@ -305,17 +305,17 @@ template void Proces temp16 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00); temp16 += (a_&0xf0) - (operand_&0xf0); - overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1; - negative_result_ = uint8_t(temp16); - zero_result_ = uint8_t(decimalResult); + flags_.overflow = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1; + flags_.negative_result = uint8_t(temp16); + flags_.zero_result = uint8_t(decimalResult); if(temp16 > 0xff) temp16 -= 0x60; - carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry; + flags_.carry = (temp16 > 0xff) ? 0 : Flag::Carry; a_ = uint8_t(temp16); if(is_65c02(personality)) { - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); read_mem(operand_, address_.full); break; } @@ -326,30 +326,30 @@ template void Proces [[fallthrough]]; case OperationADC: - if(decimal_flag_ && has_decimal_mode(personality)) { - const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_); + if(flags_.decimal && has_decimal_mode(personality)) { + const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry); - uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_; + uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + flags_.carry; if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10; uint16_t result = uint16_t(a_ & 0xf0) + uint16_t(operand_ & 0xf0) + uint16_t(low_nibble); - negative_result_ = uint8_t(result); - overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1; + flags_.negative_result = uint8_t(result); + flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1; if(result >= 0xa0) result += 0x60; - carry_flag_ = (result >> 8) ? 1 : 0; + flags_.carry = (result >> 8) ? 1 : 0; a_ = uint8_t(result); - zero_result_ = uint8_t(decimalResult); + flags_.zero_result = uint8_t(decimalResult); if(is_65c02(personality)) { - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); read_mem(operand_, address_.full); break; } } else { - const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_); - overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1; - negative_result_ = zero_result_ = a_ = uint8_t(result); - carry_flag_ = (result >> 8)&1; + const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry); + flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1; + flags_.set_nz(a_ = uint8_t(result)); + flags_.carry = (result >> 8)&1; } // fix up in case this was INS @@ -359,99 +359,99 @@ template void Proces // MARK: - Shifts and Rolls case OperationASL: - carry_flag_ = operand_ >> 7; + flags_.carry = operand_ >> 7; operand_ <<= 1; - negative_result_ = zero_result_ = operand_; + flags_.set_nz(operand_); continue; case OperationASO: - carry_flag_ = operand_ >> 7; + flags_.carry = operand_ >> 7; operand_ <<= 1; a_ |= operand_; - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); continue; case OperationROL: { - const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_); - carry_flag_ = operand_ >> 7; - operand_ = negative_result_ = zero_result_ = temp8; + const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry); + flags_.carry = operand_ >> 7; + flags_.set_nz(operand_ = temp8); } continue; case OperationRLA: { - const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_); - carry_flag_ = operand_ >> 7; + const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry); + flags_.carry = operand_ >> 7; operand_ = temp8; a_ &= operand_; - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); } continue; case OperationLSR: - carry_flag_ = operand_ & 1; + flags_.carry = operand_ & 1; operand_ >>= 1; - negative_result_ = zero_result_ = operand_; + flags_.set_nz(operand_); continue; case OperationLSE: - carry_flag_ = operand_ & 1; + flags_.carry = operand_ & 1; operand_ >>= 1; a_ ^= operand_; - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); continue; case OperationASR: a_ &= operand_; - carry_flag_ = a_ & 1; + flags_.carry = a_ & 1; a_ >>= 1; - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); continue; case OperationROR: { - const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7)); - carry_flag_ = operand_ & 1; - operand_ = negative_result_ = zero_result_ = temp8; + const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7)); + flags_.carry = operand_ & 1; + flags_.set_nz(operand_ = temp8); } continue; case OperationRRA: { - const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7)); - carry_flag_ = operand_ & 1; + const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7)); + flags_.carry = operand_ & 1; operand_ = temp8; } continue; case OperationDecrementOperand: operand_--; continue; case OperationIncrementOperand: operand_++; continue; - case OperationCLC: carry_flag_ = 0; continue; - case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue; - case OperationCLV: overflow_flag_ = 0; continue; - case OperationCLD: decimal_flag_ = 0; continue; + case OperationCLC: flags_.carry = 0; continue; + case OperationCLI: flags_.inverse_interrupt = Flag::Interrupt; continue; + case OperationCLV: flags_.overflow = 0; continue; + case OperationCLD: flags_.decimal = 0; continue; - case OperationSEC: carry_flag_ = Flag::Carry; continue; - case OperationSEI: inverse_interrupt_flag_ = 0; continue; - case OperationSED: decimal_flag_ = Flag::Decimal; continue; + case OperationSEC: flags_.carry = Flag::Carry; continue; + case OperationSEI: flags_.inverse_interrupt = 0; continue; + case OperationSED: flags_.decimal = Flag::Decimal; continue; - case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue; - case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue; - case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue; - case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue; - case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue; - case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue; - case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue; - case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue; + case OperationINC: operand_++; flags_.set_nz(operand_); continue; + case OperationDEC: operand_--; flags_.set_nz(operand_); continue; + case OperationINA: a_++; flags_.set_nz(a_); continue; + case OperationDEA: a_--; flags_.set_nz(a_); continue; + case OperationINX: x_++; flags_.set_nz(x_); continue; + case OperationDEX: x_--; flags_.set_nz(x_); continue; + case OperationINY: y_++; flags_.set_nz(y_); continue; + case OperationDEY: y_--; flags_.set_nz(y_); continue; case OperationANE: a_ = (a_ | 0xee) & operand_ & x_; - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); continue; case OperationANC: a_ &= operand_; - negative_result_ = zero_result_ = a_; - carry_flag_ = a_ >> 7; + flags_.set_nz(a_); + flags_.carry = a_ >> 7; continue; case OperationLAS: a_ = x_ = s_ = s_ & operand_; - negative_result_ = zero_result_ = a_; + flags_.set_nz(a_); continue; // MARK: - Addressing Mode Work @@ -548,7 +548,7 @@ template void Proces throwaway_read(operand_); break; - case OperationIncrementPC: pc_.full++; continue; + case OperationIncrementPC: pc_.full++; continue; case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break; case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break; @@ -560,14 +560,14 @@ template void Proces scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \ } - case OperationBPL: BRA(!(negative_result_&0x80)); continue; - case OperationBMI: BRA(negative_result_&0x80); continue; - case OperationBVC: BRA(!overflow_flag_); continue; - case OperationBVS: BRA(overflow_flag_); continue; - case OperationBCC: BRA(!carry_flag_); continue; - case OperationBCS: BRA(carry_flag_); continue; - case OperationBNE: BRA(zero_result_); continue; - case OperationBEQ: BRA(!zero_result_); continue; + case OperationBPL: BRA(!(flags_.negative_result&0x80)); continue; + case OperationBMI: BRA(flags_.negative_result&0x80); continue; + case OperationBVC: BRA(!flags_.overflow); continue; + case OperationBVS: BRA(flags_.overflow); continue; + case OperationBCC: BRA(!flags_.carry); continue; + case OperationBCS: BRA(flags_.carry); continue; + case OperationBNE: BRA(flags_.zero_result); continue; + case OperationBEQ: BRA(!flags_.zero_result); continue; case OperationBRA: BRA(true); continue; #undef BRA @@ -610,31 +610,31 @@ template void Proces // MARK: - Transfers - case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue; - case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue; - case OperationTXS: s_ = x_; continue; - case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue; - case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue; - case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue; + case OperationTXA: flags_.set_nz(a_ = x_); continue; + case OperationTYA: flags_.set_nz(a_ = y_); continue; + case OperationTXS: s_ = x_; continue; + case OperationTAY: flags_.set_nz(y_ = a_); continue; + case OperationTAX: flags_.set_nz(x_ = a_); continue; + case OperationTSX: flags_.set_nz(x_ = s_); continue; case OperationARR: - if(decimal_flag_) { + if(flags_.decimal) { a_ &= operand_; uint8_t unshiftedA = a_; - a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7)); - zero_result_ = negative_result_ = a_; - overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow; + a_ = uint8_t((a_ >> 1) | (flags_.carry << 7)); + flags_.set_nz(a_); + flags_.overflow = (a_^(a_ << 1))&Flag::Overflow; if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0); - carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0; - if(carry_flag_) a_ += 0x60; + flags_.carry = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0; + if(flags_.carry) a_ += 0x60; } else { a_ &= operand_; - a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7)); - negative_result_ = zero_result_ = a_; - carry_flag_ = (a_ >> 6)&1; - overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow; + a_ = uint8_t((a_ >> 1) | (flags_.carry << 7)); + flags_.set_nz(a_); + flags_.carry = (a_ >> 6)&1; + flags_.overflow = (a_^(a_ << 1))&Flag::Overflow; } continue; @@ -642,8 +642,8 @@ template void Proces x_ &= a_; uint16_t difference = x_ - operand_; x_ = uint8_t(difference); - negative_result_ = zero_result_ = x_; - carry_flag_ = ((difference >> 8)&1)^1; + flags_.set_nz(x_); + flags_.carry = ((difference >> 8)&1)^1; continue; } @@ -691,13 +691,13 @@ void ProcessorBase::set_power_on(bool active) { } void ProcessorBase::set_irq_line(bool active) { - irq_line_ = active ? Flag::Interrupt : 0; + irq_line_ = active ? MOS6502Esque::Flag::Interrupt : 0; } void ProcessorBase::set_overflow_line(bool active) { // a leading edge will set the overflow flag if(active && !set_overflow_line_is_enabled_) - overflow_flag_ = Flag::Overflow; + flags_.overflow = MOS6502Esque::Flag::Overflow; set_overflow_line_is_enabled_ = active; } @@ -709,14 +709,9 @@ void ProcessorBase::set_nmi_line(bool active) { } uint8_t ProcessorStorage::get_flags() const { - return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_; + return flags_.get(); } void ProcessorStorage::set_flags(uint8_t flags) { - carry_flag_ = flags & Flag::Carry; - negative_result_ = flags & Flag::Sign; - zero_result_ = (~flags) & Flag::Zero; - overflow_flag_ = flags & Flag::Overflow; - inverse_interrupt_flag_ = (~flags) & Flag::Interrupt; - decimal_flag_ = flags & Flag::Decimal; + flags_.set(flags); } diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index e87d6140a..8795e2e74 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -76,12 +76,6 @@ using namespace CPU::MOS6502; #define JAM {CycleFetchOperand, OperationScheduleJam} ProcessorStorage::ProcessorStorage(Personality personality) { - // only the interrupt flag is defined upon reset but get_flags isn't going to - // mask the other flags so we need to do that, at least - carry_flag_ &= Flag::Carry; - decimal_flag_ &= Flag::Decimal; - overflow_flag_ &= Flag::Overflow; - const InstructionList operations_6502[] = { /* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh), /* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA), diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index a5ae557ee..d4dfecdf1 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -228,7 +228,7 @@ class ProcessorStorage { */ RegisterPair16 pc_, last_operation_pc_; uint8_t a_, x_, y_, s_ = 0; - uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0; + MOS6502Esque::LazyFlags flags_; /* Temporary state for the micro programs. @@ -267,7 +267,7 @@ class ProcessorStorage { enum InterruptRequestFlags: uint8_t { Reset = 0x80, - IRQ = Flag::Interrupt, + IRQ = MOS6502Esque::Flag::Interrupt, NMI = 0x20, PowerOn = 0x10, diff --git a/Processors/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp similarity index 89% rename from Processors/6502Esque.hpp rename to Processors/6502Esque/6502Esque.hpp index c124b390d..38749b00e 100644 --- a/Processors/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -9,6 +9,8 @@ #ifndef m6502Esque_h #define m6502Esque_h +#include "../../ClockReceiver/ClockReceiver.hpp" + /* This file defines how the CPU-controlled part of a bus looks for the 6502 and for other processors with a sufficiently-similar bus. @@ -35,6 +37,21 @@ enum Register { Y }; +/* + Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for + the corresponding set. +*/ +enum Flag: uint8_t { + Sign = 0x80, + Overflow = 0x40, + Always = 0x20, + Break = 0x10, + Decimal = 0x08, + Interrupt = 0x04, + Zero = 0x02, + Carry = 0x01 +}; + /*! Bus handlers will be given the task of performing bus operations, allowing them to provide whatever interface they like between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested. diff --git a/Processors/6502Selector.hpp b/Processors/6502Esque/6502Selector.hpp similarity index 96% rename from Processors/6502Selector.hpp rename to Processors/6502Esque/6502Selector.hpp index 6045d6a2e..411bba40f 100644 --- a/Processors/6502Selector.hpp +++ b/Processors/6502Esque/6502Selector.hpp @@ -9,8 +9,8 @@ #ifndef _502Selector_h #define _502Selector_h -#include "6502/6502.hpp" -#include "65816/65816.hpp" +#include "../6502/6502.hpp" +#include "../65816/65816.hpp" namespace CPU { namespace MOS6502Esque { diff --git a/Processors/6502Esque/Implementation/LazyFlags.hpp b/Processors/6502Esque/Implementation/LazyFlags.hpp new file mode 100644 index 000000000..5f091082c --- /dev/null +++ b/Processors/6502Esque/Implementation/LazyFlags.hpp @@ -0,0 +1,66 @@ +// +// LazyFlags.hpp +// Clock Signal +// +// Created by Thomas Harte on 05/10/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef LazyFlags_h +#define LazyFlags_h + +#include "../6502Esque.hpp" + +namespace CPU { +namespace MOS6502Esque { + +struct LazyFlags { + /// Bit 7 is set if the negative flag is set; otherwise it is clear. + uint8_t negative_result; + + /// Non-zero if the zero flag is clear, zero if it is set. + uint8_t zero_result; + + /// Contains Flag::Carry. + uint8_t carry; + + /// Contains Flag::Decimal. + uint8_t decimal; + + /// Contains Flag::Overflow. + uint8_t overflow; + + /// Contains Flag::Interrupt, complemented. + uint8_t inverse_interrupt = 0; + + void set_nz(uint8_t value) { + zero_result = negative_result = value; + } + + void set(uint8_t flags) { + carry = flags & Flag::Carry; + negative_result = flags & Flag::Sign; + zero_result = (~flags) & Flag::Zero; + overflow = flags & Flag::Overflow; + inverse_interrupt = (~flags) & Flag::Interrupt; + decimal = flags & Flag::Decimal; + } + + uint8_t get() const { + return carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | decimal; + } + + LazyFlags() { + // Only the interrupt flag is defined upon reset but get_flags isn't going to + // mask the other flags so we need to do that, at least. + carry &= Flag::Carry; + decimal &= Flag::Decimal; + overflow &= Flag::Overflow; + } +}; + + +} +} + +#endif /* LazyFlags_h */ diff --git a/Processors/6502Esque/README.md b/Processors/6502Esque/README.md new file mode 100644 index 000000000..e2e0ddee4 --- /dev/null +++ b/Processors/6502Esque/README.md @@ -0,0 +1,3 @@ +# 6502Esque + +This folder contains common code for CPUs for a 6502-esque bus interface; it also contains a special template, the 6502Selector, which allows a consumer to select between the 6502-esque chips by enum. diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index a08fd59c6..0d73c0a04 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -16,7 +16,7 @@ #include "../RegisterSizes.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" -#include "../6502Esque.hpp" +#include "../6502Esque/6502Esque.hpp" namespace CPU { namespace WDC65816 { diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 50844b09c..79e0037bf 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -372,8 +372,10 @@ template void Processor::run_for(const Cycles } break; -// default: -// assert(false); + // TODO: OperationCopyPBRToData, OperationPrepareException + + default: + assert(false); } number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); From 7be983ec00c344eb572cd273cf09d84ebd7deb61 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Oct 2020 22:25:20 -0400 Subject: [PATCH 051/150] Slightly improve exposition. --- Processors/6502Esque/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Processors/6502Esque/README.md b/Processors/6502Esque/README.md index e2e0ddee4..dfb38ccb4 100644 --- a/Processors/6502Esque/README.md +++ b/Processors/6502Esque/README.md @@ -1,3 +1,5 @@ # 6502Esque This folder contains common code for CPUs for a 6502-esque bus interface; it also contains a special template, the 6502Selector, which allows a consumer to select between the 6502-esque chips by enum. + +If you know exactly which processor you want, feel free to ignore this folder entirely; just go straight to the 6502, 65816 or whatever. From 993eff1d3de6515827974ba03e9ae8af56648b26 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 16:25:30 -0400 Subject: [PATCH 052/150] Starts slowly, with flag manipulation. --- Processors/65816/65816.hpp | 2 + .../Implementation/65816Implementation.hpp | 121 +++++++++++++----- .../65816/Implementation/65816Storage.hpp | 4 +- 3 files changed, 93 insertions(+), 34 deletions(-) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index 0d73c0a04..b9e5cfbc1 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -17,12 +17,14 @@ #include "../RegisterSizes.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" #include "../6502Esque/6502Esque.hpp" +#include "../6502Esque/Implementation/LazyFlags.hpp" namespace CPU { namespace WDC65816 { using BusOperation = CPU::MOS6502Esque::BusOperation; using Register = CPU::MOS6502Esque::Register; +using Flag = CPU::MOS6502Esque::Flag; #include "Implementation/65816Storage.hpp" diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 79e0037bf..268d02574 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -21,6 +21,9 @@ template void Processor::run_for(const Cycles #define read(address, value) perform_bus(address, value, MOS6502Esque::Read) #define write(address, value) perform_bus(address, value, MOS6502Esque::Write) +#define m_flag() mx_flags_[0] +#define x_flag() mx_flags_[1] + #define x() (x_.full & x_masks_[1]) #define y() (y_.full & x_masks_[1]) @@ -160,29 +163,29 @@ template void Processor::run_for(const Cycles case OperationCopyPCToData: data_buffer_.size = 2; data_buffer_.value = pc_; - break; + continue; case OperationCopyInstructionToData: data_buffer_ = instruction_buffer_; - break; + continue; case OperationCopyAToData: - if(mx_flags_[0]) { + if(m_flag()) { data_buffer_.size = 1; data_buffer_.value = a_.halves.high; } else { data_buffer_.size = 2; data_buffer_.value = a_.full; } - break; + continue; case OperationCopyDataToA: - if(mx_flags_[0]) { + if(m_flag()) { a_.halves.high = data_buffer_.value; } else { a_.full = data_buffer_.value; } - break; + continue; // // Address construction. @@ -194,11 +197,11 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteIndexedIndirect: data_address_ = (instruction_buffer_.value + x()) & 0xffff; - break; + continue; case OperationConstructAbsoluteLongX: data_address_ = (instruction_buffer_.value + x()) & 0xffff + instruction_buffer_.value & 0xff0000; - break; + continue; case OperationConstructAbsoluteXRead: case OperationConstructAbsoluteX: @@ -209,7 +212,7 @@ template void Processor::run_for(const Cycles if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { ++next_op_; } - break; + continue; case OperationConstructAbsoluteYRead: case OperationConstructAbsoluteY: @@ -220,38 +223,38 @@ template void Processor::run_for(const Cycles if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) { ++next_op_; } - break; + continue; case OperationConstructDirect: data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; if(!(direct_&0xff)) { ++next_op_; } - break; + continue; case OperationConstructDirectIndexedIndirect: data_address_ = data_bank_ + (direct_ + x() + instruction_buffer_.value) & 0xffff; if(!(direct_&0xff)) { ++next_op_; } - break; + continue; case OperationConstructDirectIndirect: data_address_ = data_bank_ + (direct_ + instruction_buffer_.value) & 0xffff; if(!(direct_&0xff)) { ++next_op_; } - break; + continue; case OperationConstructDirectIndirectIndexedLong: // TODO: assumed here is that the low 16-bit calculation can't carry into // the high byte. Test this! data_address_ = (y() + instruction_buffer_.value) & 0xffff + instruction_buffer_.value & 0xff0000; - break; + continue; case OperationConstructDirectIndirectLong: data_address_ = instruction_buffer_.value; - break; + continue; case OperationConstructDirectX: data_address_ = (direct_ + x()) & 0xffff; @@ -259,7 +262,7 @@ template void Processor::run_for(const Cycles if(!(direct_&0xff)) { ++next_op_; } - break; + continue; case OperationConstructDirectY: data_address_ = (direct_ + y()) & 0xffff; @@ -267,41 +270,37 @@ template void Processor::run_for(const Cycles if(!(direct_&0xff)) { ++next_op_; } - break; + continue; case OperationConstructPER: data_buffer_.value = instruction_buffer_.value + pc_; data_buffer_.size = 2; - break; + continue; case OperationConstructStackRelative: data_address_ = (s_.full + instruction_buffer_.value) & 0xffff; - break; + continue; case OperationConstructStackRelativeIndexedIndirect: data_address_ = data_bank_ + (instruction_buffer_.value + y()) & 0xffff; - break; + continue; // // Performance. // +#define LD(dest, src, masks) dest.full = (dest.full & masks[0]) | (src & masks[1]) +#define m_top() (instruction_buffer_.value >> m_shift_) & 0xff +#define x_top() (x_.full >> x_shift_) & 0xff +#define y_top() (y_.full >> x_shift_) & 0xff + case OperationPerform: switch(active_instruction_->operation) { - // - // Flag manipulation. - // - - case CLD: - decimal_flag_ = 0; - break; - // // Loads, stores and transfers // -#define LD(dest, src, masks) dest.full = (dest.full & masks[0]) | (src & masks[1]) case LDA: LD(a_, data_buffer_.value, m_masks_); @@ -320,11 +319,9 @@ template void Processor::run_for(const Cycles LD(s_, x_.full, x_masks_); break; -#undef LD - case STA: data_buffer_.value = a_.full & m_masks_[1]; - data_buffer_.size = 2 - mx_flags_[0]; + data_buffer_.size = 2 - m_flag(); break; // @@ -367,10 +364,61 @@ template void Processor::run_for(const Cycles if(a_.full) pc_ -= 3; break; + // + // Flag manipulation. + // + + case CLC: flags_.carry = 0; break; + case CLI: flags_.inverse_interrupt = Flag::Interrupt; break; + case CLV: flags_.overflow = 0; break; + case CLD: flags_.decimal = 0; break; + + case SEC: flags_.carry = Flag::Carry; break; + case SEI: flags_.inverse_interrupt = 0; break; + case SED: flags_.decimal = Flag::Decimal; break; + + // + // Increments and decrements. + // + + case INC: + ++instruction_buffer_.value; + flags_.set_nz(m_top()); + break;; + + case DEC: + --instruction_buffer_.value; + flags_.set_nz(m_top()); + break; + + case INX: { + const uint16_t x_inc = x_.full + 1; + LD(x_, x_inc, x_masks_); + flags_.set_nz(x_top()); + } break; + + case DEX: { + const uint16_t x_dec = x_.full - 1; + LD(x_, x_dec, x_masks_); + flags_.set_nz(x_top()); + } break; + + case INY: { + const uint16_t y_inc = y_.full + 1; + LD(y_, y_inc, x_masks_); + flags_.set_nz(y_top()); + } break; + + case DEY: { + const uint16_t y_dec = y_.full - 1; + LD(y_, y_dec, x_masks_); + flags_.set_nz(y_top()); + } break; + default: assert(false); } - break; + continue; // TODO: OperationCopyPBRToData, OperationPrepareException @@ -378,6 +426,11 @@ template void Processor::run_for(const Cycles assert(false); } +#undef LD +#undef m_top +#undef x_top +#undef y_top + number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); } @@ -386,6 +439,8 @@ template void Processor::run_for(const Cycles #undef bus_operation #undef x #undef y +#undef m_flag +#undef x_flag cycles_left_to_run_ = number_of_cycles; } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index b0e12ccdd..5408e3ee9 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -228,10 +228,12 @@ struct ProcessorStorage { Cycles cycles_left_to_run_; // Flags aplenty. - uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0; + MOS6502Esque::LazyFlags flags_; uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + int m_shift_ = 0; + int x_shift_ = 0; bool emulation_flag_ = true; // I.e. the offset for direct addressing (outside of emulation mode). From 9ce9167e3c46a54d3e68b7ca7f10dc36e9f98295 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 19:12:19 -0400 Subject: [PATCH 053/150] Formalises work left to do. --- .../Implementation/65816Implementation.hpp | 45 ++++++++++++++++++- .../65816/Implementation/65816Storage.hpp | 4 +- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 268d02574..24a7ed01e 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -297,11 +297,13 @@ template void Processor::run_for(const Cycles case OperationPerform: switch(active_instruction_->operation) { + case NOP: + break; + // // Loads, stores and transfers // - case LDA: LD(a_, data_buffer_.value, m_masks_); break; @@ -314,6 +316,14 @@ template void Processor::run_for(const Cycles LD(y_, data_buffer_.value, x_masks_); break; + case PLB: + a_.halves.high = instruction_buffer_.value; + break; + + case PLD: + direct_ = ((instruction_buffer_.value) & 0xff) << 16; + break; + case TXS: // TODO: does this transfer in full when in 8-bit index mode? LD(s_, x_.full, x_masks_); @@ -324,12 +334,30 @@ template void Processor::run_for(const Cycles data_buffer_.size = 2 - m_flag(); break; + case STZ: + data_buffer_.value = 0; + data_buffer_.size = 2 - m_flag(); + break; + + case STX: + data_buffer_.value = x_.full & x_masks_[1]; + data_buffer_.size = 2 - x_flag(); + break; + + case STY: + data_buffer_.value = y_.full & x_masks_[1]; + data_buffer_.size = 2 - m_flag(); + break; + // // Jumps. // case JML: program_bank_ = instruction_buffer_.value & 0xff0000; + [[fallthrough]]; + + case JMP: pc_ = instruction_buffer_.value & 0xffff; break; @@ -415,6 +443,21 @@ template void Processor::run_for(const Cycles flags_.set_nz(y_top()); } break; + + + // TODO: + // ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, + // PLP, + // PHB, PHP, PHD, PHK, + // ASL, LSR, ROL, ROR, TRB, TSB, + // REP, SEP, + // BCC, BCS, BEQ, BMI, BNE, BPL, BRA, BVC, BVS, BRL, + // TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, + // XCE, XBA, + // STP, WAI, + // RTI, RTL, + // BRK, + default: assert(false); } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 5408e3ee9..63344665c 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -149,7 +149,7 @@ enum Operation: uint8_t { PHB, PHP, PHD, PHK, // These modify the value in the data buffer as part of a read-modify-write. - ASL, DEC, INC, LSR, ROL, ROR, TRB, TSB, + INC, DEC, ASL, LSR, ROL, ROR, TRB, TSB, // These merely decrement A, increment or decrement X and Y, and regress // the program counter only if appropriate. @@ -186,7 +186,7 @@ enum Operation: uint8_t { JSL, /// i.e. jump to vector. TODO: is this really distinct from JMP? I'm assuming so for now, - /// as I assume the PBR is implicitly modified. We'll see. + /// as I assume the PBR is implicitly modified. But then is it just JML? We'll see. BRK, }; From ebff83018eb8b39b6ea0a84c6e7564282f4eda9f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 20:17:03 -0400 Subject: [PATCH 054/150] Implements the bitwise operators. --- .../Implementation/65816Implementation.hpp | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 24a7ed01e..fb81cf516 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -293,6 +293,7 @@ template void Processor::run_for(const Cycles #define m_top() (instruction_buffer_.value >> m_shift_) & 0xff #define x_top() (x_.full >> x_shift_) & 0xff #define y_top() (y_.full >> x_shift_) & 0xff +#define a_top() (a_.full >> m_shift_) & 0xff case OperationPerform: switch(active_instruction_->operation) { @@ -443,10 +444,28 @@ template void Processor::run_for(const Cycles flags_.set_nz(y_top()); } break; + // + // Bitwise operations. + // + + case AND: + a_.full &= instruction_buffer_.value | m_masks_[0]; + flags_.set_nz(a_top()); + break; + + case EOR: + a_.full ^= instruction_buffer_.value; + flags_.set_nz(a_top()); + break; + + case ORA: + a_.full |= instruction_buffer_.value; + flags_.set_nz(a_top()); + break; // TODO: - // ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, + // ADC, BIT, CMP, CPX, CPY, SBC, // PLP, // PHB, PHP, PHD, PHK, // ASL, LSR, ROL, ROR, TRB, TSB, @@ -473,6 +492,7 @@ template void Processor::run_for(const Cycles #undef m_top #undef x_top #undef y_top +#undef a_top number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); } From 7065ba4857b64abbc3793536e54aa9d5f0256a10 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 21:24:43 -0400 Subject: [PATCH 055/150] Implements the single-byte branches. --- .../Implementation/65816Implementation.hpp | 40 ++++++++++++++++++- .../65816/Implementation/65816Storage.cpp | 4 +- .../65816/Implementation/65816Storage.hpp | 1 + 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index fb81cf516..63f52288f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -187,6 +187,15 @@ template void Processor::run_for(const Cycles } continue; + case OperationCopyPBRToData: + data_buffer_.size = 1; + data_buffer_.value = program_bank_ >> 16; + continue; + + case OperationCopyDataToPC: + pc_ = uint16_t(data_buffer_.value); + continue; + // // Address construction. // @@ -463,6 +472,33 @@ template void Processor::run_for(const Cycles flags_.set_nz(a_top()); break; + // + // Branches. + // + +#define BRA(condition) \ + if(!(condition)) { \ + next_op_ += 3; \ + } else { \ + data_buffer_.size = 2; \ + data_buffer_.value = pc_ + int8_t(data_buffer_.value); \ + \ + if((pc_ & 0xff00) == (data_buffer_.value & 0xff00)) { \ + ++next_op_; \ + } \ + } + + case BPL: BRA(!(flags_.negative_result&0x80)); break; + case BMI: BRA(flags_.negative_result&0x80); break; + case BVC: BRA(!flags_.overflow); break; + case BVS: BRA(flags_.overflow); break; + case BCC: BRA(!flags_.carry); break; + case BCS: BRA(flags_.carry); break; + case BNE: BRA(flags_.zero_result); break; + case BEQ: BRA(!flags_.zero_result); break; + case BRA: BRA(true); break; + +#undef BRA // TODO: // ADC, BIT, CMP, CPX, CPY, SBC, @@ -470,7 +506,7 @@ template void Processor::run_for(const Cycles // PHB, PHP, PHD, PHK, // ASL, LSR, ROL, ROR, TRB, TSB, // REP, SEP, - // BCC, BCS, BEQ, BMI, BNE, BPL, BRA, BVC, BVS, BRL, + // BRL, // TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, // XCE, XBA, // STP, WAI, @@ -482,7 +518,7 @@ template void Processor::run_for(const Cycles } continue; - // TODO: OperationCopyPBRToData, OperationPrepareException + // TODO: OperationPrepareException default: assert(false); diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 2439f2189..23d3aee9f 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -549,12 +549,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void relative(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // Offset - target(OperationPerform); // The branch instructions will all skip one or two + target(OperationPerform); // The branch instructions will all skip one or three // of the next cycles, depending on the effect of // the jump. target(CycleFetchPC); // IO target(CycleFetchPC); // IO + + target(OperationCopyDataToPC); // Install the address that was calculated above. } // 21. Relative long; rl. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 63344665c..c3b13de60 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -111,6 +111,7 @@ enum MicroOp: uint8_t { /// Copies the current program counter to the data buffer. OperationCopyPCToData, OperationCopyInstructionToData, + OperationCopyDataToPC, /// Copies the current PBR to the data buffer. OperationCopyPBRToData, From 3933bf49cfdf5e6f20679773b86ad21e90889b6d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 21:28:54 -0400 Subject: [PATCH 056/150] Implements BRL. --- .../Implementation/65816Implementation.hpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 63f52288f..5707812c9 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -306,12 +306,8 @@ template void Processor::run_for(const Cycles case OperationPerform: switch(active_instruction_->operation) { - - case NOP: - break; - // - // Loads, stores and transfers + // Loads, stores and transfers (and NOP). // case LDA: @@ -359,6 +355,13 @@ template void Processor::run_for(const Cycles data_buffer_.size = 2 - m_flag(); break; + case PHB: + data_buffer_.value = a_.halves.high; + data_buffer_.size = 1; + break; + + case NOP: break; + // // Jumps. // @@ -500,13 +503,16 @@ template void Processor::run_for(const Cycles #undef BRA + case BRL: + pc_ += int16_t(instruction_buffer_.value); + break; + // TODO: // ADC, BIT, CMP, CPX, CPY, SBC, // PLP, - // PHB, PHP, PHD, PHK, + // PHP, PHD, PHK, // ASL, LSR, ROL, ROR, TRB, TSB, // REP, SEP, - // BRL, // TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, // XCE, XBA, // STP, WAI, From 5c809e5fbf93aeefeaaa90bc37add4eff7c9344c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 21:34:39 -0400 Subject: [PATCH 057/150] Implements rolls and shifts. --- .../Implementation/65816Implementation.hpp | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 5707812c9..dc4c0169f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -507,11 +507,40 @@ template void Processor::run_for(const Cycles pc_ += int16_t(instruction_buffer_.value); break; + // + // Shifts and rolls. + // + + case ASL: + flags_.carry = data_buffer_.value >> (7 + m_shift_); + data_buffer_.value <<= 1; + flags_.set_nz(m_top()); + break; + + case LSR: + flags_.carry = data_buffer_.value & 1; + data_buffer_.value >>= 1; + flags_.set_nz(m_top()); + break; + + case ROL: + data_buffer_.value = (data_buffer_.value << 1) | flags_.carry; + flags_.carry = data_buffer_.value >> (7 + m_shift_); + flags_.set_nz(m_top()); + break; + + case ROR: { + const uint8_t next_carry = data_buffer_.value & 1; + data_buffer_.value = (data_buffer_.value >> 1) | (flags_.carry << (7 + m_shift_)); + flags_.carry = next_carry; + flags_.set_nz(m_top()); + } break; + // TODO: // ADC, BIT, CMP, CPX, CPY, SBC, // PLP, // PHP, PHD, PHK, - // ASL, LSR, ROL, ROR, TRB, TSB, + // TRB, TSB, // REP, SEP, // TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, // XCE, XBA, From e068cbc103d51ec1e2f0d593fa919809834f5942 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 21:47:26 -0400 Subject: [PATCH 058/150] Implements CMP and fixes a zero-flag error on 16-bit operations. --- .../6502Esque/Implementation/LazyFlags.hpp | 5 +++ .../Implementation/65816Implementation.hpp | 44 +++++++++++++------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Processors/6502Esque/Implementation/LazyFlags.hpp b/Processors/6502Esque/Implementation/LazyFlags.hpp index 5f091082c..47904a992 100644 --- a/Processors/6502Esque/Implementation/LazyFlags.hpp +++ b/Processors/6502Esque/Implementation/LazyFlags.hpp @@ -37,6 +37,11 @@ struct LazyFlags { zero_result = negative_result = value; } + void set_nz(uint16_t value, int shift) { + negative_result = uint8_t(value >> shift); + zero_result = uint8_t(value | (value >> shift)); + } + void set(uint8_t flags) { carry = flags & Flag::Carry; negative_result = flags & Flag::Sign; diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index dc4c0169f..f181cb267 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -424,36 +424,36 @@ template void Processor::run_for(const Cycles case INC: ++instruction_buffer_.value; - flags_.set_nz(m_top()); + flags_.set_nz(instruction_buffer_.value, m_shift_); break;; case DEC: --instruction_buffer_.value; - flags_.set_nz(m_top()); + flags_.set_nz(instruction_buffer_.value, m_shift_); break; case INX: { const uint16_t x_inc = x_.full + 1; LD(x_, x_inc, x_masks_); - flags_.set_nz(x_top()); + flags_.set_nz(x_.full, x_shift_); } break; case DEX: { const uint16_t x_dec = x_.full - 1; LD(x_, x_dec, x_masks_); - flags_.set_nz(x_top()); + flags_.set_nz(x_.full, x_shift_); } break; case INY: { const uint16_t y_inc = y_.full + 1; LD(y_, y_inc, x_masks_); - flags_.set_nz(y_top()); + flags_.set_nz(y_.full, x_shift_); } break; case DEY: { const uint16_t y_dec = y_.full - 1; LD(y_, y_dec, x_masks_); - flags_.set_nz(y_top()); + flags_.set_nz(y_.full, x_shift_); } break; // @@ -462,17 +462,17 @@ template void Processor::run_for(const Cycles case AND: a_.full &= instruction_buffer_.value | m_masks_[0]; - flags_.set_nz(a_top()); + flags_.set_nz(a_.full, m_shift_); break; case EOR: a_.full ^= instruction_buffer_.value; - flags_.set_nz(a_top()); + flags_.set_nz(a_.full, m_shift_); break; case ORA: a_.full |= instruction_buffer_.value; - flags_.set_nz(a_top()); + flags_.set_nz(a_.full, m_shift_); break; // @@ -514,30 +514,46 @@ template void Processor::run_for(const Cycles case ASL: flags_.carry = data_buffer_.value >> (7 + m_shift_); data_buffer_.value <<= 1; - flags_.set_nz(m_top()); + flags_.set_nz(instruction_buffer_.value, m_shift_); break; case LSR: flags_.carry = data_buffer_.value & 1; data_buffer_.value >>= 1; - flags_.set_nz(m_top()); + flags_.set_nz(instruction_buffer_.value, m_shift_); break; case ROL: data_buffer_.value = (data_buffer_.value << 1) | flags_.carry; flags_.carry = data_buffer_.value >> (7 + m_shift_); - flags_.set_nz(m_top()); + flags_.set_nz(instruction_buffer_.value, m_shift_); break; case ROR: { const uint8_t next_carry = data_buffer_.value & 1; data_buffer_.value = (data_buffer_.value >> 1) | (flags_.carry << (7 + m_shift_)); flags_.carry = next_carry; - flags_.set_nz(m_top()); + flags_.set_nz(instruction_buffer_.value, m_shift_); + } break; + + // + // Arithmetic. + // + + case CMP: { + if(m_flag()) { + const uint16_t temp16 = a_.halves.low - data_buffer_.value; + flags_.set_nz(uint8_t(temp16)); + flags_.carry = ((~temp16) >> 8)&1; + } else { + const uint32_t temp32 = a_.full - data_buffer_.value; + flags_.set_nz(uint16_t(temp32), 8); + flags_.carry = ((~temp32) >> 16)&1; + } } break; // TODO: - // ADC, BIT, CMP, CPX, CPY, SBC, + // ADC, BIT, CPX, CPY, SBC, // PLP, // PHP, PHD, PHK, // TRB, TSB, From 93b08390362b17f59d0316fe1a504380f42f9d66 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Oct 2020 22:29:34 -0400 Subject: [PATCH 059/150] Knocks out some transfer operations. I'm possibly only seven or eight away from being able to test with complete official-opcode-only 6502 code? --- .../Implementation/65816Implementation.hpp | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index f181cb267..85f997bad 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -51,7 +51,7 @@ template void Processor::run_for(const Cycles case OperationDecode: { // A VERY TEMPORARY piece of logging. - printf("[%04x] %02x\n", pc_ - 1, instruction_buffer_.value); + printf("[%04x] %02x\n", pc_ - 2, instruction_buffer_.value); // pc_ - 1 would be correct but this matches a log I made of the 6502. active_instruction_ = &instructions[instruction_buffer_.value]; const auto size_flag = mx_flags_[active_instruction_->size_field]; @@ -306,6 +306,7 @@ template void Processor::run_for(const Cycles case OperationPerform: switch(active_instruction_->operation) { + // // Loads, stores and transfers (and NOP). // @@ -330,11 +331,43 @@ template void Processor::run_for(const Cycles direct_ = ((instruction_buffer_.value) & 0xff) << 16; break; + + // The below attempts to obey the 8/16-bit mixed transfer rules + // as documented in https://softpixel.com/~cwright/sianse/docs/65816NFO.HTM + case TXS: - // TODO: does this transfer in full when in 8-bit index mode? - LD(s_, x_.full, x_masks_); + s_ = x_.full & x_masks_[1]; break; + case TSX: + LD(x_, s_.full, x_masks_); + break; + + case TXY: + LD(x_, y_.full, x_masks_); + break; + + case TYX: + LD(y_, x_.full, x_masks_); + break; + + case TAX: + LD(x_, a_.full, x_masks_); + break; + + case TAY: + LD(x_, a_.full, x_masks_); + break; + + case TXA: + LD(a_, x_.full, m_masks_); + break; + + case TYA: + LD(a_, y_.full, m_masks_); + break; + + case STA: data_buffer_.value = a_.full & m_masks_[1]; data_buffer_.size = 2 - m_flag(); @@ -558,11 +591,11 @@ template void Processor::run_for(const Cycles // PHP, PHD, PHK, // TRB, TSB, // REP, SEP, - // TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX, // XCE, XBA, // STP, WAI, // RTI, RTL, // BRK, + // TCD, TCS, TDC, TSC default: assert(false); From 466ca38dfa1b5a3147643fa30aabfa2aeedf79bf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 18:05:42 -0400 Subject: [PATCH 060/150] Corrects TXY and TYX; kudos to PatrickvL for the spot! --- 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 85f997bad..6f7415c4c 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -344,11 +344,11 @@ template void Processor::run_for(const Cycles break; case TXY: - LD(x_, y_.full, x_masks_); + LD(y_, x_.full, x_masks_); break; case TYX: - LD(y_, x_.full, x_masks_); + LD(x_, y_.full, x_masks_); break; case TAX: From 5ca1c0747fbb884075940cf28c51c9dcd7181be4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 18:09:56 -0400 Subject: [PATCH 061/150] Generalises CMP to implement CPX and CPY. --- .../Implementation/65816Implementation.hpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 6f7415c4c..e560877ec 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -573,20 +573,20 @@ template void Processor::run_for(const Cycles // Arithmetic. // - case CMP: { - if(m_flag()) { - const uint16_t temp16 = a_.halves.low - data_buffer_.value; - flags_.set_nz(uint8_t(temp16)); - flags_.carry = ((~temp16) >> 8)&1; - } else { - const uint32_t temp32 = a_.full - data_buffer_.value; - flags_.set_nz(uint16_t(temp32), 8); - flags_.carry = ((~temp32) >> 16)&1; - } - } break; +#define cp(v, shift) {\ + const uint32_t temp32 = v.full - data_buffer_.value; \ + flags_.set_nz(uint16_t(temp32), shift); \ + flags_.carry = ((~temp32) >> (8 + shift))&1; \ +} + + case CMP: cp(a_, m_shift_); break; + case CPX: cp(x_, x_shift_); break; + case CPY: cp(y_, x_shift_); break; + +#undef cp // TODO: - // ADC, BIT, CPX, CPY, SBC, + // ADC, BIT, SBC, // PLP, // PHP, PHD, PHK, // TRB, TSB, From 7439a326a605d5993e1e31fb184ac899ec45a56a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 18:15:18 -0400 Subject: [PATCH 062/150] Implements BIT (in regular and immediate forms). --- Processors/6502Esque/Implementation/LazyFlags.hpp | 7 +++++++ .../65816/Implementation/65816Implementation.hpp | 11 ++++++++++- Processors/65816/Implementation/65816Storage.cpp | 2 +- Processors/65816/Implementation/65816Storage.hpp | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Processors/6502Esque/Implementation/LazyFlags.hpp b/Processors/6502Esque/Implementation/LazyFlags.hpp index 47904a992..5f330ff0f 100644 --- a/Processors/6502Esque/Implementation/LazyFlags.hpp +++ b/Processors/6502Esque/Implementation/LazyFlags.hpp @@ -33,15 +33,22 @@ struct LazyFlags { /// Contains Flag::Interrupt, complemented. uint8_t inverse_interrupt = 0; + /// Sets N and Z flags per the 8-bit value @c value. void set_nz(uint8_t value) { zero_result = negative_result = value; } + /// Sets N and Z flags per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value. void set_nz(uint16_t value, int shift) { negative_result = uint8_t(value >> shift); zero_result = uint8_t(value | (value >> shift)); } + /// Sets the Z flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value. + void set_z(uint16_t value, int shift) { + zero_result = uint8_t(value | (value >> shift)); + } + void set(uint8_t flags) { carry = flags & Flag::Carry; negative_result = flags & Flag::Sign; diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index e560877ec..7ed69af32 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -508,6 +508,15 @@ template void Processor::run_for(const Cycles flags_.set_nz(a_.full, m_shift_); break; + case BIT: + flags_.set_nz(data_buffer_.value & a_.full, m_shift_); + flags_.overflow = data_buffer_.value & Flag::Overflow; + break; + + case BITimm: + flags_.set_z(data_buffer_.value & a_.full, m_shift_); + break; + // // Branches. // @@ -586,7 +595,7 @@ template void Processor::run_for(const Cycles #undef cp // TODO: - // ADC, BIT, SBC, + // ADC, SBC, // PLP, // PHP, PHD, PHK, // TRB, TSB, diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 23d3aee9f..e20e162e2 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -857,7 +857,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x86 STX d */ op(direct, STX); /* 0x87 STA [d] */ op(direct_indirect_long, STA); /* 0x88 DEY i */ op(implied, DEY); - /* 0x89 BIT # */ op(immediate, BIT); + /* 0x89 BIT # */ op(immediate, BITimm); /* 0x8a TXA i */ op(implied, TXA); /* 0x8b PHB s */ op(stack_push, PHB); /* 0x8c STY a */ op(absolute, STY); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index c3b13de60..335ccb3dc 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -137,7 +137,7 @@ enum MicroOp: uint8_t { enum Operation: uint8_t { // These perform the named operation using the value in the data buffer; // they are implicitly AccessType::Read. - ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, + ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, BITimm, // These load the respective register from the data buffer; // they are implicitly AccessType::Read. From eac722cf59302e781b03f2a7add86317250eac76 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 18:36:17 -0400 Subject: [PATCH 063/150] Implements enough of ADC and SBC for the Dormann test definitively to fail. --- .../Implementation/65816Implementation.hpp | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 7ed69af32..35a559e23 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -594,8 +594,45 @@ template void Processor::run_for(const Cycles #undef cp + case SBC: + if(flags_.decimal) { + assert(false); + break; + } + + // Implement non-decimal SBC by falling through to ADC; + // TODO: what do I need to invert to be able to fall through in both cases? And does it matter? + data_buffer_.value = ~data_buffer_.value; + [[fallthrough]]; + + case ADC: { + int result; + + if(flags_.decimal) { + result = flags_.carry; + +#define nibble(mask, limit, addition, carry) \ + result += (a_.full & mask) + (data_buffer_.value & mask); \ + if(result >= limit) result = ((result + addition) & (carry - 1)) + carry; + + nibble(0x000f, 0x000a, 0x0006, 0x00010); + nibble(0x00f0, 0x00a0, 0x0060, 0x00100); + nibble(0x0f00, 0x0a00, 0x0600, 0x01000); + nibble(0xf000, 0xa000, 0x6000, 0x10000); + +#undef nibble + + } else { + result = a_.full + data_buffer_.value + flags_.carry; + } + + flags_.overflow = (( (result ^ a_.full) & (result ^ data_buffer_.value) ) >> (1 + m_shift_))&0x40; + flags_.set_nz(result, m_shift_); + flags_.carry = (result >> (8 + m_shift_))&1; + LD(a_, result, m_masks_); + } break; + // TODO: - // ADC, SBC, // PLP, // PHP, PHD, PHK, // TRB, TSB, From 84c4fa197b18d8ffebb16240a72252400f791ed6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 18:48:03 -0400 Subject: [PATCH 064/150] Corrects DEX mapping, notes new Dormann failure case. --- OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift | 1 + Processors/65816/Implementation/65816Implementation.hpp | 2 +- Processors/65816/Implementation/65816Storage.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index 94b45a3c4..497104d4c 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -44,6 +44,7 @@ class KlausDormannTests: XCTestCase { switch address { case 0x3399: return nil // success! + case 0x052a: return "DEX did not correctly set zero flag" case 0x33a7: return "Decimal ADC result has wrong value" case 0x3502: return "Binary SBC result has wrong value" case 0x33b9: return "Decimal SBC result has wrong value" diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 35a559e23..4c23bdb2e 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -51,7 +51,7 @@ template void Processor::run_for(const Cycles case OperationDecode: { // A VERY TEMPORARY piece of logging. - printf("[%04x] %02x\n", pc_ - 2, instruction_buffer_.value); // pc_ - 1 would be correct but this matches a log I made of the 6502. + printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x\n", pc_ - 1, instruction_buffer_.value, a_.full, x_.full, y_.full, flags_.get()); // pc_ - 1 would be correct but this matches a log I made of the 6502. active_instruction_ = &instructions[instruction_buffer_.value]; const auto size_flag = mx_flags_[active_instruction_->size_field]; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index e20e162e2..58dc50a03 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -926,7 +926,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xc7 CMP [d] */ op(direct_indirect_long, CMP); /* 0xc8 INY i */ op(implied, INY); /* 0xc9 CMP # */ op(immediate, CMP); - /* 0xca DEX i */ op(implied, DEC); + /* 0xca DEX i */ op(implied, DEX); /* 0xcb WAI i */ op(wai, WAI); /* 0xcc CPY a */ op(absolute, CPY); /* 0xcd CMP a */ op(absolute, CMP); From a4cec95db163cb288e921eab740554f38925399c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 19:36:23 -0400 Subject: [PATCH 065/150] Corrects load and transfer flag oversights. --- .../Clock SignalTests/KlausDormannTests.swift | 2 +- .../Implementation/65816Implementation.hpp | 17 +++++++++++++++-- .../65816/Implementation/65816Storage.cpp | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index 497104d4c..b772fe5d0 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -44,7 +44,7 @@ class KlausDormannTests: XCTestCase { switch address { case 0x3399: return nil // success! - case 0x052a: return "DEX did not correctly set zero flag" + case 0x052a: return "TAX, DEX or LDA did not correctly set flags" case 0x33a7: return "Decimal ADC result has wrong value" case 0x3502: return "Binary SBC result has wrong value" case 0x33b9: return "Decimal SBC result has wrong value" diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 4c23bdb2e..3a5f8a387 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -313,27 +313,33 @@ template void Processor::run_for(const Cycles case LDA: LD(a_, data_buffer_.value, m_masks_); + flags_.set_nz(a_.full, m_shift_); break; case LDX: LD(x_, data_buffer_.value, x_masks_); + flags_.set_nz(x_.full, x_shift_); break; case LDY: LD(y_, data_buffer_.value, x_masks_); + flags_.set_nz(y_.full, x_shift_); break; case PLB: - a_.halves.high = instruction_buffer_.value; + data_bank_ = (instruction_buffer_.value & 0xff) << 16; + flags_.set_nz(instruction_buffer_.value); break; case PLD: - direct_ = ((instruction_buffer_.value) & 0xff) << 16; + direct_ = (instruction_buffer_.value & 0xff) << 16; + flags_.set_nz(instruction_buffer_.value); break; // The below attempts to obey the 8/16-bit mixed transfer rules // as documented in https://softpixel.com/~cwright/sianse/docs/65816NFO.HTM + // (and makes reasonable guesses as to the N flag) case TXS: s_ = x_.full & x_masks_[1]; @@ -341,30 +347,37 @@ template void Processor::run_for(const Cycles case TSX: LD(x_, s_.full, x_masks_); + flags_.set_nz(x_.full, x_shift_); break; case TXY: LD(y_, x_.full, x_masks_); + flags_.set_nz(y_.full, x_shift_); break; case TYX: LD(x_, y_.full, x_masks_); + flags_.set_nz(x_.full, x_shift_); break; case TAX: LD(x_, a_.full, x_masks_); + flags_.set_nz(x_.full, x_shift_); break; case TAY: LD(x_, a_.full, x_masks_); + flags_.set_nz(y_.full, x_shift_); break; case TXA: LD(a_, x_.full, m_masks_); + flags_.set_nz(a_.full, m_shift_); break; case TYA: LD(a_, y_.full, m_masks_); + flags_.set_nz(a_.full, m_shift_); break; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 58dc50a03..947148166 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -893,7 +893,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xa8 TAY i */ op(implied, TAY); /* 0xa9 LDA # */ op(immediate, LDA); /* 0xaa TAX i */ op(implied, TAX); - /* 0xab PLB s */ op(stack_pull, PLB); + /* 0xab PLB s */ op(stack_pull, PLB); // TODO: force to 8-bit only; ditto [at least] PHB, PLD, PHD. /* 0xac LDY a */ op(absolute, LDY); /* 0xad LDA a */ op(absolute, LDA); /* 0xae LDX a */ op(absolute, LDX); From f7b119ffe1b69e6238b982c574e1c70b00ceaa27 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 19:57:58 -0400 Subject: [PATCH 066/150] Moves temporary logging, fixes branch instructions. --- .../Mac/Clock SignalTests/KlausDormannTests.swift | 2 +- Processors/6502/AllRAM/6502AllRAM.cpp | 6 ++++++ Processors/65816/Implementation/65816Base.cpp | 14 +++++++------- .../65816/Implementation/65816Implementation.hpp | 12 +++++------- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index b772fe5d0..6fb98d533 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -44,7 +44,7 @@ class KlausDormannTests: XCTestCase { switch address { case 0x3399: return nil // success! - case 0x052a: return "TAX, DEX or LDA did not correctly set flags" + case 0x052a: return "TAX, DEX or LDA did not correctly set flags, or BEQ did not branch correctly" case 0x33a7: return "Decimal ADC result has wrong value" case 0x3502: return "Binary SBC result has wrong value" case 0x33b9: return "Decimal SBC result has wrong value" diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 0381384b4..cc7d6f8b5 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -28,6 +28,12 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ timestamp_ += Cycles(1); if(operation == BusOperation::ReadOpcode) { + // TEMPORARY LOGGING. TODO: remove. + printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x\n", address, memory_[address], + mos6502_.get_value_of_register(Register::A), + mos6502_.get_value_of_register(Register::X), + mos6502_.get_value_of_register(Register::Y), + mos6502_.get_value_of_register(Register::Flags)); check_address_for_trap(address); } diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index 736ed2268..3416217f2 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -15,7 +15,7 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { case Register::ProgramCounter: return pc_; case Register::LastOperationAddress: return last_operation_pc_; case Register::StackPointer: return s_.full; -// case Register::Flags: return get_flags(); + case Register::Flags: return flags_.get(); // TODO: include additional flags (and below). case Register::A: return a_.full; case Register::X: return x_.full; case Register::Y: return y_.full; @@ -25,12 +25,12 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { - case Register::ProgramCounter: pc_ = value; break; - case Register::StackPointer: s_.full = value; break; -// case Register::Flags: set_flags(uint8_t(value)); break; - case Register::A: a_.full = value; break; - case Register::X: x_.full = value; break; - case Register::Y: y_.full = value; break; + case Register::ProgramCounter: pc_ = value; break; + case Register::StackPointer: s_.full = value; break; + case Register::Flags: flags_.set(uint8_t(value)); break; + case Register::A: a_.full = value; break; + case Register::X: x_.full = value; break; + case Register::Y: y_.full = value; break; default: break; } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 3a5f8a387..b39fee9a4 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -50,8 +50,6 @@ template void Processor::run_for(const Cycles } continue; case OperationDecode: { - // A VERY TEMPORARY piece of logging. - printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x\n", pc_ - 1, instruction_buffer_.value, a_.full, x_.full, y_.full, flags_.get()); // pc_ - 1 would be correct but this matches a log I made of the 6502. active_instruction_ = &instructions[instruction_buffer_.value]; const auto size_flag = mx_flags_[active_instruction_->size_field]; @@ -539,11 +537,11 @@ template void Processor::run_for(const Cycles next_op_ += 3; \ } else { \ data_buffer_.size = 2; \ - data_buffer_.value = pc_ + int8_t(data_buffer_.value); \ - \ - if((pc_ & 0xff00) == (data_buffer_.value & 0xff00)) { \ - ++next_op_; \ - } \ + data_buffer_.value = pc_ + int8_t(instruction_buffer_.value); \ + \ + if((pc_ & 0xff00) == (instruction_buffer_.value & 0xff00)) { \ + ++next_op_; \ + } \ } case BPL: BRA(!(flags_.negative_result&0x80)); break; From e68b3a2f32bf71c4675313081e07a77c502bf218 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 19:59:29 -0400 Subject: [PATCH 067/150] Corrects JMP program. --- Processors/65816/Implementation/65816Storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 947148166..280933e12 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -792,7 +792,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x49 EOR # */ op(immediate, EOR); /* 0x4a LSR A */ op(accumulator, LSR); /* 0x4b PHK s */ op(stack_push, PHK); - /* 0x4c JMP a */ op(absolute, JMP); + /* 0x4c JMP a */ op(absolute_jmp, JMP); /* 0x4d EOR a */ op(absolute, EOR); /* 0x4e LSR a */ op(absolute_rmw, LSR); /* 0x4f EOR al */ op(absolute_long, EOR); From b608e11965fd7552358bcf6249f1b0201bba028a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 20:06:27 -0400 Subject: [PATCH 068/150] Realises that not all non-incrementing PC fetches should be thrown away. --- .../Implementation/65816Implementation.hpp | 4 + .../65816/Implementation/65816Storage.cpp | 94 +++++++++---------- .../65816/Implementation/65816Storage.hpp | 4 +- 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index b39fee9a4..7ff4b86c7 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -72,6 +72,10 @@ template void Processor::run_for(const Cycles break; case CycleFetchPC: + read(pc_ | program_bank_, instruction_buffer_.next_input()); + break; + + case CycleFetchPCThrowaway: read(pc_ | program_bank_, &throwaway); break; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 280933e12..5c0483872 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -186,7 +186,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void absolute_jsr(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. target(CycleFetchPC); // New PCH. - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // [JSR] target(CyclePush); // PCH @@ -206,7 +206,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void absolute_indexed_indirect_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // AAL. target(CycleFetchPC); // AAH. - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. target(OperationConstructAbsoluteIndexedIndirect); // Calculate data address. target(CycleFetchIncrementData); // New PCL target(CycleFetchData); // New PCH. @@ -222,7 +222,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePush); // PCL target(CycleFetchPC); // AAH. - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. target(OperationConstructAbsoluteIndexedIndirect); // Calculate data address. target(CycleFetchIncrementData); // New PCL @@ -350,7 +350,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 8. Accumulator; A. static void accumulator(AccessType, bool, const std::function &target) { - target(CycleFetchPC); // 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. @@ -382,7 +382,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -393,7 +393,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. read_modify_write(is8bit, target); } @@ -403,9 +403,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectIndexedIndirect); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -415,7 +415,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectIndirect); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -425,7 +425,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchData); // AAH. @@ -441,7 +441,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectIndirect); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchIncrementData); // AAH. @@ -457,7 +457,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectIndirect); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. target(CycleFetchIncrementData); // AAH. @@ -473,9 +473,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectX); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -485,9 +485,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectX); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. read_modify_write(is8bit, target); } @@ -497,9 +497,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO. target(OperationConstructDirectY); - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. - target(CycleFetchPC); // IO. + target(CycleFetchPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -514,34 +514,34 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void immediate_rep_sep(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // IDL. - target(CycleFetchPC); // "Add 1 cycle for REP and SEP" + target(CycleFetchPCThrowaway); // "Add 1 cycle for REP and SEP" target(OperationPerform); } // 19a. Implied; i. static void implied(AccessType, bool, const std::function &target) { - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO target(OperationPerform); } // 19b. Implied; i; XBA. static void implied_xba(AccessType, bool, const std::function &target) { - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO target(OperationPerform); } // 19c. Stop the Clock. static void stp(AccessType, bool, const std::function &target) { - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO target(OperationPerform); } // 19d. Wait for interrupt. static void wai(AccessType, bool, const std::function &target) { - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO target(OperationPerform); } @@ -553,8 +553,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // of the next cycles, depending on the effect of // the jump. - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO target(OperationCopyDataToPC); // Install the address that was calculated above. } @@ -563,15 +563,15 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void relative_long(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // Offset low. target(CycleFetchIncrementPC); // Offset high. - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO target(OperationPerform); // [BRL] } // 22a. Stack; s, abort/irq/nmi/res. static void stack_exception(AccessType, bool, const std::function &target) { - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO target(OperationPrepareException); // Populates the data buffer; this skips a micro-op if // in emulation mode. @@ -589,8 +589,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22b. Stack; s, PLx. static void stack_pull(AccessType, bool is8bit, const std::function &target) { - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO if(!is8bit) target(CyclePull); // REG low. target(CyclePull); // REG [high]. @@ -600,7 +600,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22c. Stack; s, PHx. static void stack_push(AccessType, bool is8bit, const std::function &target) { - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO target(OperationPerform); @@ -621,7 +621,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // DO target(OperationConstructDirect); - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO target(CycleFetchIncrementData); // AAL target(CycleFetchData); // AAH @@ -633,7 +633,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void stack_per(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // Offset low. target(CycleFetchIncrementPC); // Offset high. - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO target(OperationConstructPER); @@ -643,8 +643,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22g. Stack; s, RTI. static void stack_rti(AccessType, bool is8bit, const std::function &target) { - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO target(CyclePull); // P target(CyclePull); // New PCL @@ -656,14 +656,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 22h. Stack; s, RTS. static void stack_rts(AccessType, bool, const std::function &target) { - target(CycleFetchPC); // IO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO - target(CyclePull); // PCL - target(CyclePull); // PCH - target(CycleAccessStack); // IO + target(CyclePull); // PCL + target(CyclePull); // PCH + target(CycleAccessStack); // IO - target(OperationPerform); // [JMP, to perform the RTS] + target(OperationPerform); // [JMP, to perform the RTS] } // 22i. Stack; s, RTL. @@ -685,7 +685,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 23. Stack Relative; d, s. static void stack_relative(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // SO - target(CycleFetchPC); // IO + target(CycleFetchPCThrowaway); // IO target(OperationConstructStackRelative); read_write(type, is8bit, target); @@ -693,8 +693,8 @@ 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 &target) { - target(CycleFetchIncrementPC); // SO - target(CycleFetchPC); // IO + target(CycleFetchIncrementPC); // SO + target(CycleFetchPCThrowaway); // IO target(OperationConstructStackRelative); target(CycleFetchIncrementData); // AAL diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 335ccb3dc..71423b830 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -9,8 +9,10 @@ enum MicroOp: uint8_t { /// Fetches a byte from the program counter to the instruction buffer and increments the program counter. CycleFetchIncrementPC, - /// Fetches a byte from the program counter without incrementing it, and throws it away. + /// Fetches a byte from the program counter without incrementing it. CycleFetchPC, + /// Fetches a byte from the program counter without incrementing it, and throws it away. + CycleFetchPCThrowaway, /// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address. CycleFetchOpcode, From b510b9d337ef7c6674d063bcd8a954033e4e4586 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 20:13:12 -0400 Subject: [PATCH 069/150] Adds PHD, PHK and 8-bit PHP and PLP. --- .../Implementation/65816Implementation.hpp | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 7ff4b86c7..c107b6462 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -334,12 +334,64 @@ template void Processor::run_for(const Cycles break; case PLD: - direct_ = (instruction_buffer_.value & 0xff) << 16; + direct_ = instruction_buffer_.value; flags_.set_nz(instruction_buffer_.value); break; + case PLP: + flags_.set(instruction_buffer_.value); - // The below attempts to obey the 8/16-bit mixed transfer rules + if(!emulation_flag_) { + assert(false); // TODO: M and X. + } + break; + + case STA: + data_buffer_.value = a_.full & m_masks_[1]; + data_buffer_.size = 2 - m_flag(); + break; + + case STZ: + data_buffer_.value = 0; + data_buffer_.size = 2 - m_flag(); + break; + + case STX: + data_buffer_.value = x_.full & x_masks_[1]; + data_buffer_.size = 2 - x_flag(); + break; + + case STY: + data_buffer_.value = y_.full & x_masks_[1]; + data_buffer_.size = 2 - m_flag(); + break; + + case PHB: + data_buffer_.value = data_bank_ >> 16; + data_buffer_.size = 1; + break; + + case PHK: + data_buffer_.value = program_bank_ >> 16; + data_buffer_.size = 1; + break; + + case PHD: + data_buffer_.value = direct_; + data_buffer_.size = 2; + break; + + case PHP: + data_buffer_.value = flags_.get(); + + if(!emulation_flag_) { + assert(false); // TODO: M and X. + } + break; + + case NOP: break; + + // The below attempt to obey the 8/16-bit mixed transfer rules // as documented in https://softpixel.com/~cwright/sianse/docs/65816NFO.HTM // (and makes reasonable guesses as to the N flag) @@ -383,33 +435,6 @@ template void Processor::run_for(const Cycles break; - case STA: - data_buffer_.value = a_.full & m_masks_[1]; - data_buffer_.size = 2 - m_flag(); - break; - - case STZ: - data_buffer_.value = 0; - data_buffer_.size = 2 - m_flag(); - break; - - case STX: - data_buffer_.value = x_.full & x_masks_[1]; - data_buffer_.size = 2 - x_flag(); - break; - - case STY: - data_buffer_.value = y_.full & x_masks_[1]; - data_buffer_.size = 2 - m_flag(); - break; - - case PHB: - data_buffer_.value = a_.halves.high; - data_buffer_.size = 1; - break; - - case NOP: break; - // // Jumps. // @@ -648,8 +673,7 @@ template void Processor::run_for(const Cycles } break; // TODO: - // PLP, - // PHP, PHD, PHK, + // PHK, // TRB, TSB, // REP, SEP, // XCE, XBA, From 1ba0a117e7a168a860e1b47c3ce11d9b651bb00f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 20:23:53 -0400 Subject: [PATCH 070/150] Corrects PLB, PLD, PLP. --- OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift | 1 + Processors/65816/Implementation/65816Implementation.hpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index 6fb98d533..1b0ee5be2 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -45,6 +45,7 @@ class KlausDormannTests: XCTestCase { case 0x3399: return nil // success! case 0x052a: return "TAX, DEX or LDA did not correctly set flags, or BEQ did not branch correctly" + case 0x05db: return "PLP did not affect N flag correctly" case 0x33a7: return "Decimal ADC result has wrong value" case 0x3502: return "Binary SBC result has wrong value" case 0x33b9: return "Decimal SBC result has wrong value" diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index c107b6462..ab94aa5a7 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -329,17 +329,17 @@ template void Processor::run_for(const Cycles break; case PLB: - data_bank_ = (instruction_buffer_.value & 0xff) << 16; + data_bank_ = (data_buffer_.value & 0xff) << 16; flags_.set_nz(instruction_buffer_.value); break; case PLD: - direct_ = instruction_buffer_.value; + direct_ = data_buffer_.value; flags_.set_nz(instruction_buffer_.value); break; case PLP: - flags_.set(instruction_buffer_.value); + flags_.set(data_buffer_.value); if(!emulation_flag_) { assert(false); // TODO: M and X. From 19aea85184726212092971a07bcd75ffd5579df3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 21:23:29 -0400 Subject: [PATCH 071/150] Corrects CMP, CPX, CPY carry flags. --- Processors/6502/AllRAM/6502AllRAM.cpp | 5 +++-- .../65816/Implementation/65816Implementation.hpp | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index cc7d6f8b5..793fb5eff 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -29,11 +29,12 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ if(operation == BusOperation::ReadOpcode) { // TEMPORARY LOGGING. TODO: remove. - printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x\n", address, memory_[address], + printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x s:%02x\n", address, memory_[address], mos6502_.get_value_of_register(Register::A), mos6502_.get_value_of_register(Register::X), mos6502_.get_value_of_register(Register::Y), - mos6502_.get_value_of_register(Register::Flags)); + mos6502_.get_value_of_register(Register::Flags) & 0xff, + mos6502_.get_value_of_register(Register::StackPointer) & 0xff); check_address_for_trap(address); } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index ab94aa5a7..50c924489 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -622,15 +622,15 @@ template void Processor::run_for(const Cycles // Arithmetic. // -#define cp(v, shift) {\ - const uint32_t temp32 = v.full - data_buffer_.value; \ +#define cp(v, shift, masks) {\ + const uint32_t temp32 = (v.full & masks[1]) - (data_buffer_.value & masks[1]); \ flags_.set_nz(uint16_t(temp32), shift); \ flags_.carry = ((~temp32) >> (8 + shift))&1; \ } - case CMP: cp(a_, m_shift_); break; - case CPX: cp(x_, x_shift_); break; - case CPY: cp(y_, x_shift_); break; + case CMP: cp(a_, m_shift_, m_masks_); break; + case CPX: cp(x_, x_shift_, x_masks_); break; + case CPY: cp(y_, x_shift_, x_masks_); break; #undef cp From f83ee97439138f4949a13fc3dec436e98ee8bfc3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 21:37:50 -0400 Subject: [PATCH 072/150] PHP pushes with the BRK flag set in emulation mode. --- Processors/65816/Implementation/65816Implementation.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 50c924489..8fe4a34cb 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -383,9 +383,13 @@ template void Processor::run_for(const Cycles case PHP: data_buffer_.value = flags_.get(); + data_buffer_.size = 1; if(!emulation_flag_) { assert(false); // TODO: M and X. + } else { + // On the 6502, the break flag is set during a PHP. + data_buffer_.value |= Flag::Break; } break; From b578240993420d0c36271ec28ffa64d021a849eb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Oct 2020 21:47:58 -0400 Subject: [PATCH 073/150] Adds a further error. Clearly I've severely overloaded 'JMP' and not fully thought through where it gets its addresses from. --- OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index 1b0ee5be2..4dcfdf51c 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -46,15 +46,16 @@ class KlausDormannTests: XCTestCase { case 0x052a: return "TAX, DEX or LDA did not correctly set flags, or BEQ did not branch correctly" case 0x05db: return "PLP did not affect N flag correctly" + case 0x26d2: return "ASL zpg,x produced incorrect flags" case 0x33a7: return "Decimal ADC result has wrong value" - case 0x3502: return "Binary SBC result has wrong value" case 0x33b9: return "Decimal SBC result has wrong value" case 0x33c0: return "Decimal SBC wrong carry flag" - case 0x36d1: return "BRK: unexpected BRK or IRQ" + case 0x3502: return "Binary SBC result has wrong value" + case 0x364a: return "JMP (addr) acted as JMP addr" case 0x36ac, 0x36f6: return "Improper JSR return address on stack" - case 0x36e5: return "BRK flag not set on stack" - case 0x26d2: return "ASL zpg,x produced incorrect flags" case 0x36c6: return "Unexpected RESET" + case 0x36d1: return "BRK: unexpected BRK or IRQ" + case 0x36e5: return "BRK flag not set on stack" case 0: return "Didn't find tests" From 907c3374c3c8b16bdcd663ecf49331ac61edeb3e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Oct 2020 16:48:46 -0400 Subject: [PATCH 074/150] Attempts to clean up my JMP/JSR mess. Also takes a step forwards in decimal SBC, but it's not right yet. --- .../Implementation/65816Implementation.hpp | 45 ++++++++++--------- .../65816/Implementation/65816Storage.cpp | 12 +++-- .../65816/Implementation/65816Storage.hpp | 15 ++++--- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 8fe4a34cb..5c316b280 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -448,19 +448,23 @@ template void Processor::run_for(const Cycles [[fallthrough]]; case JMP: - pc_ = instruction_buffer_.value & 0xffff; + pc_ = uint16_t(instruction_buffer_.value); + break; + + case JMPind: + pc_ = data_buffer_.value; break; case JSL: program_bank_ = instruction_buffer_.value & 0xff0000; - instruction_buffer_.size = 2; [[fallthrough]]; - case JSR: { - const uint16_t old_pc = pc_; + case JSR: + data_buffer_.value = pc_; + data_buffer_.size = 2; + pc_ = instruction_buffer_.value; - instruction_buffer_.value = old_pc; - } break; + break; // // Block moves. @@ -639,35 +643,32 @@ template void Processor::run_for(const Cycles #undef cp case SBC: - if(flags_.decimal) { - assert(false); - break; - } - - // Implement non-decimal SBC by falling through to ADC; - // TODO: what do I need to invert to be able to fall through in both cases? And does it matter? - data_buffer_.value = ~data_buffer_.value; + data_buffer_.value = ~data_buffer_.value & m_masks_[1]; [[fallthrough]]; case ADC: { int result; + const uint16_t a = a_.full & m_masks_[1]; if(flags_.decimal) { result = flags_.carry; + const int nibble_adjustment = (active_instruction_->operation == SBC) ? 0xa : 0x6; -#define nibble(mask, limit, addition, carry) \ - result += (a_.full & mask) + (data_buffer_.value & mask); \ - if(result >= limit) result = ((result + addition) & (carry - 1)) + carry; + // TODO: this still isn't quite correct for SBC as the limit test is wrong, I think. - nibble(0x000f, 0x000a, 0x0006, 0x00010); - nibble(0x00f0, 0x00a0, 0x0060, 0x00100); - nibble(0x0f00, 0x0a00, 0x0600, 0x01000); - nibble(0xf000, 0xa000, 0x6000, 0x10000); +#define nibble(mask, limit, addition, carry) \ + result += (a & mask) + (data_buffer_.value & mask); \ + if(result >= limit) result = ((result + (addition)) & (carry - 1)) + carry; + + nibble(0x000f, 0x000a, nibble_adjustment << 0, 0x00010); + nibble(0x00f0, 0x00a0, nibble_adjustment << 8, 0x00100); + nibble(0x0f00, 0x0a00, nibble_adjustment << 16, 0x01000); + nibble(0xf000, 0xa000, nibble_adjustment << 24, 0x10000); #undef nibble } else { - result = a_.full + data_buffer_.value + flags_.carry; + result = a + data_buffer_.value + flags_.carry; } flags_.overflow = (( (result ^ a_.full) & (result ^ data_buffer_.value) ) >> (1 + m_shift_))&0x40; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 5c0483872..ac554f5e2 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -178,7 +178,6 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void absolute_jmp(AccessType, bool, const std::function &target) { target(CycleFetchIncrementPC); // New PCL. target(CycleFetchPC); // New PCH. - target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // [JMP] } @@ -187,7 +186,6 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // New PCL. target(CycleFetchPC); // New PCH. target(CycleFetchPCThrowaway); // IO - target(OperationConstructAbsolute); // Calculate data address. target(OperationPerform); // [JSR] target(CyclePush); // PCH target(CyclePush); // PCL @@ -227,7 +225,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructAbsoluteIndexedIndirect); // Calculate data address. target(CycleFetchIncrementData); // New PCL target(CycleFetchData); // New PCH. - target(OperationPerform); // ['JSR' (actually: JMP will do)] + target(OperationPerform); // ['JSR' (actually: JMPind will do)] } // 3a. Absolute Indirect; (a), JML. @@ -814,7 +812,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x5e LSR a, x */ op(absolute_x_rmw, LSR); /* 0x5f EOR al, x */ op(absolute_long_x, EOR); - /* 0x60 RTS s */ op(stack_rts, JMP); // [sic]; loads the PC from data as per an RTS. + /* 0x60 RTS s */ op(stack_rts, JMPind); // [sic]; loads the PC from data as per an RTS. /* 0x61 ADC (d, x) */ op(direct_indexed_indirect, ADC); /* 0x62 PER s */ op(stack_per, NOP); /* 0x63 ADC d, s */ op(stack_relative, ADC); @@ -826,7 +824,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x69 ADC # */ op(immediate, ADC); /* 0x6a ROR A */ op(accumulator, ROR); /* 0x6b RTL s */ op(stack_rtl, JML); - /* 0x6c JMP (a) */ op(absolute_indirect_jmp, JMP); + /* 0x6c JMP (a) */ op(absolute_indirect_jmp, JMPind); /* 0x6d ADC a */ op(absolute, ADC); /* 0x6e ROR a */ op(absolute_rmw, ROR); /* 0x6f ADC al */ op(absolute_long, ADC); @@ -843,7 +841,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x79 ADC a, y */ op(absolute_y, ADC); /* 0x7a PLY s */ op(stack_pull, LDY); /* 0x7b TDC i */ op(implied, TDC); - /* 0x7c JMP (a, x) */ op(absolute_indexed_indirect_jmp, JMP); + /* 0x7c JMP (a, x) */ op(absolute_indexed_indirect_jmp, JMPind); /* 0x7d ADC a, x */ op(absolute_x, ADC); /* 0x7e ROR a, x */ op(absolute_x_rmw, ROR); /* 0x7f ADC al, x */ op(absolute_long_x, ADC); @@ -979,7 +977,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf9 SBC a, y */ op(absolute_y, SBC); /* 0xfa PLX s */ op(stack_pull, LDX); /* 0xfb XCE i */ op(implied, XCE); - /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JMP); // [sic] + /* 0xfc JSR (a, x) */ op(absolute_indexed_indirect_jsr, JMPind); // [sic] /* 0xfd SBC a, x */ op(absolute_x, SBC); /* 0xfe INC a, x */ op(absolute_x_rmw, INC); /* 0xff SBC al, x */ op(absolute_long_x, SBC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 71423b830..f16d36bb6 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -174,18 +174,21 @@ enum Operation: uint8_t { // from the stack. RTI, RTL, - /// Loads the PC with the operand from the data buffer. + /// Loads the PC with the contents of the data buffer. + JMPind, + + /// Loads the PC with the contents of the instruction bufer. JMP, - /// Loads the PC and PBR with the operand from the data buffer. + /// Loads the PC and PBR with the operand from the instruction buffer. JML, - /// Loads the PC with the operand from the data buffer, replacing - /// it with the old PC. + /// Loads the PC with the operand from the instruction buffer, placing + /// the current PC into the data buffer. JSR, - /// Loads the PC and the PBR with the operand from the data buffer, - /// replacing it with the old PC (and only the PC; PBR not included). + /// Loads the PC and the PBR with the operand from the instruction buffer, + /// placing the old PC into the data buffer (and only the PC; PBR not included). JSL, /// i.e. jump to vector. TODO: is this really distinct from JMP? I'm assuming so for now, From 054e0af07132d0acac6208ea64052fac79158411 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Oct 2020 16:55:45 -0400 Subject: [PATCH 075/150] Corrects RTS behaviour: the return address on the stack is off by one. Dormann's tests now proceed to a BRK. --- Processors/65816/Implementation/65816Implementation.hpp | 5 ++++- Processors/65816/Implementation/65816Storage.cpp | 4 ++-- Processors/65816/Implementation/65816Storage.hpp | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 5c316b280..5028e97ac 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -455,6 +455,10 @@ template void Processor::run_for(const Cycles pc_ = data_buffer_.value; break; + case RTS: + pc_ = data_buffer_.value + 1; + break; + case JSL: program_bank_ = instruction_buffer_.value & 0xff0000; [[fallthrough]]; @@ -678,7 +682,6 @@ template void Processor::run_for(const Cycles } break; // TODO: - // PHK, // TRB, TSB, // REP, SEP, // XCE, XBA, diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index ac554f5e2..cb83334bc 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -661,7 +661,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePull); // PCH target(CycleAccessStack); // IO - target(OperationPerform); // [JMP, to perform the RTS] + target(OperationPerform); // [RTS] } // 22i. Stack; s, RTL. @@ -812,7 +812,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x5e LSR a, x */ op(absolute_x_rmw, LSR); /* 0x5f EOR al, x */ op(absolute_long_x, EOR); - /* 0x60 RTS s */ op(stack_rts, JMPind); // [sic]; loads the PC from data as per an RTS. + /* 0x60 RTS s */ op(stack_rts, RTS); /* 0x61 ADC (d, x) */ op(direct_indexed_indirect, ADC); /* 0x62 PER s */ op(stack_per, NOP); /* 0x63 ADC d, s */ op(stack_relative, ADC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index f16d36bb6..168cf2455 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -191,6 +191,9 @@ enum Operation: uint8_t { /// placing the old PC into the data buffer (and only the PC; PBR not included). JSL, + /// Loads the PC with the contents of the data buffer + 1. + RTS, + /// i.e. jump to vector. TODO: is this really distinct from JMP? I'm assuming so for now, /// as I assume the PBR is implicitly modified. But then is it just JML? We'll see. BRK, From 0418f51ef2b725c3245211230126752e531a482b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Oct 2020 17:52:13 -0400 Subject: [PATCH 076/150] Takes a shot at emulation-mode 'exceptions'. It's just RTI and correct decimal SBC left of the official 6502s now, I think. --- .../Bridges/TestMachine6502.mm | 1 - .../Clock SignalTests/KlausDormannTests.swift | 4 +- Processors/6502/AllRAM/6502AllRAM.cpp | 15 +++++- .../Implementation/65816Implementation.hpp | 46 +++++++++++++++++-- .../65816/Implementation/65816Storage.cpp | 24 ++++++++-- .../65816/Implementation/65816Storage.hpp | 4 -- 6 files changed, 80 insertions(+), 14 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm index a941f4d82..f25bbbe8c 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm @@ -9,7 +9,6 @@ #import "TestMachine6502.h" #include #include "../../../../Processors/6502/AllRAM/6502AllRAM.hpp" -//#include "../../../../Processors/65816/AllRAM/65816AllRAM.hpp" #import "TestMachine+ForSubclassEyesOnly.h" const uint8_t CSTestMachine6502JamOpcode = CPU::MOS6502::JamOpcode; diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index 4dcfdf51c..bf22e17eb 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -55,7 +55,9 @@ class KlausDormannTests: XCTestCase { case 0x36ac, 0x36f6: return "Improper JSR return address on stack" case 0x36c6: return "Unexpected RESET" case 0x36d1: return "BRK: unexpected BRK or IRQ" - case 0x36e5: return "BRK flag not set on stack" + case 0x36e5: return "BRK flag not set on stack following BRK" + case 0x36ea: return "BRK did not set the I flag" + case 0x36fd: return "Wrong address put on stack by BRK" case 0: return "Didn't find tests" diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 793fb5eff..f59b68272 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -11,6 +11,8 @@ #include #include +#define BE_NOISY + using namespace CPU::MOS6502; namespace { @@ -28,20 +30,31 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ timestamp_ += Cycles(1); if(operation == BusOperation::ReadOpcode) { - // TEMPORARY LOGGING. TODO: remove. +#ifdef BE_NOISY printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x s:%02x\n", address, memory_[address], mos6502_.get_value_of_register(Register::A), mos6502_.get_value_of_register(Register::X), mos6502_.get_value_of_register(Register::Y), mos6502_.get_value_of_register(Register::Flags) & 0xff, mos6502_.get_value_of_register(Register::StackPointer) & 0xff); +#endif check_address_for_trap(address); } if(isReadOperation(operation)) { *value = memory_[address]; +#ifdef BE_NOISY + if((address&0xff00) == 0x100) { + printf("%04x -> %02x\n", address, *value); + } +#endif } else { memory_[address] = *value; +#ifdef BE_NOISY + if((address&0xff00) == 0x100) { + printf("%04x <- %02x\n", address, *value); + } +#endif } return Cycles(1); diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 5028e97ac..6aa9543b8 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -296,6 +296,49 @@ template void Processor::run_for(const Cycles data_address_ = data_bank_ + (instruction_buffer_.value + y()) & 0xffff; continue; + case OperationPrepareException: { + // Put the proper exception vector into the data address, put the flags and PC + // into the data buffer (possibly also PBR), and skip an instruction if in + // emulation mode. + + bool is_brk = false; + + if(pending_exceptions_ & (Reset | PowerOn)) { + // TODO: set emulation mode, etc. + pending_exceptions_ &= ~(Reset | PowerOn); + data_address_ = 0xfffc; + } else if(pending_exceptions_ & NMI) { + pending_exceptions_ &= ~NMI; + data_address_ = 0xfffa; + } else if(pending_exceptions_ & IRQ) { + pending_exceptions_ &= ~IRQ; + data_address_ = 0xfffe; + } else { + is_brk = active_instruction_ == instructions; + if(is_brk) { + data_address_ = emulation_flag_ ? 0xfffe : 0xfff6; + } else { + // Implicitly: COP. + data_address_ = 0xfff4; + } + } + + data_buffer_.value = (pc_ << 8) | flags_.get(); + if(emulation_flag_) { + if(is_brk) data_buffer_.value |= Flag::Break; + data_buffer_.size = 3; + ++next_op_; + } else { + data_buffer_.value |= program_bank_ << 24; + data_buffer_.size = 4; + program_bank_ = 0; + + assert(false); // TODO: proper flags, still. + } + + flags_.inverse_interrupt = 0; + } continue; + // // Performance. // @@ -687,7 +730,6 @@ template void Processor::run_for(const Cycles // XCE, XBA, // STP, WAI, // RTI, RTL, - // BRK, // TCD, TCS, TDC, TSC default: @@ -695,8 +737,6 @@ template void Processor::run_for(const Cycles } continue; - // TODO: OperationPrepareException - default: assert(false); } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index cb83334bc..7ef6e22eb 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -111,7 +111,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { const auto map_entry = installed_patterns.find(key); storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[0] = storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[1] = uint16_t(map_entry->second.first); - storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].operation = BRK; + storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].operation = JMPind; } void install_fetch_decode_execute() { @@ -579,6 +579,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePush); // PCL target(CyclePush); // P + // TODO: I think I need a seperate vector fetch here, to signal vector pull? target(CycleFetchIncrementData); // AAVL target(CycleFetchData); // AAVH @@ -677,8 +678,23 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 22j. Stack; s, BRK/COP. + static void brk_cop(AccessType, bool, const std::function &target) { + target(CycleFetchIncrementPC); // Signature. - // Covered by stack_exception. + target(OperationPrepareException); // Populates the data buffer; this skips a micro-op if + // in emulation mode. + + target(CyclePush); // PBR [skipped in emulation mode] + target(CyclePush); // PCH + target(CyclePush); // PCL + target(CyclePush); // P + + // TODO: I think I need a seperate vector fetch here, to signal vector pull? + target(CycleFetchIncrementData); // AAVL + target(CycleFetchData); // AAVH + + target(OperationPerform); // Jumps to the vector address. + } // 23. Stack Relative; d, s. static void stack_relative(AccessType type, bool is8bit, const std::function &target) { @@ -710,9 +726,9 @@ ProcessorStorage::ProcessorStorage() { // Install the instructions. #define op(x, y) constructor.install(&ProcessorStorageConstructor::x, y) - /* 0x00 BRK s */ op(stack_exception, BRK); + /* 0x00 BRK s */ op(brk_cop, JMPind); /* 0x01 ORA (d, x) */ op(direct_indexed_indirect, ORA); - /* 0x02 COP s */ op(stack_exception, BRK); + /* 0x02 COP s */ op(brk_cop, JMPind); /* 0x03 ORA d, s */ op(stack_relative, ORA); /* 0x04 TSB d */ op(direct_rmw, TSB); /* 0x05 ORA d */ op(direct, ORA); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 168cf2455..1e6fb03da 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -193,10 +193,6 @@ enum Operation: uint8_t { /// Loads the PC with the contents of the data buffer + 1. RTS, - - /// i.e. jump to vector. TODO: is this really distinct from JMP? I'm assuming so for now, - /// as I assume the PBR is implicitly modified. But then is it just JML? We'll see. - BRK, }; class ProcessorStorageConstructor; From f8004d7096e62502bf0d10c9c55fb708bfa11ae6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Oct 2020 18:06:11 -0400 Subject: [PATCH 077/150] Implements RTI, corrects TAY. --- .../65816/Implementation/65816Implementation.hpp | 16 +++++++++++++--- Processors/65816/Implementation/65816Storage.cpp | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 6aa9543b8..ff2e997ec 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -467,7 +467,7 @@ template void Processor::run_for(const Cycles break; case TAY: - LD(x_, a_.full, x_masks_); + LD(y_, a_.full, x_masks_); flags_.set_nz(y_.full, x_shift_); break; @@ -483,7 +483,7 @@ template void Processor::run_for(const Cycles // - // Jumps. + // Jumps and returns. // case JML: @@ -513,6 +513,16 @@ template void Processor::run_for(const Cycles pc_ = instruction_buffer_.value; break; + case RTI: + pc_ = uint16_t(data_buffer_.value >> 8); + flags_.set(uint8_t(data_buffer_.value)); + + if(!emulation_flag_) { + program_bank_ = (data_buffer_.value & 0xff000000) >> 8; + assert(false); // Extra flags to unpack! + } + break; + // // Block moves. // @@ -729,7 +739,7 @@ template void Processor::run_for(const Cycles // REP, SEP, // XCE, XBA, // STP, WAI, - // RTI, RTL, + // RTL, // TCD, TCS, TDC, TSC default: diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 7ef6e22eb..b03fb70a6 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -649,6 +649,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePull); // New PCL target(CyclePull); // New PCH if(!is8bit) target(CyclePull); // PBR + // TODO: 8bit check here doesn't actually work, it needs to be an is-emulation-mode check. + // New operation needed, I think. target(OperationPerform); // [RTI] — to unpack the fields above. } From 755627f12d695f190606586d9fe80e449ae71d1a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Oct 2020 20:00:01 -0400 Subject: [PATCH 078/150] Corrects direct addressing. --- Processors/65816/Implementation/65816Implementation.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index ff2e997ec..da436c016 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -268,7 +268,7 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirectX: - data_address_ = (direct_ + x()) & 0xffff; + data_address_ = (instruction_buffer_.value + direct_ + x()) & 0xffff; incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); if(!(direct_&0xff)) { ++next_op_; @@ -276,7 +276,8 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirectY: - data_address_ = (direct_ + y()) & 0xffff; + data_address_ = (instruction_buffer_.value + direct_ + y()) & 0xffff; + // TODO: given the 16-bit internal arithmetic, confirm this is the correct spurious address. incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); if(!(direct_&0xff)) { ++next_op_; From 7dde7cc743073123d9002ea4a47437432d9a0863 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Oct 2020 22:02:14 -0400 Subject: [PATCH 079/150] Implements altered direct indexed addressing in emulation mode. --- Processors/6502/AllRAM/6502AllRAM.cpp | 12 ++++++------ Processors/6502Esque/Implementation/LazyFlags.hpp | 10 +++++----- .../65816/Implementation/65816Implementation.hpp | 14 ++++++++++---- Processors/65816/Implementation/65816Storage.cpp | 2 ++ Processors/65816/Implementation/65816Storage.hpp | 1 + 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index f59b68272..cbd8e6b36 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -44,16 +44,16 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ if(isReadOperation(operation)) { *value = memory_[address]; #ifdef BE_NOISY - if((address&0xff00) == 0x100) { - printf("%04x -> %02x\n", address, *value); - } +// if((address&0xff00) == 0x100) { +// printf("%04x -> %02x\n", address, *value); +// } #endif } else { memory_[address] = *value; #ifdef BE_NOISY - if((address&0xff00) == 0x100) { - printf("%04x <- %02x\n", address, *value); - } +// if((address&0xff00) == 0x100) { +// printf("%04x <- %02x\n", address, *value); +// } #endif } diff --git a/Processors/6502Esque/Implementation/LazyFlags.hpp b/Processors/6502Esque/Implementation/LazyFlags.hpp index 5f330ff0f..f25d8c960 100644 --- a/Processors/6502Esque/Implementation/LazyFlags.hpp +++ b/Processors/6502Esque/Implementation/LazyFlags.hpp @@ -16,19 +16,19 @@ namespace MOS6502Esque { struct LazyFlags { /// Bit 7 is set if the negative flag is set; otherwise it is clear. - uint8_t negative_result; + uint8_t negative_result = 0; /// Non-zero if the zero flag is clear, zero if it is set. - uint8_t zero_result; + uint8_t zero_result = 0; /// Contains Flag::Carry. - uint8_t carry; + uint8_t carry = 0; /// Contains Flag::Decimal. - uint8_t decimal; + uint8_t decimal = 0; /// Contains Flag::Overflow. - uint8_t overflow; + uint8_t overflow = 0; /// Contains Flag::Interrupt, complemented. uint8_t inverse_interrupt = 0; diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index da436c016..742eb0796 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -267,16 +267,22 @@ template void Processor::run_for(const Cycles data_address_ = instruction_buffer_.value; continue; - case OperationConstructDirectX: - data_address_ = (instruction_buffer_.value + direct_ + x()) & 0xffff; + case OperationConstructDirectX: { + data_address_ = ( + (direct_ & e_masks_[0]) + + ((instruction_buffer_.value + direct_ + x()) & e_masks_[1]) + ) & 0xffff; incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); if(!(direct_&0xff)) { ++next_op_; } - continue; + } continue; case OperationConstructDirectY: - data_address_ = (instruction_buffer_.value + direct_ + y()) & 0xffff; + data_address_ = ( + (direct_ & e_masks_[0]) + + ((instruction_buffer_.value + direct_ + y()) & e_masks_[1]) + ) & 0xffff; // TODO: given the 16-bit internal arithmetic, confirm this is the correct spurious address. incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); if(!(direct_&0xff)) { diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index b03fb70a6..0fe70c498 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -723,6 +723,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { }; ProcessorStorage::ProcessorStorage() { + a_.full = x_.full = y_.full = 0; // TEMPORARY (?) + ProcessorStorageConstructor constructor(*this); // Install the instructions. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 1e6fb03da..be4079fc6 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -237,6 +237,7 @@ struct ProcessorStorage { uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + uint16_t e_masks_[2] = {0xff00, 0x00ff}; int m_shift_ = 0; int x_shift_ = 0; bool emulation_flag_ = true; From 0ed98cbfac39a1aba65bc3f14c302da40f865cab Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Oct 2020 22:15:19 -0400 Subject: [PATCH 080/150] Attempts to fix direct indirect indexed; not yet successful I think. --- Processors/65816/Implementation/65816Implementation.hpp | 4 ++++ Processors/65816/Implementation/65816Storage.cpp | 1 + Processors/65816/Implementation/65816Storage.hpp | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 742eb0796..c2f28ef4b 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -171,6 +171,10 @@ template void Processor::run_for(const Cycles data_buffer_ = instruction_buffer_; continue; + case OperationCopyDataToInstruction: + instruction_buffer_ = data_buffer_; + continue; + case OperationCopyAToData: if(m_flag()) { data_buffer_.size = 1; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 0fe70c498..e451ee3ad 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -428,6 +428,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementData); // AAL. target(CycleFetchData); // AAH. + target(OperationCopyDataToInstruction); target(OperationConstructAbsoluteYRead); target(CycleFetchIncorrectDataAddress); // IO. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index be4079fc6..ff9206653 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -112,8 +112,9 @@ enum MicroOp: uint8_t { /// Copies the current program counter to the data buffer. OperationCopyPCToData, - OperationCopyInstructionToData, OperationCopyDataToPC, + OperationCopyInstructionToData, + OperationCopyDataToInstruction, /// Copies the current PBR to the data buffer. OperationCopyPBRToData, From 9b6c48631dffed1585647a60edc6254b461354ce Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 21:39:34 -0400 Subject: [PATCH 081/150] Removes usage of a JAM instruction to spot end-of-tests. --- .../WolfgangLorenzTests.swift | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 93829747a..5cd50ee2e 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -202,7 +202,6 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { machine = CSTestMachine6502(processor: .processor6502) machine.trapHandler = self -// machine.logActivity = true output = "" let dataPointer = (testData as NSData).bytes.bindMemory(to: UInt8.self, capacity: testData.count) @@ -211,6 +210,9 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { machine.setData(contents, atAddress: loadAddress) + // Cf. http://www.softwolves.com/arkiv/cbm-hackers/7/7114.html for the steps being taken here. + + // Initialise memory locations as instructed. machine.setValue(0x00, forAddress: 0x0002) machine.setValue(0x00, forAddress: 0xa002) machine.setValue(0x80, forAddress: 0xa003) @@ -219,28 +221,34 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { machine.setValue(0x48, forAddress: 0xfffe) machine.setValue(0xff, forAddress: 0xffff) + // Place the Commodore's default IRQ handler. let irqHandler: [UInt8] = [ 0x48, 0x8a, 0x48, 0x98, 0x48, 0xba, 0xbd, 0x04, 0x01, 0x29, 0x10, 0xf0, 0x03, 0x6c, 0x16, 0x03, 0x6c, 0x14, 0x03 ] machine.setData(Data(irqHandler), atAddress: 0xff48) + // Set a couple of trap addresses to capture test output. machine.addTrapAddress(0xffd2) // print character machine.addTrapAddress(0xffe4) // scan keyboard + // Set a couple of test addresses that indicate failure. machine.addTrapAddress(0x8000) // exit machine.addTrapAddress(0xa474) // exit + // Ensure that any of those addresses return control. machine.setValue(0x60, forAddress:0xffd2) // 0x60 is RTS machine.setValue(0x60, forAddress:0xffe4) machine.setValue(0x60, forAddress:0x8000) machine.setValue(0x60, forAddress:0xa474) - machine.setValue(CSTestMachine6502JamOpcode, forAddress:0xe16f) // load + // Commodore's load routine resides at $e16f; this is used to spot the end of a test. + machine.setData(Data([0x4c, 0x6f, 0xe1]), atAddress: 0xe16f) - machine.setValue(0x0801, for: CSTestMachine6502Register.programCounter) - machine.setValue(0xfd, for: CSTestMachine6502Register.stackPointer) - machine.setValue(0x04, for: CSTestMachine6502Register.flags) + // Seed program entry. + machine.setValue(0x0801, for: .programCounter) + machine.setValue(0xfd, for: .stackPointer) + machine.setValue(0x04, for: .flags) } } @@ -248,19 +256,16 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Couldn't load file \(name)", userInfo: nil).raise() } - while !machine.isJammed { + while machine.value(for: .lastOperationAddress) != 0xe16f && !machine.isJammed { machine.runForNumber(ofCycles: 1000) } - let jammedPC = machine.value(for: CSTestMachine6502Register.lastOperationAddress) - if jammedPC != 0xe16f { - let hexAddress = String(format:"%04x", jammedPC) + if machine.isJammed { + let hexAddress = String(format:"%04x", machine.value(for: .lastOperationAddress)) NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Processor jammed unexpectedly at \(hexAddress)", userInfo: nil).raise() } } -// MARK: MachineJamHandler - func petsciiToString(_ string: String) -> String { let petsciiToCharCommon: [String] = [ "?", "?", "?", "[RUN/STOP]", "?", "[WHT]", "?", "?", "[SHIFT DISABLE]", "[SHIFT ENABLE]", "?", "?", "?", "\r", "[TEXT MODE]", "?", From 88293909f412b243d10b4a47ddc8b9a0b385bd79 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 21:44:47 -0400 Subject: [PATCH 082/150] Enables running of a first test on the 65816. --- .../WolfgangLorenzTests.swift | 179 +++++++++--------- 1 file changed, 91 insertions(+), 88 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 5cd50ee2e..6a41cab86 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -12,195 +12,198 @@ import Foundation class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { func testWolfgangLorenzStart() { - self.runWolfgangLorenzTest(" start") + self.runWolfgangLorenzTest(" start", processor: .processor6502) + } + func testWolfgangLorenzStart65816() { + self.runWolfgangLorenzTest(" start", processor: .processor65816) } func testWolfgangLorenzLDA() { - self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzSTA() { - self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzLDX() { - self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"]) + self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502) } func testWolfgangLorenzSTX() { - self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"]) + self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502) } func testWolfgangLorenzLDY() { - self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"]) + self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502) } func testWolfgangLorenzSTY() { - self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"]) + self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502) } func testWolfgangLorenzTransfers() { - self.runWolfgangLorenzTest("taxn") - self.runWolfgangLorenzTest("tayn") - self.runWolfgangLorenzTest("txan") - self.runWolfgangLorenzTest("tyan") - self.runWolfgangLorenzTest("tsxn") - self.runWolfgangLorenzTest("txsn") + self.runWolfgangLorenzTest("taxn", processor: .processor6502) + self.runWolfgangLorenzTest("tayn", processor: .processor6502) + self.runWolfgangLorenzTest("txan", processor: .processor6502) + self.runWolfgangLorenzTest("tyan", processor: .processor6502) + self.runWolfgangLorenzTest("tsxn", processor: .processor6502) + self.runWolfgangLorenzTest("txsn", processor: .processor6502) } func testWolfgangLorenzStack() { - self.runWolfgangLorenzTest("phan") - self.runWolfgangLorenzTest("plan") - self.runWolfgangLorenzTest("phpn") - self.runWolfgangLorenzTest("plpn") + self.runWolfgangLorenzTest("phan", processor: .processor6502) + self.runWolfgangLorenzTest("plan", processor: .processor6502) + self.runWolfgangLorenzTest("phpn", processor: .processor6502) + self.runWolfgangLorenzTest("plpn", processor: .processor6502) } func testWolfgangLorenzIncsAndDecs() { - self.runWolfgangLorenzTest("inxn") - self.runWolfgangLorenzTest("inyn") - self.runWolfgangLorenzTest("dexn") - self.runWolfgangLorenzTest("deyn") - self.runWolfgangLorenzTest("incz") - self.runWolfgangLorenzTest("inczx") - self.runWolfgangLorenzTest("inca") - self.runWolfgangLorenzTest("incax") - self.runWolfgangLorenzTest("decz") - self.runWolfgangLorenzTest("deczx") - self.runWolfgangLorenzTest("deca") - self.runWolfgangLorenzTest("decax") + self.runWolfgangLorenzTest("inxn", processor: .processor6502) + self.runWolfgangLorenzTest("inyn", processor: .processor6502) + self.runWolfgangLorenzTest("dexn", processor: .processor6502) + self.runWolfgangLorenzTest("deyn", processor: .processor6502) + self.runWolfgangLorenzTest("incz", processor: .processor6502) + self.runWolfgangLorenzTest("inczx", processor: .processor6502) + self.runWolfgangLorenzTest("inca", processor: .processor6502) + self.runWolfgangLorenzTest("incax", processor: .processor6502) + self.runWolfgangLorenzTest("decz", processor: .processor6502) + self.runWolfgangLorenzTest("deczx", processor: .processor6502) + self.runWolfgangLorenzTest("deca", processor: .processor6502) + self.runWolfgangLorenzTest("decax", processor: .processor6502) } func testWolfgangLorenzASL() { - self.runWolfgangLorenzTest("asl", suffixes: ["n", "z", "zx", "a", "ax"]) + self.runWolfgangLorenzTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testWolfgangLorenzLSR() { - self.runWolfgangLorenzTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"]) + self.runWolfgangLorenzTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testWolfgangLorenzROL() { - self.runWolfgangLorenzTest("rol", suffixes: ["n", "z", "zx", "a", "ax"]) + self.runWolfgangLorenzTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testWolfgangLorenzROR() { - self.runWolfgangLorenzTest("ror", suffixes: ["n", "z", "zx", "a", "ax"]) + self.runWolfgangLorenzTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testWolfgangLorenzAND() { - self.runWolfgangLorenzTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzORA() { - self.runWolfgangLorenzTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzEOR() { - self.runWolfgangLorenzTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzFlagManipulation() { - self.runWolfgangLorenzTest("clcn") - self.runWolfgangLorenzTest("secn") - self.runWolfgangLorenzTest("cldn") - self.runWolfgangLorenzTest("sedn") - self.runWolfgangLorenzTest("clin") - self.runWolfgangLorenzTest("sein") - self.runWolfgangLorenzTest("clvn") + self.runWolfgangLorenzTest("clcn", processor: .processor6502) + self.runWolfgangLorenzTest("secn", processor: .processor6502) + self.runWolfgangLorenzTest("cldn", processor: .processor6502) + self.runWolfgangLorenzTest("sedn", processor: .processor6502) + self.runWolfgangLorenzTest("clin", processor: .processor6502) + self.runWolfgangLorenzTest("sein", processor: .processor6502) + self.runWolfgangLorenzTest("clvn", processor: .processor6502) } func testWolfgangLorenzADC() { - self.runWolfgangLorenzTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzSBC() { - self.runWolfgangLorenzTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzCompare() { - self.runWolfgangLorenzTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"]) - self.runWolfgangLorenzTest("cpx", suffixes: ["b", "z", "a"]) - self.runWolfgangLorenzTest("cpy", suffixes: ["b", "z", "a"]) + self.runWolfgangLorenzTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + self.runWolfgangLorenzTest("cpx", suffixes: ["b", "z", "a"], processor: .processor6502) + self.runWolfgangLorenzTest("cpy", suffixes: ["b", "z", "a"], processor: .processor6502) } func testWolfgangLorenzBIT() { - self.runWolfgangLorenzTest("bit", suffixes: ["z", "a"]) + self.runWolfgangLorenzTest("bit", suffixes: ["z", "a"], processor: .processor6502) } func testWolfgangLorenzFlow() { - self.runWolfgangLorenzTest("brkn") - self.runWolfgangLorenzTest("rtin") - self.runWolfgangLorenzTest("jsrw") - self.runWolfgangLorenzTest("rtsn") - self.runWolfgangLorenzTest("jmpw") - self.runWolfgangLorenzTest("jmpi") + self.runWolfgangLorenzTest("brkn", processor: .processor6502) + self.runWolfgangLorenzTest("rtin", processor: .processor6502) + self.runWolfgangLorenzTest("jsrw", processor: .processor6502) + self.runWolfgangLorenzTest("rtsn", processor: .processor6502) + self.runWolfgangLorenzTest("jmpw", processor: .processor6502) + self.runWolfgangLorenzTest("jmpi", processor: .processor6502) } func testWolfgangLorenzBranch() { - self.runWolfgangLorenzTest("beqr") - self.runWolfgangLorenzTest("bner") - self.runWolfgangLorenzTest("bmir") - self.runWolfgangLorenzTest("bplr") - self.runWolfgangLorenzTest("bcsr") - self.runWolfgangLorenzTest("bccr") - self.runWolfgangLorenzTest("bvsr") - self.runWolfgangLorenzTest("bvcr") + self.runWolfgangLorenzTest("beqr", processor: .processor6502) + self.runWolfgangLorenzTest("bner", processor: .processor6502) + self.runWolfgangLorenzTest("bmir", processor: .processor6502) + self.runWolfgangLorenzTest("bplr", processor: .processor6502) + self.runWolfgangLorenzTest("bcsr", processor: .processor6502) + self.runWolfgangLorenzTest("bccr", processor: .processor6502) + self.runWolfgangLorenzTest("bvsr", processor: .processor6502) + self.runWolfgangLorenzTest("bvcr", processor: .processor6502) } func testWolfgangLorenzNOP() { - self.runWolfgangLorenzTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"]) + self.runWolfgangLorenzTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502) } func testWolfgangLorenzASO() { - self.runWolfgangLorenzTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzRLA() { - self.runWolfgangLorenzTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzLSE() { - self.runWolfgangLorenzTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzRRA() { - self.runWolfgangLorenzTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzDCM() { - self.runWolfgangLorenzTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzINS() { - self.runWolfgangLorenzTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzLAX() { - self.runWolfgangLorenzTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"]) + self.runWolfgangLorenzTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzAXS() { - self.runWolfgangLorenzTest("axs", suffixes: ["z", "zy", "a", "ix"]) + self.runWolfgangLorenzTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502) } func testWolfgangLorenzALR() { - self.runWolfgangLorenzTest("alrb") + self.runWolfgangLorenzTest("alrb", processor: .processor6502) } func testWolfgangLorenzARR() { - self.runWolfgangLorenzTest("arrb") + self.runWolfgangLorenzTest("arrb", processor: .processor6502) } func testWolfgangLorenzSBX() { - self.runWolfgangLorenzTest("sbxb") + self.runWolfgangLorenzTest("sbxb", processor: .processor6502) } func testWolfgangLorenzSHA() { - self.runWolfgangLorenzTest("sha", suffixes: ["ay", "iy"]) + self.runWolfgangLorenzTest("sha", suffixes: ["ay", "iy"], processor: .processor6502) } func testWolfgangLorenzSHX() { - self.runWolfgangLorenzTest("shxay") + self.runWolfgangLorenzTest("shxay", processor: .processor6502) } func testWolfgangLorenzSHY() { - self.runWolfgangLorenzTest("shyax") + self.runWolfgangLorenzTest("shyax", processor: .processor6502) } func testWolfgangLorenzSHS() { - self.runWolfgangLorenzTest("shsay") + self.runWolfgangLorenzTest("shsay", processor: .processor6502) } func testWolfgangLorenzLXA() { - self.runWolfgangLorenzTest("lxab") + self.runWolfgangLorenzTest("lxab", processor: .processor6502) } func testWolfgangLorenzANE() { - self.runWolfgangLorenzTest("aneb") + self.runWolfgangLorenzTest("aneb", processor: .processor6502) } func testWolfgangLorenzANC() { - self.runWolfgangLorenzTest("ancb") + self.runWolfgangLorenzTest("ancb", processor: .processor6502) } func testWolfgangLorenzLAS() { - self.runWolfgangLorenzTest("lasay") + self.runWolfgangLorenzTest("lasay", processor: .processor6502) } func testWolfgangLorenzSBCB() { - self.runWolfgangLorenzTest("sbcb(eb)") + self.runWolfgangLorenzTest("sbcb(eb)", processor: .processor6502) } - fileprivate func runWolfgangLorenzTest(_ name: String, suffixes: [String]) { + fileprivate func runWolfgangLorenzTest(_ name: String, suffixes: [String], processor: CSTestMachine6502Processor) { for suffix in suffixes { let testName = name + suffix - self.runWolfgangLorenzTest(testName) + self.runWolfgangLorenzTest(testName, processor: processor) } } fileprivate var output: String = "" - fileprivate func runWolfgangLorenzTest(_ name: String) { + fileprivate func runWolfgangLorenzTest(_ name: String, processor: CSTestMachine6502Processor) { var machine: CSTestMachine6502! if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) { if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) { - machine = CSTestMachine6502(processor: .processor6502) + machine = CSTestMachine6502(processor: processor) machine.trapHandler = self output = "" From 968166b06d7760c39479e58bd4c1dafeadeee45f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 21:48:35 -0400 Subject: [PATCH 083/150] Resolves incorrectly flow after setting up an absolute address. --- Processors/65816/Implementation/65816Implementation.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index c2f28ef4b..ac750dba7 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -32,6 +32,11 @@ template void Processor::run_for(const Cycles const MicroOp operation = *next_op_; ++next_op_; +#ifndef NDEBUG + // As a sanity check. + bus_value = nullptr; +#endif + switch(operation) { // @@ -208,7 +213,7 @@ template void Processor::run_for(const Cycles case OperationConstructAbsolute: data_address_ = instruction_buffer_.value + data_bank_; - break; + continue; case OperationConstructAbsoluteIndexedIndirect: data_address_ = (instruction_buffer_.value + x()) & 0xffff; From b439f40fe2fcd1a297632c63de021c607d372fa9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 22:04:25 -0400 Subject: [PATCH 084/150] Corrects INC and DEC. --- Processors/6502/AllRAM/6502AllRAM.cpp | 6 +++--- Processors/65816/Implementation/65816Implementation.hpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index cbd8e6b36..49344126d 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -11,7 +11,7 @@ #include #include -#define BE_NOISY +//#define BE_NOISY using namespace CPU::MOS6502; @@ -45,14 +45,14 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ *value = memory_[address]; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { -// printf("%04x -> %02x\n", address, *value); + printf("%04x -> %02x\n", address, *value); // } #endif } else { memory_[address] = *value; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { -// printf("%04x <- %02x\n", address, *value); + printf("%04x <- %02x\n", address, *value); // } #endif } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index ac750dba7..5daa93055 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -577,13 +577,13 @@ template void Processor::run_for(const Cycles // case INC: - ++instruction_buffer_.value; - flags_.set_nz(instruction_buffer_.value, m_shift_); + ++data_buffer_.value; + flags_.set_nz(data_buffer_.value, m_shift_); break;; case DEC: - --instruction_buffer_.value; - flags_.set_nz(instruction_buffer_.value, m_shift_); + --data_buffer_.value; + flags_.set_nz(data_buffer_.value, m_shift_); break; case INX: { From 451f83ba519bc4657caaf713454d4abdad0364eb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 22:14:42 -0400 Subject: [PATCH 085/150] Corrects emulation-mode read-modify-writes not to empty the data buffer. --- Processors/6502/AllRAM/6502AllRAM.cpp | 6 +++--- Processors/65816/Implementation/65816Implementation.hpp | 4 ++++ Processors/65816/Implementation/65816Storage.cpp | 2 +- Processors/65816/Implementation/65816Storage.hpp | 6 ++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 49344126d..cbd8e6b36 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -11,7 +11,7 @@ #include #include -//#define BE_NOISY +#define BE_NOISY using namespace CPU::MOS6502; @@ -45,14 +45,14 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ *value = memory_[address]; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { - printf("%04x -> %02x\n", address, *value); +// printf("%04x -> %02x\n", address, *value); // } #endif } else { memory_[address] = *value; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { - printf("%04x <- %02x\n", address, *value); +// printf("%04x <- %02x\n", address, *value); // } #endif } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 5daa93055..3b1ba85f7 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -109,6 +109,10 @@ template void Processor::run_for(const Cycles write(data_address_, data_buffer_.next_output()); break; + case CycleStoreDataThrowaway: + write(data_address_, data_buffer_.preview_output()); + break; + case CycleStoreIncrementData: write(data_address_, data_buffer_.next_output()); increment_data_address(); diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index e451ee3ad..c83963d30 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -157,7 +157,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchData); // Data [high]. if(!is8bit) target(CycleFetchData); // 16-bit: reread final byte of data. - else target(CycleStoreData); // 8-bit rewrite final byte of data. + else target(CycleStoreDataThrowaway); // 8-bit 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 ff9206653..85964fa50 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -31,6 +31,8 @@ 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, /// 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. @@ -282,6 +284,10 @@ struct ProcessorStorage { return next; } + uint8_t *preview_output() { + return byte(read); + } + uint8_t *next_stack() { --size; return byte(size); From abcd86a2947fcf94e7e5bfd41138675ab08746f2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 22:18:22 -0400 Subject: [PATCH 086/150] Fixes accumulator instructions. --- 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 3b1ba85f7..54fd9e835 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -187,7 +187,7 @@ template void Processor::run_for(const Cycles case OperationCopyAToData: if(m_flag()) { data_buffer_.size = 1; - data_buffer_.value = a_.halves.high; + data_buffer_.value = a_.halves.low; } else { data_buffer_.size = 2; data_buffer_.value = a_.full; @@ -196,7 +196,7 @@ template void Processor::run_for(const Cycles case OperationCopyDataToA: if(m_flag()) { - a_.halves.high = data_buffer_.value; + a_.halves.low = data_buffer_.value; } else { a_.full = data_buffer_.value; } From c01bc784b9e0edf73569be5c52529af56f892d8a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 22:21:55 -0400 Subject: [PATCH 087/150] Slightly reduces branching. --- Processors/6502/AllRAM/6502AllRAM.cpp | 2 +- .../65816/Implementation/65816Implementation.hpp | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index cbd8e6b36..723bc98b6 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -11,7 +11,7 @@ #include #include -#define BE_NOISY +//#define BE_NOISY using namespace CPU::MOS6502; diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 54fd9e835..4d567dd9f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -185,21 +185,12 @@ template void Processor::run_for(const Cycles continue; case OperationCopyAToData: - if(m_flag()) { - data_buffer_.size = 1; - data_buffer_.value = a_.halves.low; - } else { - data_buffer_.size = 2; - data_buffer_.value = a_.full; - } + data_buffer_.value = a_.full & m_masks_[1]; + data_buffer_.size = 2 - m_flag(); continue; case OperationCopyDataToA: - if(m_flag()) { - a_.halves.low = data_buffer_.value; - } else { - a_.full = data_buffer_.value; - } + a_.full = (a_.full & m_masks_[0]) + (data_buffer_.value & m_masks_[1]); continue; case OperationCopyPBRToData: From 776f014dbed84733ae6cb713f2cd50d9864b26c8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 22:23:54 -0400 Subject: [PATCH 088/150] Attempts LDA tests against the 65816. Result: ix is faulty. Which we already knew. --- .../Mac/Clock SignalTests/WolfgangLorenzTests.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 6a41cab86..7f9e6f7c1 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -11,12 +11,16 @@ import Foundation class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { - func testWolfgangLorenzStart() { - self.runWolfgangLorenzTest(" start", processor: .processor6502) - } func testWolfgangLorenzStart65816() { self.runWolfgangLorenzTest(" start", processor: .processor65816) } + func testWolfgangLorenzLDA65816() { + self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + + func testWolfgangLorenzStart() { + self.runWolfgangLorenzTest(" start", processor: .processor6502) + } func testWolfgangLorenzLDA() { self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } From 92e72959c3037908aa8eea1a4541bff9e0a897ba Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 23:12:20 -0400 Subject: [PATCH 089/150] Makes corrections to ix addressing mode and shift/roll flags. --- Processors/65816/Implementation/65816Implementation.hpp | 9 +++++---- Processors/65816/Implementation/65816Storage.cpp | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 4d567dd9f..d8b11676b 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -182,6 +182,7 @@ template void Processor::run_for(const Cycles case OperationCopyDataToInstruction: instruction_buffer_ = data_buffer_; + data_buffer_.clear(); continue; case OperationCopyAToData: @@ -672,26 +673,26 @@ template void Processor::run_for(const Cycles case ASL: flags_.carry = data_buffer_.value >> (7 + m_shift_); data_buffer_.value <<= 1; - flags_.set_nz(instruction_buffer_.value, m_shift_); + flags_.set_nz(data_buffer_.value, m_shift_); break; case LSR: flags_.carry = data_buffer_.value & 1; data_buffer_.value >>= 1; - flags_.set_nz(instruction_buffer_.value, m_shift_); + flags_.set_nz(data_buffer_.value, m_shift_); break; case ROL: data_buffer_.value = (data_buffer_.value << 1) | flags_.carry; flags_.carry = data_buffer_.value >> (7 + m_shift_); - flags_.set_nz(instruction_buffer_.value, m_shift_); + flags_.set_nz(data_buffer_.value, m_shift_); break; case ROR: { const uint8_t next_carry = data_buffer_.value & 1; data_buffer_.value = (data_buffer_.value >> 1) | (flags_.carry << (7 + m_shift_)); flags_.carry = next_carry; - flags_.set_nz(instruction_buffer_.value, m_shift_); + flags_.set_nz(data_buffer_.value, m_shift_); } break; // diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index c83963d30..7968e27b0 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -405,6 +405,12 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPCThrowaway); // IO. + target(CycleFetchIncrementData); // AAL + target(CycleFetchData); // AAH + + target(OperationCopyDataToInstruction); + target(OperationConstructAbsolute); + read_write(type, is8bit, target); } From 290598429a526ef3642eb33dd7fac194d133d803 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 23:22:48 -0400 Subject: [PATCH 090/150] Applies indirect page zero emulation mode addressing constraint to ix addressing. Lorenz's LDA tests now pass in emulation mode. --- OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift | 5 +++++ Processors/65816/Implementation/65816Implementation.hpp | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 7f9e6f7c1..a7233d8e4 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -256,6 +256,11 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { machine.setValue(0x0801, for: .programCounter) machine.setValue(0xfd, for: .stackPointer) machine.setValue(0x04, for: .flags) + + // For consistency when debugging; otherwise immaterial. + machine.setValue(0x00, for: .A) + machine.setValue(0x00, for: .X) + machine.setValue(0x00, for: .Y) } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index d8b11676b..59ceebd56 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -249,7 +249,10 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirectIndexedIndirect: - data_address_ = data_bank_ + (direct_ + x() + instruction_buffer_.value) & 0xffff; + data_address_ = data_bank_ + ( + ((direct_ + x() + instruction_buffer_.value) & e_masks_[1]) + + (direct_ & e_masks_[0]) + ) & 0xffff; if(!(direct_&0xff)) { ++next_op_; } From d9be6ab8065164b7549d1e5ad3a7b9a41d9c13af Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Oct 2020 23:26:35 -0400 Subject: [PATCH 091/150] Confirms that a few other simple tests work immediately on the 65816. --- .../Clock SignalTests/WolfgangLorenzTests.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index a7233d8e4..76f572166 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -17,6 +17,21 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { func testWolfgangLorenzLDA65816() { self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) } + func testWolfgangLorenzSTA65816() { + self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + func testWolfgangLorenzLDX65816() { + self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor65816) + } + func testWolfgangLorenzSTX65816() { + self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor65816) + } + func testWolfgangLorenzLDY65816() { + self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor65816) + } + func testWolfgangLorenzSTY65816() { + self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor65816) + } func testWolfgangLorenzStart() { self.runWolfgangLorenzTest(" start", processor: .processor6502) From a02f88fe7cd339ae5d796dd14cafc45e041c37a8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 09:34:29 -0400 Subject: [PATCH 092/150] Confirms a couple more of the easy sets. --- .../WolfgangLorenzTests.swift | 63 +++++++++++-------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 76f572166..a0cf7d9c0 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -10,63 +10,76 @@ import XCTest import Foundation class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { + func testWolfgangLorenzTransfers(processor: CSTestMachine6502Processor) { + self.runWolfgangLorenzTest("taxn", processor: processor) + self.runWolfgangLorenzTest("tayn", processor: processor) + self.runWolfgangLorenzTest("txan", processor: processor) + self.runWolfgangLorenzTest("tyan", processor: processor) + self.runWolfgangLorenzTest("tsxn", processor: processor) + self.runWolfgangLorenzTest("txsn", processor: processor) + } + func testStack(processor: CSTestMachine6502Processor) { + self.runWolfgangLorenzTest("phan", processor: processor) + self.runWolfgangLorenzTest("plan", processor: processor) + self.runWolfgangLorenzTest("phpn", processor: processor) + self.runWolfgangLorenzTest("plpn", processor: processor) + } + func testWolfgangLorenzStart65816() { - self.runWolfgangLorenzTest(" start", processor: .processor65816) + runWolfgangLorenzTest(" start", processor: .processor65816) } func testWolfgangLorenzLDA65816() { - self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) } func testWolfgangLorenzSTA65816() { - self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) } func testWolfgangLorenzLDX65816() { - self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor65816) + runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor65816) } func testWolfgangLorenzSTX65816() { - self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor65816) + runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor65816) } func testWolfgangLorenzLDY65816() { - self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor65816) + runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor65816) } func testWolfgangLorenzSTY65816() { - self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor65816) + runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor65816) + } + func testWolfgangLorenzTransfers65816() { + testWolfgangLorenzTransfers(processor: .processor65816) + } + func testStack65816() { + testStack(processor: .processor65816) } func testWolfgangLorenzStart() { - self.runWolfgangLorenzTest(" start", processor: .processor6502) + runWolfgangLorenzTest(" start", processor: .processor6502) } func testWolfgangLorenzLDA() { - self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzSTA() { - self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testWolfgangLorenzLDX() { - self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502) + runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502) } func testWolfgangLorenzSTX() { - self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502) + runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502) } func testWolfgangLorenzLDY() { - self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502) + runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502) } func testWolfgangLorenzSTY() { - self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502) + runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502) } func testWolfgangLorenzTransfers() { - self.runWolfgangLorenzTest("taxn", processor: .processor6502) - self.runWolfgangLorenzTest("tayn", processor: .processor6502) - self.runWolfgangLorenzTest("txan", processor: .processor6502) - self.runWolfgangLorenzTest("tyan", processor: .processor6502) - self.runWolfgangLorenzTest("tsxn", processor: .processor6502) - self.runWolfgangLorenzTest("txsn", processor: .processor6502) + testWolfgangLorenzTransfers(processor: .processor6502) } - func testWolfgangLorenzStack() { - self.runWolfgangLorenzTest("phan", processor: .processor6502) - self.runWolfgangLorenzTest("plan", processor: .processor6502) - self.runWolfgangLorenzTest("phpn", processor: .processor6502) - self.runWolfgangLorenzTest("plpn", processor: .processor6502) + func testStack() { + testStack(processor: .processor6502) } func testWolfgangLorenzIncsAndDecs() { self.runWolfgangLorenzTest("inxn", processor: .processor6502) From 536c4d45c163a55501c576775b6653918a6a0a6c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 10:11:57 -0400 Subject: [PATCH 093/150] Adds additional 65816 tests, some failing; seeks to improve carry behaviour in ASL and ROL. --- .../WolfgangLorenzTests.swift | 366 ++++++++++-------- .../Implementation/65816Implementation.hpp | 4 +- 2 files changed, 204 insertions(+), 166 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index a0cf7d9c0..ddbb299a1 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -10,226 +10,264 @@ import XCTest import Foundation class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { - func testWolfgangLorenzTransfers(processor: CSTestMachine6502Processor) { - self.runWolfgangLorenzTest("taxn", processor: processor) - self.runWolfgangLorenzTest("tayn", processor: processor) - self.runWolfgangLorenzTest("txan", processor: processor) - self.runWolfgangLorenzTest("tyan", processor: processor) - self.runWolfgangLorenzTest("tsxn", processor: processor) - self.runWolfgangLorenzTest("txsn", processor: processor) - } - func testStack(processor: CSTestMachine6502Processor) { - self.runWolfgangLorenzTest("phan", processor: processor) - self.runWolfgangLorenzTest("plan", processor: processor) - self.runWolfgangLorenzTest("phpn", processor: processor) - self.runWolfgangLorenzTest("plpn", processor: processor) - } + // MARK: - 6502 Tests - func testWolfgangLorenzStart65816() { - runWolfgangLorenzTest(" start", processor: .processor65816) + func testStart() { + runTest(" start", processor: .processor6502) } - func testWolfgangLorenzLDA65816() { - runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + func testLDA() { + runTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzSTA65816() { - runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + func testSTA() { + runTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzLDX65816() { - runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor65816) + func testLDX() { + runTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502) } - func testWolfgangLorenzSTX65816() { - runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor65816) + func testSTX() { + runTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502) } - func testWolfgangLorenzLDY65816() { - runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor65816) + func testLDY() { + runTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502) } - func testWolfgangLorenzSTY65816() { - runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor65816) + func testSTY() { + runTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502) } - func testWolfgangLorenzTransfers65816() { - testWolfgangLorenzTransfers(processor: .processor65816) - } - func testStack65816() { - testStack(processor: .processor65816) - } - - func testWolfgangLorenzStart() { - runWolfgangLorenzTest(" start", processor: .processor6502) - } - func testWolfgangLorenzLDA() { - runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) - } - func testWolfgangLorenzSTA() { - runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) - } - func testWolfgangLorenzLDX() { - runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502) - } - func testWolfgangLorenzSTX() { - runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502) - } - func testWolfgangLorenzLDY() { - runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502) - } - func testWolfgangLorenzSTY() { - runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502) - } - func testWolfgangLorenzTransfers() { - testWolfgangLorenzTransfers(processor: .processor6502) + func testTransfers() { + testTransfers(processor: .processor6502) } func testStack() { testStack(processor: .processor6502) } - func testWolfgangLorenzIncsAndDecs() { - self.runWolfgangLorenzTest("inxn", processor: .processor6502) - self.runWolfgangLorenzTest("inyn", processor: .processor6502) - self.runWolfgangLorenzTest("dexn", processor: .processor6502) - self.runWolfgangLorenzTest("deyn", processor: .processor6502) - self.runWolfgangLorenzTest("incz", processor: .processor6502) - self.runWolfgangLorenzTest("inczx", processor: .processor6502) - self.runWolfgangLorenzTest("inca", processor: .processor6502) - self.runWolfgangLorenzTest("incax", processor: .processor6502) - self.runWolfgangLorenzTest("decz", processor: .processor6502) - self.runWolfgangLorenzTest("deczx", processor: .processor6502) - self.runWolfgangLorenzTest("deca", processor: .processor6502) - self.runWolfgangLorenzTest("decax", processor: .processor6502) + func testIncsAndDecs() { + testIncsAndDecs(processor: .processor6502) } - func testWolfgangLorenzASL() { - self.runWolfgangLorenzTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + func testASL() { + self.runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } - func testWolfgangLorenzLSR() { - self.runWolfgangLorenzTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + func testLSR() { + self.runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } - func testWolfgangLorenzROL() { - self.runWolfgangLorenzTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + func testROL() { + self.runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } - func testWolfgangLorenzROR() { - self.runWolfgangLorenzTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + func testROR() { + self.runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } - func testWolfgangLorenzAND() { - self.runWolfgangLorenzTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testAND() { + self.runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzORA() { - self.runWolfgangLorenzTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testORA() { + self.runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzEOR() { - self.runWolfgangLorenzTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testEOR() { + self.runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzFlagManipulation() { - self.runWolfgangLorenzTest("clcn", processor: .processor6502) - self.runWolfgangLorenzTest("secn", processor: .processor6502) - self.runWolfgangLorenzTest("cldn", processor: .processor6502) - self.runWolfgangLorenzTest("sedn", processor: .processor6502) - self.runWolfgangLorenzTest("clin", processor: .processor6502) - self.runWolfgangLorenzTest("sein", processor: .processor6502) - self.runWolfgangLorenzTest("clvn", processor: .processor6502) + func testFlagManipulation() { + self.runTest("clcn", processor: .processor6502) + self.runTest("secn", processor: .processor6502) + self.runTest("cldn", processor: .processor6502) + self.runTest("sedn", processor: .processor6502) + self.runTest("clin", processor: .processor6502) + self.runTest("sein", processor: .processor6502) + self.runTest("clvn", processor: .processor6502) } - func testWolfgangLorenzADC() { - self.runWolfgangLorenzTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testADC() { + self.runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzSBC() { - self.runWolfgangLorenzTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testSBC() { + self.runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzCompare() { - self.runWolfgangLorenzTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) - self.runWolfgangLorenzTest("cpx", suffixes: ["b", "z", "a"], processor: .processor6502) - self.runWolfgangLorenzTest("cpy", suffixes: ["b", "z", "a"], processor: .processor6502) + func testCompare() { + self.runTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + self.runTest("cpx", suffixes: ["b", "z", "a"], processor: .processor6502) + self.runTest("cpy", suffixes: ["b", "z", "a"], processor: .processor6502) } - func testWolfgangLorenzBIT() { - self.runWolfgangLorenzTest("bit", suffixes: ["z", "a"], processor: .processor6502) + func testBIT() { + self.runTest("bit", suffixes: ["z", "a"], processor: .processor6502) } - func testWolfgangLorenzFlow() { - self.runWolfgangLorenzTest("brkn", processor: .processor6502) - self.runWolfgangLorenzTest("rtin", processor: .processor6502) - self.runWolfgangLorenzTest("jsrw", processor: .processor6502) - self.runWolfgangLorenzTest("rtsn", processor: .processor6502) - self.runWolfgangLorenzTest("jmpw", processor: .processor6502) - self.runWolfgangLorenzTest("jmpi", processor: .processor6502) + func testFlow() { + self.runTest("brkn", processor: .processor6502) + self.runTest("rtin", processor: .processor6502) + self.runTest("jsrw", processor: .processor6502) + self.runTest("rtsn", processor: .processor6502) + self.runTest("jmpw", processor: .processor6502) + self.runTest("jmpi", processor: .processor6502) } - func testWolfgangLorenzBranch() { - self.runWolfgangLorenzTest("beqr", processor: .processor6502) - self.runWolfgangLorenzTest("bner", processor: .processor6502) - self.runWolfgangLorenzTest("bmir", processor: .processor6502) - self.runWolfgangLorenzTest("bplr", processor: .processor6502) - self.runWolfgangLorenzTest("bcsr", processor: .processor6502) - self.runWolfgangLorenzTest("bccr", processor: .processor6502) - self.runWolfgangLorenzTest("bvsr", processor: .processor6502) - self.runWolfgangLorenzTest("bvcr", processor: .processor6502) + func testBranch() { + self.runTest("beqr", processor: .processor6502) + self.runTest("bner", processor: .processor6502) + self.runTest("bmir", processor: .processor6502) + self.runTest("bplr", processor: .processor6502) + self.runTest("bcsr", processor: .processor6502) + self.runTest("bccr", processor: .processor6502) + self.runTest("bvsr", processor: .processor6502) + self.runTest("bvcr", processor: .processor6502) } - func testWolfgangLorenzNOP() { - self.runWolfgangLorenzTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502) + func testNOP() { + self.runTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502) } - func testWolfgangLorenzASO() { - self.runWolfgangLorenzTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testASO() { + self.runTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzRLA() { - self.runWolfgangLorenzTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testRLA() { + self.runTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzLSE() { - self.runWolfgangLorenzTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testLSE() { + self.runTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzRRA() { - self.runWolfgangLorenzTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testRRA() { + self.runTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzDCM() { - self.runWolfgangLorenzTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testDCM() { + self.runTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzINS() { - self.runWolfgangLorenzTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + func testINS() { + self.runTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzLAX() { - self.runWolfgangLorenzTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502) + func testLAX() { + self.runTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502) } - func testWolfgangLorenzAXS() { - self.runWolfgangLorenzTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502) + func testAXS() { + self.runTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502) } - func testWolfgangLorenzALR() { - self.runWolfgangLorenzTest("alrb", processor: .processor6502) + func testALR() { + self.runTest("alrb", processor: .processor6502) } - func testWolfgangLorenzARR() { - self.runWolfgangLorenzTest("arrb", processor: .processor6502) + func testARR() { + self.runTest("arrb", processor: .processor6502) } - func testWolfgangLorenzSBX() { - self.runWolfgangLorenzTest("sbxb", processor: .processor6502) + func testSBX() { + self.runTest("sbxb", processor: .processor6502) } - func testWolfgangLorenzSHA() { - self.runWolfgangLorenzTest("sha", suffixes: ["ay", "iy"], processor: .processor6502) + func testSHA() { + self.runTest("sha", suffixes: ["ay", "iy"], processor: .processor6502) } - func testWolfgangLorenzSHX() { - self.runWolfgangLorenzTest("shxay", processor: .processor6502) + func testSHX() { + self.runTest("shxay", processor: .processor6502) } - func testWolfgangLorenzSHY() { - self.runWolfgangLorenzTest("shyax", processor: .processor6502) + func testSHY() { + self.runTest("shyax", processor: .processor6502) } - func testWolfgangLorenzSHS() { - self.runWolfgangLorenzTest("shsay", processor: .processor6502) + func testSHS() { + self.runTest("shsay", processor: .processor6502) } - func testWolfgangLorenzLXA() { - self.runWolfgangLorenzTest("lxab", processor: .processor6502) + func testLXA() { + self.runTest("lxab", processor: .processor6502) } - func testWolfgangLorenzANE() { - self.runWolfgangLorenzTest("aneb", processor: .processor6502) + func testANE() { + self.runTest("aneb", processor: .processor6502) } - func testWolfgangLorenzANC() { - self.runWolfgangLorenzTest("ancb", processor: .processor6502) + func testANC() { + self.runTest("ancb", processor: .processor6502) } - func testWolfgangLorenzLAS() { - self.runWolfgangLorenzTest("lasay", processor: .processor6502) + func testLAS() { + self.runTest("lasay", processor: .processor6502) } - func testWolfgangLorenzSBCB() { - self.runWolfgangLorenzTest("sbcb(eb)", processor: .processor6502) + func testSBCB() { + self.runTest("sbcb(eb)", processor: .processor6502) } - fileprivate func runWolfgangLorenzTest(_ name: String, suffixes: [String], processor: CSTestMachine6502Processor) { + + // MARK: - 65816 Tests + + func testStart65816() { + runTest(" start", processor: .processor65816) + } + func testLDA65816() { + runTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + func testSTA65816() { + runTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + func testLDX65816() { + runTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor65816) + } + func testSTX65816() { + runTest("stx", suffixes: ["z", "zy", "a"], processor: .processor65816) + } + func testLDY65816() { + runTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor65816) + } + func testSTY65816() { + runTest("sty", suffixes: ["z", "zx", "a"], processor: .processor65816) + } + func testTransfers65816() { + testTransfers(processor: .processor65816) + } + func testStack65816() { + testStack(processor: .processor65816) + } + func testIncsAndDecs65816() { + testIncsAndDecs(processor: .processor65816) + } + func testASL65816() { + self.runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + } + func testLSR65816() { + self.runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + } + func testROL65816() { + self.runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + } + func testROR65816() { + self.runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + } + func testAND65816() { + self.runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + func testORA65816() { + self.runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + func testEOR65816() { + self.runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + + + // MARK: - Collections + + func testTransfers(processor: CSTestMachine6502Processor) { + self.runTest("taxn", processor: processor) + self.runTest("tayn", processor: processor) + self.runTest("txan", processor: processor) + self.runTest("tyan", processor: processor) + self.runTest("tsxn", processor: processor) + self.runTest("txsn", processor: processor) + } + func testStack(processor: CSTestMachine6502Processor) { + self.runTest("phan", processor: processor) + self.runTest("plan", processor: processor) + self.runTest("phpn", processor: processor) + self.runTest("plpn", processor: processor) + } + func testIncsAndDecs(processor: CSTestMachine6502Processor) { + self.runTest("inxn", processor: processor) + self.runTest("inyn", processor: processor) + self.runTest("dexn", processor: processor) + self.runTest("deyn", processor: processor) + self.runTest("incz", processor: processor) + self.runTest("inczx", processor: processor) + self.runTest("inca", processor: processor) + self.runTest("incax", processor: processor) + self.runTest("decz", processor: processor) + self.runTest("deczx", processor: processor) + self.runTest("deca", processor: processor) + self.runTest("decax", processor: processor) + } + + + // MARK: - Test Running + + fileprivate func runTest(_ name: String, suffixes: [String], processor: CSTestMachine6502Processor) { for suffix in suffixes { let testName = name + suffix - self.runWolfgangLorenzTest(testName, processor: processor) + self.runTest(testName, processor: processor) } } fileprivate var output: String = "" - fileprivate func runWolfgangLorenzTest(_ name: String, processor: CSTestMachine6502Processor) { + fileprivate func runTest(_ name: String, processor: CSTestMachine6502Processor) { var machine: CSTestMachine6502! if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) { diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 59ceebd56..d1bb9b12b 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -674,7 +674,7 @@ template void Processor::run_for(const Cycles // case ASL: - flags_.carry = data_buffer_.value >> (7 + m_shift_); + flags_.carry = (data_buffer_.value >> (7 + m_shift_)) & 1; data_buffer_.value <<= 1; flags_.set_nz(data_buffer_.value, m_shift_); break; @@ -687,7 +687,7 @@ template void Processor::run_for(const Cycles case ROL: data_buffer_.value = (data_buffer_.value << 1) | flags_.carry; - flags_.carry = data_buffer_.value >> (7 + m_shift_); + flags_.carry = (data_buffer_.value >> (7 + m_shift_)) & 1; flags_.set_nz(data_buffer_.value, m_shift_); break; From 6efe4e17530f997ab04ba9896fe2d12c756cb69b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 10:53:17 -0400 Subject: [PATCH 094/150] Fixes AND, EOR, ORA. Takes an unsuccessful shot at ROL. --- .../WolfgangLorenzTests.swift | 18 ++++++++++++------ .../Implementation/65816Implementation.hpp | 10 +++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index ddbb299a1..4c22c4b4d 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -228,12 +228,9 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { // MARK: - Collections func testTransfers(processor: CSTestMachine6502Processor) { - self.runTest("taxn", processor: processor) - self.runTest("tayn", processor: processor) - self.runTest("txan", processor: processor) - self.runTest("tyan", processor: processor) - self.runTest("tsxn", processor: processor) - self.runTest("txsn", processor: processor) + for test in ["taxn", "tayn", "txan", "tyan", "tsxn", "txsn"] { + runTest(test, processor: processor) + } } func testStack(processor: CSTestMachine6502Processor) { self.runTest("phan", processor: processor) @@ -255,6 +252,15 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { self.runTest("deca", processor: processor) self.runTest("decax", processor: processor) } + func testFlagManipulation(processor: CSTestMachine6502Processor) { + self.runTest("clcn", processor: processor) + self.runTest("secn", processor: processor) + self.runTest("cldn", processor: processor) + self.runTest("sedn", processor: processor) + self.runTest("clin", processor: processor) + self.runTest("sein", processor: processor) + self.runTest("clvn", processor: processor) + } // MARK: - Test Running diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index d1bb9b12b..697cda0e0 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -614,17 +614,17 @@ template void Processor::run_for(const Cycles // case AND: - a_.full &= instruction_buffer_.value | m_masks_[0]; + a_.full &= data_buffer_.value | m_masks_[0]; flags_.set_nz(a_.full, m_shift_); break; case EOR: - a_.full ^= instruction_buffer_.value; + a_.full ^= data_buffer_.value; flags_.set_nz(a_.full, m_shift_); break; case ORA: - a_.full |= instruction_buffer_.value; + a_.full |= data_buffer_.value; flags_.set_nz(a_.full, m_shift_); break; @@ -674,7 +674,7 @@ template void Processor::run_for(const Cycles // case ASL: - flags_.carry = (data_buffer_.value >> (7 + m_shift_)) & 1; + flags_.carry = data_buffer_.value >> (7 + m_shift_); data_buffer_.value <<= 1; flags_.set_nz(data_buffer_.value, m_shift_); break; @@ -687,7 +687,7 @@ template void Processor::run_for(const Cycles case ROL: data_buffer_.value = (data_buffer_.value << 1) | flags_.carry; - flags_.carry = (data_buffer_.value >> (7 + m_shift_)) & 1; + flags_.carry = data_buffer_.value >> (8 + m_shift_); flags_.set_nz(data_buffer_.value, m_shift_); break; From 7966592fae35818d255a103e2f68392ff39db9f4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 11:22:23 -0400 Subject: [PATCH 095/150] Corrects ROL d. --- .../WolfgangLorenzTests.swift | 174 +++++++++--------- Processors/6502/AllRAM/6502AllRAM.cpp | 4 +- .../65816/Implementation/65816Storage.cpp | 2 +- 3 files changed, 90 insertions(+), 90 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 4c22c4b4d..f8200bcee 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -44,129 +44,129 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { testIncsAndDecs(processor: .processor6502) } func testASL() { - self.runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testLSR() { - self.runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testROL() { - self.runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testROR() { - self.runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) + runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502) } func testAND() { - self.runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testORA() { - self.runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testEOR() { - self.runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testFlagManipulation() { - self.runTest("clcn", processor: .processor6502) - self.runTest("secn", processor: .processor6502) - self.runTest("cldn", processor: .processor6502) - self.runTest("sedn", processor: .processor6502) - self.runTest("clin", processor: .processor6502) - self.runTest("sein", processor: .processor6502) - self.runTest("clvn", processor: .processor6502) + runTest("clcn", processor: .processor6502) + runTest("secn", processor: .processor6502) + runTest("cldn", processor: .processor6502) + runTest("sedn", processor: .processor6502) + runTest("clin", processor: .processor6502) + runTest("sein", processor: .processor6502) + runTest("clvn", processor: .processor6502) } func testADC() { - self.runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testSBC() { - self.runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testCompare() { - self.runTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) - self.runTest("cpx", suffixes: ["b", "z", "a"], processor: .processor6502) - self.runTest("cpy", suffixes: ["b", "z", "a"], processor: .processor6502) + runTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("cpx", suffixes: ["b", "z", "a"], processor: .processor6502) + runTest("cpy", suffixes: ["b", "z", "a"], processor: .processor6502) } func testBIT() { - self.runTest("bit", suffixes: ["z", "a"], processor: .processor6502) + runTest("bit", suffixes: ["z", "a"], processor: .processor6502) } func testFlow() { - self.runTest("brkn", processor: .processor6502) - self.runTest("rtin", processor: .processor6502) - self.runTest("jsrw", processor: .processor6502) - self.runTest("rtsn", processor: .processor6502) - self.runTest("jmpw", processor: .processor6502) - self.runTest("jmpi", processor: .processor6502) + runTest("brkn", processor: .processor6502) + runTest("rtin", processor: .processor6502) + runTest("jsrw", processor: .processor6502) + runTest("rtsn", processor: .processor6502) + runTest("jmpw", processor: .processor6502) + runTest("jmpi", processor: .processor6502) } func testBranch() { - self.runTest("beqr", processor: .processor6502) - self.runTest("bner", processor: .processor6502) - self.runTest("bmir", processor: .processor6502) - self.runTest("bplr", processor: .processor6502) - self.runTest("bcsr", processor: .processor6502) - self.runTest("bccr", processor: .processor6502) - self.runTest("bvsr", processor: .processor6502) - self.runTest("bvcr", processor: .processor6502) + runTest("beqr", processor: .processor6502) + runTest("bner", processor: .processor6502) + runTest("bmir", processor: .processor6502) + runTest("bplr", processor: .processor6502) + runTest("bcsr", processor: .processor6502) + runTest("bccr", processor: .processor6502) + runTest("bvsr", processor: .processor6502) + runTest("bvcr", processor: .processor6502) } func testNOP() { - self.runTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502) + runTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502) } func testASO() { - self.runTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testRLA() { - self.runTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testLSE() { - self.runTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testRRA() { - self.runTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testDCM() { - self.runTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testINS() { - self.runTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) + runTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testLAX() { - self.runTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502) + runTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502) } func testAXS() { - self.runTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502) + runTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502) } func testALR() { - self.runTest("alrb", processor: .processor6502) + runTest("alrb", processor: .processor6502) } func testARR() { - self.runTest("arrb", processor: .processor6502) + runTest("arrb", processor: .processor6502) } func testSBX() { - self.runTest("sbxb", processor: .processor6502) + runTest("sbxb", processor: .processor6502) } func testSHA() { - self.runTest("sha", suffixes: ["ay", "iy"], processor: .processor6502) + runTest("sha", suffixes: ["ay", "iy"], processor: .processor6502) } func testSHX() { - self.runTest("shxay", processor: .processor6502) + runTest("shxay", processor: .processor6502) } func testSHY() { - self.runTest("shyax", processor: .processor6502) + runTest("shyax", processor: .processor6502) } func testSHS() { - self.runTest("shsay", processor: .processor6502) + runTest("shsay", processor: .processor6502) } func testLXA() { - self.runTest("lxab", processor: .processor6502) + runTest("lxab", processor: .processor6502) } func testANE() { - self.runTest("aneb", processor: .processor6502) + runTest("aneb", processor: .processor6502) } func testANC() { - self.runTest("ancb", processor: .processor6502) + runTest("ancb", processor: .processor6502) } func testLAS() { - self.runTest("lasay", processor: .processor6502) + runTest("lasay", processor: .processor6502) } func testSBCB() { - self.runTest("sbcb(eb)", processor: .processor6502) + runTest("sbcb(eb)", processor: .processor6502) } @@ -203,25 +203,25 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { testIncsAndDecs(processor: .processor65816) } func testASL65816() { - self.runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) } func testLSR65816() { - self.runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) } func testROL65816() { - self.runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) } func testROR65816() { - self.runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) + runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816) } func testAND65816() { - self.runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) } func testORA65816() { - self.runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) } func testEOR65816() { - self.runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) } @@ -233,33 +233,33 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { } } func testStack(processor: CSTestMachine6502Processor) { - self.runTest("phan", processor: processor) - self.runTest("plan", processor: processor) - self.runTest("phpn", processor: processor) - self.runTest("plpn", processor: processor) + runTest("phan", processor: processor) + runTest("plan", processor: processor) + runTest("phpn", processor: processor) + runTest("plpn", processor: processor) } func testIncsAndDecs(processor: CSTestMachine6502Processor) { - self.runTest("inxn", processor: processor) - self.runTest("inyn", processor: processor) - self.runTest("dexn", processor: processor) - self.runTest("deyn", processor: processor) - self.runTest("incz", processor: processor) - self.runTest("inczx", processor: processor) - self.runTest("inca", processor: processor) - self.runTest("incax", processor: processor) - self.runTest("decz", processor: processor) - self.runTest("deczx", processor: processor) - self.runTest("deca", processor: processor) - self.runTest("decax", processor: processor) + runTest("inxn", processor: processor) + runTest("inyn", processor: processor) + runTest("dexn", processor: processor) + runTest("deyn", processor: processor) + runTest("incz", processor: processor) + runTest("inczx", processor: processor) + runTest("inca", processor: processor) + runTest("incax", processor: processor) + runTest("decz", processor: processor) + runTest("deczx", processor: processor) + runTest("deca", processor: processor) + runTest("decax", processor: processor) } func testFlagManipulation(processor: CSTestMachine6502Processor) { - self.runTest("clcn", processor: processor) - self.runTest("secn", processor: processor) - self.runTest("cldn", processor: processor) - self.runTest("sedn", processor: processor) - self.runTest("clin", processor: processor) - self.runTest("sein", processor: processor) - self.runTest("clvn", processor: processor) + runTest("clcn", processor: processor) + runTest("secn", processor: processor) + runTest("cldn", processor: processor) + runTest("sedn", processor: processor) + runTest("clin", processor: processor) + runTest("sein", processor: processor) + runTest("clvn", processor: processor) } @@ -268,7 +268,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { fileprivate func runTest(_ name: String, suffixes: [String], processor: CSTestMachine6502Processor) { for suffix in suffixes { let testName = name + suffix - self.runTest(testName, processor: processor) + runTest(testName, processor: processor) } } @@ -405,7 +405,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { testMachine6502.setValue(0x3, for:CSTestMachine6502Register.A) case 0x8000, 0xa474: - NSException(name: NSExceptionName(rawValue: "Failed test"), reason: self.petsciiToString(output), userInfo: nil).raise() + NSException(name: NSExceptionName(rawValue: "Failed test"), reason: petsciiToString(output), userInfo: nil).raise() case 0x0000: NSException(name: NSExceptionName(rawValue: "Failed test"), reason: "Execution hit 0000", userInfo: nil).raise() diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 723bc98b6..49344126d 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -45,14 +45,14 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ *value = memory_[address]; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { -// printf("%04x -> %02x\n", address, *value); + printf("%04x -> %02x\n", address, *value); // } #endif } else { memory_[address] = *value; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { -// printf("%04x <- %02x\n", address, *value); + printf("%04x <- %02x\n", address, *value); // } #endif } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 7968e27b0..3785aa523 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -777,7 +777,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x23 AND d, s */ op(stack_relative, AND); /* 0x24 BIT d */ op(direct, BIT); /* 0x25 AND d */ op(direct, AND); - /* 0x26 ROL d */ op(absolute_rmw, ROL); + /* 0x26 ROL d */ op(direct_rmw, ROL); /* 0x27 AND [d] */ op(direct_indirect_long, AND); /* 0x28 PLP s */ op(stack_pull, PLP); /* 0x29 AND # */ op(immediate, AND); From d17c90edf76fbb060aa6577196b969d1e66de681 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 11:25:14 -0400 Subject: [PATCH 096/150] Corrects ROL d, x. --- Processors/65816/Implementation/65816Storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 3785aa523..930763b96 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -794,7 +794,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x33 AND (d, s), y */ op(stack_relative_indexed_indirect, AND); /* 0x34 BIT d, x */ op(direct_x, BIT); /* 0x35 AND d, x */ op(direct_x, AND); - /* 0x36 ROL d, x */ op(absolute_x_rmw, ROL); + /* 0x36 ROL d, x */ op(direct_x_rmw, ROL); /* 0x37 AND [d], y */ op(direct_indirect_indexed_long, AND); /* 0x38 SEC i */ op(implied, SEC); /* 0x39 AND a, y */ op(absolute_y, AND); From 09fba72d5801ad29e9eccc41fdc71534dfe61514 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 11:30:15 -0400 Subject: [PATCH 097/150] Adds flag manipulation, ADC and SBC 65816 tests. The latter two fail. --- .../WolfgangLorenzTests.swift | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index f8200bcee..8246bc658 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -65,13 +65,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testFlagManipulation() { - runTest("clcn", processor: .processor6502) - runTest("secn", processor: .processor6502) - runTest("cldn", processor: .processor6502) - runTest("sedn", processor: .processor6502) - runTest("clin", processor: .processor6502) - runTest("sein", processor: .processor6502) - runTest("clvn", processor: .processor6502) + testFlagManipulation(processor: .processor6502) } func testADC() { runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) @@ -223,6 +217,15 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { func testEOR65816() { runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) } + func testFlagManipulation65816() { + testFlagManipulation(processor: .processor65816) + } + func testADC65816() { + runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } + func testSBC65816() { + runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) + } // MARK: - Collections @@ -233,10 +236,9 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { } } func testStack(processor: CSTestMachine6502Processor) { - runTest("phan", processor: processor) - runTest("plan", processor: processor) - runTest("phpn", processor: processor) - runTest("plpn", processor: processor) + for test in ["phan", "plan", "phpn", "plpn"] { + runTest(test, processor: processor) + } } func testIncsAndDecs(processor: CSTestMachine6502Processor) { runTest("inxn", processor: processor) @@ -253,13 +255,9 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { runTest("decax", processor: processor) } func testFlagManipulation(processor: CSTestMachine6502Processor) { - runTest("clcn", processor: processor) - runTest("secn", processor: processor) - runTest("cldn", processor: processor) - runTest("sedn", processor: processor) - runTest("clin", processor: processor) - runTest("sein", processor: processor) - runTest("clvn", processor: processor) + for test in ["clcn", "secn", "cldn", "sedn", "clin", "sein", "clvn"] { + runTest(test, processor: processor) + } } From da4702851f0c0c06bfd9f1e4a4c6927e450116fe Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 16:29:48 -0400 Subject: [PATCH 098/150] Fixes ADC. --- Processors/65816/Implementation/65816Implementation.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 697cda0e0..490ef6a67 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -733,9 +733,9 @@ template void Processor::run_for(const Cycles if(result >= limit) result = ((result + (addition)) & (carry - 1)) + carry; nibble(0x000f, 0x000a, nibble_adjustment << 0, 0x00010); - nibble(0x00f0, 0x00a0, nibble_adjustment << 8, 0x00100); - nibble(0x0f00, 0x0a00, nibble_adjustment << 16, 0x01000); - nibble(0xf000, 0xa000, nibble_adjustment << 24, 0x10000); + nibble(0x00f0, 0x00a0, nibble_adjustment << 4, 0x00100); + nibble(0x0f00, 0x0a00, nibble_adjustment << 8, 0x01000); + nibble(0xf000, 0xa000, nibble_adjustment << 12, 0x10000); #undef nibble From 0fe09cd1e4ad501ac9da04f4fad3e341c32cb31b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 17:13:16 -0400 Subject: [PATCH 099/150] Knocks SBC into producing likely results; disables Lorenz testing. --- .../WolfgangLorenzTests.swift | 10 ++--- .../Implementation/65816Implementation.hpp | 43 +++++++++++++++---- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 8246bc658..8107a4154 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -220,12 +220,10 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { func testFlagManipulation65816() { testFlagManipulation(processor: .processor65816) } - func testADC65816() { - runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) - } - func testSBC65816() { - runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816) - } + /* + ADC and SBC tests don't apply, as the 65816 follows the 65C02 in setting flags based on the outcome of decimal + results, whereas Lorenzz's tests expect the original 6502 behaviour of setting flags based an intermediate value. + */ // MARK: - Collections diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 490ef6a67..87f945e1d 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -715,6 +715,34 @@ template void Processor::run_for(const Cycles #undef cp case SBC: + if(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 = a_.full & m_masks_[1]; + unsigned int result = 0; + unsigned int borrow = flags_.carry ^ 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); + + nibble(0x000f, 0x0006, 0x00010); + nibble(0x00f0, 0x0060, 0x00100); + nibble(0x0f00, 0x0600, 0x01000); + nibble(0xf000, 0x6000, 0x10000); + +#undef nibble + + flags_.overflow = ~(( (result ^ a_.full) & (result ^ data_buffer_.value) ) >> (1 + m_shift_))&0x40; + flags_.set_nz(result, m_shift_); + flags_.carry = ((borrow >> 16)&1)^1; + LD(a_, result, m_masks_); + + break; + } + data_buffer_.value = ~data_buffer_.value & m_masks_[1]; [[fallthrough]]; @@ -724,18 +752,15 @@ template void Processor::run_for(const Cycles if(flags_.decimal) { result = flags_.carry; - const int nibble_adjustment = (active_instruction_->operation == SBC) ? 0xa : 0x6; - // TODO: this still isn't quite correct for SBC as the limit test is wrong, I think. - -#define nibble(mask, limit, addition, carry) \ +#define nibble(mask, limit, adjustment, carry) \ result += (a & mask) + (data_buffer_.value & mask); \ - if(result >= limit) result = ((result + (addition)) & (carry - 1)) + carry; + if(result >= limit) result = ((result + (adjustment)) & (carry - 1)) + carry; - nibble(0x000f, 0x000a, nibble_adjustment << 0, 0x00010); - nibble(0x00f0, 0x00a0, nibble_adjustment << 4, 0x00100); - nibble(0x0f00, 0x0a00, nibble_adjustment << 8, 0x01000); - nibble(0xf000, 0xa000, nibble_adjustment << 12, 0x10000); + nibble(0x000f, 0x000a, 0x0006, 0x00010); + nibble(0x00f0, 0x00a0, 0x0060, 0x00100); + nibble(0x0f00, 0x0a00, 0x0600, 0x01000); + nibble(0xf000, 0xa000, 0x6000, 0x10000); #undef nibble From 340ad093a6d2fd6e82ccd6719b2e04fc331f6838 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 17:26:41 -0400 Subject: [PATCH 100/150] Adds 65816 runs of the final tranche of applicable tests. --- .../WolfgangLorenzTests.swift | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 8107a4154..4363f5952 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -74,30 +74,16 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) } func testCompare() { - runTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502) - runTest("cpx", suffixes: ["b", "z", "a"], processor: .processor6502) - runTest("cpy", suffixes: ["b", "z", "a"], processor: .processor6502) + testCompare(processor: .processor6502) } func testBIT() { runTest("bit", suffixes: ["z", "a"], processor: .processor6502) } func testFlow() { - runTest("brkn", processor: .processor6502) - runTest("rtin", processor: .processor6502) - runTest("jsrw", processor: .processor6502) - runTest("rtsn", processor: .processor6502) - runTest("jmpw", processor: .processor6502) - runTest("jmpi", processor: .processor6502) + testFlow(processor: .processor6502) } func testBranch() { - runTest("beqr", processor: .processor6502) - runTest("bner", processor: .processor6502) - runTest("bmir", processor: .processor6502) - runTest("bplr", processor: .processor6502) - runTest("bcsr", processor: .processor6502) - runTest("bccr", processor: .processor6502) - runTest("bvsr", processor: .processor6502) - runTest("bvcr", processor: .processor6502) + testBranch(processor: .processor6502) } func testNOP() { runTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502) @@ -224,6 +210,19 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { ADC and SBC tests don't apply, as the 65816 follows the 65C02 in setting flags based on the outcome of decimal results, whereas Lorenzz's tests expect the original 6502 behaviour of setting flags based an intermediate value. */ + func testCompare65816() { + testCompare(processor: .processor65816) + } + func testBIT65816() { + runTest("bit", suffixes: ["z", "a"], processor: .processor65816) + } + func testFlow65816() { + testFlow(processor: .processor65816) + } + func testBranch65816() { + testBranch(processor: .processor65816) + } + /* The NOP tests also don't apply; the 65816 has only one, well-defined NOP (well, not counting COP or WDM). */ // MARK: - Collections @@ -239,24 +238,36 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { } } func testIncsAndDecs(processor: CSTestMachine6502Processor) { - runTest("inxn", processor: processor) - runTest("inyn", processor: processor) - runTest("dexn", processor: processor) - runTest("deyn", processor: processor) - runTest("incz", processor: processor) - runTest("inczx", processor: processor) - runTest("inca", processor: processor) - runTest("incax", processor: processor) - runTest("decz", processor: processor) - runTest("deczx", processor: processor) - runTest("deca", processor: processor) - runTest("decax", processor: processor) + for test in ["inxn", "inyn", "dexn", "deyn", "incz", "inczx", "inca", "incax", "decz", "deczx", "deca", "decax"] { + runTest(test, processor: processor) + } } func testFlagManipulation(processor: CSTestMachine6502Processor) { for test in ["clcn", "secn", "cldn", "sedn", "clin", "sein", "clvn"] { runTest(test, processor: processor) } } + func testCompare(processor: CSTestMachine6502Processor) { + runTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: processor) + runTest("cpx", suffixes: ["b", "z", "a"], processor: processor) + runTest("cpy", suffixes: ["b", "z", "a"], processor: processor) + } + func testFlow(processor: CSTestMachine6502Processor) { + for test in ["brkn", "rtin", "jsrw", "rtsn", "jmpw", "jmpi"] { + // JMP indirect is explicitly different on a 65C02 and 65816, not having + // the page wraparound bug. So run that test only on a plain 6502. + if test == "jmpi" && processor != .processor6502 { + continue; + } + + runTest(test, processor: processor) + } + } + func testBranch(processor: CSTestMachine6502Processor) { + for test in ["beqr", "bner", "bmir", "bplr", "bcsr", "bccr", "bvsr", "bvcr"] { + runTest(test, processor: processor) + } + } // MARK: - Test Running From 6892ac13e82405027332f0a008abfdc91c928e68 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 17:47:33 -0400 Subject: [PATCH 101/150] Corrects BIT. All 65816-applicable Wolfgang Lorenz tests now pass. --- Processors/6502/AllRAM/6502AllRAM.cpp | 4 ++-- Processors/6502Esque/Implementation/LazyFlags.hpp | 5 +++++ Processors/65816/Implementation/65816Implementation.hpp | 3 ++- Processors/65816/Implementation/65816Storage.cpp | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 49344126d..723bc98b6 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -45,14 +45,14 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ *value = memory_[address]; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { - printf("%04x -> %02x\n", address, *value); +// printf("%04x -> %02x\n", address, *value); // } #endif } else { memory_[address] = *value; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { - printf("%04x <- %02x\n", address, *value); +// printf("%04x <- %02x\n", address, *value); // } #endif } diff --git a/Processors/6502Esque/Implementation/LazyFlags.hpp b/Processors/6502Esque/Implementation/LazyFlags.hpp index f25d8c960..756d7a779 100644 --- a/Processors/6502Esque/Implementation/LazyFlags.hpp +++ b/Processors/6502Esque/Implementation/LazyFlags.hpp @@ -49,6 +49,11 @@ struct LazyFlags { zero_result = uint8_t(value | (value >> shift)); } + /// Sets the N flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value. + void set_n(uint16_t value, int shift) { + negative_result = uint8_t(value >> shift); + } + void set(uint8_t flags) { carry = flags & Flag::Carry; negative_result = flags & Flag::Sign; diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 87f945e1d..f1373538f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -629,7 +629,8 @@ template void Processor::run_for(const Cycles break; case BIT: - flags_.set_nz(data_buffer_.value & a_.full, m_shift_); + flags_.set_n(data_buffer_.value, m_shift_); + flags_.set_z(data_buffer_.value & a_.full, m_shift_); flags_.overflow = data_buffer_.value & Flag::Overflow; break; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 930763b96..39f78fa96 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -556,7 +556,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); // The branch instructions will all skip one or three // of the next cycles, depending on the effect of - // the jump. + // the jump. It'll also calculate the correct target + // address, placing it into the data buffer. target(CycleFetchPCThrowaway); // IO target(CycleFetchPCThrowaway); // IO From 486324ecabdc328eaf3d95d47bb1ad3a62485286 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 18:19:48 -0400 Subject: [PATCH 102/150] This test isn't actually 65816-compatible. --- OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index bf22e17eb..a4d577473 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -134,8 +134,5 @@ class KlausDormannTests: XCTestCase { runTest65C02(processor: .processor65C02) } - /// Runs Klaus Dormann's 65C02 tests on a 65816. - func test65816As65C02() { - runTest65C02(processor: .processor65816) - } + /* Dormann's 65C02 tests aren't applicable to the 65816; e.g. they test BBR/BBS, which the 65816 doesn't implement. */ } From 28c8ba70c10a1ad28d8167f76c8253a7c1d16abc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 21:23:59 -0400 Subject: [PATCH 103/150] Implements REP and SEP and exposes the MX flags generally. --- Processors/6502Esque/6502Esque.hpp | 6 +- .../Implementation/65816Implementation.hpp | 39 ++++++------ .../65816/Implementation/65816Storage.cpp | 62 ++++++++++++++++++- .../65816/Implementation/65816Storage.hpp | 6 ++ 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index 38749b00e..365a6df38 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -49,7 +49,11 @@ enum Flag: uint8_t { Decimal = 0x08, Interrupt = 0x04, Zero = 0x02, - Carry = 0x01 + Carry = 0x01, + + // These are available on a 65816 only. + MemorySize = 0x20, + IndexSize = 0x10, }; /*! diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index f1373538f..3ffb34ed0 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -318,10 +318,13 @@ template void Processor::run_for(const Cycles bool is_brk = false; + // TODO: probably this should have been decided in advance? And the interrupt flag + // needs to be factored in? + if(pending_exceptions_ & (Reset | PowerOn)) { - // TODO: set emulation mode, etc. pending_exceptions_ &= ~(Reset | PowerOn); data_address_ = 0xfffc; + set_reset_state(); } else if(pending_exceptions_ & NMI) { pending_exceptions_ &= ~NMI; data_address_ = 0xfffa; @@ -338,7 +341,7 @@ template void Processor::run_for(const Cycles } } - data_buffer_.value = (pc_ << 8) | flags_.get(); + data_buffer_.value = (pc_ << 8) | get_flags(); if(emulation_flag_) { if(is_brk) data_buffer_.value |= Flag::Break; data_buffer_.size = 3; @@ -347,8 +350,6 @@ template void Processor::run_for(const Cycles data_buffer_.value |= program_bank_ << 24; data_buffer_.size = 4; program_bank_ = 0; - - assert(false); // TODO: proper flags, still. } flags_.inverse_interrupt = 0; @@ -397,11 +398,7 @@ template void Processor::run_for(const Cycles break; case PLP: - flags_.set(data_buffer_.value); - - if(!emulation_flag_) { - assert(false); // TODO: M and X. - } + set_flags(data_buffer_.value); break; case STA: @@ -440,12 +437,10 @@ template void Processor::run_for(const Cycles break; case PHP: - data_buffer_.value = flags_.get(); + data_buffer_.value = get_flags(); data_buffer_.size = 1; - if(!emulation_flag_) { - assert(false); // TODO: M and X. - } else { + if(emulation_flag_) { // On the 6502, the break flag is set during a PHP. data_buffer_.value |= Flag::Break; } @@ -530,11 +525,10 @@ template void Processor::run_for(const Cycles case RTI: pc_ = uint16_t(data_buffer_.value >> 8); - flags_.set(uint8_t(data_buffer_.value)); + set_flags(uint8_t(data_buffer_.value)); if(!emulation_flag_) { program_bank_ = (data_buffer_.value & 0xff000000) >> 8; - assert(false); // Extra flags to unpack! } break; @@ -571,6 +565,14 @@ template void Processor::run_for(const Cycles case SEI: flags_.inverse_interrupt = 0; break; case SED: flags_.decimal = Flag::Decimal; break; + case REP: + set_flags(get_flags() &~ instruction_buffer_.value); + break; + + case SEP: + set_flags(get_flags() | instruction_buffer_.value); + break; + // // Increments and decrements. // @@ -777,7 +779,6 @@ template void Processor::run_for(const Cycles // TODO: // TRB, TSB, - // REP, SEP, // XCE, XBA, // STP, WAI, // RTL, @@ -787,9 +788,6 @@ template void Processor::run_for(const Cycles assert(false); } continue; - - default: - assert(false); } #undef LD @@ -798,6 +796,9 @@ template void Processor::run_for(const Cycles #undef y_top #undef a_top + // TODO: do some sort of evaluation here on whether an interrupt or similar is pending, + // react appropriately. + number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 39f78fa96..9ba262e7b 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -731,7 +731,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { }; ProcessorStorage::ProcessorStorage() { - a_.full = x_.full = y_.full = 0; // TEMPORARY (?) + set_reset_state(); ProcessorStorageConstructor constructor(*this); @@ -1023,3 +1023,63 @@ ProcessorStorage::ProcessorStorage() { printf("Generated %zd micro-ops in total; covered %d opcodes\n", micro_ops_.size(), constructor.opcode); #endif } + +void ProcessorStorage::set_reset_state() { + data_bank_ = 0; + program_bank_ = 0; + direct_ = 0; + flags_.decimal = 0; + flags_.inverse_interrupt = 0; + set_emulation_mode(true); +} + +void ProcessorStorage::set_emulation_mode(bool enabled) { + if(emulation_flag_ == enabled) { + return; + } + + if(emulation_flag_) { + set_m_x_flags(true, true); + x_.halves.high = y_.halves.high = 0; + e_masks_[0] = 0xff00; + e_masks_[1] = 0x00ff; + } else { + e_masks_[0] = 0x0000; + e_masks_[1] = 0xffff; + } + + emulation_flag_ = enabled; +} + +void ProcessorStorage::set_m_x_flags(bool m, bool x) { + mx_flags_[0] = m; + mx_flags_[1] = x; + + m_masks_[0] = m ? 0xff00 : 0x0000; + m_masks_[1] = m ? 0x00ff : 0xffff; + m_shift_ = m ? 0 : 8; + + x_masks_[0] = x ? 0xff00 : 0x0000; + x_masks_[1] = x ? 0x00ff : 0xffff; + x_shift_ = x ? 0 : 8; +} + +uint8_t ProcessorStorage::get_flags() { + uint8_t result = flags_.get(); + + if(!emulation_flag_) { + result &= ~(Flag::MemorySize | Flag::IndexSize); + result |= mx_flags_[0] * Flag::MemorySize; + result |= mx_flags_[1] * Flag::IndexSize; + } + + return result; +} + +void ProcessorStorage::set_flags(uint8_t value) { + flags_.set(value); + + if(!emulation_flag_) { + set_m_x_flags(value & Flag::MemorySize, value & Flag::IndexSize); + } +} diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 85964fa50..c6422a6b7 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -312,4 +312,10 @@ struct ProcessorStorage { std::vector micro_ops_; MicroOp *next_op_ = nullptr; + + void set_reset_state(); + void set_emulation_mode(bool); + void set_m_x_flags(bool m, bool x); + uint8_t get_flags(); + void set_flags(uint8_t); }; From ae877287700a741019a2f567b328a9771b61b027 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 21:33:56 -0400 Subject: [PATCH 104/150] Ensures M and X are exposed to the public interface. --- Processors/65816/Implementation/65816Base.cpp | 4 ++-- Processors/65816/Implementation/65816Storage.cpp | 2 +- Processors/65816/Implementation/65816Storage.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index 3416217f2..55f06aa4a 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -15,7 +15,7 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { case Register::ProgramCounter: return pc_; case Register::LastOperationAddress: return last_operation_pc_; case Register::StackPointer: return s_.full; - case Register::Flags: return flags_.get(); // TODO: include additional flags (and below). + case Register::Flags: return get_flags(); case Register::A: return a_.full; case Register::X: return x_.full; case Register::Y: return y_.full; @@ -27,7 +27,7 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { case Register::ProgramCounter: pc_ = value; break; case Register::StackPointer: s_.full = value; break; - case Register::Flags: flags_.set(uint8_t(value)); break; + case Register::Flags: set_flags(uint8_t(value)); break; case Register::A: a_.full = value; break; case Register::X: x_.full = value; break; case Register::Y: y_.full = value; break; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 9ba262e7b..472616e2e 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -1064,7 +1064,7 @@ void ProcessorStorage::set_m_x_flags(bool m, bool x) { x_shift_ = x ? 0 : 8; } -uint8_t ProcessorStorage::get_flags() { +uint8_t ProcessorStorage::get_flags() const { uint8_t result = flags_.get(); if(!emulation_flag_) { diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index c6422a6b7..011892c70 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -316,6 +316,6 @@ struct ProcessorStorage { void set_reset_state(); void set_emulation_mode(bool); void set_m_x_flags(bool m, bool x); - uint8_t get_flags(); + uint8_t get_flags() const; void set_flags(uint8_t); }; From aface1f8be32103bc705b73a147e6073c5b6d7c2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 21:34:22 -0400 Subject: [PATCH 105/150] Implements XBA and XCE. --- .../65816/Implementation/65816Implementation.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 3ffb34ed0..34b8dca01 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -491,6 +491,12 @@ template void Processor::run_for(const Cycles flags_.set_nz(a_.full, m_shift_); break; + case XBA: { + const uint8_t a_low = a_.halves.low; + a_.halves.low = a_.halves.high; + a_.halves.high = a_low; + flags_.set_nz(a_.halves.low); + } break; // // Jumps and returns. @@ -573,6 +579,12 @@ template void Processor::run_for(const Cycles set_flags(get_flags() | instruction_buffer_.value); break; + case XCE: { + const bool old_emulation_flag = emulation_flag_; + set_emulation_mode(flags_.carry); + flags_.carry = old_emulation_flag; + } break; + // // Increments and decrements. // @@ -779,7 +791,6 @@ template void Processor::run_for(const Cycles // TODO: // TRB, TSB, - // XCE, XBA, // STP, WAI, // RTL, // TCD, TCS, TDC, TSC From b8848d8580ec04f291e92e3548b835764ca84d2e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 21:43:05 -0400 Subject: [PATCH 106/150] Implements TCD, TDC, TCS, TSC. --- .../Implementation/65816Implementation.hpp | 33 ++++++++++++++----- .../65816/Implementation/65816Storage.cpp | 2 ++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 34b8dca01..9a8ab646d 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -26,6 +26,7 @@ template void Processor::run_for(const Cycles #define x() (x_.full & x_masks_[1]) #define y() (y_.full & x_masks_[1]) +#define stack_address() ((s_.full & e_masks_[1]) | (0x0100 & e_masks_[0])) Cycles number_of_cycles = cycles + cycles_left_to_run_; while(number_of_cycles > Cycles(0)) { @@ -143,11 +144,7 @@ template void Processor::run_for(const Cycles // #define stack_access(value, operation) \ - if(emulation_flag_) { \ - bus_address = s_.halves.low | 0x100; \ - } else { \ - bus_address = s_.full; \ - } \ + bus_address = stack_address(); \ bus_value = value; \ bus_operation = operation; @@ -369,7 +366,7 @@ template void Processor::run_for(const Cycles switch(active_instruction_->operation) { // - // Loads, stores and transfers (and NOP). + // Loads, stores and transfers (and NOP, and XBA). // case LDA: @@ -450,7 +447,7 @@ template void Processor::run_for(const Cycles // The below attempt to obey the 8/16-bit mixed transfer rules // as documented in https://softpixel.com/~cwright/sianse/docs/65816NFO.HTM - // (and makes reasonable guesses as to the N flag) + // (and make reasonable guesses as to the N flag). case TXS: s_ = x_.full & x_masks_[1]; @@ -491,6 +488,26 @@ template void Processor::run_for(const Cycles flags_.set_nz(a_.full, m_shift_); break; + case TCD: + direct_ = a_.full; + flags_.set_nz(a_.full, 8); + break; + + case TDC: + a_.full = direct_; + flags_.set_nz(a_.full, 8); + break; + + case TCS: + s_.full = a_.full; + // No need to worry about byte masking here; for the stack it's handled as the emulation runs. + break; + + case TSC: + a_.full = stack_address(); + flags_.set_nz(a_.full, 8); + break; + case XBA: { const uint8_t a_low = a_.halves.low; a_.halves.low = a_.halves.high; @@ -793,7 +810,6 @@ template void Processor::run_for(const Cycles // TRB, TSB, // STP, WAI, // RTL, - // TCD, TCS, TDC, TSC default: assert(false); @@ -820,6 +836,7 @@ template void Processor::run_for(const Cycles #undef y #undef m_flag #undef x_flag +#undef stack_address cycles_left_to_run_ = number_of_cycles; } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 472616e2e..d4943da02 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -1046,6 +1046,8 @@ void ProcessorStorage::set_emulation_mode(bool enabled) { } else { e_masks_[0] = 0x0000; e_masks_[1] = 0xffff; + s_.halves.high = 1; // To pretend it was 1 all along; this implementation actually ignores + // the top byte while in emulation mode. } emulation_flag_ = enabled; From 0619e49eaca79c217cee8fb80d38c9233a5f3c9a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 22:00:17 -0400 Subject: [PATCH 107/150] Takes a short at TSB and TRB. Three to go. --- .../65816/Implementation/65816Implementation.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 9a8ab646d..19a4e5278 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -669,6 +669,16 @@ template void Processor::run_for(const Cycles flags_.set_z(data_buffer_.value & a_.full, m_shift_); break; + case TRB: + flags_.set_z(data_buffer_.value & a_.full, m_shift_); + data_buffer_.value &= ~a_.full; + break; + + case TSB: + flags_.set_z(data_buffer_.value & a_.full, m_shift_); + data_buffer_.value |= a_.full; + break; + // // Branches. // @@ -807,7 +817,6 @@ template void Processor::run_for(const Cycles } break; // TODO: - // TRB, TSB, // STP, WAI, // RTL, From 071ad6b767230e856e46dd596a284511364a7a1b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Oct 2020 22:16:35 -0400 Subject: [PATCH 108/150] I don't think RTL is needed; JML looks like it covers it. --- Processors/65816/Implementation/65816Implementation.hpp | 1 - Processors/65816/Implementation/65816Storage.hpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 19a4e5278..e45232492 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -818,7 +818,6 @@ template void Processor::run_for(const Cycles // TODO: // STP, WAI, - // RTL, default: assert(false); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 011892c70..e032e638d 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -175,7 +175,7 @@ enum Operation: uint8_t { // These unpack values from the data buffer, which has been filled // from the stack. - RTI, RTL, + RTI, /// Loads the PC with the contents of the data buffer. JMPind, From 20cbe729851af5e17e6a25afb03f2cb87e734177 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Oct 2020 14:38:35 -0400 Subject: [PATCH 109/150] Ties to 8- or 16-bit those instructions that aren't M/X-dependent. This is technically redundant for PEI, PEA and PER since they have dedicated bus programs anyway, but it's good to be explicit. --- .../65816/Implementation/65816Storage.cpp | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index d4943da02..db79323c9 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -46,7 +46,12 @@ struct CPU::WDC65816::ProcessorStorageConstructor { std::map> installed_patterns; int opcode = 0; - void install(Generator generator, Operation operation) { + enum class AccessMode { + Mixed, + Always8Bit, + Always16Bit + }; + void install(Generator generator, Operation operation, AccessMode access_mode = AccessMode::Mixed) { // Determine the access type implied by this operation. const AccessType access_type = access_type_for_operation(operation); @@ -97,12 +102,10 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // Fill in the proper table entries and increment the opcode pointer. - storage_.instructions[opcode].program_offsets[0] = uint16_t(micro_op_location_16); - storage_.instructions[opcode].program_offsets[1] = uint16_t(micro_op_location_8); + storage_.instructions[opcode].program_offsets[0] = (access_mode == AccessMode::Always8Bit) ? uint16_t(micro_op_location_8) : uint16_t(micro_op_location_16); + storage_.instructions[opcode].program_offsets[1] = (access_mode == AccessMode::Always16Bit) ? uint16_t(micro_op_location_16) : uint16_t(micro_op_location_8); storage_.instructions[opcode].operation = operation; - // TODO: fill in size_field. - ++opcode; } @@ -734,9 +737,10 @@ ProcessorStorage::ProcessorStorage() { set_reset_state(); ProcessorStorageConstructor constructor(*this); + using AccessMode = ProcessorStorageConstructor::AccessMode; // Install the instructions. -#define op(x, y) constructor.install(&ProcessorStorageConstructor::x, y) +#define op(x, ...) constructor.install(&ProcessorStorageConstructor::x, __VA_ARGS__) /* 0x00 BRK s */ op(brk_cop, JMPind); /* 0x01 ORA (d, x) */ op(direct_indexed_indirect, ORA); @@ -749,7 +753,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x08 PHP s */ op(stack_push, PHP); /* 0x09 ORA # */ op(immediate, ORA); /* 0x0a ASL A */ op(accumulator, ASL); - /* 0x0b PHD s */ op(stack_push, PHD); + /* 0x0b PHD s */ op(stack_push, PHD, AccessMode::Always8Bit); /* 0x0c TSB a */ op(absolute_rmw, TSB); /* 0x0d ORA a */ op(absolute, ORA); /* 0x0e ASL a */ op(absolute_rmw, ASL); @@ -783,7 +787,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x28 PLP s */ op(stack_pull, PLP); /* 0x29 AND # */ op(immediate, AND); /* 0x2a ROL A */ op(accumulator, ROL); - /* 0x2b PLD s */ op(stack_pull, PLD); + /* 0x2b PLD s */ op(stack_pull, PLD, AccessMode::Always8Bit); /* 0x2c BIT a */ op(absolute, BIT); /* 0x2d AND a */ op(absolute, AND); /* 0x2e ROL a */ op(absolute_rmw, ROL); @@ -817,7 +821,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x48 PHA s */ op(stack_push, STA); /* 0x49 EOR # */ op(immediate, EOR); /* 0x4a LSR A */ op(accumulator, LSR); - /* 0x4b PHK s */ op(stack_push, PHK); + /* 0x4b PHK s */ op(stack_push, PHK, AccessMode::Always8Bit); /* 0x4c JMP a */ op(absolute_jmp, JMP); /* 0x4d EOR a */ op(absolute, EOR); /* 0x4e LSR a */ op(absolute_rmw, LSR); @@ -842,7 +846,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x60 RTS s */ op(stack_rts, RTS); /* 0x61 ADC (d, x) */ op(direct_indexed_indirect, ADC); - /* 0x62 PER s */ op(stack_per, NOP); + /* 0x62 PER s */ op(stack_per, NOP, AccessMode::Always16Bit); /* 0x63 ADC d, s */ op(stack_relative, ADC); /* 0x64 STZ d */ op(direct, STZ); /* 0x65 ADC d */ op(direct, ADC); @@ -885,7 +889,7 @@ ProcessorStorage::ProcessorStorage() { /* 0x88 DEY i */ op(implied, DEY); /* 0x89 BIT # */ op(immediate, BITimm); /* 0x8a TXA i */ op(implied, TXA); - /* 0x8b PHB s */ op(stack_push, PHB); + /* 0x8b PHB s */ op(stack_push, PHB, AccessMode::Always8Bit); /* 0x8c STY a */ op(absolute, STY); /* 0x8d STA a */ op(absolute, STA); /* 0x8e STX a */ op(absolute, STX); @@ -919,7 +923,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xa8 TAY i */ op(implied, TAY); /* 0xa9 LDA # */ op(immediate, LDA); /* 0xaa TAX i */ op(implied, TAX); - /* 0xab PLB s */ op(stack_pull, PLB); // TODO: force to 8-bit only; ditto [at least] PHB, PLD, PHD. + /* 0xab PLB s */ op(stack_pull, PLB, AccessMode::Always8Bit); /* 0xac LDY a */ op(absolute, LDY); /* 0xad LDA a */ op(absolute, LDA); /* 0xae LDX a */ op(absolute, LDX); @@ -963,7 +967,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd1 CMP (d), y */ op(direct_indirect_indexed, CMP); /* 0xd2 CMP (d) */ op(direct_indirect, CMP); /* 0xd3 CMP (d, s), y */ op(stack_relative_indexed_indirect, CMP); - /* 0xd4 PEI s */ op(stack_pei, NOP); + /* 0xd4 PEI s */ op(stack_pei, NOP, AccessMode::Always16Bit); /* 0xd5 CMP d, x */ op(direct_x, CMP); /* 0xd6 DEC d, x */ op(direct_x_rmw, DEC); /* 0xd7 CMP [d], y */ op(direct_indirect_indexed_long, CMP); @@ -997,7 +1001,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xf1 SBC (d), y */ op(direct_indirect_indexed, SBC); /* 0xf2 SBC (d) */ op(direct_indirect, SBC); /* 0xf3 SBC (d, s), y */ op(stack_relative_indexed_indirect, SBC); - /* 0xf4 PEA s */ op(stack_pea, NOP); + /* 0xf4 PEA s */ op(stack_pea, NOP, AccessMode::Always16Bit); /* 0xf5 SBC d, x */ op(direct_x, SBC); /* 0xf6 INC d, x */ op(direct_x_rmw, INC); /* 0xf7 SBC [d], y */ op(direct_indirect_indexed_long, SBC); From 8eaf1303a3cdf5f3cc04084520a6422a1fa3a8c2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Oct 2020 15:25:13 -0400 Subject: [PATCH 110/150] Attempts proactively to ensure proper RTI behaviour on the 65816. --- Processors/6502Esque/6502Esque.hpp | 10 +++++++--- .../Implementation/65816Implementation.hpp | 6 ++++++ .../65816/Implementation/65816Storage.cpp | 18 ++++++++---------- .../65816/Implementation/65816Storage.hpp | 2 ++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index 365a6df38..06942e04c 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -64,13 +64,17 @@ enum Flag: uint8_t { isReadOperation macro to make a binary choice between reading and writing. */ enum BusOperation { - Read, ReadOpcode, Write, Ready, None + Read, + ReadOpcode, + Write, + Ready, + None }; /*! - Evaluates to `true` if the operation is a read; `false` if it is a write. + Evaluates to `true` if the operation is a read; `false` if it is a write or ready. */ -#define isReadOperation(v) (v == CPU::MOS6502Esque::BusOperation::Read || v == CPU::MOS6502Esque::BusOperation::ReadOpcode) +#define isReadOperation(v) (v < CPU::MOS6502Esque::BusOperation::Write) /*! A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus, diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index e45232492..000d0eb38 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -153,6 +153,12 @@ template void Processor::run_for(const Cycles --s_.full; break; + case CyclePullIfNotEmulation: + if(emulation_flag_) { + continue; + } + [[fallthrough]]; + case CyclePull: ++s_.full; stack_access(data_buffer_.next_input(), MOS6502Esque::Read); diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index db79323c9..137f3889f 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -652,18 +652,16 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } // 22g. Stack; s, RTI. - static void stack_rti(AccessType, bool is8bit, const std::function &target) { - target(CycleFetchPCThrowaway); // IO - target(CycleFetchPCThrowaway); // IO + static void stack_rti(AccessType, bool, const std::function &target) { + target(CycleFetchPCThrowaway); // IO + target(CycleFetchPCThrowaway); // IO - target(CyclePull); // P - target(CyclePull); // New PCL - target(CyclePull); // New PCH - if(!is8bit) target(CyclePull); // PBR - // TODO: 8bit check here doesn't actually work, it needs to be an is-emulation-mode check. - // New operation needed, I think. + target(CyclePull); // P + target(CyclePull); // New PCL + target(CyclePull); // New PCH + target(CyclePullIfNotEmulation); // PBR - target(OperationPerform); // [RTI] — to unpack the fields above. + target(OperationPerform); // [RTI] — to unpack the fields above. } // 22h. Stack; s, RTS. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index e032e638d..b7ba1d467 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -44,6 +44,8 @@ enum MicroOp: uint8_t { CycleAccessStack, /// Pulls a single byte to the data buffer from the stack. CyclePull, + /// Performs as CyclePull if the 65816 is not in emulation mode; otherwise skips itself. + CyclePullIfNotEmulation, /// Sets the data address by copying the final two bytes of the instruction buffer and /// using the data register as a high byte. From a0885ab7d02b593606921d8649b369422cc16368 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Oct 2020 17:56:55 -0400 Subject: [PATCH 111/150] Implements STP and WAI. Albeit still without fully-implemented reactions to exceptions in general. --- .../Implementation/65816Implementation.hpp | 27 ++++++++++++++++--- .../65816/Implementation/65816Storage.cpp | 19 ++++++------- .../65816/Implementation/65816Storage.hpp | 7 +++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 000d0eb38..0f25f1185 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -170,6 +170,19 @@ template void Processor::run_for(const Cycles #undef stack_access + // + // STP and WAI. + // + + case CycleRepeatingNone: + if(pending_exceptions_ & required_exceptions_) { + continue; + } else { + --next_op_; + perform_bus(0xffffff, nullptr, MOS6502Esque::None); + } + break; + // // Data movement. // @@ -822,11 +835,17 @@ template void Processor::run_for(const Cycles LD(a_, result, m_masks_); } break; - // TODO: - // STP, WAI, + // + // STP and WAI + // - default: - assert(false); + case STP: + required_exceptions_ = Reset; + break; + + case WAI: + required_exceptions_ = Reset | IRQ | NMI; + break; } continue; } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 137f3889f..03cb4feca 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -539,18 +539,15 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationPerform); } - // 19c. Stop the Clock. - static void stp(AccessType, bool, const std::function &target) { - target(CycleFetchPCThrowaway); // IO - target(CycleFetchPCThrowaway); // IO - target(OperationPerform); - } - + // 19c. Stop the Clock; also // 19d. Wait for interrupt. - static void wai(AccessType, bool, const std::function &target) { + static void stp_wai(AccessType, bool, const std::function &target) { + target(OperationPerform); // Establishes the termination condition. target(CycleFetchPCThrowaway); // IO target(CycleFetchPCThrowaway); // IO - target(OperationPerform); + target(CycleRepeatingNone); // This will first check whether the STP/WAI exit + // condition has occurred; if not then it'll issue + // a BusOperation::None and then reschedule itself. } // 20. Relative; r. @@ -955,7 +952,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xc8 INY i */ op(implied, INY); /* 0xc9 CMP # */ op(immediate, CMP); /* 0xca DEX i */ op(implied, DEX); - /* 0xcb WAI i */ op(wai, WAI); + /* 0xcb WAI i */ op(stp_wai, WAI); /* 0xcc CPY a */ op(absolute, CPY); /* 0xcd CMP a */ op(absolute, CMP); /* 0xce DEC a */ op(absolute_rmw, DEC); @@ -972,7 +969,7 @@ ProcessorStorage::ProcessorStorage() { /* 0xd8 CLD i */ op(implied, CLD); /* 0xd9 CMP a, y */ op(absolute_y, CMP); /* 0xda PHX s */ op(stack_push, STX); - /* 0xdb STP i */ op(stp, STP); + /* 0xdb STP i */ op(stp_wai, STP); /* 0xdc JML (a) */ op(absolute_indirect_jml, JML); /* 0xdd CMP a, x */ op(absolute_x, CMP); /* 0xde DEC a, x */ op(absolute_x_rmw, DEC); diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index b7ba1d467..b4a42de06 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -47,6 +47,10 @@ enum MicroOp: uint8_t { /// Performs as CyclePull if the 65816 is not in emulation mode; otherwise skips itself. CyclePullIfNotEmulation, + /// Issues a BusOperation::None and regresses the micro-op counter until an established + /// STP or WAI condition is satisfied. + CycleRepeatingNone, + /// Sets the data address by copying the final two bytes of the instruction buffer and /// using the data register as a high byte. OperationConstructAbsolute, @@ -261,6 +265,9 @@ struct ProcessorStorage { static constexpr int NMI = 1 << 3; int pending_exceptions_ = PowerOn; // By default. + /// Sets the required exception flags necessary to exit a STP or WAI. + int required_exceptions_ = 0; + /// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte /// to most significant. struct Buffer { From 82797fd395273618dc723b6a5272e28b0931eab4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Oct 2020 21:10:44 -0400 Subject: [PATCH 112/150] Attempts to do the proper thing for interrupts. --- .../Implementation/65816Implementation.hpp | 20 +++++++++++-------- .../65816/Implementation/65816Storage.hpp | 8 +++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 0f25f1185..0f2b4e2b2 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -48,7 +48,7 @@ template void Processor::run_for(const Cycles // The exception program will determine the appropriate way to respond // based on the pending exception if one exists; otherwise just do a // standard fetch-decode-execute. - const auto offset = instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offsets[0]; + const auto offset = instructions[selected_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offsets[0]; next_op_ = µ_ops_[offset]; instruction_buffer_.clear(); data_buffer_.clear(); @@ -331,12 +331,14 @@ template void Processor::run_for(const Cycles // Put the proper exception vector into the data address, put the flags and PC // into the data buffer (possibly also PBR), and skip an instruction if in // emulation mode. + // + // I've assumed here that interrupts, BRKs and COPs can be usurped similarly + // to a 6502 but may not have the exact details correct. E.g. if IRQ has + // become inactive since the decision was made to start an interrupt, should + // that turn into a BRK? bool is_brk = false; - // TODO: probably this should have been decided in advance? And the interrupt flag - // needs to be factored in? - if(pending_exceptions_ & (Reset | PowerOn)) { pending_exceptions_ &= ~(Reset | PowerOn); data_address_ = 0xfffc; @@ -344,11 +346,11 @@ template void Processor::run_for(const Cycles } else if(pending_exceptions_ & NMI) { pending_exceptions_ &= ~NMI; data_address_ = 0xfffa; - } else if(pending_exceptions_ & IRQ) { + } else if(pending_exceptions_ & IRQ & flags_.inverse_interrupt) { pending_exceptions_ &= ~IRQ; data_address_ = 0xfffe; } else { - is_brk = active_instruction_ == instructions; + is_brk = active_instruction_ == instructions; // Given that BRK has opcode 00. if(is_brk) { data_address_ = emulation_flag_ ? 0xfffe : 0xfff6; } else { @@ -856,9 +858,11 @@ template void Processor::run_for(const Cycles #undef y_top #undef a_top - // TODO: do some sort of evaluation here on whether an interrupt or similar is pending, - // react appropriately. + // TODO: the ready line. + // Store a selection as to the exceptions, if any, that would be honoured after this cycle if the + // next thing is a MoveToNextProgram. + selected_exceptions_ = pending_exceptions_ & (flags_.inverse_interrupt | PowerOn | Reset | NMI); number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index b4a42de06..b294e9bcb 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -261,9 +261,15 @@ struct ProcessorStorage { static constexpr int PowerOn = 1 << 0; static constexpr int Reset = 1 << 1; - static constexpr int IRQ = 1 << 2; + static constexpr int IRQ = Flag::Interrupt; // This makes masking a lot easier later on; this is 1 << 2. static constexpr int NMI = 1 << 3; int pending_exceptions_ = PowerOn; // By default. + int selected_exceptions_ = 0; + + // Just to be safe. + static_assert(PowerOn != IRQ); + static_assert(Reset != IRQ); + static_assert(NMI != IRQ); /// Sets the required exception flags necessary to exit a STP or WAI. int required_exceptions_ = 0; From 3039a445f06c9de0b58b95ea954af71932770471 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Oct 2020 21:18:01 -0400 Subject: [PATCH 113/150] Ups the 65816 test machine to a full 16mb RAM. --- Processors/6502/AllRAM/6502AllRAM.cpp | 7 ++++--- Processors/6502/AllRAM/6502AllRAM.hpp | 2 +- Processors/AllRAMProcessor.cpp | 12 ++++++------ Processors/AllRAMProcessor.hpp | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 723bc98b6..696701be8 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -21,12 +21,13 @@ using Type = CPU::MOS6502Esque::Type; template class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { public: - ConcreteAllRAMProcessor() : + ConcreteAllRAMProcessor(size_t memory_size) : + AllRAMProcessor(memory_size), mos6502_(*this) { mos6502_.set_power_on(false); } - inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) { + inline Cycles perform_bus_operation(BusOperation operation, uint32_t address, uint8_t *value) { timestamp_ += Cycles(1); if(operation == BusOperation::ReadOpcode) { @@ -91,7 +92,7 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ } AllRAMProcessor *AllRAMProcessor::Processor(Type type) { -#define Bind(p) case p: return new ConcreteAllRAMProcessor

(); +#define Bind(p) case p: return new ConcreteAllRAMProcessor

(type == Type::TWDC65816 ? 16*1024*1024 : 64*1024); switch(type) { default: Bind(Type::T6502) diff --git a/Processors/6502/AllRAM/6502AllRAM.hpp b/Processors/6502/AllRAM/6502AllRAM.hpp index f6a354f78..ad4c51cfe 100644 --- a/Processors/6502/AllRAM/6502AllRAM.hpp +++ b/Processors/6502/AllRAM/6502AllRAM.hpp @@ -29,7 +29,7 @@ class AllRAMProcessor: virtual void set_value_of_register(Register r, uint16_t value) = 0; protected: - AllRAMProcessor() : ::CPU::AllRAMProcessor(65536) {} + AllRAMProcessor(size_t memory_size) : ::CPU::AllRAMProcessor(memory_size) {} }; } diff --git a/Processors/AllRAMProcessor.cpp b/Processors/AllRAMProcessor.cpp index 7a283d926..5cb7a21ef 100644 --- a/Processors/AllRAMProcessor.cpp +++ b/Processors/AllRAMProcessor.cpp @@ -15,14 +15,14 @@ AllRAMProcessor::AllRAMProcessor(std::size_t memory_size) : traps_(memory_size, false), timestamp_(0) {} -void AllRAMProcessor::set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data) { - std::size_t endAddress = std::min(startAddress + length, size_t(65536)); - std::memcpy(&memory_[startAddress], data, endAddress - startAddress); +void AllRAMProcessor::set_data_at_address(size_t start_address, std::size_t length, const uint8_t *data) { + const size_t end_address = std::min(start_address + length, memory_.size()); + memcpy(&memory_[start_address], data, end_address - start_address); } -void AllRAMProcessor::get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data) { - std::size_t endAddress = std::min(startAddress + length, size_t(65536)); - std::memcpy(data, &memory_[startAddress], endAddress - startAddress); +void AllRAMProcessor::get_data_at_address(size_t start_address, std::size_t length, uint8_t *data) { + const size_t end_address = std::min(start_address + length, memory_.size()); + memcpy(data, &memory_[start_address], end_address - start_address); } HalfCycles AllRAMProcessor::get_timestamp() { diff --git a/Processors/AllRAMProcessor.hpp b/Processors/AllRAMProcessor.hpp index 117b85711..0ad2f31c5 100644 --- a/Processors/AllRAMProcessor.hpp +++ b/Processors/AllRAMProcessor.hpp @@ -21,8 +21,8 @@ class AllRAMProcessor { public: AllRAMProcessor(std::size_t memory_size); HalfCycles get_timestamp(); - void set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data); - void get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data); + void set_data_at_address(size_t startAddress, size_t length, const uint8_t *data); + void get_data_at_address(size_t startAddress, size_t length, uint8_t *data); class TrapHandler { public: From 5dc3cd3a2fa8f687e768924b133d6c45b1546145 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Oct 2020 22:02:46 -0400 Subject: [PATCH 114/150] Starts using Jeek816 for a basic native-mode audit. Fixes absolute long addressing. --- .../Clock Signal.xcodeproj/project.pbxproj | 12 + .../Mac/Clock SignalTests/jeek816/Makefile | 11 + OSBindings/Mac/Clock SignalTests/jeek816/RUN | 2 + .../Mac/Clock SignalTests/jeek816/license.txt | 24 ++ .../Mac/Clock SignalTests/jeek816/readme.txt | 23 ++ .../Mac/Clock SignalTests/jeek816/suite-a.asm | 218 +++++++++++++++++ .../Mac/Clock SignalTests/jeek816/suite-a.prg | Bin 0 -> 2308 bytes .../Mac/Clock SignalTests/jeek816/suite-a.r | 220 ++++++++++++++++++ .../Clock SignalTests/jeek816/test-816.txt | 147 ++++++++++++ Processors/6502/AllRAM/6502AllRAM.cpp | 6 +- .../Implementation/65816Implementation.hpp | 4 + .../65816/Implementation/65816Storage.cpp | 17 +- .../65816/Implementation/65816Storage.hpp | 3 + 13 files changed, 675 insertions(+), 12 deletions(-) create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/Makefile create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/RUN create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/license.txt create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/readme.txt create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/suite-a.prg create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r create mode 100644 OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 30c4d4bd2..1fdf6508d 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -213,6 +213,7 @@ 4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; 4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; 4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; + 4B4F47652533EA64004245B8 /* suite-a.prg in Resources */ = {isa = PBXBuildFile; fileRef = 4B4F475E2533EA64004245B8 /* suite-a.prg */; }; 4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; }; 4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; }; 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; }; @@ -1128,6 +1129,7 @@ 4B4DEC18252BFA9C004583AC /* 6502Esque.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = ""; }; 4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LazyFlags.hpp; sourceTree = ""; }; 4B4F2B7024DF99D4000DA6B0 /* CSScanTarget+CppScanTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSScanTarget+CppScanTarget.h"; sourceTree = ""; }; + 4B4F475E2533EA64004245B8 /* suite-a.prg */ = {isa = PBXFileReference; lastKnownFileType = file; path = "suite-a.prg"; sourceTree = ""; }; 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = ""; }; 4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = ""; }; @@ -1993,6 +1995,7 @@ 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */, 4B2530F2244E6773007980BF /* FM Synthesis */, 4BBF49B41ED2881600AB3669 /* FUSE */, + 4B4F475B2533EA64004245B8 /* jeek816 */, 4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */, 4B9F11C72272375400701480 /* QL Startup */, 4B85322B227793CA00F26553 /* TOS Startup */, @@ -2482,6 +2485,14 @@ path = Implementation; sourceTree = ""; }; + 4B4F475B2533EA64004245B8 /* jeek816 */ = { + isa = PBXGroup; + children = ( + 4B4F475E2533EA64004245B8 /* suite-a.prg */, + ); + path = jeek816; + sourceTree = ""; + }; 4B51F70820A521D700AFA2C1 /* Activity */ = { isa = PBXGroup; children = ( @@ -4292,6 +4303,7 @@ 4BB299051B587D8400A49093 /* arrb in Resources */, 4BB299DC1B587D8400A49093 /* stazx in Resources */, 4B670A9D2401CB8400D4E002 /* z80ccf.tap in Resources */, + 4B4F47652533EA64004245B8 /* suite-a.prg in Resources */, 4BB299C41B587D8400A49093 /* sbca in Resources */, 4BB298F41B587D8400A49093 /* adcay in Resources */, 4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */, diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/Makefile b/OSBindings/Mac/Clock SignalTests/jeek816/Makefile new file mode 100644 index 000000000..5e2f27fdc --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/jeek816/Makefile @@ -0,0 +1,11 @@ + +F=suite-a + +all: $(F).prg + +$(F).prg: $(F).asm + acme --format plain -o $(F).prg -r $(F).r $(F).asm + +clean: + rm -f $(F).prg + rm -f *~ diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/RUN b/OSBindings/Mac/Clock SignalTests/jeek816/RUN new file mode 100644 index 000000000..d40f7f1b0 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/jeek816/RUN @@ -0,0 +1,2 @@ +xscpu64 -scpu64 /usr/local/lib64/vice/SCPU64/scpu64 suite-a.prg +#xscpu64.devel -scpu64 /usr/local/lib64/vice/SCPU64/scpu64 suite-a.prg diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/license.txt b/OSBindings/Mac/Clock SignalTests/jeek816/license.txt new file mode 100644 index 000000000..3e374355c --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/jeek816/license.txt @@ -0,0 +1,24 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/readme.txt b/OSBindings/Mac/Clock SignalTests/jeek816/readme.txt new file mode 100644 index 000000000..e813527a4 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/jeek816/readme.txt @@ -0,0 +1,23 @@ + +65C816 instruction set test + + +2017-12-13 J.E. Klasek j+816 AT klasek DOT at + + +ACME syntax, green border shows success. in case of failure red border +is shown and $0400 contains number of failed test and $0401 a bitmap +showing which tests actually failed. +If all tests fail on screen "f?" will be shown (corresponds to 6 failures) +and the bitmap %00111111 ($3F = '?') + +There are 6 tests (bit 5 to bit 0): + + STX $FFFF fails in 16 mode for X/Y if wrapping to location 0 + STY $FFFF fails in 16 mode for X/Y if wrapping to location 0 + LDX $FFFF,Y fails if wrapping to same bank + LDY $FFFF,X fails if wrapping to same bank + TRB $FFFF fails in 16 mode for A/M if wrapping to location 0 + TSB $FFFF fails in 16 mode for A/M if wrapping to location 0 + +------------------------------------------------------------------------------- diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm b/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm new file mode 100644 index 000000000..bd291f75c --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm @@ -0,0 +1,218 @@ +!cpu 65816 + +; 2017-12-13 J.E. Klasek j+816 AT klasek DOT at + + + +videoram = $0400 +colorram = $d800 + +;------------------------------------------------------------------------------- + *=$07ff + !word $0801 + !word bend + !word 10 + !byte $9e + !text "2061", 0 +bend: !word 0 +;------------------------------------------------------------------------------- + + sei + lda #$17 + sta $d018 + lda #$35 + sta $01 + lda #$7f + sta $dc0d + sta $dd0d + lda $dc0d + lda $dd0d + ldx #0 +- + lda #$20 + sta videoram,x + sta videoram+$0100,x + sta videoram+$0200,x + sta videoram+$0300,x + lda #1 + sta colorram,x + sta colorram+$0100,x + sta colorram+$0200,x + sta colorram+$0300,x + inx + bne - + + jmp start +theend: + sep #$30 ; 8-bit for X/Y and A/M + !as + !rs + lda $040210 + cmp #$ff + bne error + + lda #5 + sta $d020 + ldx #0 ; success + stx $d7ff + jmp * +error + sta $0400 + lda $040211 ; failure map (which test failed) + sta $0401 + lda #10 + sta $d020 + ldx #$ff ; failure + stx $d7ff + jmp * + +;------------------------------------------------------------------------------- + + * = $1000 +start: +; EXPECTED FINAL RESULTS: $0210 = FF +; (any other number will be the +; test that failed) + +; initialize: + lda #$00 + sta $040210 + sta $040211 + + +test00: + +; setup cpu + clc + xce ; native mode + rep #$30 ; 16-bit for X/Y and A/M + !al + !rl + +; setup registers + lda #$0404 ; Data Bank Register register + pha ; akku in 16 bit + plb ; pull DBR twice + plb + ldy #$8888 ; change marker + tyx + tya + +; setup memory + lda #$5555 ; wrap marker + sta $048887 ; into bank 4, for LDX/LDY + lda #$7777 ; no-wrap marker + sta $058887 ; into bank 5, for LDX/LDY + +;--------------------------------------------------------------------- + + stz $0000 ; init wrap marker + lda #$7777 ; no-wrap marker + sta $050000 ; to start of bank 5 + + sty $ffff ; high byte of Y is where? + lda $0000 + bne + + lda $ffff ; fetch, does not wrap + cmp #$8888 + bne + + lda $050000 + cmp #$7788 ; write to bank 5 + beq ++ ++ inc $0210 ; fail counter + clc +++ + rol $0211 ; update failure map +;--------------------------------------------------------------------- + + stz $0000 ; init wrap marker + lda #$7777 ; no-wrap marker + sta $050000 ; to start of bank 5 + + tyx ; change marker + stx $ffff ; high byte of Y is where? + lda $0000 + bne + + lda $ffff ; fetch, does not wrap + cmp #$8888 + bne + + lda $050000 + cmp #$7788 ; write to bank 5 + beq ++ ++ inc $0210 ; fail counter + clc +++ + rol $0211 ; update failure map +;--------------------------------------------------------------------- + + ldy $ffff,x ; Y=5555 Y=7777 value for Y comes from which bank? + cpy #$7777 + beq + + inc $0210 ; fail counter + clc ++ + rol $0211 ; update failure map +;--------------------------------------------------------------------- + + txy ; reinitialize y + ldx $ffff,y ; X=5555 X=7777 value for X comes from which bank? + cpx #$7777 + beq + + inc $0210 ; fail counter + clc ++ + rol $0211 ; update failure map +;--------------------------------------------------------------------- + + stz $0000 ; init wrap marker + lda #$7777 ; no-wrap marker + sta $050000 ; to start of bank 5 + + lda #$7788 + inc $0000 ; $0000 = 1 + trb $ffff ; 88 77 & ^(88 77) -> 00 00 + lda $0000 + cmp #$0001 ; $0000 not reset by trb (does not wrap) + bne + + lda $050000 + cmp #$7700 ; $050001 reset by trb + beq ++ ++ inc $0210 ; fail counter + clc +++ + rol $0211 ; update failure map +;--------------------------------------------------------------------- + + lda #$7788 + sta $050000 ; 00 88 | 88 77 -> 88 ff + tsb $ffff ; set bits (which are already cleared) + lda $0000 + cmp #$0001 ; $0000 not set by tsb (does not wrap!) + bne + + lda $050000 + cmp #$77ff ; $050001 all bits set by tsb + beq ++ ++ inc $0210 ; fail counter + clc +++ + rol $0211 ; update failure map +;--------------------------------------------------------------------- + + +test00pass: + lda $0210 + eor #%0011111100000000 ; invert failure map + sta $0210 + bne + + dec $0210 ; 0 -> FF ++ + + lda #$0000 + pha + plb + plb ; program bank = 0 + sec + xce ; emulation mode + sep #$30 ; a/m, x/y 8 bit + + jmp theend diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.prg b/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.prg new file mode 100644 index 0000000000000000000000000000000000000000..3d064738250690c0b3a6e2356bca6caf60a50a45 GIT binary patch literal 2308 zcmeH|ze)o^5XQe-_w&-L02T*5RN=RM8xhI z*)IM`KvP)-`xNV*2q!j{HoML2eta{t^J~TnR)Pkuw$hzV07tYY6HVJE9v#bYEW=3= zj*D>812q-6g0HZsutc81qr#KIv%<@}e#;O|(?P^|X>>@wZ0a65W|SQS5_}LdEH!+V0vFoy_l3;(Wi~zZPP6PVIL0RB+038~jV4 zx&i)~F$JLG!kAJBy*#j5De@0Ki&(M|v)VcA{9(N@<}R-@FK&%_{qe3Vk;rGj`d7Kq m!*^5Esn$^aZS|MK)Q%!zE$qpN>4k@g5n$)soln!O6?_6Sh>)8A literal 0 HcmV?d00001 diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r b/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r new file mode 100644 index 000000000..9b5e89c4e --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r @@ -0,0 +1,220 @@ + +; ******** Source: suite-a.asm + 1 !cpu 65816 + 2 + 3 ; 2017-12-13 J.E. Klasek j+816 AT klasek DOT at + 4 + 5 + 6 + 7 videoram = $0400 + 8 colorram = $d800 + 9 + 10 ;------------------------------------------------------------------------------- + 11 *=$07ff + 12 07ff 0108 !word $0801 + 13 0801 0b08 !word bend + 14 0803 0a00 !word 10 + 15 0805 9e !byte $9e + 16 0806 3230363100 !text "2061", 0 + 17 080b 0000 bend: !word 0 + 18 ;------------------------------------------------------------------------------- + 19 + 20 080d 78 sei + 21 080e a917 lda #$17 + 22 0810 8d18d0 sta $d018 + 23 0813 a935 lda #$35 + 24 0815 8501 sta $01 + 25 0817 a97f lda #$7f + 26 0819 8d0ddc sta $dc0d + 27 081c 8d0ddd sta $dd0d + 28 081f ad0ddc lda $dc0d + 29 0822 ad0ddd lda $dd0d + 30 0825 a200 ldx #0 + 31 - + 32 0827 a920 lda #$20 + 33 0829 9d0004 sta videoram,x + 34 082c 9d0005 sta videoram+$0100,x + 35 082f 9d0006 sta videoram+$0200,x + 36 0832 9d0007 sta videoram+$0300,x + 37 0835 a901 lda #1 + 38 0837 9d00d8 sta colorram,x + 39 083a 9d00d9 sta colorram+$0100,x + 40 083d 9d00da sta colorram+$0200,x + 41 0840 9d00db sta colorram+$0300,x + 42 0843 e8 inx + 43 0844 d0e1 bne - + 44 + 45 0846 4c0010 jmp start + 46 theend: + 47 0849 e230 sep #$30 ; 8-bit for X/Y and A/M + 48 !as + 49 !rs + 50 084b af100204 lda $040210 + 51 084f c9ff cmp #$ff + 52 0851 d00d bne error + 53 + 54 0853 a905 lda #5 + 55 0855 8d20d0 sta $d020 + 56 0858 a200 ldx #0 ; success + 57 085a 8effd7 stx $d7ff + 58 085d 4c5d08 jmp * + 59 error + 60 0860 8d0004 sta $0400 + 61 0863 af110204 lda $040211 ; failure map (which test failed) + 62 0867 8d0104 sta $0401 + 63 086a a90a lda #10 + 64 086c 8d20d0 sta $d020 + 65 086f a2ff ldx #$ff ; failure + 66 0871 8effd7 stx $d7ff + 67 0874 4c7408 jmp * + 68 + 69 ;------------------------------------------------------------------------------- + 70 + 71 * = $1000 + 72 start: + 73 ; EXPECTED FINAL RESULTS: $0210 = FF + 74 ; (any other number will be the + 75 ; test that failed) + 76 + 77 ; initialize: + 78 1000 a900 lda #$00 + 79 1002 8f100204 sta $040210 + 80 1006 8f110204 sta $040211 + 81 + 82 + 83 test00: + 84 + 85 ; setup cpu + 86 100a 18 clc + 87 100b fb xce ; native mode + 88 100c c230 rep #$30 ; 16-bit for X/Y and A/M + 89 !al + 90 !rl + 91 + 92 ; setup registers + 93 100e a90404 lda #$0404 ; Data Bank Register register + 94 1011 48 pha ; akku in 16 bit + 95 1012 ab plb ; pull DBR twice + 96 1013 ab plb + 97 1014 a08888 ldy #$8888 ; change marker + 98 1017 bb tyx + 99 1018 98 tya + 100 + 101 ; setup memory + 102 1019 a95555 lda #$5555 ; wrap marker + 103 101c 8f878804 sta $048887 ; into bank 4, for LDX/LDY + 104 1020 a97777 lda #$7777 ; no-wrap marker + 105 1023 8f878805 sta $058887 ; into bank 5, for LDX/LDY + 106 + 107 ;--------------------------------------------------------------------- + 108 + 109 1027 9c0000 stz $0000 ; init wrap marker + 110 102a a97777 lda #$7777 ; no-wrap marker + 111 102d 8f000005 sta $050000 ; to start of bank 5 + 112 + 113 1031 8cffff sty $ffff ; high byte of Y is where? + 114 1034 ad0000 lda $0000 + 115 1037 d011 bne + + 116 1039 adffff lda $ffff ; fetch, does not wrap + 117 103c c98888 cmp #$8888 + 118 103f d009 bne + + 119 1041 af000005 lda $050000 + 120 1045 c98877 cmp #$7788 ; write to bank 5 + 121 1048 f004 beq ++ + 122 104a ee1002 + inc $0210 ; fail counter + 123 104d 18 clc + 124 ++ + 125 104e 2e1102 rol $0211 ; update failure map + 126 ;--------------------------------------------------------------------- + 127 + 128 1051 9c0000 stz $0000 ; init wrap marker + 129 1054 a97777 lda #$7777 ; no-wrap marker + 130 1057 8f000005 sta $050000 ; to start of bank 5 + 131 + 132 105b bb tyx ; change marker + 133 105c 8effff stx $ffff ; high byte of Y is where? + 134 105f ad0000 lda $0000 + 135 1062 d011 bne + + 136 1064 adffff lda $ffff ; fetch, does not wrap + 137 1067 c98888 cmp #$8888 + 138 106a d009 bne + + 139 106c af000005 lda $050000 + 140 1070 c98877 cmp #$7788 ; write to bank 5 + 141 1073 f004 beq ++ + 142 1075 ee1002 + inc $0210 ; fail counter + 143 1078 18 clc + 144 ++ + 145 1079 2e1102 rol $0211 ; update failure map + 146 ;--------------------------------------------------------------------- + 147 + 148 107c bcffff ldy $ffff,x ; Y=5555 Y=7777 value for Y comes from which bank? + 149 107f c07777 cpy #$7777 + 150 1082 f004 beq + + 151 1084 ee1002 inc $0210 ; fail counter + 152 1087 18 clc + 153 + + 154 1088 2e1102 rol $0211 ; update failure map + 155 ;--------------------------------------------------------------------- + 156 + 157 108b 9b txy ; reinitialize y + 158 108c beffff ldx $ffff,y ; X=5555 X=7777 value for X comes from which bank? + 159 108f e07777 cpx #$7777 + 160 1092 f004 beq + + 161 1094 ee1002 inc $0210 ; fail counter + 162 1097 18 clc + 163 + + 164 1098 2e1102 rol $0211 ; update failure map + 165 ;--------------------------------------------------------------------- + 166 + 167 109b 9c0000 stz $0000 ; init wrap marker + 168 109e a97777 lda #$7777 ; no-wrap marker + 169 10a1 8f000005 sta $050000 ; to start of bank 5 + 170 + 171 10a5 a98877 lda #$7788 + 172 10a8 ee0000 inc $0000 ; $0000 = 1 + 173 10ab 1cffff trb $ffff ; 88 77 & ^(88 77) -> 00 00 + 174 10ae ad0000 lda $0000 + 175 10b1 c90100 cmp #$0001 ; $0000 not reset by trb (does not wrap) + 176 10b4 d009 bne + + 177 10b6 af000005 lda $050000 + 178 10ba c90077 cmp #$7700 ; $050001 reset by trb + 179 10bd f004 beq ++ + 180 10bf ee1002 + inc $0210 ; fail counter + 181 10c2 18 clc + 182 ++ + 183 10c3 2e1102 rol $0211 ; update failure map + 184 ;--------------------------------------------------------------------- + 185 + 186 10c6 a98877 lda #$7788 + 187 10c9 8f000005 sta $050000 ; 00 88 | 88 77 -> 88 ff + 188 10cd 0cffff tsb $ffff ; set bits (which are already cleared) + 189 10d0 ad0000 lda $0000 + 190 10d3 c90100 cmp #$0001 ; $0000 not set by tsb (does not wrap!) + 191 10d6 d009 bne + + 192 10d8 af000005 lda $050000 + 193 10dc c9ff77 cmp #$77ff ; $050001 all bits set by tsb + 194 10df f004 beq ++ + 195 10e1 ee1002 + inc $0210 ; fail counter + 196 10e4 18 clc + 197 ++ + 198 10e5 2e1102 rol $0211 ; update failure map + 199 ;--------------------------------------------------------------------- + 200 + 201 + 202 test00pass: + 203 10e8 ad1002 lda $0210 + 204 10eb 49003f eor #%0011111100000000 ; invert failure map + 205 10ee 8d1002 sta $0210 + 206 10f1 d003 bne + + 207 10f3 ce1002 dec $0210 ; 0 -> FF + 208 + + 209 + 210 10f6 a90000 lda #$0000 + 211 10f9 48 pha + 212 10fa ab plb + 213 10fb ab plb ; program bank = 0 + 214 10fc 38 sec + 215 10fd fb xce ; emulation mode + 216 10fe e230 sep #$30 ; a/m, x/y 8 bit + 217 + 218 1100 4c4908 jmp theend diff --git a/OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt b/OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt new file mode 100644 index 000000000..8fd95a640 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt @@ -0,0 +1,147 @@ +65816 behavior on VICE and true hardware +======================================== + +Johann Klasek, j AT klasek at +2017-12-02 + + +Testenvironment: + + Hardware: + Tested on LTC64, a turbo card for the Commodore 64 + in-place of CPU 6510 (which moves to the card) + with a WDC's 65C816, 32 KByte ROM and 256 KByte RAM. + based on magazine article c't "C64 aufgemotzt", 1987/06 p. 94 + http://klasek.at/c64/ltc64 (german) + Redesigned and implemented by Christoph Egretzberger + and software integration by Johann Klasek + + Software: + Jamaica Monitor (Jammon) 4.1, native '816 + C64 ROMs (Kernal+BASIC) + +--------------------------------------------------------------------- +Case 1: Wrap-around for addressing mode "absolute" + for opcodes TSB, TRB, STX, STY, LDX, LDY + + VICE WDC +!e +a 1000 clc +xce native mode +rep #$30 16-bit for X/Y and A/M +lda #$0404 Data Bank Register register +pha akku in 16 bit +plb pull DBR twice +plb +lda #$7777 no-wrap marker +sta $050000 to bank 5 +sta $058887 +lda #$5555 unchanged marker +sta $048887 into bank 4 +ldy #$8888 change marker +tyx now in X and Y +sty $ffff high byte of Y is where? +ldy $ffff,x Y=5555 Y=7777 value for Y comes from which bank? +sty $0002 save as intermediate result, because we do more +lda $ffff A=7788 A=8888 fetch, does no wrap even on VICE +ldx $0000 X=2088 X=20b5 see if wrapping occured (from sty above) +eor #$ffff A=8877 A=7777 invert akku, 2nd part ... +trb $ffff M=0088 M=8888 reset bits (which are already cleared) +tsb $ffff M=88ff M=ffff set these bits giving a $ffff value +ldy $ffff Y=88ff Y=ffff back to ldy test +brk + +!d Jammon alternate display mode +d disassemble on current PC +m 04ffff show memory including 050000 +m 040000 start of bank 4 (wrap around area), + and intermediate result in 040002 +m 048887 X indexed wrap area +m 058887 X indexed no wrap area +z 1000 start single-stepping +z single step until BRK or ... +g go to BRK + +Results VICE WDC + +$050001 $77 $77 marker, never changed +$050000 $77 $ff modified by TRB/TSB +$04ffff $ff $ff modified by TRB/TSB +$040003 $55 $77 read value (low) from LDY $FFFF,X +$040002 $55 $77 read value (high) from LDY $FFFF,X + read value +$040000 $88 ?? (if no wrapping for STY $FFFF, TRB/TSB $FFFF) + only changed if wrapping +$058888 $77 $77 marker, never changed +$058887 $77 $77 marker, never changed +$048888 $55 $55 marker, never changed +$048887 $55 $55 marker, never changed + +A: $8877 $7777 value from $04ffff/$050000 after STY $FFFF +X: $??88 $???? value from $040000/$040001 with high byte of Y + in low byte of X after STY $FFFF +Y: $88ff $ffff value from TRB/TSB $FFFF + + + +--------------------------------------------------------------------- +Case 2: Wrap-around for Direct Page Indexed Addressing + in Emulation Mode + +### Variant 1 + +r +# set M X E to "1" +e +# set DBR to 00 + +a 1000 sec +xce emulation mode +lda #$20 set highbyte +xba +lda #$00 lowbyte 16-bit akku +tcd direct page address to $2000 +ldx #$20 index +lda #$88 marker for wrap +sta $2010 +lda #$77 marker for no-wrap +sta $2110 +lda $f0,x which marker? +stz $f0,x write (non-6502) +brk + +!d Jammon alternate display mode +d disassemble on PC +m 2010 show wrap area +m 2110 show no-wrap area +z 1000 start single-stepping +z single step until BRK or ... +g go to BRK + +Results VICE WDC + +$2010 00 00 wrap area: =00 from STZ +$2110 77 77 no-wrap area + +A: $2088 $2088 A low: 88 = wrap, 77 = no-wrap + + +### Variant 2 + +Based on variant 1: + +a 1005 lda #$01 direct page not aligned! +tcd direct page at $2001 +ldx #$1f correction to hit the + same memory locations + for monitoring + +g 1000 + +Results VICE WDC + +$2011 88 88 wrap area: =00 from STZ +$2111 00 00 no-wrap area + +A: $2077 $2077 A low: 88 = wrap, 77 = no-wrap + diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 696701be8..620c5c24a 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -11,7 +11,7 @@ #include #include -//#define BE_NOISY +#define BE_NOISY using namespace CPU::MOS6502; @@ -46,14 +46,14 @@ template class ConcreteAllRAMProcessor: public AllRAMProcessor, publ *value = memory_[address]; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { -// printf("%04x -> %02x\n", address, *value); + printf("%04x -> %02x\n", address, *value); // } #endif } else { memory_[address] = *value; #ifdef BE_NOISY // if((address&0xff00) == 0x100) { -// printf("%04x <- %02x\n", address, *value); + printf("%04x <- %02x\n", address, *value); // } #endif } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 0f2b4e2b2..f1fb3f73d 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -227,6 +227,10 @@ template void Processor::run_for(const Cycles data_address_ = instruction_buffer_.value + data_bank_; continue; + case OperationConstructAbsoluteLong: + data_address_ = instruction_buffer_.value; + continue; + case OperationConstructAbsoluteIndexedIndirect: data_address_ = (instruction_buffer_.value + x()) & 0xffff; continue; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 03cb4feca..a5c5c0f93 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -258,23 +258,22 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 4a. Absolute long; al. static void absolute_long(AccessType type, bool is8bit, const std::function &target) { - target(CycleFetchIncrementPC); // AAL. - target(CycleFetchIncrementPC); // AAH. - target(CycleFetchPC); // AAB. + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(CycleFetchIncrementPC); // AAB. - target(OperationConstructAbsolute); // Calculate data address. + target(OperationConstructAbsoluteLong); // Calculate data address. read_write(type, is8bit, target); } // 4b. Absolute long; al, JMP. static void absolute_long_jmp(AccessType, bool, const std::function &target) { - target(CycleFetchIncrementPC); // New PCL. - target(CycleFetchIncrementPC); // New PCH. - target(CycleFetchPC); // New PBR. + target(CycleFetchIncrementPC); // New PCL. + target(CycleFetchIncrementPC); // New PCH. + target(CycleFetchPC); // New PBR. - target(OperationConstructAbsolute); // Calculate data address. - target(OperationPerform); // ['JMP' (though it's JML in internal terms)] + target(OperationPerform); // ['JMP' (though it's JML in internal terms)] } // 4c. Absolute long al, JSL. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index b294e9bcb..98a448dc0 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -55,6 +55,9 @@ enum MicroOp: uint8_t { /// using the data register as a high byte. OperationConstructAbsolute, + /// Sets the data address by copying the entire instruction buffer. + OperationConstructAbsoluteLong, + /// Sets the data address to the 16-bit result of adding x to the value in the instruction buffer. OperationConstructAbsoluteIndexedIndirect, From f529eadbec66d5d1b97ebc1e6b26a050f2da4ccb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Oct 2020 18:36:09 -0400 Subject: [PATCH 115/150] Corrects 16-bit read-modify-write. Subject to the TODO proviso on 'correct'; has my 6502 prejudice pushed me into unrealistic bus signalling? --- Processors/65816/Implementation/65816Implementation.hpp | 8 ++++++-- Processors/65816/Implementation/65816Storage.cpp | 6 ++++-- Processors/65816/Implementation/65816Storage.hpp | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index f1fb3f73d..d125b207f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -97,6 +97,10 @@ template void Processor::run_for(const Cycles read(data_address_, data_buffer_.next_input()); break; + case CycleFetchDataThrowaway: + read(data_address_, &throwaway); + break; + case CycleFetchIncorrectDataAddress: read(incorrect_data_address_, &throwaway); break; @@ -120,7 +124,7 @@ template void Processor::run_for(const Cycles break; case CycleStoreDecrementData: - write(data_address_, data_buffer_.next_output()); + write(data_address_, data_buffer_.next_output_descending()); decrement_data_address(); break; @@ -149,7 +153,7 @@ template void Processor::run_for(const Cycles bus_operation = operation; case CyclePush: - stack_access(data_buffer_.next_stack(), MOS6502Esque::Write); + stack_access(data_buffer_.next_output_descending(), MOS6502Esque::Write); --s_.full; break; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index a5c5c0f93..7ede1d332 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -159,7 +159,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { if(!is8bit) target(CycleFetchIncrementData); // Data low. target(CycleFetchData); // Data [high]. - if(!is8bit) target(CycleFetchData); // 16-bit: reread final byte of data. + // TODO: does this look like another read? Or if VDA and VPA are both low, does the 65816 actually do no access? + if(!is8bit) target(CycleFetchDataThrowaway); // 16-bit: reread final byte of data. else target(CycleStoreDataThrowaway); // 8-bit rewrite final byte of data. target(OperationPerform); // Perform operation within the data buffer. @@ -720,7 +721,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(OperationConstructStackRelative); target(CycleFetchIncrementData); // AAL target(CycleFetchData); // AAH - target(CycleFetchData); // IO. + target(CycleFetchDataThrowaway); // IO. target(OperationConstructStackRelativeIndexedIndirect); read_write(type, is8bit, target); @@ -1052,6 +1053,7 @@ void ProcessorStorage::set_emulation_mode(bool enabled) { } void ProcessorStorage::set_m_x_flags(bool m, bool x) { + // true/1 => 8bit for both flags. mx_flags_[0] = m; mx_flags_[1] = x; diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 98a448dc0..f3348ea7f 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -23,6 +23,8 @@ enum MicroOp: uint8_t { /// Fetches from the address formed by the low byte of the data address and the high byte /// of the instruction buffer, throwing the result away. CycleFetchIncorrectDataAddress, + /// Fetches a byte from the data address and throws it away. + CycleFetchDataThrowaway, // Dedicated block-move cycles; these use the data buffer as an intermediary. CycleFetchBlockX, @@ -306,7 +308,7 @@ struct ProcessorStorage { return byte(read); } - uint8_t *next_stack() { + uint8_t *next_output_descending() { --size; return byte(size); } From 28da1a724a2c058dbd66ea6756d1e3c41110cc4b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Oct 2020 21:43:44 -0400 Subject: [PATCH 116/150] Introduces Jeek816 test case. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++- .../Mac/Clock SignalTests/Jeek816Tests.swift | 41 +++++++++++++++++++ Processors/6502/AllRAM/6502AllRAM.cpp | 2 +- .../65816/Implementation/65816Storage.cpp | 5 +-- 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 1fdf6508d..77708221a 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -214,6 +214,7 @@ 4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; 4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; 4B4F47652533EA64004245B8 /* suite-a.prg in Resources */ = {isa = PBXBuildFile; fileRef = 4B4F475E2533EA64004245B8 /* suite-a.prg */; }; + 4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */; }; 4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; }; 4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; }; 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; }; @@ -1130,6 +1131,7 @@ 4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LazyFlags.hpp; sourceTree = ""; }; 4B4F2B7024DF99D4000DA6B0 /* CSScanTarget+CppScanTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSScanTarget+CppScanTarget.h"; sourceTree = ""; }; 4B4F475E2533EA64004245B8 /* suite-a.prg */ = {isa = PBXFileReference; lastKnownFileType = file; path = "suite-a.prg"; sourceTree = ""; }; + 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Jeek816Tests.swift; sourceTree = ""; }; 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = ""; }; 4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = ""; }; @@ -3433,7 +3435,6 @@ 4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = { isa = PBXGroup; children = ( - 4BC0CB272446BC7B00A79DBB /* OPLTests.mm */, 4B85322922778E4200F26553 /* Comparative68000.hpp */, 4B90467222C6FA31000E2074 /* TestRunner68000.hpp */, 4B97ADC722C6FD9B00A22A41 /* 68000ArithmeticTests.mm */, @@ -3452,6 +3453,7 @@ 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */, 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */, 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */, + 4BC0CB272446BC7B00A79DBB /* OPLTests.mm */, 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */, 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */, 4BE76CF822641ED300ACD6FA /* QLTests.mm */, @@ -3467,6 +3469,7 @@ 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */, 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */, 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */, + 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */, 4B1414611B58888700E04248 /* KlausDormannTests.swift */, 4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */, 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */, @@ -4908,6 +4911,7 @@ 4B778F4D23A5F20F0000D260 /* StaticAnalyser.cpp in Sources */, 4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */, 4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */, + 4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */, 4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */, 4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */, 4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift b/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift new file mode 100644 index 000000000..204f59c7a --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift @@ -0,0 +1,41 @@ +// +// Jeek816Tests.swift +// Clock Signal +// +// Created by Thomas Harte on 12/10/2020. +// Copyright 2020 Thomas Harte. All rights reserved. +// + +import XCTest +import Foundation + +class Jeek816Tests: XCTestCase { + func testJeek816() { + var machine: CSTestMachine6502! + + if let filename = Bundle(for: type(of: self)).path(forResource: "suite-a.prg", ofType: nil) { + if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) { + machine = CSTestMachine6502(processor: .processor65816) + + let contents = testData.subdata(in: 0xe ..< testData.count) + machine.setData(contents, atAddress: 0x080d) + + machine.setValue(0x080d, for: .programCounter) + } + } + + if machine == nil { + NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Couldn't load file \(name)", userInfo: nil).raise() + } + + while machine.value(for: .lastOperationAddress) != 0x0874 { + machine.runForNumber(ofCycles: 1000) + } + + // The test leaves $ff in $d7ff to indicate failure; $0000 to indicate success. + // If the tests failed, it'll leave a bitmap of failures in address $0401. + if machine.value(forAddress: 0xd7ff) != 0 { + NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Failed tests with bitmap: \(String(format:"%02x", machine.value(forAddress: 0x401)))", userInfo: nil).raise() + } + } +} diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index 620c5c24a..372cc40da 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -11,7 +11,7 @@ #include #include -#define BE_NOISY +//#define BE_NOISY using namespace CPU::MOS6502; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 7ede1d332..461290d5f 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -1036,8 +1036,9 @@ void ProcessorStorage::set_emulation_mode(bool enabled) { if(emulation_flag_ == enabled) { return; } + emulation_flag_ = enabled; - if(emulation_flag_) { + if(enabled) { set_m_x_flags(true, true); x_.halves.high = y_.halves.high = 0; e_masks_[0] = 0xff00; @@ -1048,8 +1049,6 @@ void ProcessorStorage::set_emulation_mode(bool enabled) { s_.halves.high = 1; // To pretend it was 1 all along; this implementation actually ignores // the top byte while in emulation mode. } - - emulation_flag_ = enabled; } void ProcessorStorage::set_m_x_flags(bool m, bool x) { From 7479dc74ed2f6733fa30db07edcdbe574921fee5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Oct 2020 21:52:58 -0400 Subject: [PATCH 117/150] Removes printf. It's no longer telling me anything. --- Processors/65816/Implementation/65816Storage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 461290d5f..6c5d376ee 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -1017,10 +1017,8 @@ ProcessorStorage::ProcessorStorage() { next_op_ = micro_ops_.data(); while(*next_op_ != OperationMoveToNextProgram) ++next_op_; -#ifndef NDEBUG - assert(micro_ops_.size() < 65536); - printf("Generated %zd micro-ops in total; covered %d opcodes\n", micro_ops_.size(), constructor.opcode); -#endif + // This is primarily to keep tabs, in case I want to pick a shorter form for the instruction table. + assert(micro_ops_.size() < 1024); } void ProcessorStorage::set_reset_state() { From 6a47571d171790f0cea5d3ac3c2d7352c977eddf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Oct 2020 21:53:27 -0400 Subject: [PATCH 118/150] Stops truncating tests by two bytes. Not that it seems to have been problematic. --- OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index 4363f5952..ebc24a371 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -292,7 +292,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { let dataPointer = (testData as NSData).bytes.bindMemory(to: UInt8.self, capacity: testData.count) let loadAddress = UInt16(dataPointer[0]) | (UInt16(dataPointer[1]) << 8) - let contents = testData.subdata(in: 2..<(testData.count - 2)) + let contents = testData.subdata(in: 2 ..< testData.count) machine.setData(contents, atAddress: loadAddress) From a15d4a156b27349cb6cb3b1943ba65ccce003743 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Oct 2020 22:33:43 -0400 Subject: [PATCH 119/150] Starts trying to ensure appropriate address wrapping. --- .../Implementation/65816Implementation.hpp | 54 +++++++++++++------ .../65816/Implementation/65816Storage.cpp | 2 + .../65816/Implementation/65816Storage.hpp | 1 + 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index d125b207f..63861d4c1 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -89,8 +89,8 @@ template void Processor::run_for(const Cycles // Data fetches and stores. // -#define increment_data_address() data_address_ = (data_address_ & 0xff0000) + ((data_address_ + 1) & 0xffff) -#define decrement_data_address() data_address_ = (data_address_ & 0xff0000) + ((data_address_ - 1) & 0xffff) +#define increment_data_address() data_address_ = (data_address_ & ~data_address_increment_mask_) + ((data_address_ + 1) & data_address_increment_mask_) +#define decrement_data_address() data_address_ = (data_address_ & ~data_address_increment_mask_) + ((data_address_ - 1) & data_address_increment_mask_) case CycleFetchData: @@ -229,18 +229,22 @@ template void Processor::run_for(const Cycles case OperationConstructAbsolute: data_address_ = instruction_buffer_.value + data_bank_; + data_address_increment_mask_ = 0xff'ff'ff; continue; case OperationConstructAbsoluteLong: data_address_ = instruction_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; continue; case OperationConstructAbsoluteIndexedIndirect: data_address_ = (instruction_buffer_.value + x()) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; continue; case OperationConstructAbsoluteLongX: - data_address_ = (instruction_buffer_.value + x()) & 0xffff + instruction_buffer_.value & 0xff0000; + data_address_ = instruction_buffer_.value + x(); + data_address_increment_mask_ = 0xff'ff'ff; continue; case OperationConstructAbsoluteXRead: @@ -252,6 +256,7 @@ template void Processor::run_for(const Cycles if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { ++next_op_; } + data_address_increment_mask_ = 0xff'ff'ff; continue; case OperationConstructAbsoluteYRead: @@ -263,10 +268,13 @@ template void Processor::run_for(const Cycles if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) { ++next_op_; } + data_address_increment_mask_ = 0xff'ff'ff; continue; case OperationConstructDirect: + // TODO: is this address correct in emulation mode when the low byte is zero? data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; if(!(direct_&0xff)) { ++next_op_; } @@ -277,62 +285,74 @@ template void Processor::run_for(const Cycles ((direct_ + x() + instruction_buffer_.value) & e_masks_[1]) + (direct_ & e_masks_[0]) ) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + if(!(direct_&0xff)) { ++next_op_; } continue; - case OperationConstructDirectIndirect: - data_address_ = data_bank_ + (direct_ + instruction_buffer_.value) & 0xffff; + case OperationConstructDirectIndirect: // TODO: seems very incorrect. Check this and the programs that use it; + // 12 looks wrong, the others look correct? + data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + if(!(direct_&0xff)) { ++next_op_; } continue; case OperationConstructDirectIndirectIndexedLong: - // TODO: assumed here is that the low 16-bit calculation can't carry into - // the high byte. Test this! - data_address_ = (y() + instruction_buffer_.value) & 0xffff + instruction_buffer_.value & 0xff0000; + data_address_ = y() + instruction_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; continue; case OperationConstructDirectIndirectLong: data_address_ = instruction_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; continue; - case OperationConstructDirectX: { + // TODO: confirm incorrect_data_address_ below. + + case OperationConstructDirectX: data_address_ = ( (direct_ & e_masks_[0]) + ((instruction_buffer_.value + direct_ + x()) & e_masks_[1]) ) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); if(!(direct_&0xff)) { ++next_op_; } - } continue; + continue; case OperationConstructDirectY: data_address_ = ( (direct_ & e_masks_[0]) + ((instruction_buffer_.value + direct_ + y()) & e_masks_[1]) ) & 0xffff; - // TODO: given the 16-bit internal arithmetic, confirm this is the correct spurious address. + data_address_increment_mask_ = 0x00'ff'ff; + incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); if(!(direct_&0xff)) { ++next_op_; } continue; - case OperationConstructPER: - data_buffer_.value = instruction_buffer_.value + pc_; - data_buffer_.size = 2; - continue; - case OperationConstructStackRelative: data_address_ = (s_.full + instruction_buffer_.value) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; continue; case OperationConstructStackRelativeIndexedIndirect: - data_address_ = data_bank_ + (instruction_buffer_.value + y()) & 0xffff; + data_address_ = (instruction_buffer_.value + y()) & 0xffff; + data_address_increment_mask_ = 0xff'ff'ff; + continue; + + case OperationConstructPER: + data_buffer_.value = instruction_buffer_.value + pc_; + data_buffer_.size = 2; continue; case OperationPrepareException: { diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 6c5d376ee..d0c0d8fcb 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -238,6 +238,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPC); // New AAH. target(OperationConstructAbsolute); // Calculate data address. + // TODO: needs to apply a 16-bit limit here: target(CycleFetchIncrementData); // New PCL target(CycleFetchIncrementData); // New PCH target(CycleFetchData); // New PBR @@ -251,6 +252,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchPC); // New AAH. target(OperationConstructAbsolute); // Calculate data address. + // TODO: needs to apply a 16-bit limit here: target(CycleFetchIncrementData); // New PCL target(CycleFetchData); // New PCH diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index f3348ea7f..3606b0b47 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -328,6 +328,7 @@ struct ProcessorStorage { }; Buffer instruction_buffer_, data_buffer_; uint32_t data_address_; + uint32_t data_address_increment_mask_ = 0xffff; uint32_t incorrect_data_address_; std::vector micro_ops_; From 8f5537aaaa97ad8844b557caed6368259ff09aa6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Oct 2020 20:21:53 -0400 Subject: [PATCH 120/150] Attempts to resolve my direct-indirect addressing stumble. --- .../65816/Implementation/65816Implementation.hpp | 15 +++++---------- Processors/65816/Implementation/65816Storage.cpp | 11 ++++++++--- Processors/65816/Implementation/65816Storage.hpp | 5 ++--- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 63861d4c1..f2e19b42d 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -280,6 +280,11 @@ template void Processor::run_for(const Cycles } continue; + case OperationConstructDirectIndirect: + data_address_ = data_bank_ + direct_ + data_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; + continue; + case OperationConstructDirectIndexedIndirect: data_address_ = data_bank_ + ( ((direct_ + x() + instruction_buffer_.value) & e_masks_[1]) + @@ -292,16 +297,6 @@ template void Processor::run_for(const Cycles } continue; - case OperationConstructDirectIndirect: // TODO: seems very incorrect. Check this and the programs that use it; - // 12 looks wrong, the others look correct? - data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; - data_address_increment_mask_ = 0x00'ff'ff; - - if(!(direct_&0xff)) { - ++next_op_; - } - continue; - case OperationConstructDirectIndirectIndexedLong: data_address_ = y() + instruction_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index d0c0d8fcb..988a962a7 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -423,9 +423,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void direct_indirect(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // DO. - target(OperationConstructDirectIndirect); + target(OperationConstructDirect); target(CycleFetchPCThrowaway); // IO. + target(CycleFetchIncrementData); // AAL. + target(CycleFetchData); // AAH. + + target(OperationConstructDirectIndirect); + read_write(type, is8bit, target); } @@ -450,7 +455,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void direct_indirect_indexed_long(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // DO. - target(OperationConstructDirectIndirect); + target(OperationConstructDirect); target(CycleFetchPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. @@ -466,7 +471,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void direct_indirect_long(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // DO. - target(OperationConstructDirectIndirect); + target(OperationConstructDirect); target(CycleFetchPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 3606b0b47..d9537fb49 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -89,9 +89,8 @@ enum MicroOp: uint8_t { /// Skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirectIndexedIndirect, - /// Constructs the current direct indexed indirect address using the data bank and - /// direct registers plus the value currently in the instruction buffer. - /// Skips the next micro-op if the low byte of the direct register is 0. + /// Constructs the current direct indexed indirect address using the value + /// currently in the data buffer. OperationConstructDirectIndirect, /// Adds y to the low 16-bits currently in the instruction buffer and appends a high 8-bits From 3e6a2adaafff5cfd567725cfe47300fff2a262a5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Oct 2020 20:30:39 -0400 Subject: [PATCH 121/150] Corrects absolute, x and absolute, y addressing modes. --- OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift | 3 ++- Processors/65816/Implementation/65816Implementation.hpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift b/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift index 204f59c7a..31dbe7d17 100644 --- a/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift @@ -28,7 +28,8 @@ class Jeek816Tests: XCTestCase { NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Couldn't load file \(name)", userInfo: nil).raise() } - while machine.value(for: .lastOperationAddress) != 0x0874 { + // $874 is the failure stopping point and $85d is success. + while machine.value(for: .lastOperationAddress) != 0x0874 && machine.value(for: .lastOperationAddress) != 0x085d { machine.runForNumber(ofCycles: 1000) } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index f2e19b42d..fae4e64e6 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -249,7 +249,7 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteXRead: case OperationConstructAbsoluteX: - data_address_ = ((instruction_buffer_.value + x()) & 0xffff) + data_bank_; + data_address_ = instruction_buffer_.value + x() + data_bank_; incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) + data_bank_; // If the incorrect address isn't actually incorrect, skip its usage. @@ -261,7 +261,7 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteYRead: case OperationConstructAbsoluteY: - data_address_ = ((instruction_buffer_.value + y()) & 0xffff) + data_bank_; + data_address_ = instruction_buffer_.value + y() + data_bank_; incorrect_data_address_ = (data_address_ & 0xff) + (instruction_buffer_.value & 0xff00) + data_bank_; // If the incorrect address isn't actually incorrect, skip its usage. From b22aa5d69929427e2dbf2b32d6487d2513b39993 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Oct 2020 21:38:30 -0400 Subject: [PATCH 122/150] Starts transcribing the addressing examples I have into tests. Correspondingly extends the exposed register set and test-machine addressing range. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 + .../6502InterruptTests.swift | 2 +- .../65816AddressingTests.swift | 93 +++++++++++++++++++ .../Bridges/TestMachine6502.h | 10 +- .../Bridges/TestMachine6502.mm | 10 +- .../WolfgangLorenzTests.swift | 2 +- Processors/6502Esque/6502Esque.hpp | 8 +- Processors/65816/Implementation/65816Base.cpp | 20 ++-- .../Implementation/65816Implementation.hpp | 8 +- .../65816/Implementation/65816Storage.cpp | 6 +- .../65816/Implementation/65816Storage.hpp | 3 + 11 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 77708221a..34ce90551 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -215,6 +215,7 @@ 4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; 4B4F47652533EA64004245B8 /* suite-a.prg in Resources */ = {isa = PBXBuildFile; fileRef = 4B4F475E2533EA64004245B8 /* suite-a.prg */; }; 4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */; }; + 4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */; }; 4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; }; 4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; }; 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; }; @@ -1132,6 +1133,7 @@ 4B4F2B7024DF99D4000DA6B0 /* CSScanTarget+CppScanTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSScanTarget+CppScanTarget.h"; sourceTree = ""; }; 4B4F475E2533EA64004245B8 /* suite-a.prg */ = {isa = PBXFileReference; lastKnownFileType = file; path = "suite-a.prg"; sourceTree = ""; }; 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Jeek816Tests.swift; sourceTree = ""; }; + 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 65816AddressingTests.swift; sourceTree = ""; }; 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = ""; }; 4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = ""; }; @@ -3464,6 +3466,7 @@ 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */, 4BC751B11D157E61006C31D9 /* 6522Tests.swift */, 4B1E85801D176468001EF87D /* 6532Tests.swift */, + 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */, 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */, 4B049CDC1DA3C82F00322067 /* BCDTest.swift */, 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */, @@ -5009,6 +5012,7 @@ 4B778F5723A5F2BB0000D260 /* ZX8081.cpp in Sources */, 4B778F2F23A5F0B10000D260 /* ScanTarget.cpp in Sources */, 4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */, + 4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */, 4B778F0B23A5EC150000D260 /* TapeUEF.cpp in Sources */, 4B778F0523A5EBB00000D260 /* ST.cpp in Sources */, 4B778F0C23A5EC150000D260 /* TZX.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift b/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift index 23edefea8..fdcc41ea4 100644 --- a/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift +++ b/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift @@ -17,7 +17,7 @@ class MOS6502InterruptTests: XCTestCase { // create a machine full of NOPs machine = CSTestMachine6502(processor: .processor6502) for c in 0...65535 { - machine.setValue(0xea, forAddress: UInt16(c)) + machine.setValue(0xea, forAddress: UInt32(c)) } // set the IRQ vector to be 0x1234 diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift new file mode 100644 index 000000000..f567fdaf0 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -0,0 +1,93 @@ +// +// WDC65816AddressingTests.swift +// Clock Signal +// +// Created by Thomas Harte on 13/10/2020. +// Copyright 2020 Thomas Harte. All rights reserved. +// + +import Foundation +import XCTest + +// This exactly transcribes the examples given in http://6502.org/tutorials/65c816opcodes.html#5.9 +class WDC65816AddressingTests: XCTestCase { + + private func machine16() -> CSTestMachine6502 { + let machine = CSTestMachine6502(processor: .processor65816) + machine.setValue(0, for: .emulationFlag) + machine.setValue(0, for: .flags) + return machine + } + + func testAbsolute() { + let machine = machine16() + + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xab, forAddress: 0x12ffff) + machine.setValue(0xcd, forAddress: 0x130000) + + // LDA $ffff; NOP + machine.setData(Data([0xad, 0xff, 0xff, 0xea]), atAddress: 0x200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 6) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) + } + + func testAbsoluteX() { + let machine = machine16() + + machine.setValue(0x12, for: .dataBank) + machine.setValue(0x0a, for: .X) + + machine.setValue(0xab, forAddress: 0x130008) + machine.setValue(0xcd, forAddress: 0x130009) + + // LDA $fffe, x; NOP + machine.setData(Data([0xbd, 0xfe, 0xff, 0xea]), atAddress: 0x200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) + } + + func testJMPAbsoluteIndirect() { + let machine = machine16() + + machine.setValue(0x12, for: .programBank) + machine.setValue(0x0a, for: .X) + + machine.setValue(0x34, forAddress: 0x0000) + machine.setValue(0x56, forAddress: 0xffff) + + // JMP ($ffff); NOP + machine.setData(Data([0x6c, 0xff, 0xff, 0xea]), atAddress: 0x120200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 6) + + XCTAssertEqual(machine.value(for: .programCounter), 0x3456 + 1) + XCTAssertEqual(machine.value(for: .programBank), 0x12) + } + + func testIndirectAbsoluteX() { + let machine = machine16() + + machine.setValue(0x12, for: .programBank) + machine.setValue(0x0a, for: .X) + machine.setValue(0x56, forAddress: 0x120008) + machine.setValue(0x34, forAddress: 0x120009) + + // JMP ($fffe, x); NOP + machine.setData(Data([0x7c, 0xfe, 0xff, 0xea]), atAddress: 0x120200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .programCounter), 0x3456 + 1) + XCTAssertEqual(machine.value(for: .programBank), 0x12) + } +} diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h index ffb2a9e1a..206701607 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.h @@ -17,6 +17,10 @@ typedef NS_ENUM(NSInteger, CSTestMachine6502Register) { CSTestMachine6502RegisterA, CSTestMachine6502RegisterX, CSTestMachine6502RegisterY, + CSTestMachine6502RegisterEmulationFlag, + CSTestMachine6502RegisterDataBank, + CSTestMachine6502RegisterProgramBank, + CSTestMachine6502RegisterDirect, }; typedef NS_ENUM(NSInteger, CSTestMachine6502Processor) { @@ -33,11 +37,11 @@ extern const uint8_t CSTestMachine6502JamOpcode; - (nonnull instancetype)initWithProcessor:(CSTestMachine6502Processor)processor; -- (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress; +- (void)setData:(nonnull NSData *)data atAddress:(uint32_t)startAddress; - (void)runForNumberOfCycles:(int)cycles; -- (void)setValue:(uint8_t)value forAddress:(uint16_t)address; -- (uint8_t)valueForAddress:(uint16_t)address; +- (void)setValue:(uint8_t)value forAddress:(uint32_t)address; +- (uint8_t)valueForAddress:(uint32_t)address; - (void)setValue:(uint16_t)value forRegister:(CSTestMachine6502Register)reg; - (uint16_t)valueForRegister:(CSTestMachine6502Register)reg; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm index f25bbbe8c..c5f073a62 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm @@ -24,6 +24,10 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg) case CSTestMachine6502RegisterX: return CPU::MOS6502::Register::X; case CSTestMachine6502RegisterY: return CPU::MOS6502::Register::Y; case CSTestMachine6502RegisterStackPointer: return CPU::MOS6502::Register::StackPointer; + case CSTestMachine6502RegisterEmulationFlag: return CPU::MOS6502::Register::EmulationFlag; + case CSTestMachine6502RegisterDataBank: return CPU::MOS6502::Register::DataBank; + case CSTestMachine6502RegisterProgramBank: return CPU::MOS6502::Register::ProgramBank; + case CSTestMachine6502RegisterDirect: return CPU::MOS6502::Register::Direct; } } @@ -60,13 +64,13 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg) #pragma mark - Accessors -- (uint8_t)valueForAddress:(uint16_t)address { +- (uint8_t)valueForAddress:(uint32_t)address { uint8_t value; _processor->get_data_at_address(address, 1, &value); return value; } -- (void)setValue:(uint8_t)value forAddress:(uint16_t)address { +- (void)setValue:(uint8_t)value forAddress:(uint32_t)address { _processor->set_data_at_address(address, 1, &value); } @@ -78,7 +82,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg) return _processor->get_value_of_register(registerForRegister(reg)); } -- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress { +- (void)setData:(NSData *)data atAddress:(uint32_t)startAddress { _processor->set_data_at_address(startAddress, data.length, (const uint8_t *)data.bytes); } diff --git a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift index ebc24a371..2f5bc3b21 100644 --- a/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift +++ b/OSBindings/Mac/Clock SignalTests/WolfgangLorenzTests.swift @@ -291,7 +291,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler { output = "" let dataPointer = (testData as NSData).bytes.bindMemory(to: UInt8.self, capacity: testData.count) - let loadAddress = UInt16(dataPointer[0]) | (UInt16(dataPointer[1]) << 8) + let loadAddress = UInt32(dataPointer[0]) | (UInt32(dataPointer[1]) << 8) let contents = testData.subdata(in: 2 ..< testData.count) machine.setData(contents, atAddress: loadAddress) diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index 06942e04c..2e6f5a929 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -34,7 +34,13 @@ enum Register { Flags, A, X, - Y + Y, + + // These exist on the 65816 only. + EmulationFlag, + DataBank, + ProgramBank, + Direct }; /* diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index 55f06aa4a..d68f21007 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -19,18 +19,26 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { case Register::A: return a_.full; case Register::X: return x_.full; case Register::Y: return y_.full; + case Register::EmulationFlag: return emulation_flag_; + case Register::DataBank: return data_bank_ >> 16; + case Register::ProgramBank: return program_bank_ >> 16; + case Register::Direct: return direct_; default: return 0; } } void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { - case Register::ProgramCounter: pc_ = value; break; - case Register::StackPointer: s_.full = value; break; - case Register::Flags: set_flags(uint8_t(value)); break; - case Register::A: a_.full = value; break; - case Register::X: x_.full = value; break; - case Register::Y: y_.full = value; break; + case Register::ProgramCounter: pc_ = value; break; + case Register::StackPointer: s_.full = value; break; + case Register::Flags: set_flags(uint8_t(value)); break; + case Register::A: a_.full = value; break; + case Register::X: x_.full = value; break; + case Register::Y: y_.full = value; break; + case Register::EmulationFlag: set_emulation_mode(value); break; + case Register::DataBank: data_bank_ = uint32_t(value) << 16; break; + case Register::ProgramBank: program_bank_ = uint32_t(value) << 16; break; + case Register::Direct: direct_ = value; break; default: break; } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index fae4e64e6..eca58721a 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -232,13 +232,19 @@ template void Processor::run_for(const Cycles data_address_increment_mask_ = 0xff'ff'ff; continue; + case OperationConstructAbsolute16: + data_address_ = instruction_buffer_.value; + data_address_increment_mask_ = 0x00'ff'ff; + continue; + case OperationConstructAbsoluteLong: data_address_ = instruction_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; continue; + // Used for JMP and JSR (absolute, x). case OperationConstructAbsoluteIndexedIndirect: - data_address_ = (instruction_buffer_.value + x()) & 0xffff; + data_address_ = program_bank_ + ((instruction_buffer_.value + x()) & 0xffff); data_address_increment_mask_ = 0x00'ff'ff; continue; diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 988a962a7..6790d6430 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -237,8 +237,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // New AAL. target(CycleFetchPC); // New AAH. - target(OperationConstructAbsolute); // Calculate data address. - // TODO: needs to apply a 16-bit limit here: + target(OperationConstructAbsolute16); // Calculate data address. target(CycleFetchIncrementData); // New PCL target(CycleFetchIncrementData); // New PCH target(CycleFetchData); // New PBR @@ -251,8 +250,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CycleFetchIncrementPC); // New AAL. target(CycleFetchPC); // New AAH. - target(OperationConstructAbsolute); // Calculate data address. - // TODO: needs to apply a 16-bit limit here: + target(OperationConstructAbsolute16); // Calculate data address. target(CycleFetchIncrementData); // New PCL target(CycleFetchData); // New PCH diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index d9537fb49..d89fc4b39 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -57,6 +57,9 @@ enum MicroOp: uint8_t { /// using the data register as a high byte. OperationConstructAbsolute, + /// Constructs a strictly 16-bit address from the instruction buffer. + OperationConstructAbsolute16, + /// Sets the data address by copying the entire instruction buffer. OperationConstructAbsoluteLong, From f05e0d956b6e64ed948efc0280f478be1d0af4cb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Oct 2020 21:43:42 -0400 Subject: [PATCH 123/150] Adds a TODO list in order to keep an end in sight. --- .../Clock SignalTests/65816AddressingTests.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index f567fdaf0..93e1c5746 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -90,4 +90,17 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .programCounter), 0x3456 + 1) XCTAssertEqual(machine.value(for: .programBank), 0x12) } + + // TODO: + // Direct [x2] + // Direct, X [x2] + // (Direct) [x2] + // [Direct] + // (Direct, X) [x2] + // (Direct), Y [x2] + // [Direct], Y + // Long + // Long, X + // Stack, S + // (Stack, S), Y } From 979186e71deaa8775f56bc1fb084c92e52689e11 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Oct 2020 13:56:37 -0400 Subject: [PATCH 124/150] Transcribes the English-language versions of the outstanding tests. Passing these will make me willing to call the 65816 functionality provisionally done, other than making sureI signal VPA, VDA, VPB, etc, correctly. --- .../65816AddressingTests.swift | 189 ++++++++++++++++-- 1 file changed, 177 insertions(+), 12 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index 93e1c5746..46ba34107 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -10,8 +10,12 @@ import Foundation import XCTest // This exactly transcribes the examples given in http://6502.org/tutorials/65c816opcodes.html#5.9 +// Quoted text is taken verbatim from that document. class WDC65816AddressingTests: XCTestCase { + // MARK: - Test Machines + + /// Returns a CSTestMachine6502 that is currently configured in native mode with 16-bit memory and index registers. private func machine16() -> CSTestMachine6502 { let machine = CSTestMachine6502(processor: .processor65816) machine.setValue(0, for: .emulationFlag) @@ -19,7 +23,17 @@ class WDC65816AddressingTests: XCTestCase { return machine } + /// Returns a CSTestMachine6502 that is currently configured in emulation mode. + private func machine8() -> CSTestMachine6502 { + return CSTestMachine6502(processor: .processor65816) + } + + // MARK: - Tests + func testAbsolute() { + // "If the DBR is $12 and the m flag is 0, then LDA $FFFF loads the low byte of + // the data from address $12FFFF, and the high byte from address $130000" + let machine = machine16() machine.setValue(0x12, for: .dataBank) @@ -37,6 +51,13 @@ class WDC65816AddressingTests: XCTestCase { } func testAbsoluteX() { + // "If the DBR is $12, the X register is $000A, and the m flag is 0, then + // LDA $FFFE,X loads the low byte of the data from address $130008, and + // the high byte from address $130009" + // + // "Note that this is one of the rare instances where emulation mode has + // different behavior than the 65C02 or NMOS 6502 ..." + let machine = machine16() machine.setValue(0x12, for: .dataBank) @@ -55,6 +76,11 @@ class WDC65816AddressingTests: XCTestCase { } func testJMPAbsoluteIndirect() { + // "If the K register is $12 and + // * $000000 contains $34 + // * $00FFFF contains $56 + // then JMP ($FFFF) jumps to $123456" + let machine = machine16() machine.setValue(0x12, for: .programBank) @@ -74,6 +100,11 @@ class WDC65816AddressingTests: XCTestCase { } func testIndirectAbsoluteX() { + // "If the K register is $12, the X register is $000A, and + // * $120008 contains $56 + // * $120009 contains $34 + //then JMP ($FFFE,X) jumps to $123456" + let machine = machine16() machine.setValue(0x12, for: .programBank) @@ -91,16 +122,150 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .programBank), 0x12) } - // TODO: - // Direct [x2] - // Direct, X [x2] - // (Direct) [x2] - // [Direct] - // (Direct, X) [x2] - // (Direct), Y [x2] - // [Direct], Y - // Long - // Long, X - // Stack, S - // (Stack, S), Y + // TODO: all tests from this point downward. + + func testAbsoluteJMP() { + // TODO. + // "If the K register is $12, then JMP $FFFF jumps to $12FFFF" + } + + func testDirect8() { + // "If the D register is $FF00 and the e flag is 1 (note that + // this means the m flag must be 1), then LDA $FF loads the low + // byte of the data from address $00FFFF" + } + + func testDirect16() { + // "If the D register is $FF00 and the m flag is 0 (note that + // this means the e flag must be 0), then LDA $FF loads the low + // byte of the data from address $00FFFF, and the high byte + // from address $000000" + } + + func testDirextX8() { + // "If the D register is $FF00, the X register is $000A, and + // the e flag is 1 (note that this means the m flag must be 1), + // then LDA $FE,X loads the low byte of the data from + // address $00FF08" + } + + func testDirectX16() { + // "If the D register is $FF00, the X register is $000A, and the + // m flag is 0 (note that this means the e flag must be 0), then + // LDA $FE,X loads the low byte of the data from address $000008, + // and the high byte from address $000009" + } + + func testDirectIndirect8() { + // "If the D register is $FF00 and the e flag is 1 (note this means the + // m flag must be 1), then for LDA ($FF), the address of the low byte of + // the pointer is $00FFFF and the address of the high byte is $00FF00. + // Furthermore, if the DBR is $12 and + // * $00FF00 contains $FF + // * $00FFFF contains $FF + // then LDA ($FF) loads the low byte of the data from address $12FFFF." + } + + func testDirectIndirect16() { + // "If the D register is $FF00 and the m flag is 0 (note this means the e + // flag must be 0), then for LDA ($FF), the address of the low byte of the + // pointer is $00FFFF and the address of the high byte is $000000. + // Furthermore, if the DBR is $12 and + // * $000000 contains $FF + // * $00FFFF contains $FF + // then LDA ($FF) loads the low byte of the data from address $12FFFF, and + // the high byte from $130000." + } + + func testDirectIndirectLong() { + // "If the D register is $FF00 and the m flag is 0, then for LDA [$FE], the + // address of the low byte of the pointer is $00FFFE, the address of the middle + // byte is $00FFFF, and the address of the high byte is $000000. Furthermore, if + // * $000000 contains $12 + // * $00FFFE contains $FF + // * $00FFFF contains $FF + // then LDA [$FE] loads the low byte of the data from address $12FFFF, and the + // high byte from $130000." + } + + func testIndirectDirextX8() { + // "If the D register is $FF00, the X register is $000A, and the e flag is 1 (note + // that this means the m flag must be 1), then for LDA ($FE,X), the address of the + // low byte of the pointer is $00FF08 and the address of the high byte is $00FF09. + // Furthermore, if the DBR is $12 and + // * $00FF08 contains $FF + // * $00FF09 contains $FF + // then LDA ($FE,X) loads the low byte of the data from address $12FFFF." + } + + func testIndirectDirextX16() { + // "If the D register is $FF00, the X register is $000A, and the m flag is 0 + // (note that this means the e flag must be 0), then for LDA ($FE,X), the address + // of the low byte of the pointer is $000008 and the address of the high byte + // is $000009. Furthermore, if the DBR is $12 and + // * $000008 contains $FF + // * $000009 contains $FF + // then LDA ($FE,X) loads the low byte of the data from address $12FFFF, and the + // high byte from $130000." + } + + func testIndirectDirectY8() { + // "If the D register is $FF00 and the e flag is 1 (note that this means the + // m flag must be 1), then for LDA ($FF),Y, the address of the low byte of the + // pointer is $00FFFF and the address of the high byte is $00FF00. + // Furthermore, if the DBR is $12, the Y register is $000A, and + // * $00FF00 contains $FF + // * $00FFFF contains $FE + // then LDA ($FF),Y loads the low byte of the data from address $130008." + // + // "this is one of the rare instances where emulation mode has + // different behavior than the 65C02 or NMOS 6502..." + } + + func testIndirectDirectY16() { + // "If the D register is $FF00 and the m flag is 0 (note that this means the + // e flag must be 0), then for LDA ($FF),Y, the address of the low byte of the + // pointer is $00FFFF and the address of the high byte is $000000. + // Furthermore, if the DBR is $12, the Y register is $000A, and + // * $000000 contains $FF + // * $00FFFF contains $FE + // then LDA ($FF),Y loads the low byte of the data from address $130008, and the + // high byte from $130009." + } + + func testIndirectDirectYLong() { + // "If the D register is $FF00 and the m flag is 0, then for LDA [$FE],Y, the address + // of the low byte of the pointer is $00FFFE, the address of the middle byte is $00FFFF, + // and the address of the high byte is $000000. Furthermore, if the Y register is $000A, and + // * $000000 contains $12 + // * $00FFFE contains $FC + // * $00FFFF contains $FF + // then LDA [$FE],Y loads the low byte of the data from address $130006, and the high byte + // from $130007." + } + + func testLong() { + // "If the m flag is 0, then LDA $12FFFF loads the low byte of the data from address $12FFFF, + // and the high byte from address $130000." + } + + func testLongX() { + // "If the X register is $000A and the m flag is 0, then LDA $12FFFE,X loads the low byte of + // the data from address $130008, and the high byte from address $130009." + } + + func testStackS() { + // "If the S register is $FF10 and the m flag is 0, then LDA $FA,S loads the low byte + // of the data from address $00000A, and the high byte from address $00000B." + } + + func testIndirectStackSY() { + // "If the S register is $FF10 and the m flag is 0, then for LDA ($FA,S),Y, the address + // of the low byte of the pointer is $00000A and the address of the high byte is $00000B. + // Furthermore, if the DBR is $12, the Y register is $0050, and + // * $00000A contains $F0 + // * $00000B contains $FF + // then LDA ($FA,S),Y loads the low byte of the data from address $130040, and the high + // byte from $130041." + } } From db7178495f57ca2cd5f9770b8a6ace4bd6674912 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Oct 2020 20:57:47 -0400 Subject: [PATCH 125/150] Implements direct and final absolute test. 14 to go. --- .../65816AddressingTests.swift | 57 ++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index 46ba34107..eaa0c4118 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -9,7 +9,7 @@ import Foundation import XCTest -// This exactly transcribes the examples given in http://6502.org/tutorials/65c816opcodes.html#5.9 +// This exactly transcribes the examples given in http://6502.org/tutorials/65c816opcodes.html#5 // Quoted text is taken verbatim from that document. class WDC65816AddressingTests: XCTestCase { @@ -30,6 +30,22 @@ class WDC65816AddressingTests: XCTestCase { // MARK: - Tests + func testAbsoluteJMP() { + // "If the K register is $12, then JMP $FFFF jumps to $12FFFF" + let machine = machine16() + + machine.setValue(0x12, for: .programBank) + + // JMP $ffff + machine.setData(Data([0x4c, 0xff, 0xff]), atAddress: 0x120200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 4) + + XCTAssertEqual(machine.value(for: .programCounter), 0) // i.e. 0xffff + 1 + XCTAssertEqual(machine.value(for: .programBank), 0x12) + } + func testAbsolute() { // "If the DBR is $12 and the m flag is 0, then LDA $FFFF loads the low byte of // the data from address $12FFFF, and the high byte from address $130000" @@ -122,17 +138,24 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .programBank), 0x12) } - // TODO: all tests from this point downward. - - func testAbsoluteJMP() { - // TODO. - // "If the K register is $12, then JMP $FFFF jumps to $12FFFF" - } - func testDirect8() { // "If the D register is $FF00 and the e flag is 1 (note that // this means the m flag must be 1), then LDA $FF loads the low // byte of the data from address $00FFFF" + + let machine = machine8() + + machine.setValue(0xff00, for: .direct) + + machine.setValue(0xab, forAddress: 0xffff) + + // LDA $ff; NOP + machine.setData(Data([0xa5, 0xff, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 4) + + XCTAssertEqual(machine.value(for: .A), 0xab) } func testDirect16() { @@ -140,8 +163,26 @@ class WDC65816AddressingTests: XCTestCase { // this means the e flag must be 0), then LDA $FF loads the low // byte of the data from address $00FFFF, and the high byte // from address $000000" + + let machine = machine16() + + machine.setValue(0xff00, for: .direct) + + machine.setValue(0xab, forAddress: 0xffff) + machine.setValue(0xcd, forAddress: 0x0000) + + // LDA $ff; NOP + machine.setData(Data([0xa5, 0xff, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 5) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } + + // TODO: all tests from this point downward. + func testDirextX8() { // "If the D register is $FF00, the X register is $000A, and // the e flag is 1 (note that this means the m flag must be 1), From 327ab814367f394e1fdb06881ae1f95f5d7eb001 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Oct 2020 21:17:28 -0400 Subject: [PATCH 126/150] Fills in direct, x and (direct) tests, fixing implementation of the latter. 10 to go. --- .../65816AddressingTests.swift | 70 ++++++++++++++++++- .../Implementation/65816Implementation.hpp | 7 +- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index eaa0c4118..829d56527 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -180,14 +180,26 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .A), 0xcdab) } - - // TODO: all tests from this point downward. - func testDirextX8() { // "If the D register is $FF00, the X register is $000A, and // the e flag is 1 (note that this means the m flag must be 1), // then LDA $FE,X loads the low byte of the data from // address $00FF08" + + let machine = machine8() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x000a, for: .X) + + machine.setValue(0xab, forAddress: 0xff08) + + // LDA $fe, X; NOP + machine.setData(Data([0xb5, 0xfe, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 5) + + XCTAssertEqual(machine.value(for: .A), 0xab) } func testDirectX16() { @@ -195,6 +207,21 @@ class WDC65816AddressingTests: XCTestCase { // m flag is 0 (note that this means the e flag must be 0), then // LDA $FE,X loads the low byte of the data from address $000008, // and the high byte from address $000009" + let machine = machine16() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x000a, for: .X) + + machine.setValue(0xab, forAddress: 0x0008) + machine.setValue(0xcd, forAddress: 0x0009) + + // LDA $fe, X; NOP + machine.setData(Data([0xb5, 0xfe, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 6) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } func testDirectIndirect8() { @@ -205,6 +232,23 @@ class WDC65816AddressingTests: XCTestCase { // * $00FF00 contains $FF // * $00FFFF contains $FF // then LDA ($FF) loads the low byte of the data from address $12FFFF." + let machine = machine8() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xff, forAddress: 0xff00) + machine.setValue(0xff, forAddress: 0xffff) + + machine.setValue(0xab, forAddress: 0x12ffff) + + // LDA ($ff); NOP + machine.setData(Data([0xb2, 0xff, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 6) + + XCTAssertEqual(machine.value(for: .A), 0xab) } func testDirectIndirect16() { @@ -216,8 +260,28 @@ class WDC65816AddressingTests: XCTestCase { // * $00FFFF contains $FF // then LDA ($FF) loads the low byte of the data from address $12FFFF, and // the high byte from $130000." + let machine = machine16() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xff, forAddress: 0x0000) + machine.setValue(0xff, forAddress: 0xffff) + + machine.setValue(0xab, forAddress: 0x12ffff) + machine.setValue(0xcd, forAddress: 0x130000) + + // LDA ($ff); NOP + machine.setData(Data([0xb2, 0xff, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } + // TODO: all tests from this point downward. + func testDirectIndirectLong() { // "If the D register is $FF00 and the m flag is 0, then for LDA [$FE], the // address of the low byte of the pointer is $00FFFE, the address of the middle diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index eca58721a..35d5addf9 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -278,17 +278,20 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirect: - // TODO: is this address correct in emulation mode when the low byte is zero? data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; if(!(direct_&0xff)) { + // If the low byte is 0 and this is emulation mode, incrementing + // is restricted to the low byte. + data_address_increment_mask_ = e_masks_[1]; ++next_op_; } continue; case OperationConstructDirectIndirect: - data_address_ = data_bank_ + direct_ + data_buffer_.value; + data_address_ = data_bank_ + data_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); continue; case OperationConstructDirectIndexedIndirect: From 27afb8f0a7b1babbd41105d0d77244963ea0568b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Oct 2020 21:26:20 -0400 Subject: [PATCH 127/150] Adds direct indirect long test, and thereby fixes addressing mode. Nine to go! --- .../65816AddressingTests.swift | 22 +++++++++++++++++-- .../Implementation/65816Implementation.hpp | 11 +++++++++- .../65816/Implementation/65816Storage.cpp | 3 ++- .../65816/Implementation/65816Storage.hpp | 4 ++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index 829d56527..71db0770b 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -280,8 +280,6 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .A), 0xcdab) } - // TODO: all tests from this point downward. - func testDirectIndirectLong() { // "If the D register is $FF00 and the m flag is 0, then for LDA [$FE], the // address of the low byte of the pointer is $00FFFE, the address of the middle @@ -291,8 +289,28 @@ class WDC65816AddressingTests: XCTestCase { // * $00FFFF contains $FF // then LDA [$FE] loads the low byte of the data from address $12FFFF, and the // high byte from $130000." + let machine = machine16() + + machine.setValue(0xff00, for: .direct) + + machine.setValue(0x12, forAddress: 0x0000) + machine.setValue(0xff, forAddress: 0xfffe) + machine.setValue(0xff, forAddress: 0xffff) + + machine.setValue(0xab, forAddress: 0x12ffff) + machine.setValue(0xcd, forAddress: 0x130000) + + // LDA [$fe]; NOP + machine.setData(Data([0xa7, 0xfe, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 8) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } + // TODO: all tests from this point downward. + func testIndirectDirextX8() { // "If the D register is $FF00, the X register is $000A, and the e flag is 1 (note // that this means the m flag must be 1), then for LDA ($FE,X), the address of the diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 35d5addf9..9366747c7 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -288,6 +288,14 @@ template void Processor::run_for(const Cycles } continue; + case OperationConstructDirectLong: + data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + if(!(direct_&0xff)) { + ++next_op_; + } + continue; + case OperationConstructDirectIndirect: data_address_ = data_bank_ + data_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; @@ -312,8 +320,9 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirectIndirectLong: - data_address_ = instruction_buffer_.value; + data_address_ = data_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); continue; // TODO: confirm incorrect_data_address_ below. diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 6790d6430..c6a2d80ac 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -469,7 +469,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { static void direct_indirect_long(AccessType type, bool is8bit, const std::function &target) { target(CycleFetchIncrementPC); // DO. - target(OperationConstructDirect); + target(OperationConstructDirectLong); target(CycleFetchPCThrowaway); // IO. target(CycleFetchIncrementData); // AAL. @@ -735,6 +735,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { ProcessorStorage::ProcessorStorage() { set_reset_state(); + micro_ops_.reserve(1024); ProcessorStorageConstructor constructor(*this); using AccessMode = ProcessorStorageConstructor::AccessMode; diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index d89fc4b39..6758d07f8 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -87,6 +87,10 @@ enum MicroOp: uint8_t { /// Skips the next micro-op if the low byte of the direct register is 0. OperationConstructDirect, + /// Exactly like OperationConstructDirect, but doesn't retain any single-byte wrapping + /// behaviour in emulation mode. + OperationConstructDirectLong, + /// Constructs the current direct indexed indirect address using the data bank, /// direct and x registers plus the value currently in the instruction buffer. /// Skips the next micro-op if the low byte of the direct register is 0. From c35969d677d0498eb697e3beb6082aba40c85dd3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Oct 2020 21:38:00 -0400 Subject: [PATCH 128/150] Adds tests for (d, x) and (d), y. Both amply tested in emulation mode, so no problems. Five to go, all potentially troublesome. --- .../65816AddressingTests.swift | 86 +++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index 71db0770b..bbb66db99 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -309,9 +309,7 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .A), 0xcdab) } - // TODO: all tests from this point downward. - - func testIndirectDirextX8() { + func testDirectXIndirectX8() { // "If the D register is $FF00, the X register is $000A, and the e flag is 1 (note // that this means the m flag must be 1), then for LDA ($FE,X), the address of the // low byte of the pointer is $00FF08 and the address of the high byte is $00FF09. @@ -319,9 +317,27 @@ class WDC65816AddressingTests: XCTestCase { // * $00FF08 contains $FF // * $00FF09 contains $FF // then LDA ($FE,X) loads the low byte of the data from address $12FFFF." + let machine = machine8() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x000a, for: .X) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xff, forAddress: 0xff08) + machine.setValue(0xff, forAddress: 0xff09) + + machine.setValue(0xab, forAddress: 0x12ffff) + + // LDA ($fe, x); NOP + machine.setData(Data([0xa1, 0xfe, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .A), 0xab) } - func testIndirectDirextX16() { + func testDirectXIndirect16() { // "If the D register is $FF00, the X register is $000A, and the m flag is 0 // (note that this means the e flag must be 0), then for LDA ($FE,X), the address // of the low byte of the pointer is $000008 and the address of the high byte @@ -330,9 +346,28 @@ class WDC65816AddressingTests: XCTestCase { // * $000009 contains $FF // then LDA ($FE,X) loads the low byte of the data from address $12FFFF, and the // high byte from $130000." + let machine = machine16() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x000a, for: .X) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xff, forAddress: 0x0008) + machine.setValue(0xff, forAddress: 0x0009) + + machine.setValue(0xab, forAddress: 0x12ffff) + machine.setValue(0xcd, forAddress: 0x130000) + + // LDA ($fe, x); NOP + machine.setData(Data([0xa1, 0xfe, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 8) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } - func testIndirectDirectY8() { + func testIndirectY8() { // "If the D register is $FF00 and the e flag is 1 (note that this means the // m flag must be 1), then for LDA ($FF),Y, the address of the low byte of the // pointer is $00FFFF and the address of the high byte is $00FF00. @@ -343,9 +378,27 @@ class WDC65816AddressingTests: XCTestCase { // // "this is one of the rare instances where emulation mode has // different behavior than the 65C02 or NMOS 6502..." + let machine = machine8() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x000a, for: .Y) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xff, forAddress: 0xff00) + machine.setValue(0xfe, forAddress: 0xffff) + + machine.setValue(0xab, forAddress: 0x130008) + + // LDA ($ff), y; NOP + machine.setData(Data([0xb1, 0xff, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .A), 0xab) } - func testIndirectDirectY16() { + func testIndirectY16() { // "If the D register is $FF00 and the m flag is 0 (note that this means the // e flag must be 0), then for LDA ($FF),Y, the address of the low byte of the // pointer is $00FFFF and the address of the high byte is $000000. @@ -354,8 +407,29 @@ class WDC65816AddressingTests: XCTestCase { // * $00FFFF contains $FE // then LDA ($FF),Y loads the low byte of the data from address $130008, and the // high byte from $130009." + let machine = machine16() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x000a, for: .Y) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xff, forAddress: 0x0000) + machine.setValue(0xfe, forAddress: 0xffff) + + machine.setValue(0xab, forAddress: 0x130008) + machine.setValue(0xcd, forAddress: 0x130009) + + // LDA ($ff), y; NOP + machine.setData(Data([0xb1, 0xff, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 8) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } + // TODO: all tests from this point downward. + func testIndirectDirectYLong() { // "If the D register is $FF00 and the m flag is 0, then for LDA [$FE],Y, the address // of the low byte of the pointer is $00FFFE, the address of the middle byte is $00FFFF, From e511d33a7c281548c550388d7216a90c15f68636 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Oct 2020 21:42:41 -0400 Subject: [PATCH 129/150] Adds test for [d], y; fixes implementation. --- .../65816AddressingTests.swift | 26 ++++++++++++++++--- .../Implementation/65816Implementation.hpp | 3 ++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index bbb66db99..caea8529b 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -428,9 +428,7 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .A), 0xcdab) } - // TODO: all tests from this point downward. - - func testIndirectDirectYLong() { + func testIndirectYLong() { // "If the D register is $FF00 and the m flag is 0, then for LDA [$FE],Y, the address // of the low byte of the pointer is $00FFFE, the address of the middle byte is $00FFFF, // and the address of the high byte is $000000. Furthermore, if the Y register is $000A, and @@ -439,8 +437,30 @@ class WDC65816AddressingTests: XCTestCase { // * $00FFFF contains $FF // then LDA [$FE],Y loads the low byte of the data from address $130006, and the high byte // from $130007." + let machine = machine16() + + machine.setValue(0xff00, for: .direct) + machine.setValue(0x000a, for: .Y) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0x12, forAddress: 0x0000) + machine.setValue(0xfc, forAddress: 0xfffe) + machine.setValue(0xff, forAddress: 0xffff) + + machine.setValue(0xab, forAddress: 0x130006) + machine.setValue(0xcd, forAddress: 0x130007) + + // LDA [$fe], y; NOP + machine.setData(Data([0xb7, 0xfe, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 8) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } + // TODO: all tests from this point downward. + func testLong() { // "If the m flag is 0, then LDA $12FFFF loads the low byte of the data from address $12FFFF, // and the high byte from address $130000." diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 9366747c7..2cbb792bb 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -315,8 +315,9 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirectIndirectIndexedLong: - data_address_ = y() + instruction_buffer_.value; + data_address_ = y() + data_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); continue; case OperationConstructDirectIndirectLong: From 3c6adc1ff45aeddb99e49892deda39c03e7b422e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Oct 2020 22:00:52 -0400 Subject: [PATCH 130/150] Completes 65816 addressing mode tests and corresponding fixes. --- .../65816AddressingTests.swift | 65 +++++++++++++++++-- .../Implementation/65816Implementation.hpp | 3 +- .../65816/Implementation/65816Storage.hpp | 1 + 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index caea8529b..7fe5feec9 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -459,21 +459,59 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .A), 0xcdab) } - // TODO: all tests from this point downward. - - func testLong() { + func testAbsoluteLong() { // "If the m flag is 0, then LDA $12FFFF loads the low byte of the data from address $12FFFF, // and the high byte from address $130000." + let machine = machine16() + + machine.setValue(0xab, forAddress: 0x12ffff) + machine.setValue(0xcd, forAddress: 0x130000) + + // LDA $12ffff; NOP + machine.setData(Data([0xaf, 0xff, 0xff, 0x12, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } - func testLongX() { + func testAbsoluteLongX() { // "If the X register is $000A and the m flag is 0, then LDA $12FFFE,X loads the low byte of // the data from address $130008, and the high byte from address $130009." + let machine = machine16() + + machine.setValue(0x000a, for: .X) + + machine.setValue(0xab, forAddress: 0x130008) + machine.setValue(0xcd, forAddress: 0x130009) + + // LDA $12fffe, x; NOP + machine.setData(Data([0xbf, 0xfe, 0xff, 0x12, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } func testStackS() { // "If the S register is $FF10 and the m flag is 0, then LDA $FA,S loads the low byte // of the data from address $00000A, and the high byte from address $00000B." + let machine = machine16() + + machine.setValue(0xff10, for: .stackPointer) + + machine.setValue(0xab, forAddress: 0x000a) + machine.setValue(0xcd, forAddress: 0x000b) + + // LDA $fa, s; NOP + machine.setData(Data([0xa3, 0xfa, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 7) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } func testIndirectStackSY() { @@ -484,5 +522,24 @@ class WDC65816AddressingTests: XCTestCase { // * $00000B contains $FF // then LDA ($FA,S),Y loads the low byte of the data from address $130040, and the high // byte from $130041." + let machine = machine16() + + machine.setValue(0xff10, for: .stackPointer) + machine.setValue(0x0050, for: .Y) + machine.setValue(0x12, for: .dataBank) + + machine.setValue(0xf0, forAddress: 0x000a) + machine.setValue(0xff, forAddress: 0x000b) + + machine.setValue(0xab, forAddress: 0x130040) + machine.setValue(0xcd, forAddress: 0x130041) + + // LDA ($fa, s), y; NOP + machine.setData(Data([0xb3, 0xfa, 0xea]), atAddress: 0x0200) + + machine.setValue(0x200, for: .programCounter) + machine.runForNumber(ofCycles: 9) + + XCTAssertEqual(machine.value(for: .A), 0xcdab) } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 2cbb792bb..5c99ffe34 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -360,8 +360,9 @@ template void Processor::run_for(const Cycles continue; case OperationConstructStackRelativeIndexedIndirect: - data_address_ = (instruction_buffer_.value + y()) & 0xffff; + data_address_ = data_bank_ + data_buffer_.value + y(); data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); continue; case OperationConstructPER: diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 6758d07f8..7698345e0 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -325,6 +325,7 @@ struct ProcessorStorage { private: uint8_t *byte(int pointer) { + assert(pointer >= 0 && pointer < 4); #if TARGET_RT_BIG_ENDIAN return reinterpret_cast(&value) + (3 ^ pointer); #else From c0a1c3401236378edfd77c2cfb47451b798c6980 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 18:42:38 -0400 Subject: [PATCH 131/150] Wraps all registers into a struct, so that I can implement abort. Makes some preparations for ready too. --- Processors/6502Esque/6502Selector.hpp | 4 +- Processors/65816/65816.hpp | 19 +- Processors/65816/Implementation/65816Base.cpp | 38 +- .../Implementation/65816Implementation.hpp | 441 +++++++++--------- .../65816/Implementation/65816Storage.cpp | 56 +-- .../65816/Implementation/65816Storage.hpp | 57 ++- 6 files changed, 321 insertions(+), 294 deletions(-) diff --git a/Processors/6502Esque/6502Selector.hpp b/Processors/6502Esque/6502Selector.hpp index 411bba40f..d91932c17 100644 --- a/Processors/6502Esque/6502Selector.hpp +++ b/Processors/6502Esque/6502Selector.hpp @@ -36,8 +36,8 @@ template class }; template class Processor: - public CPU::WDC65816::Processor { - using CPU::WDC65816::Processor::Processor; + public CPU::WDC65816::Processor { + using CPU::WDC65816::Processor::Processor; }; } diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index b9e5cfbc1..f98d9deb5 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -34,17 +34,34 @@ class ProcessorBase: protected ProcessorStorage { inline void set_irq_line(bool); inline void set_nmi_line(bool); inline void set_reset_line(bool); + inline void set_abort_line(bool); void set_value_of_register(Register r, uint16_t value); inline bool is_jammed() const; uint16_t get_value_of_register(Register r) const; }; -template class Processor: public ProcessorBase { +template class Processor: public ProcessorBase { public: + /*! + Constructs an instance of the 6502 that will use @c bus_handler for all bus communications. + */ Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {} + + /*! + Runs the 6502 for a supplied number of cycles. + + @param cycles The number of cycles to run the 6502 for. + */ void run_for(const Cycles cycles); + /*! + Sets the current level of the RDY line. + + @param active @c true if the line is logically active; @c false otherwise. + */ + void set_ready_line(bool active); + private: BusHandler &bus_handler_; }; diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index d68f21007..fed760fa7 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -12,33 +12,33 @@ using namespace CPU::WDC65816; uint16_t ProcessorBase::get_value_of_register(Register r) const { switch (r) { - case Register::ProgramCounter: return pc_; + case Register::ProgramCounter: return registers_.pc; case Register::LastOperationAddress: return last_operation_pc_; - case Register::StackPointer: return s_.full; + case Register::StackPointer: return registers_.s.full; case Register::Flags: return get_flags(); - case Register::A: return a_.full; - case Register::X: return x_.full; - case Register::Y: return y_.full; - case Register::EmulationFlag: return emulation_flag_; - case Register::DataBank: return data_bank_ >> 16; - case Register::ProgramBank: return program_bank_ >> 16; - case Register::Direct: return direct_; + case Register::A: return registers_.a.full; + case Register::X: return registers_.x.full; + case Register::Y: return registers_.y.full; + case Register::EmulationFlag: return registers_.emulation_flag; + case Register::DataBank: return registers_.data_bank >> 16; + case Register::ProgramBank: return registers_.program_bank >> 16; + case Register::Direct: return registers_.direct; default: return 0; } } void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { - case Register::ProgramCounter: pc_ = value; break; - case Register::StackPointer: s_.full = value; break; - case Register::Flags: set_flags(uint8_t(value)); break; - case Register::A: a_.full = value; break; - case Register::X: x_.full = value; break; - case Register::Y: y_.full = value; break; - case Register::EmulationFlag: set_emulation_mode(value); break; - case Register::DataBank: data_bank_ = uint32_t(value) << 16; break; - case Register::ProgramBank: program_bank_ = uint32_t(value) << 16; break; - case Register::Direct: direct_ = value; break; + case Register::ProgramCounter: registers_.pc = value; break; + case Register::StackPointer: registers_.s.full = value; break; + case Register::Flags: set_flags(uint8_t(value)); break; + case Register::A: registers_.a.full = value; break; + case Register::X: registers_.x.full = value; break; + case Register::Y: registers_.y.full = value; break; + case Register::EmulationFlag: set_emulation_mode(value); break; + case Register::DataBank: registers_.data_bank = uint32_t(value) << 16; break; + case Register::ProgramBank: registers_.program_bank = uint32_t(value) << 16; break; + case Register::Direct: registers_.direct = value; break; default: break; } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 5c99ffe34..1080bbe2b 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -6,27 +6,22 @@ // Copyright © 2020 Thomas Harte. All rights reserved. // -template void Processor::run_for(const Cycles cycles) { - // Temporary storage for the next bus cycle. - uint32_t bus_address = 0; - uint8_t *bus_value = nullptr; - uint8_t throwaway = 0; - BusOperation bus_operation = BusOperation::None; +template void Processor::run_for(const Cycles cycles) { #define perform_bus(address, value, operation) \ - bus_address = address; \ - bus_value = value; \ - bus_operation = operation + bus_address_ = address; \ + bus_value_ = value; \ + bus_operation_ = operation #define read(address, value) perform_bus(address, value, MOS6502Esque::Read) #define write(address, value) perform_bus(address, value, MOS6502Esque::Write) -#define m_flag() mx_flags_[0] -#define x_flag() mx_flags_[1] +#define m_flag() registers_.mx_flags[0] +#define x_flag() registers_.mx_flags[1] -#define x() (x_.full & x_masks_[1]) -#define y() (y_.full & x_masks_[1]) -#define stack_address() ((s_.full & e_masks_[1]) | (0x0100 & e_masks_[0])) +#define x() (registers_.x.full & registers_.x_masks[1]) +#define y() (registers_.y.full & registers_.x_masks[1]) +#define stack_address() ((registers_.s.full & registers_.e_masks[1]) | (0x0100 & registers_.e_masks[0])) Cycles number_of_cycles = cycles + cycles_left_to_run_; while(number_of_cycles > Cycles(0)) { @@ -35,7 +30,7 @@ template void Processor::run_for(const Cycles #ifndef NDEBUG // As a sanity check. - bus_value = nullptr; + bus_value_ = nullptr; #endif switch(operation) { @@ -52,13 +47,13 @@ template void Processor::run_for(const Cycles next_op_ = µ_ops_[offset]; instruction_buffer_.clear(); data_buffer_.clear(); - last_operation_pc_ = pc_; + last_operation_pc_ = registers_.pc; } continue; case OperationDecode: { active_instruction_ = &instructions[instruction_buffer_.value]; - const auto size_flag = mx_flags_[active_instruction_->size_field]; + const auto size_flag = registers_.mx_flags[active_instruction_->size_field]; next_op_ = µ_ops_[active_instruction_->program_offsets[size_flag]]; instruction_buffer_.clear(); } continue; @@ -68,21 +63,21 @@ template void Processor::run_for(const Cycles // case CycleFetchIncrementPC: - read(pc_ | program_bank_, instruction_buffer_.next_input()); - ++pc_; + read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); + ++registers_.pc; break; case CycleFetchOpcode: - perform_bus(pc_ | program_bank_, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode); - ++pc_; + perform_bus(registers_.pc | registers_.program_bank, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode); + ++registers_.pc; break; case CycleFetchPC: - read(pc_ | program_bank_, instruction_buffer_.next_input()); + read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); break; case CycleFetchPCThrowaway: - read(pc_ | program_bank_, &throwaway); + read(registers_.pc | registers_.program_bank, &bus_throwaway_); break; // @@ -98,11 +93,11 @@ template void Processor::run_for(const Cycles break; case CycleFetchDataThrowaway: - read(data_address_, &throwaway); + read(data_address_, &bus_throwaway_); break; case CycleFetchIncorrectDataAddress: - read(incorrect_data_address_, &throwaway); + read(incorrect_data_address_, &bus_throwaway_); break; case CycleFetchIncrementData: @@ -133,7 +128,7 @@ template void Processor::run_for(const Cycles break; case CycleFetchBlockY: - read(((instruction_buffer_.value & 0xff00) << 8) | y(), &throwaway); + read(((instruction_buffer_.value & 0xff00) << 8) | y(), &bus_throwaway_); break; case CycleStoreBlockY: @@ -148,28 +143,28 @@ template void Processor::run_for(const Cycles // #define stack_access(value, operation) \ - bus_address = stack_address(); \ - bus_value = value; \ - bus_operation = operation; + bus_address_ = stack_address(); \ + bus_value_ = value; \ + bus_operation_ = operation; case CyclePush: stack_access(data_buffer_.next_output_descending(), MOS6502Esque::Write); - --s_.full; + --registers_.s.full; break; case CyclePullIfNotEmulation: - if(emulation_flag_) { + if(registers_.emulation_flag) { continue; } [[fallthrough]]; case CyclePull: - ++s_.full; + ++registers_.s.full; stack_access(data_buffer_.next_input(), MOS6502Esque::Read); break; case CycleAccessStack: - stack_access(&throwaway, MOS6502Esque::Read); + stack_access(&bus_throwaway_, MOS6502Esque::Read); break; #undef stack_access @@ -193,7 +188,7 @@ template void Processor::run_for(const Cycles case OperationCopyPCToData: data_buffer_.size = 2; - data_buffer_.value = pc_; + data_buffer_.value = registers_.pc; continue; case OperationCopyInstructionToData: @@ -206,21 +201,21 @@ template void Processor::run_for(const Cycles continue; case OperationCopyAToData: - data_buffer_.value = a_.full & m_masks_[1]; + data_buffer_.value = registers_.a.full & registers_.m_masks[1]; data_buffer_.size = 2 - m_flag(); continue; case OperationCopyDataToA: - a_.full = (a_.full & m_masks_[0]) + (data_buffer_.value & m_masks_[1]); + registers_.a.full = (registers_.a.full & registers_.m_masks[0]) + (data_buffer_.value & registers_.m_masks[1]); continue; case OperationCopyPBRToData: data_buffer_.size = 1; - data_buffer_.value = program_bank_ >> 16; + data_buffer_.value = registers_.program_bank >> 16; continue; case OperationCopyDataToPC: - pc_ = uint16_t(data_buffer_.value); + registers_.pc = uint16_t(data_buffer_.value); continue; // @@ -228,7 +223,7 @@ template void Processor::run_for(const Cycles // case OperationConstructAbsolute: - data_address_ = instruction_buffer_.value + data_bank_; + data_address_ = instruction_buffer_.value + registers_.data_bank; data_address_increment_mask_ = 0xff'ff'ff; continue; @@ -244,7 +239,7 @@ template void Processor::run_for(const Cycles // Used for JMP and JSR (absolute, x). case OperationConstructAbsoluteIndexedIndirect: - data_address_ = program_bank_ + ((instruction_buffer_.value + x()) & 0xffff); + data_address_ = registers_.program_bank + ((instruction_buffer_.value + x()) & 0xffff); data_address_increment_mask_ = 0x00'ff'ff; continue; @@ -255,8 +250,8 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteXRead: case OperationConstructAbsoluteX: - data_address_ = instruction_buffer_.value + x() + data_bank_; - incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) + data_bank_; + data_address_ = instruction_buffer_.value + x() + 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 == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { @@ -267,8 +262,8 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteYRead: case OperationConstructAbsoluteY: - data_address_ = instruction_buffer_.value + y() + data_bank_; - incorrect_data_address_ = (data_address_ & 0xff) + (instruction_buffer_.value & 0xff00) + data_bank_; + data_address_ = instruction_buffer_.value + y() + 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_) { @@ -278,38 +273,38 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirect: - data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; + data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - if(!(direct_&0xff)) { + if(!(registers_.direct&0xff)) { // If the low byte is 0 and this is emulation mode, incrementing // is restricted to the low byte. - data_address_increment_mask_ = e_masks_[1]; + data_address_increment_mask_ = registers_.e_masks[1]; ++next_op_; } continue; case OperationConstructDirectLong: - data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; + data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - if(!(direct_&0xff)) { + if(!(registers_.direct&0xff)) { ++next_op_; } continue; case OperationConstructDirectIndirect: - data_address_ = data_bank_ + data_buffer_.value; + data_address_ = registers_.data_bank + data_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; data_buffer_.clear(); continue; case OperationConstructDirectIndexedIndirect: - data_address_ = data_bank_ + ( - ((direct_ + x() + instruction_buffer_.value) & e_masks_[1]) + - (direct_ & e_masks_[0]) + data_address_ = registers_.data_bank + ( + ((registers_.direct + x() + instruction_buffer_.value) & registers_.e_masks[1]) + + (registers_.direct & registers_.e_masks[0]) ) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - if(!(direct_&0xff)) { + if(!(registers_.direct&0xff)) { ++next_op_; } continue; @@ -330,43 +325,43 @@ template void Processor::run_for(const Cycles case OperationConstructDirectX: data_address_ = ( - (direct_ & e_masks_[0]) + - ((instruction_buffer_.value + direct_ + x()) & e_masks_[1]) + (registers_.direct & registers_.e_masks[0]) + + ((instruction_buffer_.value + registers_.direct + x()) & registers_.e_masks[1]) ) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); - if(!(direct_&0xff)) { + incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); + if(!(registers_.direct&0xff)) { ++next_op_; } continue; case OperationConstructDirectY: data_address_ = ( - (direct_ & e_masks_[0]) + - ((instruction_buffer_.value + direct_ + y()) & e_masks_[1]) + (registers_.direct & registers_.e_masks[0]) + + ((instruction_buffer_.value + registers_.direct + y()) & registers_.e_masks[1]) ) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); - if(!(direct_&0xff)) { + incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); + if(!(registers_.direct&0xff)) { ++next_op_; } continue; case OperationConstructStackRelative: - data_address_ = (s_.full + instruction_buffer_.value) & 0xffff; + data_address_ = (registers_.s.full + instruction_buffer_.value) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; continue; case OperationConstructStackRelativeIndexedIndirect: - data_address_ = data_bank_ + data_buffer_.value + y(); + data_address_ = registers_.data_bank + data_buffer_.value + y(); data_address_increment_mask_ = 0xff'ff'ff; data_buffer_.clear(); continue; case OperationConstructPER: - data_buffer_.value = instruction_buffer_.value + pc_; + data_buffer_.value = instruction_buffer_.value + registers_.pc; data_buffer_.size = 2; continue; @@ -389,31 +384,31 @@ template void Processor::run_for(const Cycles } else if(pending_exceptions_ & NMI) { pending_exceptions_ &= ~NMI; data_address_ = 0xfffa; - } else if(pending_exceptions_ & IRQ & flags_.inverse_interrupt) { + } else if(pending_exceptions_ & IRQ & registers_.flags.inverse_interrupt) { pending_exceptions_ &= ~IRQ; data_address_ = 0xfffe; } else { is_brk = active_instruction_ == instructions; // Given that BRK has opcode 00. if(is_brk) { - data_address_ = emulation_flag_ ? 0xfffe : 0xfff6; + data_address_ = registers_.emulation_flag ? 0xfffe : 0xfff6; } else { // Implicitly: COP. data_address_ = 0xfff4; } } - data_buffer_.value = (pc_ << 8) | get_flags(); - if(emulation_flag_) { + data_buffer_.value = (registers_.pc << 8) | get_flags(); + if(registers_.emulation_flag) { if(is_brk) data_buffer_.value |= Flag::Break; data_buffer_.size = 3; ++next_op_; } else { - data_buffer_.value |= program_bank_ << 24; + data_buffer_.value |= registers_.program_bank << 24; data_buffer_.size = 4; - program_bank_ = 0; + registers_.program_bank = 0; } - flags_.inverse_interrupt = 0; + registers_.flags.inverse_interrupt = 0; } continue; // @@ -421,10 +416,10 @@ template void Processor::run_for(const Cycles // #define LD(dest, src, masks) dest.full = (dest.full & masks[0]) | (src & masks[1]) -#define m_top() (instruction_buffer_.value >> m_shift_) & 0xff -#define x_top() (x_.full >> x_shift_) & 0xff -#define y_top() (y_.full >> x_shift_) & 0xff -#define a_top() (a_.full >> m_shift_) & 0xff +#define m_top() (instruction_buffer_.value >> registers_.m_shift) & 0xff +#define x_top() (registers_.x.full >> registers_.x_shift) & 0xff +#define y_top() (registers_.y.full >> registers_.x_shift) & 0xff +#define a_top() (registers_.a.full >> registers_.m_shift) & 0xff case OperationPerform: switch(active_instruction_->operation) { @@ -434,28 +429,28 @@ template void Processor::run_for(const Cycles // case LDA: - LD(a_, data_buffer_.value, m_masks_); - flags_.set_nz(a_.full, m_shift_); + LD(registers_.a, data_buffer_.value, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case LDX: - LD(x_, data_buffer_.value, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, data_buffer_.value, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case LDY: - LD(y_, data_buffer_.value, x_masks_); - flags_.set_nz(y_.full, x_shift_); + LD(registers_.y, data_buffer_.value, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); break; case PLB: - data_bank_ = (data_buffer_.value & 0xff) << 16; - flags_.set_nz(instruction_buffer_.value); + registers_.data_bank = (data_buffer_.value & 0xff) << 16; + registers_.flags.set_nz(instruction_buffer_.value); break; case PLD: - direct_ = data_buffer_.value; - flags_.set_nz(instruction_buffer_.value); + registers_.direct = data_buffer_.value; + registers_.flags.set_nz(instruction_buffer_.value); break; case PLP: @@ -463,7 +458,7 @@ template void Processor::run_for(const Cycles break; case STA: - data_buffer_.value = a_.full & m_masks_[1]; + data_buffer_.value = registers_.a.full & registers_.m_masks[1]; data_buffer_.size = 2 - m_flag(); break; @@ -473,27 +468,27 @@ template void Processor::run_for(const Cycles break; case STX: - data_buffer_.value = x_.full & x_masks_[1]; + data_buffer_.value = registers_.x.full & registers_.x_masks[1]; data_buffer_.size = 2 - x_flag(); break; case STY: - data_buffer_.value = y_.full & x_masks_[1]; + data_buffer_.value = registers_.y.full & registers_.x_masks[1]; data_buffer_.size = 2 - m_flag(); break; case PHB: - data_buffer_.value = data_bank_ >> 16; + data_buffer_.value = registers_.data_bank >> 16; data_buffer_.size = 1; break; case PHK: - data_buffer_.value = program_bank_ >> 16; + data_buffer_.value = registers_.program_bank >> 16; data_buffer_.size = 1; break; case PHD: - data_buffer_.value = direct_; + data_buffer_.value = registers_.direct; data_buffer_.size = 2; break; @@ -501,7 +496,7 @@ template void Processor::run_for(const Cycles data_buffer_.value = get_flags(); data_buffer_.size = 1; - if(emulation_flag_) { + if(registers_.emulation_flag) { // On the 6502, the break flag is set during a PHP. data_buffer_.value |= Flag::Break; } @@ -514,69 +509,69 @@ template void Processor::run_for(const Cycles // (and make reasonable guesses as to the N flag). case TXS: - s_ = x_.full & x_masks_[1]; + registers_.s = registers_.x.full & registers_.x_masks[1]; break; case TSX: - LD(x_, s_.full, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, registers_.s.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case TXY: - LD(y_, x_.full, x_masks_); - flags_.set_nz(y_.full, x_shift_); + LD(registers_.y, registers_.x.full, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); break; case TYX: - LD(x_, y_.full, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, registers_.y.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case TAX: - LD(x_, a_.full, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, registers_.a.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case TAY: - LD(y_, a_.full, x_masks_); - flags_.set_nz(y_.full, x_shift_); + LD(registers_.y, registers_.a.full, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); break; case TXA: - LD(a_, x_.full, m_masks_); - flags_.set_nz(a_.full, m_shift_); + LD(registers_.a, registers_.x.full, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case TYA: - LD(a_, y_.full, m_masks_); - flags_.set_nz(a_.full, m_shift_); + LD(registers_.a, registers_.y.full, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case TCD: - direct_ = a_.full; - flags_.set_nz(a_.full, 8); + registers_.direct = registers_.a.full; + registers_.flags.set_nz(registers_.a.full, 8); break; case TDC: - a_.full = direct_; - flags_.set_nz(a_.full, 8); + registers_.a.full = registers_.direct; + registers_.flags.set_nz(registers_.a.full, 8); break; case TCS: - s_.full = a_.full; + registers_.s.full = registers_.a.full; // No need to worry about byte masking here; for the stack it's handled as the emulation runs. break; case TSC: - a_.full = stack_address(); - flags_.set_nz(a_.full, 8); + registers_.a.full = stack_address(); + registers_.flags.set_nz(registers_.a.full, 8); break; case XBA: { - const uint8_t a_low = a_.halves.low; - a_.halves.low = a_.halves.high; - a_.halves.high = a_low; - flags_.set_nz(a_.halves.low); + const uint8_t a_low = registers_.a.halves.low; + registers_.a.halves.low = registers_.a.halves.high; + registers_.a.halves.high = a_low; + registers_.flags.set_nz(registers_.a.halves.low); } break; // @@ -584,38 +579,38 @@ template void Processor::run_for(const Cycles // case JML: - program_bank_ = instruction_buffer_.value & 0xff0000; + registers_.program_bank = instruction_buffer_.value & 0xff0000; [[fallthrough]]; case JMP: - pc_ = uint16_t(instruction_buffer_.value); + registers_.pc = uint16_t(instruction_buffer_.value); break; case JMPind: - pc_ = data_buffer_.value; + registers_.pc = data_buffer_.value; break; case RTS: - pc_ = data_buffer_.value + 1; + registers_.pc = data_buffer_.value + 1; break; case JSL: - program_bank_ = instruction_buffer_.value & 0xff0000; + registers_.program_bank = instruction_buffer_.value & 0xff0000; [[fallthrough]]; case JSR: - data_buffer_.value = pc_; + data_buffer_.value = registers_.pc; data_buffer_.size = 2; - pc_ = instruction_buffer_.value; + registers_.pc = instruction_buffer_.value; break; case RTI: - pc_ = uint16_t(data_buffer_.value >> 8); + registers_.pc = uint16_t(data_buffer_.value >> 8); set_flags(uint8_t(data_buffer_.value)); - if(!emulation_flag_) { - program_bank_ = (data_buffer_.value & 0xff000000) >> 8; + if(!registers_.emulation_flag) { + registers_.program_bank = (data_buffer_.value & 0xff000000) >> 8; } break; @@ -624,33 +619,33 @@ template void Processor::run_for(const Cycles // case MVP: - data_bank_ = (instruction_buffer_.value & 0xff) << 16; - --x_.full; - --y_.full; - --a_.full; - if(a_.full) pc_ -= 3; + registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; + --registers_.x.full; + --registers_.y.full; + --registers_.a.full; + if(registers_.a.full) registers_.pc -= 3; break; case MVN: - data_bank_ = (instruction_buffer_.value & 0xff) << 16; - ++x_.full; - ++y_.full; - --a_.full; - if(a_.full) pc_ -= 3; + registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; + ++registers_.x.full; + ++registers_.y.full; + --registers_.a.full; + if(registers_.a.full) registers_.pc -= 3; break; // // Flag manipulation. // - case CLC: flags_.carry = 0; break; - case CLI: flags_.inverse_interrupt = Flag::Interrupt; break; - case CLV: flags_.overflow = 0; break; - case CLD: flags_.decimal = 0; break; + case CLC: registers_.flags.carry = 0; break; + case CLI: registers_.flags.inverse_interrupt = Flag::Interrupt; break; + case CLV: registers_.flags.overflow = 0; break; + case CLD: registers_.flags.decimal = 0; break; - case SEC: flags_.carry = Flag::Carry; break; - case SEI: flags_.inverse_interrupt = 0; break; - case SED: flags_.decimal = Flag::Decimal; break; + case SEC: registers_.flags.carry = Flag::Carry; break; + case SEI: registers_.flags.inverse_interrupt = 0; break; + case SED: registers_.flags.decimal = Flag::Decimal; break; case REP: set_flags(get_flags() &~ instruction_buffer_.value); @@ -661,9 +656,9 @@ template void Processor::run_for(const Cycles break; case XCE: { - const bool old_emulation_flag = emulation_flag_; - set_emulation_mode(flags_.carry); - flags_.carry = old_emulation_flag; + const bool old_emulation_flag = registers_.emulation_flag; + set_emulation_mode(registers_.flags.carry); + registers_.flags.carry = old_emulation_flag; } break; // @@ -672,36 +667,36 @@ template void Processor::run_for(const Cycles case INC: ++data_buffer_.value; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break;; case DEC: --data_buffer_.value; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case INX: { - const uint16_t x_inc = x_.full + 1; - LD(x_, x_inc, x_masks_); - flags_.set_nz(x_.full, x_shift_); + const uint16_t x_inc = registers_.x.full + 1; + LD(registers_.x, x_inc, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); } break; case DEX: { - const uint16_t x_dec = x_.full - 1; - LD(x_, x_dec, x_masks_); - flags_.set_nz(x_.full, x_shift_); + const uint16_t x_dec = registers_.x.full - 1; + LD(registers_.x, x_dec, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); } break; case INY: { - const uint16_t y_inc = y_.full + 1; - LD(y_, y_inc, x_masks_); - flags_.set_nz(y_.full, x_shift_); + const uint16_t y_inc = registers_.y.full + 1; + LD(registers_.y, y_inc, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); } break; case DEY: { - const uint16_t y_dec = y_.full - 1; - LD(y_, y_dec, x_masks_); - flags_.set_nz(y_.full, x_shift_); + const uint16_t y_dec = registers_.y.full - 1; + LD(registers_.y, y_dec, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); } break; // @@ -709,38 +704,38 @@ template void Processor::run_for(const Cycles // case AND: - a_.full &= data_buffer_.value | m_masks_[0]; - flags_.set_nz(a_.full, m_shift_); + registers_.a.full &= data_buffer_.value | registers_.m_masks[0]; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case EOR: - a_.full ^= data_buffer_.value; - flags_.set_nz(a_.full, m_shift_); + registers_.a.full ^= data_buffer_.value; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case ORA: - a_.full |= data_buffer_.value; - flags_.set_nz(a_.full, m_shift_); + registers_.a.full |= data_buffer_.value; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case BIT: - flags_.set_n(data_buffer_.value, m_shift_); - flags_.set_z(data_buffer_.value & a_.full, m_shift_); - flags_.overflow = data_buffer_.value & Flag::Overflow; + registers_.flags.set_n(data_buffer_.value, registers_.m_shift); + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + registers_.flags.overflow = data_buffer_.value & Flag::Overflow; break; case BITimm: - flags_.set_z(data_buffer_.value & a_.full, m_shift_); + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); break; case TRB: - flags_.set_z(data_buffer_.value & a_.full, m_shift_); - data_buffer_.value &= ~a_.full; + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + data_buffer_.value &= ~registers_.a.full; break; case TSB: - flags_.set_z(data_buffer_.value & a_.full, m_shift_); - data_buffer_.value |= a_.full; + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + data_buffer_.value |= registers_.a.full; break; // @@ -752,27 +747,27 @@ template void Processor::run_for(const Cycles next_op_ += 3; \ } else { \ data_buffer_.size = 2; \ - data_buffer_.value = pc_ + int8_t(instruction_buffer_.value); \ + data_buffer_.value = registers_.pc + int8_t(instruction_buffer_.value); \ \ - if((pc_ & 0xff00) == (instruction_buffer_.value & 0xff00)) { \ + if((registers_.pc & 0xff00) == (instruction_buffer_.value & 0xff00)) { \ ++next_op_; \ } \ } - case BPL: BRA(!(flags_.negative_result&0x80)); break; - case BMI: BRA(flags_.negative_result&0x80); break; - case BVC: BRA(!flags_.overflow); break; - case BVS: BRA(flags_.overflow); break; - case BCC: BRA(!flags_.carry); break; - case BCS: BRA(flags_.carry); break; - case BNE: BRA(flags_.zero_result); break; - case BEQ: BRA(!flags_.zero_result); break; + case BPL: BRA(!(registers_.flags.negative_result&0x80)); break; + case BMI: BRA(registers_.flags.negative_result&0x80); break; + case BVC: BRA(!registers_.flags.overflow); break; + case BVS: BRA(registers_.flags.overflow); break; + case BCC: BRA(!registers_.flags.carry); break; + case BCS: BRA(registers_.flags.carry); break; + case BNE: BRA(registers_.flags.zero_result); break; + case BEQ: BRA(!registers_.flags.zero_result); break; case BRA: BRA(true); break; #undef BRA case BRL: - pc_ += int16_t(instruction_buffer_.value); + registers_.pc += int16_t(instruction_buffer_.value); break; // @@ -780,28 +775,28 @@ template void Processor::run_for(const Cycles // case ASL: - flags_.carry = data_buffer_.value >> (7 + m_shift_); + registers_.flags.carry = data_buffer_.value >> (7 + registers_.m_shift); data_buffer_.value <<= 1; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case LSR: - flags_.carry = data_buffer_.value & 1; + registers_.flags.carry = data_buffer_.value & 1; data_buffer_.value >>= 1; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case ROL: - data_buffer_.value = (data_buffer_.value << 1) | flags_.carry; - flags_.carry = data_buffer_.value >> (8 + m_shift_); - flags_.set_nz(data_buffer_.value, m_shift_); + data_buffer_.value = (data_buffer_.value << 1) | registers_.flags.carry; + registers_.flags.carry = data_buffer_.value >> (8 + registers_.m_shift); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case ROR: { const uint8_t next_carry = data_buffer_.value & 1; - data_buffer_.value = (data_buffer_.value >> 1) | (flags_.carry << (7 + m_shift_)); - flags_.carry = next_carry; - flags_.set_nz(data_buffer_.value, m_shift_); + data_buffer_.value = (data_buffer_.value >> 1) | (registers_.flags.carry << (7 + registers_.m_shift)); + registers_.flags.carry = next_carry; + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); } break; // @@ -810,23 +805,23 @@ template void Processor::run_for(const Cycles #define cp(v, shift, masks) {\ const uint32_t temp32 = (v.full & masks[1]) - (data_buffer_.value & masks[1]); \ - flags_.set_nz(uint16_t(temp32), shift); \ - flags_.carry = ((~temp32) >> (8 + shift))&1; \ + registers_.flags.set_nz(uint16_t(temp32), shift); \ + registers_.flags.carry = ((~temp32) >> (8 + shift))&1; \ } - case CMP: cp(a_, m_shift_, m_masks_); break; - case CPX: cp(x_, x_shift_, x_masks_); break; - case CPY: cp(y_, x_shift_, x_masks_); break; + case CMP: cp(registers_.a, registers_.m_shift, registers_.m_masks); break; + case CPX: cp(registers_.x, registers_.x_shift, registers_.x_masks); break; + case CPY: cp(registers_.y, registers_.x_shift, registers_.x_masks); break; #undef cp case SBC: - if(flags_.decimal) { + 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 = a_.full & m_masks_[1]; + const uint16_t a = registers_.a.full & registers_.m_masks[1]; unsigned int result = 0; - unsigned int borrow = flags_.carry ^ 1; + unsigned int borrow = registers_.flags.carry ^ 1; #define nibble(mask, adjustment, carry) \ result += (a & mask) - (data_buffer_.value & mask) - borrow; \ @@ -841,23 +836,23 @@ template void Processor::run_for(const Cycles #undef nibble - flags_.overflow = ~(( (result ^ a_.full) & (result ^ data_buffer_.value) ) >> (1 + m_shift_))&0x40; - flags_.set_nz(result, m_shift_); - flags_.carry = ((borrow >> 16)&1)^1; - LD(a_, result, m_masks_); + registers_.flags.overflow = ~(( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; + registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.carry = ((borrow >> 16)&1)^1; + LD(registers_.a, result, registers_.m_masks); break; } - data_buffer_.value = ~data_buffer_.value & m_masks_[1]; + data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; [[fallthrough]]; case ADC: { int result; - const uint16_t a = a_.full & m_masks_[1]; + const uint16_t a = registers_.a.full & registers_.m_masks[1]; - if(flags_.decimal) { - result = flags_.carry; + if(registers_.flags.decimal) { + result = registers_.flags.carry; #define nibble(mask, limit, adjustment, carry) \ result += (a & mask) + (data_buffer_.value & mask); \ @@ -871,13 +866,13 @@ template void Processor::run_for(const Cycles #undef nibble } else { - result = a + data_buffer_.value + flags_.carry; + result = a + data_buffer_.value + registers_.flags.carry; } - flags_.overflow = (( (result ^ a_.full) & (result ^ data_buffer_.value) ) >> (1 + m_shift_))&0x40; - flags_.set_nz(result, m_shift_); - flags_.carry = (result >> (8 + m_shift_))&1; - LD(a_, result, m_masks_); + registers_.flags.overflow = (( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; + registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.carry = (result >> (8 + registers_.m_shift))&1; + LD(registers_.a, result, registers_.m_masks); } break; // @@ -901,12 +896,12 @@ template void Processor::run_for(const Cycles #undef y_top #undef a_top - // TODO: the ready line. - // Store a selection as to the exceptions, if any, that would be honoured after this cycle if the // next thing is a MoveToNextProgram. - selected_exceptions_ = pending_exceptions_ & (flags_.inverse_interrupt | PowerOn | Reset | NMI); - number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); + selected_exceptions_ = pending_exceptions_ & (registers_.flags.inverse_interrupt | PowerOn | Reset | NMI); + number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation_, bus_address_, bus_value_); + + // TODO: RDY line. } #undef read diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index c6a2d80ac..593801914 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -1028,63 +1028,63 @@ ProcessorStorage::ProcessorStorage() { } void ProcessorStorage::set_reset_state() { - data_bank_ = 0; - program_bank_ = 0; - direct_ = 0; - flags_.decimal = 0; - flags_.inverse_interrupt = 0; + registers_.data_bank = 0; + registers_.program_bank = 0; + registers_.direct = 0; + registers_.flags.decimal = 0; + registers_.flags.inverse_interrupt = 0; set_emulation_mode(true); } void ProcessorStorage::set_emulation_mode(bool enabled) { - if(emulation_flag_ == enabled) { + if(registers_.emulation_flag == enabled) { return; } - emulation_flag_ = enabled; + registers_.emulation_flag = enabled; if(enabled) { set_m_x_flags(true, true); - x_.halves.high = y_.halves.high = 0; - e_masks_[0] = 0xff00; - e_masks_[1] = 0x00ff; + registers_.x.halves.high = registers_.y.halves.high = 0; + registers_.e_masks[0] = 0xff00; + registers_.e_masks[1] = 0x00ff; } else { - e_masks_[0] = 0x0000; - e_masks_[1] = 0xffff; - s_.halves.high = 1; // To pretend it was 1 all along; this implementation actually ignores - // the top byte while in emulation mode. + registers_.e_masks[0] = 0x0000; + registers_.e_masks[1] = 0xffff; + registers_.s.halves.high = 1; // To pretend it was 1 all along; this implementation actually ignores + // the top byte while in emulation mode. } } void ProcessorStorage::set_m_x_flags(bool m, bool x) { // true/1 => 8bit for both flags. - mx_flags_[0] = m; - mx_flags_[1] = x; + registers_.mx_flags[0] = m; + registers_.mx_flags[1] = x; - m_masks_[0] = m ? 0xff00 : 0x0000; - m_masks_[1] = m ? 0x00ff : 0xffff; - m_shift_ = m ? 0 : 8; + registers_.m_masks[0] = m ? 0xff00 : 0x0000; + registers_.m_masks[1] = m ? 0x00ff : 0xffff; + registers_.m_shift = m ? 0 : 8; - x_masks_[0] = x ? 0xff00 : 0x0000; - x_masks_[1] = x ? 0x00ff : 0xffff; - x_shift_ = x ? 0 : 8; + registers_.x_masks[0] = x ? 0xff00 : 0x0000; + registers_.x_masks[1] = x ? 0x00ff : 0xffff; + registers_.x_shift = x ? 0 : 8; } uint8_t ProcessorStorage::get_flags() const { - uint8_t result = flags_.get(); + uint8_t result = registers_.flags.get(); - if(!emulation_flag_) { + if(!registers_.emulation_flag) { result &= ~(Flag::MemorySize | Flag::IndexSize); - result |= mx_flags_[0] * Flag::MemorySize; - result |= mx_flags_[1] * Flag::IndexSize; + result |= registers_.mx_flags[0] * Flag::MemorySize; + result |= registers_.mx_flags[1] * Flag::IndexSize; } return result; } void ProcessorStorage::set_flags(uint8_t value) { - flags_.set(value); + registers_.flags.set(value); - if(!emulation_flag_) { + if(!registers_.emulation_flag) { set_m_x_flags(value & Flag::MemorySize, value & Flag::IndexSize); } } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 7698345e0..41f6709f3 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -241,49 +241,64 @@ struct ProcessorStorage { FetchDecodeExecute }; - // Registers. - RegisterPair16 a_; - RegisterPair16 x_, y_; - RegisterPair16 s_; - uint16_t pc_; - // A helper for testing. uint16_t last_operation_pc_; Instruction *active_instruction_; Cycles cycles_left_to_run_; - // Flags aplenty. - MOS6502Esque::LazyFlags flags_; - uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. - uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. - uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. - uint16_t e_masks_[2] = {0xff00, 0x00ff}; - int m_shift_ = 0; - int x_shift_ = 0; - bool emulation_flag_ = true; + // All registers are boxed up into a struct so that they can be stored and restored in support of abort. + struct Registers { + // Registers. + RegisterPair16 a; + RegisterPair16 x, y; + RegisterPair16 s; + uint16_t pc; - // I.e. the offset for direct addressing (outside of emulation mode). - uint16_t direct_ = 0; + // Flags aplenty. + MOS6502Esque::LazyFlags flags; + uint8_t mx_flags[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. + uint16_t m_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + uint16_t x_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + uint16_t e_masks[2] = {0xff00, 0x00ff}; + int m_shift = 0; + int x_shift = 0; + bool emulation_flag = true; - // Banking registers are all stored with the relevant byte - // shifted up bits 16–23. - uint32_t data_bank_ = 0; // i.e. DBR. - uint32_t program_bank_ = 0; // i.e. PBR. + // I.e. the offset for direct addressing (outside of emulation mode). + uint16_t direct = 0; + // Banking registers are all stored with the relevant byte + // shifted up bits 16–23. + uint32_t data_bank = 0; // i.e. DBR. + uint32_t program_bank = 0; // i.e. PBR. + } registers_, abort_registers_copy_; + + // The next bus transaction. + uint32_t bus_address_ = 0; + uint8_t *bus_value_ = nullptr; + static inline uint8_t bus_throwaway_ = 0; + BusOperation bus_operation_ = BusOperation::None; + + // A bitfield for various exceptions. static constexpr int PowerOn = 1 << 0; static constexpr int Reset = 1 << 1; static constexpr int IRQ = Flag::Interrupt; // This makes masking a lot easier later on; this is 1 << 2. static constexpr int NMI = 1 << 3; + static constexpr int Abort = 1 << 4; int pending_exceptions_ = PowerOn; // By default. int selected_exceptions_ = 0; + bool ready_line_ = false; + // Just to be safe. static_assert(PowerOn != IRQ); static_assert(Reset != IRQ); static_assert(NMI != IRQ); + static_assert(Abort != IRQ); /// Sets the required exception flags necessary to exit a STP or WAI. int required_exceptions_ = 0; + BusOperation stp_wai_bus_operation_ = BusOperation::None; /// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte /// to most significant. From dfda2adf0de71f1e96bd8c8a43328da11da86e74 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 20:46:18 -0400 Subject: [PATCH 132/150] Attempts implementations of both ready and abort. Which I think concludes the inputs? --- Processors/65816/65816.hpp | 1 + .../Implementation/65816Implementation.hpp | 1541 +++++++++-------- 2 files changed, 789 insertions(+), 753 deletions(-) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index f98d9deb5..c5f2daf53 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -35,6 +35,7 @@ class ProcessorBase: protected ProcessorStorage { inline void set_nmi_line(bool); inline void set_reset_line(bool); inline void set_abort_line(bool); + inline bool get_is_resetting() const; void set_value_of_register(Register r, uint16_t value); inline bool is_jammed() const; diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 1080bbe2b..23359bb25 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -25,395 +25,410 @@ template void Processor Cycles(0)) { - const MicroOp operation = *next_op_; - ++next_op_; + // Wait for ready to be inactive before proceeding. + while(uses_ready_line && ready_line_ && number_of_cycles > Cycles(0)) { + number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, &bus_throwaway_); + } + + // Process for as much time is left and/or until ready is signalled. + while((!uses_ready_line || !ready_line_) && number_of_cycles > Cycles(0)) { + const MicroOp operation = *next_op_; + ++next_op_; #ifndef NDEBUG - // As a sanity check. - bus_value_ = nullptr; + // As a sanity check. + bus_value_ = nullptr; #endif - switch(operation) { + switch(operation) { - // - // Scheduling. - // + // + // Scheduling. + // - case OperationMoveToNextProgram: { - // The exception program will determine the appropriate way to respond - // based on the pending exception if one exists; otherwise just do a - // standard fetch-decode-execute. - const auto offset = instructions[selected_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offsets[0]; - next_op_ = µ_ops_[offset]; - instruction_buffer_.clear(); - data_buffer_.clear(); - last_operation_pc_ = registers_.pc; - } continue; + case OperationMoveToNextProgram: { + // The exception program will determine the appropriate way to respond + // based on the pending exception if one exists; otherwise just do a + // standard fetch-decode-execute. + const auto offset = instructions[selected_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offsets[0]; + next_op_ = µ_ops_[offset]; + instruction_buffer_.clear(); + data_buffer_.clear(); + last_operation_pc_ = registers_.pc; + } continue; - case OperationDecode: { - active_instruction_ = &instructions[instruction_buffer_.value]; + case OperationDecode: { + active_instruction_ = &instructions[instruction_buffer_.value]; - const auto size_flag = registers_.mx_flags[active_instruction_->size_field]; - next_op_ = µ_ops_[active_instruction_->program_offsets[size_flag]]; - instruction_buffer_.clear(); - } continue; + const auto size_flag = registers_.mx_flags[active_instruction_->size_field]; + next_op_ = µ_ops_[active_instruction_->program_offsets[size_flag]]; + instruction_buffer_.clear(); + } continue; - // - // PC fetches. - // + // + // PC fetches. + // - case CycleFetchIncrementPC: - read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); - ++registers_.pc; - break; + case CycleFetchIncrementPC: + read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); + ++registers_.pc; + break; - case CycleFetchOpcode: - perform_bus(registers_.pc | registers_.program_bank, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode); - ++registers_.pc; - break; + case CycleFetchOpcode: + perform_bus(registers_.pc | registers_.program_bank, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode); + ++registers_.pc; + break; - case CycleFetchPC: - read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); - break; + case CycleFetchPC: + read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); + break; - case CycleFetchPCThrowaway: - read(registers_.pc | registers_.program_bank, &bus_throwaway_); - break; + case CycleFetchPCThrowaway: + read(registers_.pc | registers_.program_bank, &bus_throwaway_); + break; - // - // Data fetches and stores. - // + // + // Data fetches and stores. + // #define increment_data_address() data_address_ = (data_address_ & ~data_address_increment_mask_) + ((data_address_ + 1) & data_address_increment_mask_) #define decrement_data_address() data_address_ = (data_address_ & ~data_address_increment_mask_) + ((data_address_ - 1) & data_address_increment_mask_) - case CycleFetchData: - read(data_address_, data_buffer_.next_input()); - break; + case CycleFetchData: + read(data_address_, data_buffer_.next_input()); + break; - case CycleFetchDataThrowaway: - read(data_address_, &bus_throwaway_); - break; + case CycleFetchDataThrowaway: + read(data_address_, &bus_throwaway_); + break; - case CycleFetchIncorrectDataAddress: - read(incorrect_data_address_, &bus_throwaway_); - break; + case CycleFetchIncorrectDataAddress: + read(incorrect_data_address_, &bus_throwaway_); + break; - case CycleFetchIncrementData: - read(data_address_, data_buffer_.next_input()); - increment_data_address(); - break; + case CycleFetchIncrementData: + read(data_address_, data_buffer_.next_input()); + increment_data_address(); + break; - case CycleStoreData: - write(data_address_, data_buffer_.next_output()); - break; + case CycleStoreData: + write(data_address_, data_buffer_.next_output()); + break; - case CycleStoreDataThrowaway: - write(data_address_, data_buffer_.preview_output()); - break; + case CycleStoreDataThrowaway: + write(data_address_, data_buffer_.preview_output()); + break; - case CycleStoreIncrementData: - write(data_address_, data_buffer_.next_output()); - increment_data_address(); - break; + case CycleStoreIncrementData: + write(data_address_, data_buffer_.next_output()); + increment_data_address(); + break; - case CycleStoreDecrementData: - write(data_address_, data_buffer_.next_output_descending()); - decrement_data_address(); - break; + case CycleStoreDecrementData: + write(data_address_, data_buffer_.next_output_descending()); + decrement_data_address(); + break; - case CycleFetchBlockX: - read(((instruction_buffer_.value & 0xff00) << 8) | x(), data_buffer_.any_byte()); - break; + case CycleFetchBlockX: + read(((instruction_buffer_.value & 0xff00) << 8) | x(), data_buffer_.any_byte()); + break; - case CycleFetchBlockY: - read(((instruction_buffer_.value & 0xff00) << 8) | y(), &bus_throwaway_); - break; + case CycleFetchBlockY: + read(((instruction_buffer_.value & 0xff00) << 8) | y(), &bus_throwaway_); + break; - case CycleStoreBlockY: - write(((instruction_buffer_.value & 0xff00) << 8) | x(), data_buffer_.any_byte()); - break; + case CycleStoreBlockY: + write(((instruction_buffer_.value & 0xff00) << 8) | x(), data_buffer_.any_byte()); + break; #undef increment_data_address #undef decrement_data_address - // - // Stack accesses. - // + // + // Stack accesses. + // #define stack_access(value, operation) \ bus_address_ = stack_address(); \ bus_value_ = value; \ bus_operation_ = operation; - case CyclePush: - stack_access(data_buffer_.next_output_descending(), MOS6502Esque::Write); - --registers_.s.full; - break; + case CyclePush: + stack_access(data_buffer_.next_output_descending(), MOS6502Esque::Write); + --registers_.s.full; + break; - case CyclePullIfNotEmulation: - if(registers_.emulation_flag) { - continue; - } - [[fallthrough]]; + case CyclePullIfNotEmulation: + if(registers_.emulation_flag) { + continue; + } + [[fallthrough]]; - case CyclePull: - ++registers_.s.full; - stack_access(data_buffer_.next_input(), MOS6502Esque::Read); - break; + case CyclePull: + ++registers_.s.full; + stack_access(data_buffer_.next_input(), MOS6502Esque::Read); + break; - case CycleAccessStack: - stack_access(&bus_throwaway_, MOS6502Esque::Read); - break; + case CycleAccessStack: + stack_access(&bus_throwaway_, MOS6502Esque::Read); + break; #undef stack_access - // - // STP and WAI. - // - - case CycleRepeatingNone: - if(pending_exceptions_ & required_exceptions_) { - continue; - } else { - --next_op_; - perform_bus(0xffffff, nullptr, MOS6502Esque::None); - } - break; - - // - // Data movement. - // - - case OperationCopyPCToData: - data_buffer_.size = 2; - data_buffer_.value = registers_.pc; - continue; - - case OperationCopyInstructionToData: - data_buffer_ = instruction_buffer_; - continue; - - case OperationCopyDataToInstruction: - instruction_buffer_ = data_buffer_; - data_buffer_.clear(); - continue; - - case OperationCopyAToData: - data_buffer_.value = registers_.a.full & registers_.m_masks[1]; - data_buffer_.size = 2 - m_flag(); - continue; - - case OperationCopyDataToA: - registers_.a.full = (registers_.a.full & registers_.m_masks[0]) + (data_buffer_.value & registers_.m_masks[1]); - continue; - - case OperationCopyPBRToData: - data_buffer_.size = 1; - data_buffer_.value = registers_.program_bank >> 16; - continue; - - case OperationCopyDataToPC: - registers_.pc = uint16_t(data_buffer_.value); - continue; - - // - // Address construction. - // - - case OperationConstructAbsolute: - data_address_ = instruction_buffer_.value + registers_.data_bank; - data_address_increment_mask_ = 0xff'ff'ff; - continue; - - case OperationConstructAbsolute16: - data_address_ = instruction_buffer_.value; - data_address_increment_mask_ = 0x00'ff'ff; - continue; - - case OperationConstructAbsoluteLong: - data_address_ = instruction_buffer_.value; - data_address_increment_mask_ = 0xff'ff'ff; - continue; - - // Used for JMP and JSR (absolute, x). - case OperationConstructAbsoluteIndexedIndirect: - data_address_ = registers_.program_bank + ((instruction_buffer_.value + x()) & 0xffff); - data_address_increment_mask_ = 0x00'ff'ff; - continue; - - case OperationConstructAbsoluteLongX: - data_address_ = instruction_buffer_.value + x(); - data_address_increment_mask_ = 0xff'ff'ff; - continue; - - case OperationConstructAbsoluteXRead: - case OperationConstructAbsoluteX: - data_address_ = instruction_buffer_.value + x() + 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 == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { - ++next_op_; - } - data_address_increment_mask_ = 0xff'ff'ff; - continue; - - case OperationConstructAbsoluteYRead: - case OperationConstructAbsoluteY: - data_address_ = instruction_buffer_.value + y() + 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_) { - ++next_op_; - } - data_address_increment_mask_ = 0xff'ff'ff; - continue; - - case OperationConstructDirect: - data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; - data_address_increment_mask_ = 0x00'ff'ff; - if(!(registers_.direct&0xff)) { - // If the low byte is 0 and this is emulation mode, incrementing - // is restricted to the low byte. - data_address_increment_mask_ = registers_.e_masks[1]; - ++next_op_; - } - continue; - - case OperationConstructDirectLong: - data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; - data_address_increment_mask_ = 0x00'ff'ff; - if(!(registers_.direct&0xff)) { - ++next_op_; - } - continue; - - case OperationConstructDirectIndirect: - data_address_ = registers_.data_bank + data_buffer_.value; - data_address_increment_mask_ = 0xff'ff'ff; - data_buffer_.clear(); - continue; - - case OperationConstructDirectIndexedIndirect: - data_address_ = registers_.data_bank + ( - ((registers_.direct + x() + instruction_buffer_.value) & registers_.e_masks[1]) + - (registers_.direct & registers_.e_masks[0]) - ) & 0xffff; - data_address_increment_mask_ = 0x00'ff'ff; - - if(!(registers_.direct&0xff)) { - ++next_op_; - } - continue; - - case OperationConstructDirectIndirectIndexedLong: - data_address_ = y() + data_buffer_.value; - data_address_increment_mask_ = 0xff'ff'ff; - data_buffer_.clear(); - continue; - - case OperationConstructDirectIndirectLong: - data_address_ = data_buffer_.value; - data_address_increment_mask_ = 0xff'ff'ff; - data_buffer_.clear(); - continue; - - // TODO: confirm incorrect_data_address_ below. - - case OperationConstructDirectX: - data_address_ = ( - (registers_.direct & registers_.e_masks[0]) + - ((instruction_buffer_.value + registers_.direct + x()) & registers_.e_masks[1]) - ) & 0xffff; - data_address_increment_mask_ = 0x00'ff'ff; - - incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); - if(!(registers_.direct&0xff)) { - ++next_op_; - } - continue; - - case OperationConstructDirectY: - data_address_ = ( - (registers_.direct & registers_.e_masks[0]) + - ((instruction_buffer_.value + registers_.direct + y()) & registers_.e_masks[1]) - ) & 0xffff; - data_address_increment_mask_ = 0x00'ff'ff; - - incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); - if(!(registers_.direct&0xff)) { - ++next_op_; - } - continue; - - case OperationConstructStackRelative: - data_address_ = (registers_.s.full + instruction_buffer_.value) & 0xffff; - data_address_increment_mask_ = 0x00'ff'ff; - continue; - - case OperationConstructStackRelativeIndexedIndirect: - data_address_ = registers_.data_bank + data_buffer_.value + y(); - data_address_increment_mask_ = 0xff'ff'ff; - data_buffer_.clear(); - continue; - - case OperationConstructPER: - data_buffer_.value = instruction_buffer_.value + registers_.pc; - data_buffer_.size = 2; - continue; - - case OperationPrepareException: { - // Put the proper exception vector into the data address, put the flags and PC - // into the data buffer (possibly also PBR), and skip an instruction if in - // emulation mode. // - // I've assumed here that interrupts, BRKs and COPs can be usurped similarly - // to a 6502 but may not have the exact details correct. E.g. if IRQ has - // become inactive since the decision was made to start an interrupt, should - // that turn into a BRK? + // STP and WAI. + // - bool is_brk = false; - - if(pending_exceptions_ & (Reset | PowerOn)) { - pending_exceptions_ &= ~(Reset | PowerOn); - data_address_ = 0xfffc; - set_reset_state(); - } else if(pending_exceptions_ & NMI) { - pending_exceptions_ &= ~NMI; - data_address_ = 0xfffa; - } else if(pending_exceptions_ & IRQ & registers_.flags.inverse_interrupt) { - pending_exceptions_ &= ~IRQ; - data_address_ = 0xfffe; - } else { - is_brk = active_instruction_ == instructions; // Given that BRK has opcode 00. - if(is_brk) { - data_address_ = registers_.emulation_flag ? 0xfffe : 0xfff6; + case CycleRepeatingNone: + if(pending_exceptions_ & required_exceptions_) { + continue; } else { - // Implicitly: COP. - data_address_ = 0xfff4; + --next_op_; + perform_bus(0xffffff, nullptr, MOS6502Esque::None); } - } + break; - data_buffer_.value = (registers_.pc << 8) | get_flags(); - if(registers_.emulation_flag) { - if(is_brk) data_buffer_.value |= Flag::Break; - data_buffer_.size = 3; - ++next_op_; - } else { - data_buffer_.value |= registers_.program_bank << 24; - data_buffer_.size = 4; - registers_.program_bank = 0; - } + // + // Data movement. + // - registers_.flags.inverse_interrupt = 0; - } continue; + case OperationCopyPCToData: + data_buffer_.size = 2; + data_buffer_.value = registers_.pc; + continue; - // - // Performance. - // + case OperationCopyInstructionToData: + data_buffer_ = instruction_buffer_; + continue; + + case OperationCopyDataToInstruction: + instruction_buffer_ = data_buffer_; + data_buffer_.clear(); + continue; + + case OperationCopyAToData: + data_buffer_.value = registers_.a.full & registers_.m_masks[1]; + data_buffer_.size = 2 - m_flag(); + continue; + + case OperationCopyDataToA: + registers_.a.full = (registers_.a.full & registers_.m_masks[0]) + (data_buffer_.value & registers_.m_masks[1]); + continue; + + case OperationCopyPBRToData: + data_buffer_.size = 1; + data_buffer_.value = registers_.program_bank >> 16; + continue; + + case OperationCopyDataToPC: + registers_.pc = uint16_t(data_buffer_.value); + continue; + + // + // Address construction. + // + + case OperationConstructAbsolute: + data_address_ = instruction_buffer_.value + registers_.data_bank; + data_address_increment_mask_ = 0xff'ff'ff; + continue; + + case OperationConstructAbsolute16: + data_address_ = instruction_buffer_.value; + data_address_increment_mask_ = 0x00'ff'ff; + continue; + + case OperationConstructAbsoluteLong: + data_address_ = instruction_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; + continue; + + // Used for JMP and JSR (absolute, x). + case OperationConstructAbsoluteIndexedIndirect: + data_address_ = registers_.program_bank + ((instruction_buffer_.value + x()) & 0xffff); + data_address_increment_mask_ = 0x00'ff'ff; + continue; + + case OperationConstructAbsoluteLongX: + data_address_ = instruction_buffer_.value + x(); + data_address_increment_mask_ = 0xff'ff'ff; + continue; + + case OperationConstructAbsoluteXRead: + case OperationConstructAbsoluteX: + data_address_ = instruction_buffer_.value + x() + 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 == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { + ++next_op_; + } + data_address_increment_mask_ = 0xff'ff'ff; + continue; + + case OperationConstructAbsoluteYRead: + case OperationConstructAbsoluteY: + data_address_ = instruction_buffer_.value + y() + 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_) { + ++next_op_; + } + data_address_increment_mask_ = 0xff'ff'ff; + continue; + + case OperationConstructDirect: + data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + if(!(registers_.direct&0xff)) { + // If the low byte is 0 and this is emulation mode, incrementing + // is restricted to the low byte. + data_address_increment_mask_ = registers_.e_masks[1]; + ++next_op_; + } + continue; + + case OperationConstructDirectLong: + data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + if(!(registers_.direct&0xff)) { + ++next_op_; + } + continue; + + case OperationConstructDirectIndirect: + data_address_ = registers_.data_bank + data_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); + continue; + + case OperationConstructDirectIndexedIndirect: + data_address_ = registers_.data_bank + ( + ((registers_.direct + x() + instruction_buffer_.value) & registers_.e_masks[1]) + + (registers_.direct & registers_.e_masks[0]) + ) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + + if(!(registers_.direct&0xff)) { + ++next_op_; + } + continue; + + case OperationConstructDirectIndirectIndexedLong: + data_address_ = y() + data_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); + continue; + + case OperationConstructDirectIndirectLong: + data_address_ = data_buffer_.value; + data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); + continue; + + // TODO: confirm incorrect_data_address_ below. + + case OperationConstructDirectX: + data_address_ = ( + (registers_.direct & registers_.e_masks[0]) + + ((instruction_buffer_.value + registers_.direct + x()) & registers_.e_masks[1]) + ) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + + incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); + if(!(registers_.direct&0xff)) { + ++next_op_; + } + continue; + + case OperationConstructDirectY: + data_address_ = ( + (registers_.direct & registers_.e_masks[0]) + + ((instruction_buffer_.value + registers_.direct + y()) & registers_.e_masks[1]) + ) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + + incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); + if(!(registers_.direct&0xff)) { + ++next_op_; + } + continue; + + case OperationConstructStackRelative: + data_address_ = (registers_.s.full + instruction_buffer_.value) & 0xffff; + data_address_increment_mask_ = 0x00'ff'ff; + continue; + + case OperationConstructStackRelativeIndexedIndirect: + data_address_ = registers_.data_bank + data_buffer_.value + y(); + data_address_increment_mask_ = 0xff'ff'ff; + data_buffer_.clear(); + continue; + + case OperationConstructPER: + data_buffer_.value = instruction_buffer_.value + registers_.pc; + data_buffer_.size = 2; + continue; + + case OperationPrepareException: { + // Put the proper exception vector into the data address, put the flags and PC + // into the data buffer (possibly also PBR), and skip an instruction if in + // emulation mode. + // + // I've assumed here that interrupts, BRKs and COPs can be usurped similarly + // to a 6502 but may not have the exact details correct. E.g. if IRQ has + // become inactive since the decision was made to start an interrupt, should + // that turn into a BRK? + // + // Also: priority here is a guess. + + bool is_brk = false; + + if(pending_exceptions_ & (Reset | PowerOn)) { + pending_exceptions_ &= ~(Reset | PowerOn); + data_address_ = 0xfffc; + set_reset_state(); + } else if(pending_exceptions_ & NMI) { + pending_exceptions_ &= ~NMI; + data_address_ = registers_.emulation_flag ? 0xfffa : 0xffea; + } else if(pending_exceptions_ & IRQ & registers_.flags.inverse_interrupt) { + pending_exceptions_ &= ~IRQ; + data_address_ = registers_.emulation_flag ? 0xfffe : 0xffee; + } else if(pending_exceptions_ & Abort) { + // Special case: restore registers from start of instruction. + registers_ = abort_registers_copy_; + + pending_exceptions_ &= ~Abort; + data_address_ = registers_.emulation_flag ? 0xfff8 : 0xffe8;; + } else { + is_brk = active_instruction_ == instructions; // Given that BRK has opcode 00. + if(is_brk) { + data_address_ = registers_.emulation_flag ? 0xfffe : 0xffe6; + } else { + // Implicitly: COP. + data_address_ = registers_.emulation_flag ? 0xfff4 : 0xffe8; + } + } + + data_buffer_.value = (registers_.pc << 8) | get_flags(); + if(registers_.emulation_flag) { + if(is_brk) data_buffer_.value |= Flag::Break; + data_buffer_.size = 3; + ++next_op_; + } else { + data_buffer_.value |= registers_.program_bank << 24; + data_buffer_.size = 4; + registers_.program_bank = 0; + } + + registers_.flags.inverse_interrupt = 0; + } continue; + + // + // Performance. + // #define LD(dest, src, masks) dest.full = (dest.full & masks[0]) | (src & masks[1]) #define m_top() (instruction_buffer_.value >> registers_.m_shift) & 0xff @@ -421,326 +436,326 @@ template void Processor> registers_.x_shift) & 0xff #define a_top() (registers_.a.full >> registers_.m_shift) & 0xff - case OperationPerform: - switch(active_instruction_->operation) { - - // - // Loads, stores and transfers (and NOP, and XBA). - // - - case LDA: - LD(registers_.a, data_buffer_.value, registers_.m_masks); - registers_.flags.set_nz(registers_.a.full, registers_.m_shift); - break; - - case LDX: - LD(registers_.x, data_buffer_.value, registers_.x_masks); - registers_.flags.set_nz(registers_.x.full, registers_.x_shift); - break; - - case LDY: - LD(registers_.y, data_buffer_.value, registers_.x_masks); - registers_.flags.set_nz(registers_.y.full, registers_.x_shift); - break; - - case PLB: - registers_.data_bank = (data_buffer_.value & 0xff) << 16; - registers_.flags.set_nz(instruction_buffer_.value); - break; - - case PLD: - registers_.direct = data_buffer_.value; - registers_.flags.set_nz(instruction_buffer_.value); - break; - - case PLP: - set_flags(data_buffer_.value); - break; - - case STA: - data_buffer_.value = registers_.a.full & registers_.m_masks[1]; - data_buffer_.size = 2 - m_flag(); - break; - - case STZ: - data_buffer_.value = 0; - data_buffer_.size = 2 - m_flag(); - break; - - case STX: - data_buffer_.value = registers_.x.full & registers_.x_masks[1]; - data_buffer_.size = 2 - x_flag(); - break; - - case STY: - data_buffer_.value = registers_.y.full & registers_.x_masks[1]; - data_buffer_.size = 2 - m_flag(); - break; - - case PHB: - data_buffer_.value = registers_.data_bank >> 16; - data_buffer_.size = 1; - break; - - case PHK: - data_buffer_.value = registers_.program_bank >> 16; - data_buffer_.size = 1; - break; - - case PHD: - data_buffer_.value = registers_.direct; - data_buffer_.size = 2; - break; - - 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; - - // The below attempt to obey the 8/16-bit mixed transfer rules - // as documented in https://softpixel.com/~cwright/sianse/docs/65816NFO.HTM - // (and make reasonable guesses as to the N flag). - - case TXS: - registers_.s = registers_.x.full & registers_.x_masks[1]; - break; - - case TSX: - LD(registers_.x, registers_.s.full, registers_.x_masks); - registers_.flags.set_nz(registers_.x.full, registers_.x_shift); - break; - - case TXY: - LD(registers_.y, registers_.x.full, registers_.x_masks); - registers_.flags.set_nz(registers_.y.full, registers_.x_shift); - break; - - case TYX: - LD(registers_.x, registers_.y.full, registers_.x_masks); - registers_.flags.set_nz(registers_.x.full, registers_.x_shift); - break; - - case TAX: - LD(registers_.x, registers_.a.full, registers_.x_masks); - registers_.flags.set_nz(registers_.x.full, registers_.x_shift); - break; - - case TAY: - LD(registers_.y, registers_.a.full, registers_.x_masks); - registers_.flags.set_nz(registers_.y.full, registers_.x_shift); - break; - - case TXA: - LD(registers_.a, registers_.x.full, registers_.m_masks); - registers_.flags.set_nz(registers_.a.full, registers_.m_shift); - break; - - case TYA: - LD(registers_.a, registers_.y.full, registers_.m_masks); - registers_.flags.set_nz(registers_.a.full, registers_.m_shift); - break; - - case TCD: - registers_.direct = registers_.a.full; - registers_.flags.set_nz(registers_.a.full, 8); - break; - - case TDC: - registers_.a.full = registers_.direct; - registers_.flags.set_nz(registers_.a.full, 8); - break; - - case TCS: - registers_.s.full = registers_.a.full; - // No need to worry about byte masking here; for the stack it's handled as the emulation runs. - break; - - case TSC: - registers_.a.full = stack_address(); - registers_.flags.set_nz(registers_.a.full, 8); - break; - - case XBA: { - const uint8_t a_low = registers_.a.halves.low; - registers_.a.halves.low = registers_.a.halves.high; - registers_.a.halves.high = a_low; - registers_.flags.set_nz(registers_.a.halves.low); - } break; - - // - // Jumps and returns. - // - - case JML: - registers_.program_bank = instruction_buffer_.value & 0xff0000; - [[fallthrough]]; - - case JMP: - registers_.pc = uint16_t(instruction_buffer_.value); - break; - - case JMPind: - registers_.pc = data_buffer_.value; - break; - - case RTS: - registers_.pc = data_buffer_.value + 1; - break; - - case JSL: - registers_.program_bank = instruction_buffer_.value & 0xff0000; - [[fallthrough]]; - - case JSR: - data_buffer_.value = registers_.pc; - data_buffer_.size = 2; - - registers_.pc = instruction_buffer_.value; - break; - - case RTI: - registers_.pc = uint16_t(data_buffer_.value >> 8); - set_flags(uint8_t(data_buffer_.value)); - - if(!registers_.emulation_flag) { - registers_.program_bank = (data_buffer_.value & 0xff000000) >> 8; - } - break; - - // - // Block moves. - // - - case MVP: - registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; - --registers_.x.full; - --registers_.y.full; - --registers_.a.full; - if(registers_.a.full) registers_.pc -= 3; - break; - - case MVN: - registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; - ++registers_.x.full; - ++registers_.y.full; - --registers_.a.full; - if(registers_.a.full) registers_.pc -= 3; - break; - - // - // Flag manipulation. - // - - case CLC: registers_.flags.carry = 0; break; - case CLI: registers_.flags.inverse_interrupt = Flag::Interrupt; break; - case CLV: registers_.flags.overflow = 0; break; - case CLD: registers_.flags.decimal = 0; break; - - case SEC: registers_.flags.carry = Flag::Carry; break; - case SEI: registers_.flags.inverse_interrupt = 0; break; - case SED: registers_.flags.decimal = Flag::Decimal; break; - - case REP: - set_flags(get_flags() &~ instruction_buffer_.value); - break; - - case SEP: - set_flags(get_flags() | instruction_buffer_.value); - break; - - case XCE: { - const bool old_emulation_flag = registers_.emulation_flag; - set_emulation_mode(registers_.flags.carry); - registers_.flags.carry = old_emulation_flag; - } break; - - // - // Increments and decrements. - // - - case INC: - ++data_buffer_.value; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); - break;; - - case DEC: - --data_buffer_.value; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); - break; - - case INX: { - const uint16_t x_inc = registers_.x.full + 1; - LD(registers_.x, x_inc, registers_.x_masks); - registers_.flags.set_nz(registers_.x.full, registers_.x_shift); - } break; - - case DEX: { - const uint16_t x_dec = registers_.x.full - 1; - LD(registers_.x, x_dec, registers_.x_masks); - registers_.flags.set_nz(registers_.x.full, registers_.x_shift); - } break; - - case INY: { - const uint16_t y_inc = registers_.y.full + 1; - LD(registers_.y, y_inc, registers_.x_masks); - registers_.flags.set_nz(registers_.y.full, registers_.x_shift); - } break; - - case DEY: { - const uint16_t y_dec = registers_.y.full - 1; - LD(registers_.y, y_dec, registers_.x_masks); - registers_.flags.set_nz(registers_.y.full, registers_.x_shift); - } break; - - // - // Bitwise operations. - // - - case AND: - registers_.a.full &= data_buffer_.value | registers_.m_masks[0]; - registers_.flags.set_nz(registers_.a.full, registers_.m_shift); - break; - - case EOR: - registers_.a.full ^= data_buffer_.value; - registers_.flags.set_nz(registers_.a.full, registers_.m_shift); - break; - - case ORA: - registers_.a.full |= data_buffer_.value; - registers_.flags.set_nz(registers_.a.full, registers_.m_shift); - break; - - case BIT: - registers_.flags.set_n(data_buffer_.value, registers_.m_shift); - registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); - registers_.flags.overflow = data_buffer_.value & Flag::Overflow; - break; - - case BITimm: - registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); - break; - - case TRB: - registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); - data_buffer_.value &= ~registers_.a.full; - break; - - case TSB: - registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); - data_buffer_.value |= registers_.a.full; - break; - - // - // Branches. - // + case OperationPerform: + switch(active_instruction_->operation) { + + // + // Loads, stores and transfers (and NOP, and XBA). + // + + case LDA: + LD(registers_.a, data_buffer_.value, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); + break; + + case LDX: + LD(registers_.x, data_buffer_.value, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); + break; + + case LDY: + LD(registers_.y, data_buffer_.value, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); + break; + + case PLB: + registers_.data_bank = (data_buffer_.value & 0xff) << 16; + registers_.flags.set_nz(instruction_buffer_.value); + break; + + case PLD: + registers_.direct = data_buffer_.value; + registers_.flags.set_nz(instruction_buffer_.value); + break; + + case PLP: + set_flags(data_buffer_.value); + break; + + case STA: + data_buffer_.value = registers_.a.full & registers_.m_masks[1]; + data_buffer_.size = 2 - m_flag(); + break; + + case STZ: + data_buffer_.value = 0; + data_buffer_.size = 2 - m_flag(); + break; + + case STX: + data_buffer_.value = registers_.x.full & registers_.x_masks[1]; + data_buffer_.size = 2 - x_flag(); + break; + + case STY: + data_buffer_.value = registers_.y.full & registers_.x_masks[1]; + data_buffer_.size = 2 - m_flag(); + break; + + case PHB: + data_buffer_.value = registers_.data_bank >> 16; + data_buffer_.size = 1; + break; + + case PHK: + data_buffer_.value = registers_.program_bank >> 16; + data_buffer_.size = 1; + break; + + case PHD: + data_buffer_.value = registers_.direct; + data_buffer_.size = 2; + break; + + 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; + + // The below attempt to obey the 8/16-bit mixed transfer rules + // as documented in https://softpixel.com/~cwright/sianse/docs/65816NFO.HTM + // (and make reasonable guesses as to the N flag). + + case TXS: + registers_.s = registers_.x.full & registers_.x_masks[1]; + break; + + case TSX: + LD(registers_.x, registers_.s.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); + break; + + case TXY: + LD(registers_.y, registers_.x.full, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); + break; + + case TYX: + LD(registers_.x, registers_.y.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); + break; + + case TAX: + LD(registers_.x, registers_.a.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); + break; + + case TAY: + LD(registers_.y, registers_.a.full, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); + break; + + case TXA: + LD(registers_.a, registers_.x.full, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); + break; + + case TYA: + LD(registers_.a, registers_.y.full, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); + break; + + case TCD: + registers_.direct = registers_.a.full; + registers_.flags.set_nz(registers_.a.full, 8); + break; + + case TDC: + registers_.a.full = registers_.direct; + registers_.flags.set_nz(registers_.a.full, 8); + break; + + case TCS: + registers_.s.full = registers_.a.full; + // No need to worry about byte masking here; for the stack it's handled as the emulation runs. + break; + + case TSC: + registers_.a.full = stack_address(); + registers_.flags.set_nz(registers_.a.full, 8); + break; + + case XBA: { + const uint8_t a_low = registers_.a.halves.low; + registers_.a.halves.low = registers_.a.halves.high; + registers_.a.halves.high = a_low; + registers_.flags.set_nz(registers_.a.halves.low); + } break; + + // + // Jumps and returns. + // + + case JML: + registers_.program_bank = instruction_buffer_.value & 0xff0000; + [[fallthrough]]; + + case JMP: + registers_.pc = uint16_t(instruction_buffer_.value); + break; + + case JMPind: + registers_.pc = data_buffer_.value; + break; + + case RTS: + registers_.pc = data_buffer_.value + 1; + break; + + case JSL: + registers_.program_bank = instruction_buffer_.value & 0xff0000; + [[fallthrough]]; + + case JSR: + data_buffer_.value = registers_.pc; + data_buffer_.size = 2; + + registers_.pc = instruction_buffer_.value; + break; + + case RTI: + registers_.pc = uint16_t(data_buffer_.value >> 8); + set_flags(uint8_t(data_buffer_.value)); + + if(!registers_.emulation_flag) { + registers_.program_bank = (data_buffer_.value & 0xff000000) >> 8; + } + break; + + // + // Block moves. + // + + case MVP: + registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; + --registers_.x.full; + --registers_.y.full; + --registers_.a.full; + if(registers_.a.full) registers_.pc -= 3; + break; + + case MVN: + registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; + ++registers_.x.full; + ++registers_.y.full; + --registers_.a.full; + if(registers_.a.full) registers_.pc -= 3; + break; + + // + // Flag manipulation. + // + + case CLC: registers_.flags.carry = 0; break; + case CLI: registers_.flags.inverse_interrupt = Flag::Interrupt; break; + case CLV: registers_.flags.overflow = 0; break; + case CLD: registers_.flags.decimal = 0; break; + + case SEC: registers_.flags.carry = Flag::Carry; break; + case SEI: registers_.flags.inverse_interrupt = 0; break; + case SED: registers_.flags.decimal = Flag::Decimal; break; + + case REP: + set_flags(get_flags() &~ instruction_buffer_.value); + break; + + case SEP: + set_flags(get_flags() | instruction_buffer_.value); + break; + + case XCE: { + const bool old_emulation_flag = registers_.emulation_flag; + set_emulation_mode(registers_.flags.carry); + registers_.flags.carry = old_emulation_flag; + } break; + + // + // Increments and decrements. + // + + case INC: + ++data_buffer_.value; + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + break;; + + case DEC: + --data_buffer_.value; + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + break; + + case INX: { + const uint16_t x_inc = registers_.x.full + 1; + LD(registers_.x, x_inc, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); + } break; + + case DEX: { + const uint16_t x_dec = registers_.x.full - 1; + LD(registers_.x, x_dec, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); + } break; + + case INY: { + const uint16_t y_inc = registers_.y.full + 1; + LD(registers_.y, y_inc, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); + } break; + + case DEY: { + const uint16_t y_dec = registers_.y.full - 1; + LD(registers_.y, y_dec, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); + } break; + + // + // Bitwise operations. + // + + case AND: + registers_.a.full &= data_buffer_.value | registers_.m_masks[0]; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); + break; + + case EOR: + registers_.a.full ^= data_buffer_.value; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); + break; + + case ORA: + registers_.a.full |= data_buffer_.value; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); + break; + + case BIT: + registers_.flags.set_n(data_buffer_.value, registers_.m_shift); + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + registers_.flags.overflow = data_buffer_.value & Flag::Overflow; + break; + + case BITimm: + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + break; + + case TRB: + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + data_buffer_.value &= ~registers_.a.full; + break; + + case TSB: + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + data_buffer_.value |= registers_.a.full; + break; + + // + // Branches. + // #define BRA(condition) \ if(!(condition)) { \ @@ -754,54 +769,54 @@ template void Processor> (7 + registers_.m_shift); - data_buffer_.value <<= 1; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); - break; + case ASL: + registers_.flags.carry = data_buffer_.value >> (7 + registers_.m_shift); + data_buffer_.value <<= 1; + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + break; - case LSR: - registers_.flags.carry = data_buffer_.value & 1; - data_buffer_.value >>= 1; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); - break; + case LSR: + registers_.flags.carry = data_buffer_.value & 1; + data_buffer_.value >>= 1; + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + break; - case ROL: - data_buffer_.value = (data_buffer_.value << 1) | registers_.flags.carry; - registers_.flags.carry = data_buffer_.value >> (8 + registers_.m_shift); - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); - break; + case ROL: + data_buffer_.value = (data_buffer_.value << 1) | registers_.flags.carry; + registers_.flags.carry = data_buffer_.value >> (8 + registers_.m_shift); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + break; - case ROR: { - const uint8_t next_carry = data_buffer_.value & 1; - data_buffer_.value = (data_buffer_.value >> 1) | (registers_.flags.carry << (7 + registers_.m_shift)); - registers_.flags.carry = next_carry; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); - } break; + case ROR: { + const uint8_t next_carry = data_buffer_.value & 1; + data_buffer_.value = (data_buffer_.value >> 1) | (registers_.flags.carry << (7 + registers_.m_shift)); + registers_.flags.carry = next_carry; + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + } break; - // - // Arithmetic. - // + // + // Arithmetic. + // #define cp(v, shift, masks) {\ const uint32_t temp32 = (v.full & masks[1]) - (data_buffer_.value & masks[1]); \ @@ -809,19 +824,19 @@ template void Processor> (8 + shift))&1; \ } - case CMP: cp(registers_.a, registers_.m_shift, registers_.m_masks); break; - case CPX: cp(registers_.x, registers_.x_shift, registers_.x_masks); break; - case CPY: cp(registers_.y, registers_.x_shift, registers_.x_masks); break; + case CMP: cp(registers_.a, registers_.m_shift, registers_.m_masks); break; + case CPX: cp(registers_.x, registers_.x_shift, registers_.x_masks); break; + case CPY: cp(registers_.y, registers_.x_shift, registers_.x_masks); break; #undef cp - 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; + 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; #define nibble(mask, adjustment, carry) \ result += (a & mask) - (data_buffer_.value & mask) - borrow; \ @@ -829,66 +844,66 @@ template void Processor mask) ? carry : 0; \ result &= (carry - 1); - nibble(0x000f, 0x0006, 0x00010); - nibble(0x00f0, 0x0060, 0x00100); - nibble(0x0f00, 0x0600, 0x01000); - nibble(0xf000, 0x6000, 0x10000); + nibble(0x000f, 0x0006, 0x00010); + nibble(0x00f0, 0x0060, 0x00100); + nibble(0x0f00, 0x0600, 0x01000); + nibble(0xf000, 0x6000, 0x10000); #undef nibble - registers_.flags.overflow = ~(( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; - registers_.flags.set_nz(result, registers_.m_shift); - registers_.flags.carry = ((borrow >> 16)&1)^1; - LD(registers_.a, result, registers_.m_masks); + registers_.flags.overflow = ~(( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; + registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.carry = ((borrow >> 16)&1)^1; + LD(registers_.a, result, registers_.m_masks); - break; - } + break; + } - data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; - [[fallthrough]]; + data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; + [[fallthrough]]; - case ADC: { - int result; - const uint16_t a = registers_.a.full & registers_.m_masks[1]; + case ADC: { + int result; + const uint16_t a = registers_.a.full & registers_.m_masks[1]; - if(registers_.flags.decimal) { - result = registers_.flags.carry; + if(registers_.flags.decimal) { + result = registers_.flags.carry; #define nibble(mask, limit, adjustment, carry) \ result += (a & mask) + (data_buffer_.value & 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); + nibble(0x000f, 0x000a, 0x0006, 0x00010); + nibble(0x00f0, 0x00a0, 0x0060, 0x00100); + nibble(0x0f00, 0x0a00, 0x0600, 0x01000); + nibble(0xf000, 0xa000, 0x6000, 0x10000); #undef nibble - } else { - result = a + data_buffer_.value + registers_.flags.carry; - } + } else { + result = a + data_buffer_.value + registers_.flags.carry; + } - registers_.flags.overflow = (( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; - registers_.flags.set_nz(result, registers_.m_shift); - registers_.flags.carry = (result >> (8 + registers_.m_shift))&1; - LD(registers_.a, result, registers_.m_masks); - } break; + registers_.flags.overflow = (( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; + registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.carry = (result >> (8 + registers_.m_shift))&1; + LD(registers_.a, result, registers_.m_masks); + } break; - // - // STP and WAI - // + // + // STP and WAI + // - case STP: - required_exceptions_ = Reset; - break; + case STP: + required_exceptions_ = Reset; + break; - case WAI: - required_exceptions_ = Reset | IRQ | NMI; - break; - } - continue; - } + case WAI: + required_exceptions_ = Reset | IRQ | NMI; + break; + } + continue; + } #undef LD #undef m_top @@ -896,12 +911,11 @@ template void Processor void Processor::set_ready_line(bool active) { + assert(uses_ready_line); + ready_line_ = active; +} + // The 65816 can't jam. bool ProcessorBase::is_jammed() const { return false; } + +bool ProcessorBase::get_is_resetting() const { + return pending_exceptions_ & (Reset | PowerOn); +} From 7aa6cf4c6b0d2a3769fe0422bfffde10e137ebd5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 20:51:23 -0400 Subject: [PATCH 133/150] Tidies up layout very slightly. --- .../Implementation/65816Implementation.hpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 23359bb25..34ed2189f 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -150,8 +150,8 @@ template void Processor void Processor void Processor mask) result -= adjustment;\ - borrow = (result > mask) ? carry : 0; \ + if(result > mask) result -= adjustment; \ + borrow = (result > mask) ? carry : 0; \ result &= (carry - 1); nibble(0x000f, 0x0006, 0x00010); From 9a2f32795f392ceaf98327136a3b5f8d42610a42 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 21:03:10 -0400 Subject: [PATCH 134/150] Revokes stack-local storage non-optimisation. --- .../Implementation/6502Implementation.hpp | 83 ++++++++----------- .../6502/Implementation/6502Storage.hpp | 1 + 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 673970e31..f5f2a6512 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -13,16 +13,6 @@ */ template void Processor::run_for(const Cycles cycles) { - uint8_t throwaway_target; - - // These plus program below act to give the compiler permission to update these values - // without touching the class storage (i.e. it explicitly says they need be completely up - // to date in this stack frame only); which saves some complicated addressing - RegisterPair16 nextAddress = next_address_; - BusOperation nextBusOperation = next_bus_operation_; - uint16_t busAddress = bus_address_; - uint8_t *busValue = bus_value_; - #define checkSchedule() \ if(!scheduled_program_counter_) {\ if(interrupt_requests_) {\ @@ -43,8 +33,8 @@ template void Proces #define bus_access() \ interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \ irq_request_history_ = irq_line_ & flags_.inverse_interrupt; \ - number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \ - nextBusOperation = BusOperation::None; \ + number_of_cycles -= bus_handler_.perform_bus_operation(next_bus_operation_, bus_address_, bus_value_); \ + next_bus_operation_ = BusOperation::None; \ if(number_of_cycles <= Cycles(0)) break; checkSchedule(); @@ -54,12 +44,12 @@ template void Proces // Deal with a potential RDY state, if this 6502 has anything connected to ready. while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) { - number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue); + number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_); } // Deal with a potential STP state, if this 6502 implements STP. while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) { - number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue); + number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_); if(interrupt_requests_ & InterruptRequestFlags::Reset) { stop_is_active_ = false; checkSchedule(); @@ -69,7 +59,7 @@ template void Proces // Deal with a potential WAI state, if this 6502 implements WAI. while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) { - number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue); + number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_); interrupt_requests_ |= (irq_line_ & flags_.inverse_interrupt); if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) { wait_is_active_ = false; @@ -79,7 +69,7 @@ template void Proces } if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) { - if(nextBusOperation != BusOperation::None) { + if(next_bus_operation_ != BusOperation::None) { bus_access(); } @@ -88,10 +78,10 @@ template void Proces const MicroOp cycle = *scheduled_program_counter_; scheduled_program_counter_++; -#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff -#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff -#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff -#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val +#define read_op(val, addr) next_bus_operation_ = BusOperation::ReadOpcode; bus_address_ = addr; bus_value_ = &val; val = 0xff +#define read_mem(val, addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &val; val = 0xff +#define throwaway_read(addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &bus_throwaway_; bus_throwaway_ = 0xff +#define write_mem(val, addr) next_bus_operation_ = BusOperation::Write; bus_address_ = addr; bus_value_ = &val switch(cycle) { @@ -155,17 +145,17 @@ template void Proces case OperationBRKPickVector: if(is_65c02(personality)) { - nextAddress.full = 0xfffe; + next_address_.full = 0xfffe; } else { // NMI can usurp BRK-vector operations on the pre-C 6502s. - nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe; + next_address_.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe; interrupt_requests_ &= ~InterruptRequestFlags::NMI; } continue; - case OperationNMIPickVector: nextAddress.full = 0xfffa; continue; - case OperationRSTPickVector: nextAddress.full = 0xfffc; continue; - case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break; - case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break; + case OperationNMIPickVector: next_address_.full = 0xfffa; continue; + case OperationRSTPickVector: next_address_.full = 0xfffc; continue; + case CycleReadVectorLow: read_mem(pc_.halves.low, next_address_.full); break; + case CycleReadVectorHigh: read_mem(pc_.halves.high, next_address_.full+1); break; case OperationSetIRQFlags: flags_.inverse_interrupt = 0; if(is_65c02(personality)) flags_.decimal = 0; @@ -464,36 +454,36 @@ template void Proces } case CycleAddXToAddressLow: - nextAddress.full = address_.full + x_; - address_.halves.low = nextAddress.halves.low; - if(address_.halves.high != nextAddress.halves.high) { + next_address_.full = address_.full + x_; + address_.halves.low = next_address_.halves.low; + if(address_.halves.high != next_address_.halves.high) { page_crossing_stall_read(); break; } continue; case CycleAddXToAddressLowRead: - nextAddress.full = address_.full + x_; - address_.halves.low = nextAddress.halves.low; + next_address_.full = address_.full + x_; + address_.halves.low = next_address_.halves.low; page_crossing_stall_read(); break; case CycleAddYToAddressLow: - nextAddress.full = address_.full + y_; - address_.halves.low = nextAddress.halves.low; - if(address_.halves.high != nextAddress.halves.high) { + next_address_.full = address_.full + y_; + address_.halves.low = next_address_.halves.low; + if(address_.halves.high != next_address_.halves.high) { page_crossing_stall_read(); break; } continue; case CycleAddYToAddressLowRead: - nextAddress.full = address_.full + y_; - address_.halves.low = nextAddress.halves.low; + next_address_.full = address_.full + y_; + address_.halves.low = next_address_.halves.low; page_crossing_stall_read(); break; #undef page_crossing_stall_read case OperationCorrectAddressHigh: - address_.full = nextAddress.full; + address_.full = next_address_.full; continue; case CycleIncrementPCFetchAddressLowFromOperand: pc_.full++; @@ -573,12 +563,12 @@ template void Proces #undef BRA case CycleAddSignedOperandToPC: - nextAddress.full = uint16_t(pc_.full + int8_t(operand_)); - pc_.halves.low = nextAddress.halves.low; - if(nextAddress.halves.high != pc_.halves.high) { - uint16_t halfUpdatedPc = pc_.full; - pc_.full = nextAddress.full; - throwaway_read(halfUpdatedPc); + next_address_.full = uint16_t(pc_.full + int8_t(operand_)); + pc_.halves.low = next_address_.halves.low; + if(next_address_.halves.high != pc_.halves.high) { + const uint16_t half_updated_pc = pc_.full; + pc_.full = next_address_.full; + throwaway_read(half_updated_pc); break; } else if(is_65c02(personality)) { // 65C02 modification to all branches: a branch that is taken but requires only a single cycle @@ -650,7 +640,7 @@ template void Proces if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) { break; } - if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) { + if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(next_bus_operation_))) { ready_is_active_ = true; break; } @@ -660,11 +650,6 @@ template void Proces } cycles_left_to_run_ = number_of_cycles; - next_address_ = nextAddress; - next_bus_operation_ = nextBusOperation; - bus_address_ = busAddress; - bus_value_ = busValue; - bus_handler_.flush(); } diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index d4dfecdf1..5984e4c27 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -243,6 +243,7 @@ class ProcessorStorage { BusOperation next_bus_operation_ = BusOperation::None; uint16_t bus_address_; uint8_t *bus_value_; + static inline uint8_t bus_throwaway_; /*! Gets the flags register. From 68c15bd605bb35fc1c3c9a88eb077c3327676689 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 21:09:22 -0400 Subject: [PATCH 135/150] Updates Qt project; catches another couple of issues via its compiler. --- OSBindings/Qt/ClockSignal.pro | 5 +++++ OSBindings/Qt/mainwindow.cpp | 2 +- Processors/65816/65816.hpp | 3 +-- Processors/65816/Implementation/65816Storage.hpp | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index 3e42b9a02..98bd28638 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -91,6 +91,7 @@ SOURCES += \ \ $$SRC/Processors/6502/Implementation/*.cpp \ $$SRC/Processors/6502/State/*.cpp \ + $$SRC/Processors/65816/Implementation/*.cpp \ $$SRC/Processors/68000/Implementation/*.cpp \ $$SRC/Processors/68000/State/*.cpp \ $$SRC/Processors/Z80/Implementation/*.cpp \ @@ -214,6 +215,10 @@ HEADERS += \ $$SRC/Processors/6502/*.hpp \ $$SRC/Processors/6502/Implementation/*.hpp \ $$SRC/Processors/6502/State/*.hpp \ + $$SRC/Processors/6502Esque/*.hpp \ + $$SRC/Processors/6502Esque/Implementation/*.hpp \ + $$SRC/Processors/65816/*.hpp \ + $$SRC/Processors/65816/Implementation/*.hpp \ $$SRC/Processors/68000/*.hpp \ $$SRC/Processors/68000/Implementation/*.hpp \ $$SRC/Processors/68000/State/*.hpp \ diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 1f23266ff..04138e7fc 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -287,7 +287,7 @@ void MainWindow::launchMachine() { // Populate request text. QString requestText = romRequestBaseText; size_t index = 0; - for(const auto rom: missingRoms) { + for(const auto &rom: missingRoms) { requestText += "• "; requestText += rom.descriptive_name.c_str(); diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index c5f2daf53..3c168ac80 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -9,9 +9,8 @@ #ifndef WDC65816_hpp #define WDC65816_hpp -#include // TEMPORARILY. +#include #include -#include // TEMPORARILY. #include #include "../RegisterSizes.hpp" diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 41f6709f3..0e7eba2b9 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -215,7 +215,7 @@ enum Operation: uint8_t { RTS, }; -class ProcessorStorageConstructor; +struct ProcessorStorageConstructor; struct ProcessorStorage { ProcessorStorage(); From 9c0c0255f61fde534dcffceb1c0d156d8f472236 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 21:10:32 -0400 Subject: [PATCH 136/150] Ensures data/program bank can't accidentally be set to 16-bit values. --- Processors/65816/Implementation/65816Base.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index fed760fa7..cb7d5e1ce 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -29,16 +29,16 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const { void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { - case Register::ProgramCounter: registers_.pc = value; break; - case Register::StackPointer: registers_.s.full = value; break; - case Register::Flags: set_flags(uint8_t(value)); break; - case Register::A: registers_.a.full = value; break; - case Register::X: registers_.x.full = value; break; - case Register::Y: registers_.y.full = value; break; - case Register::EmulationFlag: set_emulation_mode(value); break; - case Register::DataBank: registers_.data_bank = uint32_t(value) << 16; break; - case Register::ProgramBank: registers_.program_bank = uint32_t(value) << 16; break; - case Register::Direct: registers_.direct = value; break; + case Register::ProgramCounter: registers_.pc = value; break; + case Register::StackPointer: registers_.s.full = value; break; + case Register::Flags: set_flags(uint8_t(value)); break; + case Register::A: registers_.a.full = value; break; + case Register::X: registers_.x.full = value; break; + case Register::Y: registers_.y.full = value; break; + case Register::EmulationFlag: set_emulation_mode(value); break; + case Register::DataBank: registers_.data_bank = uint32_t(value & 0xff) << 16; break; + case Register::ProgramBank: registers_.program_bank = uint32_t(value &0xff) << 16; break; + case Register::Direct: registers_.direct = value; break; default: break; } } From 5dcf720bb57a22e97a0f101583c980758fc0c95d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 21:35:01 -0400 Subject: [PATCH 137/150] Extends list of BusOperations. Now to retest, widely. --- Machines/Atari/2600/Cartridges/Cartridge.hpp | 2 +- Processors/6502Esque/6502Esque.hpp | 43 ++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Machines/Atari/2600/Cartridges/Cartridge.hpp b/Machines/Atari/2600/Cartridges/Cartridge.hpp index 60ffafd7b..90706fb04 100644 --- a/Machines/Atari/2600/Cartridges/Cartridge.hpp +++ b/Machines/Atari/2600/Cartridges/Cartridge.hpp @@ -75,7 +75,7 @@ template class Cartridge: cycles_since_6532_update_ += Cycles(cycles_run_for / 3); bus_extender_.advance_cycles(cycles_run_for / 3); - if(operation != CPU::MOS6502::BusOperation::Ready) { + if(isAccessOperation(operation)) { // give the cartridge a chance to respond to the bus access bus_extender_.perform_bus_operation(operation, address, value); diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index 2e6f5a929..e9fd9d9ee 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -36,7 +36,7 @@ enum Register { X, Y, - // These exist on the 65816 only. + // These exist on a 65816 only. EmulationFlag, DataBank, ProgramBank, @@ -64,24 +64,53 @@ enum Flag: uint8_t { /*! Bus handlers will be given the task of performing bus operations, allowing them to provide whatever interface they like - between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested. - - @c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the - isReadOperation macro to make a binary choice between reading and writing. + between a 6502-esque chip and the rest of the system. @c BusOperation lists the types of bus operation that may be requested. */ enum BusOperation { + /// 6502: indicates that a read was signalled. + /// 65816: indicates that a read was signalled with VDA. Read, + /// 6502: indicates that a read was signalled with SYNC. + /// 65816: indicates that a read was signalled with VDA and VPA. ReadOpcode, + /// 6502: never signalled. + /// 65816: indicates that a read was signalled with VPA. + ReadProgram, + /// 6502: never signalled. + /// 65816: indicates that a read was signalled with VPB. + ReadVector, + + /// 6502: indicates that a write was signalled. + /// 65816: indicates that a write was signalled with VDA. Write, + + /// All processors: indicates that the processor is holding state due to the RDY input. + /// 65C02 and 65816: indicates a WAI is ongoing. Ready, - None + + /// 6502: never signalled. + /// 65816: indicates that a read was signalled, but neither VDA or VPA were active. + InternalOperation, + + /// 65C02 and 65816: indicates a STP condition. + None, }; /*! - Evaluates to `true` if the operation is a read; `false` if it is a write or ready. + Evaluates to @c true if the operation is any sort of read; @c false otherwise. */ #define isReadOperation(v) (v < CPU::MOS6502Esque::BusOperation::Write) +/*! + Evaluates to @c true if the operation is any sort of write; @c false otherwise. +*/ +#define isWriteOperation(v) (v == CPU::MOS6502Esque::BusOperation::Write) + +/*! + Evaluates to @c true if the operation is any sort of memory access; @c false otherwise. +*/ +#define isAccessOperation(v) (v < CPU::MOS6502Esque::BusOperation::Ready) + /*! A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus, machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus From 98c81749c8bd622f17fb6e762aec1ebf76090e5e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 21:36:04 -0400 Subject: [PATCH 138/150] Adds the conventional `flush`. --- Processors/65816/Implementation/65816Implementation.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 34ed2189f..f2810cc7e 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -928,6 +928,7 @@ template void Processor Date: Thu, 15 Oct 2020 21:37:37 -0400 Subject: [PATCH 139/150] Reports ::Ready upon a WAI. --- 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 f2810cc7e..10df709d6 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -185,7 +185,7 @@ template void Processor Date: Fri, 16 Oct 2020 21:05:42 -0400 Subject: [PATCH 140/150] Exposes non-BusOperation bus outputs. --- Processors/65816/65816.hpp | 18 +++++++++++++++++- .../Implementation/65816Implementation.hpp | 17 +++++++++++++++++ .../65816/Implementation/65816Storage.cpp | 2 ++ .../65816/Implementation/65816Storage.hpp | 4 ++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index 3c168ac80..503b65e44 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -25,6 +25,13 @@ using BusOperation = CPU::MOS6502Esque::BusOperation; using Register = CPU::MOS6502Esque::Register; using Flag = CPU::MOS6502Esque::Flag; +enum ExtendedBusOutput { + Emulation = (1 << 0), + MemorySize = (1 << 1), + IndexSize = (1 << 2), + MemoryLock = (1 << 3), +}; + #include "Implementation/65816Storage.hpp" class ProcessorBase: protected ProcessorStorage { @@ -35,9 +42,18 @@ class ProcessorBase: protected ProcessorStorage { inline void set_reset_line(bool); inline void set_abort_line(bool); inline bool get_is_resetting() const; - void set_value_of_register(Register r, uint16_t value); + /*! + Returns the current state of all lines not ordinarily pushed to the BusHandler. + */ + inline int get_extended_bus_output(); + + /*! + Provided for symmetry with the 6502; a 65816 is never jammed. + */ inline bool is_jammed() const; + + 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 10df709d6..c536ea818 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -55,6 +55,7 @@ template void Processor void Processor &target) { + target(OperationSetMemoryLock); + if(!is8bit) target(CycleFetchIncrementData); // Data low. target(CycleFetchData); // Data [high]. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 0e7eba2b9..4bc731372 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -149,6 +149,9 @@ enum MicroOp: uint8_t { /// address register. OperationPrepareException, + /// Sets the memory lock output for the rest of this instruction. + OperationSetMemoryLock, + /// Complete this set of micr-ops. OperationMoveToNextProgram, @@ -289,6 +292,7 @@ struct ProcessorStorage { int selected_exceptions_ = 0; bool ready_line_ = false; + bool memory_lock_ = false; // Just to be safe. static_assert(PowerOn != IRQ); From 3b398f7a9a8f660513b2aa339f7a29ffba94943b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 16 Oct 2020 21:56:20 -0400 Subject: [PATCH 141/150] Attempts to complete all 65816 bus signalling. --- Processors/6502Esque/6502Esque.hpp | 24 +++++++------- .../Implementation/65816Implementation.hpp | 33 ++++++++++++------- .../65816/Implementation/65816Storage.cpp | 25 +++++++------- .../65816/Implementation/65816Storage.hpp | 4 +++ 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index e9fd9d9ee..900ac8c6a 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -79,37 +79,39 @@ enum BusOperation { /// 6502: never signalled. /// 65816: indicates that a read was signalled with VPB. ReadVector, + /// 6502: never signalled. + /// 65816: indicates that a read was signalled, but neither VDA nor VPA were active. + InternalOperationRead, /// 6502: indicates that a write was signalled. /// 65816: indicates that a write was signalled with VDA. Write, + /// 6502: never signalled. + /// 65816: indicates that a write was signalled, but neither VDA nor VPA were active. + InternalOperationWrite, - /// All processors: indicates that the processor is holding state due to the RDY input. + /// All processors: indicates that the processor is paused due to the RDY input. /// 65C02 and 65816: indicates a WAI is ongoing. Ready, - /// 6502: never signalled. - /// 65816: indicates that a read was signalled, but neither VDA or VPA were active. - InternalOperation, - /// 65C02 and 65816: indicates a STP condition. None, }; /*! - Evaluates to @c true if the operation is any sort of read; @c false otherwise. + For a machine watching only the RWB line, evaluates to @c true if the operation should be treated as a read; @c false otherwise. */ -#define isReadOperation(v) (v < CPU::MOS6502Esque::BusOperation::Write) +#define isReadOperation(v) (v < CPU::MOS6502Esque::Write) /*! - Evaluates to @c true if the operation is any sort of write; @c false otherwise. + For a machine watching only the RWB line, evaluates to @c true if the operation is any sort of write; @c false otherwise. */ -#define isWriteOperation(v) (v == CPU::MOS6502Esque::BusOperation::Write) +#define isWriteOperation(v) (v == CPU::MOS6502Esque::Write || v == CPU::MOS6502Esque::InternalOperationWrite) /*! - Evaluates to @c true if the operation is any sort of memory access; @c false otherwise. + Evaluates to @c true if the operation actually expects a response; @c false otherwise. */ -#define isAccessOperation(v) (v < CPU::MOS6502Esque::BusOperation::Ready) +#define isAccessOperation(v) ((v < CPU::MOS6502Esque::Ready) && (v != CPU::MOS6502Esque::InternalOperationRead) && (v != CPU::MOS6502Esque::InternalOperationWrite)) /*! A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus, diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index c536ea818..157563a20 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -70,22 +70,22 @@ template void Processor void Processor void Processor void Processor void Processor &target) { - target(OperationSetMemoryLock); + target(OperationSetMemoryLock); // Set the memory lock output until the end of this instruction. if(!is8bit) target(CycleFetchIncrementData); // Data low. target(CycleFetchData); // Data [high]. - // TODO: does this look like another read? Or if VDA and VPA are both low, does the 65816 actually do no access? if(!is8bit) target(CycleFetchDataThrowaway); // 16-bit: reread final byte of data. else target(CycleStoreDataThrowaway); // 8-bit rewrite final byte of data. @@ -182,7 +181,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 1b. Absolute; a, JMP. static void absolute_jmp(AccessType, bool, const std::function &target) { - target(CycleFetchIncrementPC); // New PCL. + target(CycleFetchIncrementPC); // New PCL.] target(CycleFetchPC); // New PCH. target(OperationPerform); // [JMP] } @@ -199,9 +198,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor { // 1d. Absolute; a, read-modify-write. static void absolute_rmw(AccessType, bool is8bit, const std::function &target) { - target(CycleFetchIncrementPC); // AAL. - target(CycleFetchIncrementPC); // AAH. - target(OperationConstructAbsolute); // Calculate data address. + target(CycleFetchIncrementPC); // AAL. + target(CycleFetchIncrementPC); // AAH. + target(OperationConstructAbsolute); // Calculate data address. read_modify_write(is8bit, target); } @@ -382,10 +381,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 &target) { - target(CycleFetchIncrementPC); // DO. + target(CycleFetchIncrementPC); // DO. target(OperationConstructDirect); - target(CycleFetchPCThrowaway); // IO. + target(CycleFetchPCThrowaway); // IO. read_write(type, is8bit, target); } @@ -594,9 +593,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePush); // PCL target(CyclePush); // P - // TODO: I think I need a seperate vector fetch here, to signal vector pull? - target(CycleFetchIncrementData); // AAVL - target(CycleFetchData); // AAVH + target(CycleFetchIncrementVector); // AAVL + target(CycleFetchVector); // AAVH target(OperationPerform); // Jumps to the vector address. } @@ -704,9 +702,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { target(CyclePush); // PCL target(CyclePush); // P - // TODO: I think I need a seperate vector fetch here, to signal vector pull? - target(CycleFetchIncrementData); // AAVL - target(CycleFetchData); // AAVH + target(CycleFetchIncrementVector); // AAVL + target(CycleFetchVector); // AAVH target(OperationPerform); // Jumps to the vector address. } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 4bc731372..4edf9cf76 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -25,6 +25,10 @@ enum MicroOp: uint8_t { CycleFetchIncorrectDataAddress, /// Fetches a byte from the data address and throws it away. CycleFetchDataThrowaway, + /// Fetches a byte from the data address to the data buffer, signalling VPB . + CycleFetchVector, + /// Fetches a byte from the data address to the data buffer and increments the data address, signalling VPB. + CycleFetchIncrementVector, // Dedicated block-move cycles; these use the data buffer as an intermediary. CycleFetchBlockX, From e5f57ea7437724d6e6f996a134b3ab8c77040e5a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Oct 2020 22:27:04 -0400 Subject: [PATCH 142/150] Make `isReadOperation` more overt. --- Processors/6502Esque/6502Esque.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index 900ac8c6a..3ef3ba16f 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -101,7 +101,7 @@ enum BusOperation { /*! For a machine watching only the RWB line, evaluates to @c true if the operation should be treated as a read; @c false otherwise. */ -#define isReadOperation(v) (v < CPU::MOS6502Esque::Write) +#define isReadOperation(v) (v <= CPU::MOS6502Esque::InternalOperationRead) /*! For a machine watching only the RWB line, evaluates to @c true if the operation is any sort of write; @c false otherwise. From 42228ea955eef04ca61c10ccb806950a29a9921d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Oct 2020 22:31:32 -0400 Subject: [PATCH 143/150] Adds 65C02As6502 test, to round out the set. --- OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index a4d577473..ebd464b3a 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -124,6 +124,11 @@ class KlausDormannTests: XCTestCase { runTest6502(processor: .processor6502) } + /// Runs Klaus Dormann's standard 6502 tests on a 65C02. + func test65C02As6502() { + runTest6502(processor: .processor65C02) + } + /// Runs Klaus Dormann's standard 6502 tests on a 65816. func test65816As6502() { runTest6502(processor: .processor65816) From c3187fdbe1ddc19838cbbec6d9ff1adbaf0c053f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Oct 2020 22:31:51 -0400 Subject: [PATCH 144/150] Makes minor formatting improvement. --- OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift b/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift index 31dbe7d17..c40a2729a 100644 --- a/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift @@ -10,6 +10,7 @@ import XCTest import Foundation class Jeek816Tests: XCTestCase { + func testJeek816() { var machine: CSTestMachine6502! @@ -39,4 +40,5 @@ class Jeek816Tests: XCTestCase { NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Failed tests with bitmap: \(String(format:"%02x", machine.value(forAddress: 0x401)))", userInfo: nil).raise() } } + } From 69509f6502329a04bae999366ffe130399694919 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Oct 2020 22:42:54 -0400 Subject: [PATCH 145/150] Attempts to bring a little more consistency to my use of Swift in test code. --- .../6502InterruptTests.swift | 29 +++++++------- .../Clock SignalTests/6502TimingTests.swift | 38 +++++++++---------- .../Mac/Clock SignalTests/6532Tests.swift | 2 +- .../65816AddressingTests.swift | 1 + .../Clock SignalTests/AllSuiteATests.swift | 1 + .../Mac/Clock SignalTests/BCDTest.swift | 3 +- .../Mac/Clock SignalTests/C1540Tests.swift | 15 ++++---- .../Clock SignalTests/KlausDormannTests.swift | 6 +-- .../Clock SignalTests/PatrikRakTests.swift | 5 ++- .../Z80MachineCycleTests.swift | 1 + .../Clock SignalTests/Z80MemptrTests.swift | 1 + .../Mac/Clock SignalTests/ZexallTests.swift | 5 ++- 12 files changed, 58 insertions(+), 49 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift b/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift index fdcc41ea4..5adede92f 100644 --- a/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift +++ b/OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift @@ -14,38 +14,38 @@ class MOS6502InterruptTests: XCTestCase { override func setUp() { super.setUp() - // create a machine full of NOPs + // Create a machine full of NOPs. machine = CSTestMachine6502(processor: .processor6502) for c in 0...65535 { machine.setValue(0xea, forAddress: UInt32(c)) } - // set the IRQ vector to be 0x1234 + // Set the IRQ vector to 0x1234. machine.setValue(0x34, forAddress: 0xfffe) machine.setValue(0x12, forAddress: 0xffff) - // add a CLI + // Add a CLI. machine.setValue(0x58, forAddress: 0x4000) - // pick things off at 0x4000 - machine.setValue(0x4000, for: CSTestMachine6502Register.programCounter) + // Begin at 0x4000. + machine.setValue(0x4000, for: .programCounter) } func testIRQLine() { - // run for six cycles; check that no interrupt has occurred + // Run for six cycles; check that no interrupt has occurred. machine.runForNumber(ofCycles: 6) - XCTAssert(machine.value(for: .programCounter) == 0x4003, "No interrupt should have occurred with line low") + XCTAssertEqual(machine.value(for: .programCounter), 0x4003, "No interrupt should have occurred with line low") // enable the interrupt line, check that it was too late machine.irqLine = true machine.runForNumber(ofCycles: 2) - XCTAssert(machine.value(for: .programCounter) == 0x4004, "No interrupt should have occurred from interrupt raised between instructions") + XCTAssertEqual(machine.value(for: .programCounter), 0x4004, "No interrupt should have occurred from interrupt raised between instructions") // run for a further 7 cycles, confirm that the IRQ vector was jumped to machine.runForNumber(ofCycles: 6) - XCTAssert(machine.value(for: .programCounter) != 0x1234, "Interrupt routine should not yet have begun") + XCTAssertNotEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should not yet have begun") machine.runForNumber(ofCycles: 1) - XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun") + XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun") } func testIFlagSet() { @@ -53,8 +53,8 @@ class MOS6502InterruptTests: XCTestCase { machine.irqLine = true machine.runForNumber(ofCycles: 11) - XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun") - XCTAssert(machine.value(for: .flags) & 0x04 == 0x04, "Interrupt status flag should be set") + XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun") + XCTAssertEqual(machine.value(for: .flags) & 0x04, 0x04, "Interrupt status flag should be set") } func testCLISEIFlagClear() { @@ -64,12 +64,13 @@ class MOS6502InterruptTests: XCTestCase { // run for four cycles; the CLI and SEI should have been performed machine.runForNumber(ofCycles: 4) - XCTAssert(machine.value(for: .programCounter) == 0x4002, "CLI/SEI pair should have been performed in their entirety") + XCTAssertEqual(machine.value(for: .programCounter), 0x4002, "CLI/SEI pair should have been performed in their entirety") // run for seven more cycles machine.runForNumber(ofCycles: 7) // interrupt should have taken place despite SEI - XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun") + XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun") } + } diff --git a/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift b/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift index 3ca39bc35..10ff300b9 100644 --- a/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/6502TimingTests.swift @@ -22,7 +22,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x18, // [2] CLC 0x2a, // [2] ROL A ] - self.runTest(code, expectedRunLength: 10) + runTest(code, expectedRunLength: 10) } func testLDA() { @@ -40,7 +40,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0xb1, 0x00, // [5] LDA ($00), y (no wrap) 0xb1, 0x02, // [6] LDA ($01), y (wrap) ] - self.runTest(code, expectedRunLength: 48) + runTest(code, expectedRunLength: 48) } func testBIT() { @@ -48,7 +48,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x24, 0x2a, // [3] BIT $2a 0x2c, 0x2a, 0x2b, // [4] BIT $2b2a ] - self.runTest(code, expectedRunLength: 7) + runTest(code, expectedRunLength: 7) } func testSTA() { @@ -64,7 +64,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x91, 0x00, // [6] STA ($00), y (no wrap) 0x91, 0x02, // [6] STA ($01), y (wrap) ] - self.runTest(code, expectedRunLength: 49) + runTest(code, expectedRunLength: 49) } func testINC() { @@ -75,7 +75,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0xfe, 0x00, 0x00, // [7] INC $0000, x (no wrap) 0xfe, 0x02, 0x00, // [7] INC $0002, x (wrap) ] - self.runTest(code, expectedRunLength: 31) + runTest(code, expectedRunLength: 31) } func testJSR() { @@ -85,7 +85,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x60, // [6] RTS ] machine.addTrapAddress(0x0203) - self.runTest(code, expectedRunLength: 12) + runTest(code, expectedRunLength: 12) } func testJMP() { @@ -94,7 +94,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0b, 0x02, // [3] JMP 020b ] - self.runTest(code, expectedRunLength: 8) + runTest(code, expectedRunLength: 8) } func testPHAPLA() { @@ -103,7 +103,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x48, // [3] PHA 0x68, // [4] PLA ] - self.runTest(code, expectedRunLength: 10) + runTest(code, expectedRunLength: 10) } func testBCS() { @@ -130,7 +130,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] - self.runTest(code, expectedRunLength: 14) + runTest(code, expectedRunLength: 14) } func testSnippet1() { @@ -138,7 +138,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x8d, 0x08, 0x00, // [4] STA $0008 0xc6, 0xb4, // [5] DEC $B4 ] - self.runTest(code, expectedRunLength: 9) + runTest(code, expectedRunLength: 9) } func testSnippet2() { @@ -146,7 +146,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x16, 0x16, // [6] ASL $16, x 0x46, 0x46, // [5] LSR $46 ] - self.runTest(code, expectedRunLength: 11) + runTest(code, expectedRunLength: 11) } func testSnippet3() { @@ -174,7 +174,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0x60, // [6] RTS ] machine.addTrapAddress(0x0203) - self.runTest(code, expectedRunLength: 66) + runTest(code, expectedRunLength: 66) } func testNOP() { @@ -194,10 +194,10 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { 0xe2, 0x00, // [2] NOP # 0xf4, 0x00, // [4] NOP zpg, x ] - self.runTest(code, expectedRunLength: 43) + runTest(code, expectedRunLength: 43) } - func runTest(_ code: [UInt8], expectedRunLength: UInt32) { + private func runTest(_ code: [UInt8], expectedRunLength: UInt32) { machine.trapHandler = self let immediateCode = Data(code) @@ -213,17 +213,17 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { machine.setValue(0xff, for: CSTestMachine6502Register.X) machine.setValue(0xfe, for: CSTestMachine6502Register.Y) - self.endTime = 0 - while self.endTime == 0 { + endTime = 0 + while endTime == 0 { machine.runForNumber(ofCycles: 10) } - XCTAssert(self.endTime == expectedRunLength, "Took \(self.endTime) cycles to perform rather than \(expectedRunLength)") + XCTAssertEqual(endTime, expectedRunLength, "Took \(endTime) cycles to perform rather than \(expectedRunLength)") } func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) { - if self.endTime == 0 { - self.endTime = (machine.timestamp / 2) - 1 + if endTime == 0 { + endTime = (machine.timestamp / 2) - 1 } } } diff --git a/OSBindings/Mac/Clock SignalTests/6532Tests.swift b/OSBindings/Mac/Clock SignalTests/6532Tests.swift index f599a25f5..f892372ca 100644 --- a/OSBindings/Mac/Clock SignalTests/6532Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6532Tests.swift @@ -11,7 +11,7 @@ import Foundation class MOS6532Tests: XCTestCase { - fileprivate func with6532(_ action: (MOS6532Bridge) -> ()) { + private func with6532(_ action: (MOS6532Bridge) -> ()) { let bridge = MOS6532Bridge() action(bridge) } diff --git a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift index 7fe5feec9..6c9bcd669 100644 --- a/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift +++ b/OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift @@ -542,4 +542,5 @@ class WDC65816AddressingTests: XCTestCase { XCTAssertEqual(machine.value(for: .A), 0xcdab) } + } diff --git a/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift b/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift index a058a039e..12076e958 100644 --- a/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift +++ b/OSBindings/Mac/Clock SignalTests/AllSuiteATests.swift @@ -27,4 +27,5 @@ class AllSuiteATests: XCTestCase { } } } + } diff --git a/OSBindings/Mac/Clock SignalTests/BCDTest.swift b/OSBindings/Mac/Clock SignalTests/BCDTest.swift index 72a7025ee..235d201dd 100644 --- a/OSBindings/Mac/Clock SignalTests/BCDTest.swift +++ b/OSBindings/Mac/Clock SignalTests/BCDTest.swift @@ -40,7 +40,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler { } } - fileprivate var output: String = "" + private var output: String = "" func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) { let machine6502 = testMachine as! CSTestMachine6502 @@ -48,4 +48,5 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler { let character = machine6502.value(for: .A) output.append(Character(UnicodeScalar(character)!)) } + } diff --git a/OSBindings/Mac/Clock SignalTests/C1540Tests.swift b/OSBindings/Mac/Clock SignalTests/C1540Tests.swift index 5ce0246a2..7b8f4f39a 100644 --- a/OSBindings/Mac/Clock SignalTests/C1540Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/C1540Tests.swift @@ -10,21 +10,21 @@ import XCTest class C1540Tests: XCTestCase { - fileprivate func with1540(_ action: (C1540Bridge) -> ()) { + private func with1540(_ action: (C1540Bridge) -> ()) { let bridge = C1540Bridge() action(bridge) } - fileprivate func transmit(_ c1540: C1540Bridge, value: Int) { + private func transmit(_ c1540: C1540Bridge, value: Int) { var shiftedValue = value c1540.dataLine = true c1540.run(forCycles: 256) - XCTAssert(c1540.dataLine == false, "Listener should have taken data line low for start of transmission") + XCTAssertFalse(c1540.dataLine, "Listener should have taken data line low for start of transmission") c1540.clockLine = true c1540.run(forCycles: 256) // this isn't time limited on real hardware - XCTAssert(c1540.dataLine == true, "Listener should have let data line go high again") + XCTAssertTrue(c1540.dataLine, "Listener should have let data line go high again") // set up for byte transfer c1540.clockLine = false @@ -47,7 +47,7 @@ class C1540Tests: XCTestCase { // check for acknowledgment c1540.dataLine = true c1540.run(forCycles: 1000) - XCTAssert(c1540.dataLine == false, "Listener should have acknowledged byte") + XCTAssertFalse(c1540.dataLine, "Listener should have acknowledged byte") } // MARK: EOI @@ -64,10 +64,11 @@ class C1540Tests: XCTestCase { // proceed 1 ms and check that the 1540 pulled the data line low $0.run(forCycles: 1000) - XCTAssert($0.dataLine == false, "Listener should have taken data line low") + XCTAssertFalse($0.dataLine, "Listener should have taken data line low") // transmit LISTEN #8 - self.transmit($0, value: 0x28) + transmit($0, value: 0x28) } } + } diff --git a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift index ebd464b3a..68c4c1642 100644 --- a/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift +++ b/OSBindings/Mac/Clock SignalTests/KlausDormannTests.swift @@ -11,7 +11,7 @@ import XCTest class KlausDormannTests: XCTestCase { - fileprivate func runTest(resource: String, processor: CSTestMachine6502Processor) -> UInt16 { + private func runTest(resource: String, processor: CSTestMachine6502Processor) -> UInt16 { if let filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") { if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) { let machine = CSTestMachine6502(processor: processor) @@ -39,7 +39,7 @@ class KlausDormannTests: XCTestCase { return 0 } - func runTest6502(processor: CSTestMachine6502Processor) { + private func runTest6502(processor: CSTestMachine6502Processor) { func errorForTrapAddress(_ address: UInt16) -> String? { switch address { case 0x3399: return nil // success! @@ -70,7 +70,7 @@ class KlausDormannTests: XCTestCase { XCTAssert(error == nil, "Failed with error \(error!)") } - func runTest65C02(processor: CSTestMachine6502Processor) { + private func runTest65C02(processor: CSTestMachine6502Processor) { func errorForTrapAddress(_ address: UInt16) -> String? { switch address { case 0x24f1: return nil // success! diff --git a/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift b/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift index 0328d23eb..e15dfba9a 100644 --- a/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift +++ b/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift @@ -11,8 +11,8 @@ import Foundation class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler { - fileprivate var done = false - fileprivate var output = "" + private var done = false + private var output = "" private func runTest(_ name: String) { if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "tap") { @@ -124,4 +124,5 @@ class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler { break } } + } diff --git a/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift b/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift index e719e977c..42c5e6fb0 100644 --- a/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift +++ b/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift @@ -1247,4 +1247,5 @@ class Z80MachineCycleTests: XCTestCase { ] ) } + } diff --git a/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift b/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift index 0a8bd65e4..2f9a460cf 100644 --- a/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift +++ b/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift @@ -9,6 +9,7 @@ import XCTest class Z80MemptrTester: XCTestCase { + let machine = CSTestMachineZ80() private func test(program : [UInt8], initialValue : UInt16) -> UInt16 { diff --git a/OSBindings/Mac/Clock SignalTests/ZexallTests.swift b/OSBindings/Mac/Clock SignalTests/ZexallTests.swift index 714a571bb..c2a3208ac 100644 --- a/OSBindings/Mac/Clock SignalTests/ZexallTests.swift +++ b/OSBindings/Mac/Clock SignalTests/ZexallTests.swift @@ -11,8 +11,8 @@ import Foundation class ZexallTests: XCTestCase, CSTestMachineTrapHandler { - fileprivate var done = false - fileprivate var output = "" + private var done = false + private var output = "" private func runTest(_ name: String) { if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "com") { @@ -177,4 +177,5 @@ class ZexallTests: XCTestCase, CSTestMachineTrapHandler { break } } + } From 99eba2f8ba424285e95827abfd3c8542b2451129 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Oct 2020 14:43:47 -0400 Subject: [PATCH 146/150] Ensures intended 65816 exception behaviour. i.e. the relevant micro-op sequence exists, and its operation isn't lost. Also sets the 65816 by default to jump straight into power-on, not to execute an instruction first. That shouldn't make a functional difference, but it makes debugging easier because it makes startup fully deterministic. --- .../xcschemes/Clock Signal.xcscheme | 5 - .../Implementation/65816Implementation.hpp | 7 +- .../65816/Implementation/65816Storage.cpp | 107 ++++++++++-------- .../65816/Implementation/65816Storage.hpp | 6 +- 4 files changed, 68 insertions(+), 57 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 610481df8..01069c685 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -48,11 +48,6 @@ BlueprintName = "Clock SignalTests" ReferencedContainer = "container:Clock Signal.xcodeproj"> - - - - diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 157563a20..5e888c359 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -50,8 +50,10 @@ template void Processorprogram_offsets[0]]; instruction_buffer_.clear(); data_buffer_.clear(); last_operation_pc_ = registers_.pc; @@ -954,6 +956,7 @@ void ProcessorBase::set_power_on(bool active) { pending_exceptions_ |= PowerOn; } else { pending_exceptions_ &= ~PowerOn; + selected_exceptions_ &= ~PowerOn; } } diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 4aa494b50..f254806f9 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -43,7 +43,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor { typedef void (* Generator)(AccessType, bool, const std::function&); using GeneratorKey = std::tuple; - std::map> installed_patterns; + using PatternTable = std::map>; + PatternTable installed_patterns; int opcode = 0; enum class AccessMode { @@ -51,55 +52,15 @@ struct CPU::WDC65816::ProcessorStorageConstructor { Always8Bit, Always16Bit }; + void install(Generator generator, Operation operation, AccessMode access_mode = AccessMode::Mixed) { // Determine the access type implied by this operation. const AccessType access_type = access_type_for_operation(operation); - // Check whether this access type + addressing mode generator has already been generated. - const auto key = std::make_pair(access_type, generator); - const auto map_entry = installed_patterns.find(key); - size_t micro_op_location_8, micro_op_location_16; - - // If it wasn't found, generate it now in both 8- and 16-bit variants. - // Otherwise, get the location of the existing entries. - if(map_entry == installed_patterns.end()) { - // Generate 8-bit steps. - micro_op_location_8 = storage_.micro_ops_.size(); - (*generator)(access_type, true, [this] (MicroOp op) { - this->storage_.micro_ops_.push_back(op); - }); - storage_.micro_ops_.push_back(OperationMoveToNextProgram); - - // Generate 16-bit steps. - micro_op_location_16 = storage_.micro_ops_.size(); - (*generator)(access_type, false, [this] (MicroOp op) { - this->storage_.micro_ops_.push_back(op); - }); - storage_.micro_ops_.push_back(OperationMoveToNextProgram); - - // Minor optimisation: elide the steps if 8- and 16-bit steps are equal. - bool are_equal = true; - size_t c = 0; - while(true) { - if(storage_.micro_ops_[micro_op_location_8 + c] != storage_.micro_ops_[micro_op_location_16 + c]) { - are_equal = false; - break; - } - if(storage_.micro_ops_[micro_op_location_8 + c] == OperationMoveToNextProgram) break; - ++c; - } - - if(are_equal) { - storage_.micro_ops_.resize(micro_op_location_16); - micro_op_location_16 = micro_op_location_8; - } - - // Insert into the map. - installed_patterns[key] = std::make_pair(micro_op_location_8, micro_op_location_16); - } else { - micro_op_location_8 = map_entry->second.first; - micro_op_location_16 = map_entry->second.second; - } + // Install the bus pattern. + const auto map_entry = install(generator, access_type); + const size_t micro_op_location_8 = map_entry->second.first; + const size_t micro_op_location_16 = map_entry->second.second; // Fill in the proper table entries and increment the opcode pointer. storage_.instructions[opcode].program_offsets[0] = (access_mode == AccessMode::Always8Bit) ? uint16_t(micro_op_location_8) : uint16_t(micro_op_location_16); @@ -110,8 +71,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } void set_exception_generator(Generator generator) { - const auto key = std::make_pair(AccessType::Read, generator); - const auto map_entry = installed_patterns.find(key); + const auto map_entry = install(generator); storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[0] = storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[1] = uint16_t(map_entry->second.first); storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].operation = JMPind; @@ -124,6 +84,57 @@ struct CPU::WDC65816::ProcessorStorageConstructor { storage_.micro_ops_.push_back(OperationDecode); } + private: + + PatternTable::iterator install(Generator generator, AccessType access_type = AccessType::Read) { + // Check whether this access type + addressing mode generator has already been generated. + const auto key = std::make_pair(access_type, generator); + const auto map_entry = installed_patterns.find(key); + + // If it wasn't found, generate it now in both 8- and 16-bit variants. + // Otherwise, get the location of the existing entries. + if(map_entry != installed_patterns.end()) { + return map_entry; + } + + // Generate 8-bit steps. + const size_t micro_op_location_8 = storage_.micro_ops_.size(); + (*generator)(access_type, true, [this] (MicroOp op) { + this->storage_.micro_ops_.push_back(op); + }); + storage_.micro_ops_.push_back(OperationMoveToNextProgram); + + // Generate 16-bit steps. + size_t micro_op_location_16 = storage_.micro_ops_.size(); + (*generator)(access_type, false, [this] (MicroOp op) { + this->storage_.micro_ops_.push_back(op); + }); + storage_.micro_ops_.push_back(OperationMoveToNextProgram); + + // Minor optimisation: elide the steps if 8- and 16-bit steps are equal. + bool are_equal = true; + size_t c = 0; + while(true) { + if(storage_.micro_ops_[micro_op_location_8 + c] != storage_.micro_ops_[micro_op_location_16 + c]) { + are_equal = false; + break; + } + if(storage_.micro_ops_[micro_op_location_8 + c] == OperationMoveToNextProgram) break; + ++c; + } + + if(are_equal) { + storage_.micro_ops_.resize(micro_op_location_16); + micro_op_location_16 = micro_op_location_8; + } + + // Insert into the map. + auto [iterator, _] = installed_patterns.insert(std::make_pair(key, std::make_pair(micro_op_location_8, micro_op_location_16))); + return iterator; + } + + public: + /* Code below is structured to ease translation from Table 5-7 of the 2018 edition of the WDC 65816 datasheet. diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 4edf9cf76..3dc0c55a4 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -292,8 +292,10 @@ struct ProcessorStorage { static constexpr int IRQ = Flag::Interrupt; // This makes masking a lot easier later on; this is 1 << 2. static constexpr int NMI = 1 << 3; static constexpr int Abort = 1 << 4; - int pending_exceptions_ = PowerOn; // By default. - int selected_exceptions_ = 0; + + static constexpr int default_exceptions = PowerOn; + int pending_exceptions_ = default_exceptions; + int selected_exceptions_ = default_exceptions; bool ready_line_ = false; bool memory_lock_ = false; From c3f8982c6295fea35c63101b8f2865ba7a66141c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Oct 2020 14:55:17 -0400 Subject: [PATCH 147/150] Resolves all internal implicit type-conversion warnings. Chasing those down, it looks like flags were wrong for PLB and PLD. So it's official: warnings help. --- .../Implementation/65816Implementation.hpp | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 5e888c359..3d84bcd9e 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -432,7 +432,7 @@ template void Processor void Processor void Processor void Processor void Processor void Processor void Processor void Processor void Processor> (7 + registers_.m_shift); + registers_.flags.carry = uint8_t(data_buffer_.value >> (7 + registers_.m_shift)); data_buffer_.value <<= 1; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + registers_.flags.set_nz(uint16_t(data_buffer_.value), registers_.m_shift); break; case LSR: - registers_.flags.carry = data_buffer_.value & 1; + registers_.flags.carry = uint8_t(data_buffer_.value & 1); data_buffer_.value >>= 1; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + registers_.flags.set_nz(uint16_t(data_buffer_.value), registers_.m_shift); break; case ROL: data_buffer_.value = (data_buffer_.value << 1) | registers_.flags.carry; - registers_.flags.carry = data_buffer_.value >> (8 + registers_.m_shift); - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + registers_.flags.carry = uint8_t(data_buffer_.value >> (8 + registers_.m_shift)); + registers_.flags.set_nz(uint16_t(data_buffer_.value), registers_.m_shift); break; case ROR: { const uint8_t next_carry = data_buffer_.value & 1; - data_buffer_.value = (data_buffer_.value >> 1) | (registers_.flags.carry << (7 + registers_.m_shift)); + data_buffer_.value = (data_buffer_.value >> 1) | (uint32_t(registers_.flags.carry) << (7 + registers_.m_shift)); registers_.flags.carry = next_carry; - registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); + registers_.flags.set_nz(uint16_t(data_buffer_.value), registers_.m_shift); } break; // @@ -872,7 +872,7 @@ template void Processor> (1 + registers_.m_shift))&0x40; - registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.set_nz(uint16_t(result), registers_.m_shift); registers_.flags.carry = ((borrow >> 16)&1)^1; LD(registers_.a, result, registers_.m_masks); @@ -901,11 +901,11 @@ template void Processor> (1 + registers_.m_shift))&0x40; - registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.overflow = (( (uint16_t(result) ^ registers_.a.full) & (uint16_t(result) ^ 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; LD(registers_.a, result, registers_.m_masks); } break; From 76d98938664063f9ae057f5dcaa0057e1a7bdd47 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Oct 2020 15:08:21 -0400 Subject: [PATCH 148/150] Declares address-bus sizes formally. This allows me to fix the final two implicit conversion warnings, albeit that it would have been nice to find a templatey way just to get the type directly from the declaration of `perform_bus_operation`. --- Processors/6502Esque/6502Esque.hpp | 6 ++++-- Processors/65816/Implementation/65816Implementation.hpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp index 3ef3ba16f..953333b44 100644 --- a/Processors/6502Esque/6502Esque.hpp +++ b/Processors/6502Esque/6502Esque.hpp @@ -118,8 +118,10 @@ enum BusOperation { machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus handler. */ -template class BusHandler { +template class BusHandler { public: + using AddressType = addr_t; + /*! Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502, all bus cycles take one clock cycle so the amoutn of time advanced is implicit. @@ -136,7 +138,7 @@ template class BusHandler { during some periods; one way to simulate that is to have the bus handler return a number other than Cycles(1) to describe lengthened bus cycles. */ - Cycles perform_bus_operation([[maybe_unused]] BusOperation operation, [[maybe_unused]] AddressType address, [[maybe_unused]] uint8_t *value) { + Cycles perform_bus_operation([[maybe_unused]] BusOperation operation, [[maybe_unused]] addr_t address, [[maybe_unused]] uint8_t *value) { return Cycles(1); } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 3d84bcd9e..39180736c 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -27,7 +27,7 @@ template void Processor Cycles(0)) { // Wait for ready to be inactive before proceeding. while(uses_ready_line && ready_line_ && number_of_cycles > Cycles(0)) { - number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, &bus_throwaway_); + number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, static_cast(bus_address_), &bus_throwaway_); } // Process for as much time is left and/or until ready is signalled. @@ -934,7 +934,7 @@ template void Processor(bus_address_), bus_value_); } } From 1fa94e1b0802a8e71e85daa558fb65d9dcba8293 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Oct 2020 21:43:08 -0400 Subject: [PATCH 149/150] Adds the 65816 as an in-code option for Oric emulation. This also means it'll be exposed via the SDL build, but that's okay. --- Analyser/Static/Oric/Target.hpp | 8 ++++++++ Machines/Oric/Oric.cpp | 29 ++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/Analyser/Static/Oric/Target.hpp b/Analyser/Static/Oric/Target.hpp index 66aead3dd..74e686dff 100644 --- a/Analyser/Static/Oric/Target.hpp +++ b/Analyser/Static/Oric/Target.hpp @@ -33,8 +33,14 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl; using Speaker = Outputs::Speaker::LowpassSpeaker; @@ -205,7 +206,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { Keyboard &keyboard_; }; -template class ConcreteMachine: +template class ConcreteMachine: public MachineTypes::TimedMachine, public MachineTypes::ScanProducer, public MachineTypes::AudioProducer, @@ -656,7 +657,7 @@ template class Co const uint16_t basic_invisible_ram_top_ = 0xffff; const uint16_t basic_visible_ram_top_ = 0xbfff; - CPU::MOS6502::Processor m6502_; + CPU::MOS6502Esque::Processor m6502_; // RAM and ROM std::vector rom_, disk_rom_; @@ -759,13 +760,23 @@ using namespace Oric; Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMachine::ROMFetcher &rom_fetcher) { auto *const oric_target = dynamic_cast(target_hint); - switch(oric_target->disk_interface) { - default: return new ConcreteMachine(*oric_target, rom_fetcher); - case DiskInterface::Microdisc: return new ConcreteMachine(*oric_target, rom_fetcher); - case DiskInterface::Pravetz: return new ConcreteMachine(*oric_target, rom_fetcher); - case DiskInterface::Jasmin: return new ConcreteMachine(*oric_target, rom_fetcher); - case DiskInterface::BD500: return new ConcreteMachine(*oric_target, rom_fetcher); + +#define DiskInterfaceSwitch(processor) \ + switch(oric_target->disk_interface) { \ + default: return new ConcreteMachine(*oric_target, rom_fetcher); \ + case DiskInterface::Microdisc: return new ConcreteMachine(*oric_target, rom_fetcher); \ + case DiskInterface::Pravetz: return new ConcreteMachine(*oric_target, rom_fetcher); \ + case DiskInterface::Jasmin: return new ConcreteMachine(*oric_target, rom_fetcher); \ + case DiskInterface::BD500: return new ConcreteMachine(*oric_target, rom_fetcher); \ } + + switch(oric_target->processor) { + case Processor::WDC65816: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::TWDC65816); + case Processor::MOS6502: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::T6502); + } + +#undef DiskInterfaceSwitch + } Machine::~Machine() {} From 0cd08aa79d6e501e39f7553c88e6b001c6463ce6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Oct 2020 21:44:44 -0400 Subject: [PATCH 150/150] Permits the Oric analyser to check CPC-style DSKs. Oric Mist seems to use that format, so some of these now exist out in the wild. --- Analyser/Static/StaticAnalyser.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index 9652c8ae1..35e11e5ce 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -107,7 +107,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Format("dmk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DMK Format("do", result.disks, Disk::DiskImageHolder, TargetPlatform::DiskII) // DO Format("dsd", result.disks, Disk::DiskImageHolder, TargetPlatform::Acorn) // DSD - Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC) + Format( "dsk", + result.disks, + Disk::DiskImageHolder, + TargetPlatform::AmstradCPC | TargetPlatform::Oric) // DSK (Amstrad CPC) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::DiskII) // DSK (Apple II) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk) Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk)