// Simulator for the RISC-V[ECTOR] mini-ISA
// Copyright (C) 2025 Siddarth Suresh
// Copyright (C) 2025 bdunahu
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see .
#include "id.h"
#include "accessor.h"
#include "instr.h"
#include "instrDTO.h"
#include "logger.h"
#include "response.h"
#include "stage.h"
ID::ID(Stage *stage) : Stage(stage) { this->id = DCDE; }
void ID::split_instr(signed int &raw, unsigned int &type, Mnemonic &m)
{
	unsigned int opcode, opcode_size;
	type = GET_LS_BITS(raw, TYPE_SIZE);
	opcode_size = (type == 0b0) ? R_OPCODE_SIZE : OPCODE_SIZE;
	opcode = GET_MID_BITS(raw, TYPE_SIZE, TYPE_SIZE + opcode_size);
	try {
		m = instr::mnemonic_map.at((opcode << TYPE_SIZE) + type);
	} catch (std::out_of_range const &) {
		m = NOP;
	}
	raw = (unsigned int)raw >> (TYPE_SIZE + opcode_size);
}
Response ID::read_guard(signed int &v)
{
	Response r;
	if (this->is_checked_out(v))
		r = STALLED;
	else {
		r = OK;
		v = this->dereference_register(v);
	}
	return r;
}
void ID::write_guard(signed int &v)
{
	// zero register shouldn't be written.
	if (v != 0) {
		// keep track in the instrDTO for displaying to user and writeback
		// keep track in checked_out so we can still access this information!
		this->checked_out.push_back(v);
		this->curr_instr->set_checked_out(v);
	}
	v = this->dereference_register(v);
}
void ID::advance_helper()
{
	signed int s1, s2, s3;
	Mnemonic m;
	Type t;
	if (curr_instr->get_mnemonic() == NOP)
		this->status = OK;
	else {
		s1 = curr_instr->get_instr_bits();
		get_instr_fields(s1, s2, s3, m, t);
		if (this->status == OK) {
			curr_instr->set_s1(s1);
			curr_instr->set_s2(s2);
			curr_instr->set_s3(s3);
			curr_instr->set_mnemonic(m);
			curr_instr->set_type(t);
		}
	}
}
void ID::get_instr_fields(
	signed int &s1, signed int &s2, signed int &s3, Mnemonic &m, Type &t)
{
	unsigned int type;
	this->split_instr(s1, type, m);
	switch (type) {
	case 0b00:
		t = R;
		this->decode_R_type(s1, s2, s3, m);
		break;
	case 0b01:
		t = I;
		this->decode_I_type(s1, s2, s3, m);
		break;
	case 0b10:
		t = J;
		this->decode_J_type(s1, s2, s3, m);
		break;
	case 0b11:
		t = INV;
		this->status = OK;
	}
}
void ID::decode_R_type(
	signed int &s1, signed int &s2, signed int &s3, Mnemonic &m)
{
	unsigned int s0b, s1b, s2b;
	Response r1, r2;
	s0b = REG_SIZE;
	s1b = s0b + REG_SIZE;
	s2b = s1b + REG_SIZE;
	s3 = GET_MID_BITS(s1, s1b, s2b);
	s2 = GET_MID_BITS(s1, s0b, s1b);
	s1 = GET_LS_BITS(s1, s0b);
	r1 = this->read_guard(s1);
	r2 = this->read_guard(s2);
	this->status = (r1 == OK && r2 == OK) ? OK : STALLED;
	switch (m) {
	case CMP:
	case CEV:
		break;
	default:
		if (this->status == OK)
			this->write_guard(s3);
	}
}
void ID::decode_I_type(
	signed int &s1, signed int &s2, signed int &s3, Mnemonic &m)
{
	unsigned int s0b, s1b, s2b;
	Response r1, r2;
	s0b = REG_SIZE;
	s1b = s0b + REG_SIZE;
	s2b = WORD_SPEC - LINE_SPEC - OPCODE_SIZE;
	s3 = GET_BITS_SIGN_EXTEND(s1, s1b, s2b);
	switch (m) {
	case STORE:
	case STOREV:
		s2 = GET_MID_BITS(s1, s0b, s1b);
		s1 = GET_LS_BITS(s1, s0b);
		// both operands are read values
		r1 = this->read_guard(s1);
		r2 = this->read_guard(s2);
		this->status = (r1 == OK && r2 == OK) ? OK : STALLED;
		return;
	case LOAD:
	case LOADV:
		s2 = GET_LS_BITS(s1, s0b);
		s1 = GET_MID_BITS(s1, s0b, s1b);
		break;
	default:
		s2 = GET_MID_BITS(s1, s0b, s1b);
		s1 = GET_LS_BITS(s1, s0b);
	}
	r1 = this->read_guard(s1);
	if (r1 == OK)
		this->write_guard(s2);
	this->status = r1;
}
void ID::decode_J_type(
	signed int &s1, signed int &s2, signed int &s3, Mnemonic &m)
{
	Response r1, r2;
	unsigned int s0b, s1b;
	s0b = REG_SIZE;
	s1b = WORD_SPEC - LINE_SPEC - OPCODE_SIZE;
	s3 = 0;
	s2 = GET_BITS_SIGN_EXTEND(s1, s0b, s1b);
	s1 = GET_LS_BITS(s1, s0b);
	switch (m) {
	case PUSH:
		s2 = s1; // source
		s3 = 2;  // stack pointer
		s1 = -1; // increment amount
		r1 = this->read_guard(s2);
		r2 = (this->is_checked_out(s3)) ? STALLED : OK; // we read the stack pointer
		if (r1 == OK && r2 == OK) {
			this->write_guard(s3); // we write the stack pointer
		}
		this->status = (r1 == OK && r2 == OK) ? OK : STALLED;
		break;
	case POP:
		s2 = s1; // destination
		s3 = 2;  // stack pointer
		s1 = 1;  // increment amount
		r1 = (this->is_checked_out(s3)) ? STALLED : OK; // we read the stack pointer
		if (r1 == OK) {
			this->write_guard(s2);
			this->write_guard(s3); // we write the stack pointer
		}
		this->status = r1;
		break;
	default:
		this->status = this->read_guard(*&s1);
	}
}
std::vector ID::stage_info()
{
	std::vector info;
	if (this->curr_instr) {
		info.push_back(this->curr_instr->get_pc());
		info.push_back(this->curr_instr->get_instr_bits());
	}
	return info;
}