/*
 * Author: Pawel Jablonski
 * E-mail: pj@xirx.net
 * WWW: xirx.net
 * GIT: git.xirx.net
 *
 * License: You can use this code however you like
 * but leave information about the original author.
 * Code is free for non-commercial and commercial use.
 */

#include "uromdata.h"

//! Default constructor for creating an empty uROM opcode table
URomData::URomData()
{
	this->data[0].fill(0);
	this->data[1].fill(0);
}

/**
 * Add an instruction to the uROM opcode table
 *
 * @param instruction Instruction to add
 */
void URomData::addInstruction(const Instruction &instruction)
{
	int opcodeWidth = 1;

	// Calculate how many consecutive opcodes the instruction uses
	opcodeWidth *= ((instruction.getArg0() == Instruction::Arg::AB) ? 2 : 1);
	opcodeWidth *= ((instruction.getArg0() == Instruction::Arg::ABXY) ? 4 : 1);

	opcodeWidth *= ((instruction.getArg1() == Instruction::Arg::AB) ? 2 : 1);
	opcodeWidth *= ((instruction.getArg1() == Instruction::Arg::ABXY) ? 4 : 1);

	// Calculate how many steps is used by the instruction
	int stepQuantity = qMin(instruction.getStepList().size(), INSTRUCTION_CYCLE_QUANTITY);

	// Add instruction entrys to the opcode table
	for(int i = 0; i < opcodeWidth; i++)
	{
		for(int s = 0; s < stepQuantity; s++)
		{
			// Calculate the base offset of the entry
			int offsetBase = ((static_cast<int>(s) * INSTRUCTION_QUANTITY) + static_cast<int>(instruction.getOpcode()) + i);
			Step step = instruction.getStepList().at(s);

			// Fill only entries where the carry and the zero flags fit to the selected by instruction
			for(int c = 0; c < 2; c++)
			{
				for(int z = 0; z < 2; z++)
				{
					if(!((instruction.getC() == Instruction::Flag::Any) || (static_cast<int>(instruction.getC()) == c)))
					{
						continue;
					}

					if(!((instruction.getZ() == Instruction::Flag::Any) || (static_cast<int>(instruction.getZ()) == z)))
					{
						continue;
					}

					int offset = (offsetBase + (c << ADDRESS_FLAG_C_POSITION) + (z << ADDRESS_FLAG_Z_POSITION));

					this->data[0][offset] = step.getUCode().getCode0();
					this->data[1][offset] = step.getUCode().getCode1();
				}
			}
		}
	}
}

/**
 * Save a binary microcode to files
 *
 * @param urom0Path Path to the first output file
 * @param urom1Path Path to the second output file
 *
 * @return Status of the save operation
 */
bool URomData::saveFiles(const QString &urom0Path, const QString &urom1Path)
{
	QFile urom0File(urom0Path);

	// Try to save the first output file
	if(urom0File.open(QIODevice::WriteOnly))
	{
		urom0File.write(reinterpret_cast<const char *>(this->data[0].constData()), UROM_SIZE);
		urom0File.close();
	}
	else
	{
		return(false);
	}

	QFile urom1File(urom1Path);

	// Try to save the second output file
	if(urom1File.open(QIODevice::WriteOnly))
	{
		urom1File.write(reinterpret_cast<const char *>(this->data[1].constData()), UROM_SIZE);
		urom1File.close();
	}
	else
	{
		return(false);
	}

	return(true);
}