<commit_before>//
//  65816Storage.cpp
//  Clock Signal
//
//  Created by <NAME> on 23/09/2020.
//  Copyright © 2020 <NAME>. All rights reserved.
//

#include "../65816.hpp"

#include <cassert>
#include <functional>
#include <map>

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
	};

	/// Divides memory-accessing instructions by whether they read or write.
	/// Read-modify-writes are documented with completely distinct bus programs,
	/// so there's no real ambiguity there.
	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:
			case SBC:

			case LDA:	case LDX:	case LDY:

			// The access type for everything else is arbitrary; they're
			// not relevantly either read or write.
			default:
			return AccessType::Read;

			case STA:	case STX:	case STY:	case STZ:
			return AccessType::Write;
		}
	}

	/// Indicates which of the memory-accessing instructions take their cue from the current
	/// size of the index registers, rather than 'memory'[/accumulator].
	constexpr static bool operation_is_index_sized(Operation operation) {
		switch(operation) {
			case CPX:	case CPY:
			case LDX:	case LDY:
			case STX:	case STY:
			return true;

			default:
			return false;
		}
		return false;
	}

	typedef void (* Generator)(AccessType, bool, const std::function<void(MicroOp)>&);
	using GeneratorKey = std::tuple<AccessType, Generator>;
	using PatternTable = std::map<GeneratorKey, std::pair<size_t, size_t>>;
	PatternTable installed_patterns;

	int opcode = 0;
	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);

		// 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);
		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;
		storage_.instructions[opcode].size_field = operation_is_index_sized(operation);

		++opcode;
	}

	void set_exception_generator(Generator generator, Generator reset_tail_generator) {
		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;

		const auto reset_tail_entry = install(reset_tail_generator);
		storage_.instructions[size_t(ProcessorStorage::OperationSlot::ResetTail)].program_offsets[0] =
		storage_.instructions[size_t(ProcessorStorage::OperationSlot::ResetTail)].program_offsets[1] = uint16_t(reset_tail_entry->second.first);
		storage_.instructions[size_t(ProcessorStorage::OperationSlot::ResetTail)].operation = JMPind;
	}

	void install_fetch_decode_execute() {
		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);
	}

	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<commit_msg>Resolves one further GCC warning.<commit_after>//
//  65816Storage.cpp
//  Clock Signal
//
//  Created by <NAME> on 23/09/2020.
//  Copyright © 2020 <NAME>. All rights reserved.
//

#include "../65816.hpp"

#include <cassert>
#include <functional>
#include <map>

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
	};

	/// Divides memory-accessing instructions by whether they read or write.
	/// Read-modify-writes are documented with completely distinct bus programs,
	/// so there's no real ambiguity there.
	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:
			case SBC:

			case LDA:	case LDX:	case LDY:

			// The access type for everything else is arbitrary; they're
			// not relevantly either read or write.
			default:
			return AccessType::Read;

			case STA:	case STX:	case STY:	case STZ:
			return AccessType::Write;
		}
	}

	/// Indicates which of the memory-accessing instructions take their cue from the current
	/// size of the index registers, rather than 'memory'[/accumulator].
	constexpr static bool operation_is_index_sized(Operation operation) {
		switch(operation) {
			case CPX:	case CPY:
			case LDX:	case LDY:
			case STX:	case STY:
			return true;

			default:
			return false;
		}
		return false;
	}

	typedef void (* Generator)(AccessType, bool, const std::function<void(MicroOp)>&);
	using GeneratorKey = std::tuple<AccessType, Generator>;
	using PatternTable = std::map<GeneratorKey, std::pair<size_t, size_t>>;
	PatternTable installed_patterns;

	int opcode = 0;
	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);

		// 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);
		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;
		storage_.instructions[opcode].size_field = operation_is_index_sized(operation);

		++opcode;
	}

	void set_exception_generator(Generator generator, Generator reset_tail_generator) {
		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;

		const auto reset_tail_entry = install(reset_tail_generator);
		storage_.instructions[size_t(ProcessorStorage::OperationSlot::ResetTail)].program_offsets[0] =
		storage_.instructions[size_t(ProcessorStorage::OperationSlot::ResetTail)].program_offsets[1] = uint16_t(reset_tail_entry->second.first);
		storage_.instructions[size_t(ProcessorStorage::OperationSlot::ResetTail)].operation = JMPind;
	}

	void install_fetch_decode_execute() {
		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);
	}

	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 and return the resulting iterator.
		return installed_patterns.insert(std::make_pair(key, std::make_pair(micro_op_location_8, micro_op_location_16))).first;
	}

	public:

	/*
		Code below is structured to ease translation from Table 5-7 of the 2018
{'fraction_non_alphanumeric': 0.10640073437369607, 'fraction_numerical': 0.01685721438704832, 'mean_word_length': 5.206110823407561, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1453238', 'n_tokens_mistral': 4207, 'n_tokens_neox': 3707, 'n_words': 871}