// Copyright (c) 2020 - 2021 kio@little-bat.de
// BSD-2-Clause license
// https://opensource.org/licenses/BSD-2-Clause

#include "kio/kio.h"
#include "CpuRiscV.h"
#include "isa_id.h"

CpuRiscV::CpuRiscV(Machine* machine, char* memory) :
	Item(isa_CpuRiscV,machine),
	memory(memory),
	pc(0)
{}

CpuRiscV::~CpuRiscV()
{}

void CpuRiscV::powerOn ()
{
	pc = 0;
}

void CpuRiscV::reset (Time)
{
	pc = 0;
}




int CpuRiscV::run (uint options)
{
	// Run the Cpu:
	// primary implementation

	Opcode op;
	char* ip = memory+pc;

loop:
	op = *reinterpret_cast<Opcode*>(ip); ip += 4;

	switch(op.btype.op())
	{
	case 0b01101111:
		/* JAL ("b?????????????????????????1101111")
		=> Jump And Link, Jtype
		The jump and link (JAL) instruction uses the J-type format, where the J-immediate
		encodes a signed offset in multiples of 2 bytes. The offset is sign-extended and
		added to the address of the jump instruction to form the jump target address.
		Jumps can therefore target a ±1 MiB range. JAL stores the address of the instruction
		following the jump (pc+4) into register rd. The standard software calling convention
		uses x1 as the return address register and x5 as an alternate link register.
		Plain unconditional jumps (assembler pseudoinstruction J) are encoded as a JAL with rd=x0.
		*/
	{
		int32 offs = op.jtype.ival();
		if (offs & 3) TODO(); // instruction-address-misaligned exception
		if (op.jtype.rd()) ireg[op.jtype.rd()] = uint32(ip - memory);
		ip = ip - 4 + offs;
		break;
	}
	case 0b01100111:
		/* JALR ("b?????????????????000?????1100111")
		=> Jump And Link Relative, Itype
		The indirect jump instruction JALR (jump and link register) uses the I-type encoding.
		The target address is obtained by adding the sign-extended 12-bit I-immediate to the
		register rs1, then setting the least-significant bit of the result to zero. The
		address of the instruction following the jump (pc+4) is written to register rd.
		Register x0 can be used as the destination if the result is not required.
		The JAL and JALR instructions will generate an instruction-address-misaligned
		exception if the target address is not aligned to a four-byte boundary.
		*/
	{
		uint32 dest = ireg[op.itype.r1()] + uint32(op.itype.ival()) & 0xfffffffe;
		if (dest & 3) TODO(); // instruction-address-misaligned exception
		if (op.itype.rd()) ireg[op.itype.rd()] = uint32(ip - memory);
		ip = memory + dest;
		break;
	}
	case 0b01100011:
		/* BEQ ("b?????????????????000?????1100011")
		   BNE        ("b?????????????????001?????1100011")
		   BLT        ("b?????????????????100?????1100011")
		   BGE        ("b?????????????????101?????1100011")
		   BLTU       ("b?????????????????110?????1100011")
		   BGEU       ("b?????????????????111?????1100011")
		All branch instructions use the B-type instruction format. The 12-bit B-immediate
		encodes signed offsets in multiples of 2 bytes. The offset is sign-extended and
		added to the address of the branch instruction to give the target address. The
		conditional branch range is ±4 KiB.
		Branch instructions compare two registers. BEQ and BNE take the branch if registers
		rs1 and rs2 are equal or unequal respectively. BLT and BLTU take the branch if rs1
		is less than rs2, using signed and unsigned comparison respectively. BGE and BGEU
		take the branch if rs1 is greater than or equal to rs2, using signed and unsigned
		comparison respectively. Note, BGT, BGTU, BLE, and BLEU can be synthesized by
		reversing the operands to BLT, BLTU, BGE, and BGEU, respectively.
		The conditional branch instructions will generate an instruction-address-misaligned
		exception if the target address is not aligned to a four-byte boundary and the
		branch condition evaluates to true. If the branch condition evaluates to false, the
		instruction-address-misaligned exception will not be raised.
		*/
	{
		uint32 r1 = ireg[op.btype.r1()];
		uint32 r2 = ireg[op.btype.r2()];
		bool f;
		switch(op.btype.f3())
		{
		case 0: f = r1 == r2; break; // BEQ
		case 1: f = r1 != r2; break; // BNE
		case 4: f = r1 <  r2; break; // BLT
		case 5: f = r1 >= r2; break; // BGE
		case 6: f = uint32(r1) <  uint32(r2); break; // BLTU
		case 7:	f = uint32(r1) >= uint32(r2); break; // BGEU
		default: TODO(); // f3 = 2, 3 => ?
		}
		if (f)
		{
			int32 offs = op.btype.ival();
			if (offs & 3) TODO(); // instruction-address-misaligned exception
			ip = ip - 4 + offs;
		}
		break;
	}

	case 0b00110111:
		// LUI       ("b?????????????????????????0110111")
	case 0b00010111:
		// AUIPC     ("b?????????????????????????0010111")
	case 0b00010011:
		// ADDI      ("b?????????????????000?????0010011")
		// SLLI      ("b000000???????????001?????0010011")
		// SLTI      ("b?????????????????010?????0010011")
		// SLTIU     ("b?????????????????011?????0010011")
		// XORI      ("b?????????????????100?????0010011")
		// SRLI      ("b000000???????????101?????0010011")
		// SRAI      ("b010000???????????101?????0010011")
		// ORI       ("b?????????????????110?????0010011")
		// ANDI      ("b?????????????????111?????0010011")

		// SLLI_RV32 ("b0000000??????????001?????0010011")
		// SRLI_RV32 ("b0000000??????????101?????0010011")
		// SRAI_RV32 ("b0100000??????????101?????0010011")

	case 0b00110011:
		// ADD       ("b0000000??????????000?????0110011")
		// SUB       ("b0100000??????????000?????0110011")
		// SLL       ("b0000000??????????001?????0110011")
		// SLT       ("b0000000??????????010?????0110011")
		// SLTU      ("b0000000??????????011?????0110011")
		// XOR       ("b0000000??????????100?????0110011")
		// SRL       ("b0000000??????????101?????0110011")
		// SRA       ("b0100000??????????101?????0110011")
		// OR        ("b0000000??????????110?????0110011")
		// AND       ("b0000000??????????111?????0110011")

		// MUL       ("b0000001??????????000?????0110011")
		// MULH      ("b0000001??????????001?????0110011")
		// MULHSU    ("b0000001??????????010?????0110011")
		// MULHU     ("b0000001??????????011?????0110011")
		// DIV       ("b0000001??????????100?????0110011")
		// DIVU      ("b0000001??????????101?????0110011")
		// REM       ("b0000001??????????110?????0110011")
		// REMU      ("b0000001??????????111?????0110011")

	case 0b00011011:
		// ADDIW     ("b?????????????????000?????0011011")
		// SLLIW     ("b0000000??????????001?????0011011")
		// SRLIW     ("b0000000??????????101?????0011011")
		// SRAIW     ("b0100000??????????101?????0011011")

	case 0b00111011:
		// ADDW      ("b0000000??????????000?????0111011")
		// SUBW      ("b0100000??????????000?????0111011")
		// SLLW      ("b0000000??????????001?????0111011")
		// SRLW      ("b0000000??????????101?????0111011")
		// SRAW      ("b0100000??????????101?????0111011")

		// MULW      ("b0000001??????????000?????0111011")
		// DIVW      ("b0000001??????????100?????0111011")
		// DIVUW     ("b0000001??????????101?????0111011")
		// REMW      ("b0000001??????????110?????0111011")
		// REMUW     ("b0000001??????????111?????0111011")


	case 0b00000011:
		// LB        ("b?????????????????000?????0000011")
		// LH        ("b?????????????????001?????0000011")
		// LW        ("b?????????????????010?????0000011")
		// LD        ("b?????????????????011?????0000011")
		// LBU       ("b?????????????????100?????0000011")
		// LHU       ("b?????????????????101?????0000011")
		// LWU       ("b?????????????????110?????0000011")

	case 0b00100011:
		// SB        ("b?????????????????000?????0100011")
		// SH        ("b?????????????????001?????0100011")
		// SW        ("b?????????????????010?????0100011")
		// SD        ("b?????????????????011?????0100011")

	case 0b00001111:
		// FENCE     ("b?????????????????000?????0001111")
		// FENCE_I   ("b?????????????????001?????0001111")

	case 0b00101111:
		// LR_W      ("b00010??00000?????010?????0101111")
		// SC_W      ("b00011????????????010?????0101111")
		// LR_D      ("b00010??00000?????011?????0101111")
		// SC_D      ("b00011????????????011?????0101111")

	case 0b01110011:
		// ECALL      ("b00000000000000000000000001110011")
		// EBREAK     ("b00000000000100000000000001110011")
		// URET       ("b00000000001000000000000001110011")
		// MRET       ("b00110000001000000000000001110011")
		// DRET       ("b01111011001000000000000001110011")
		// SFENCE_VMA ("b0001001??????????000000001110011")
		// WFI        ("b00010000010100000000000001110011")
		// CSRRW      ("b?????????????????001?????1110011")
		// CSRRS      ("b?????????????????010?????1110011")
		// CSRRC      ("b?????????????????011?????1110011")
		// CSRRWI     ("b?????????????????101?????1110011")
		// CSRRSI     ("b?????????????????110?????1110011")
		// CSRRCI     ("b?????????????????111?????1110011")

		// RDCYCLE    ("b11000000000000000010?????1110011")
		// RDTIME     ("b11000000000100000010?????1110011")
		// RDINSTRET  ("b11000000001000000010?????1110011")
		// RDCYCLEH   ("b11001000000000000010?????1110011")
		// RDTIMEH    ("b11001000000100000010?????1110011")
		// RDINSTRETH ("b11001000001000000010?????1110011")

	case 0b00001011:
	case 0b00101011:
	case 0b01011011:
	case 0b01111011:
		// CUSTOM0            ("b?????????????????000?????0001011")
		// CUSTOM0_RS1        ("b?????????????????010?????0001011")
		// CUSTOM0_RS1_RS2    ("b?????????????????011?????0001011")
		// CUSTOM0_RD         ("b?????????????????100?????0001011")
		// CUSTOM0_RD_RS1     ("b?????????????????110?????0001011")
		// CUSTOM0_RD_RS1_RS2 ("b?????????????????111?????0001011")

		// CUSTOM1            ("b?????????????????000?????0101011")
		// CUSTOM1_RS1        ("b?????????????????010?????0101011")
		// CUSTOM1_RS1_RS2    ("b?????????????????011?????0101011")
		// CUSTOM1_RD         ("b?????????????????100?????0101011")
		// CUSTOM1_RD_RS1     ("b?????????????????110?????0101011")
		// CUSTOM1_RD_RS1_RS2 ("b?????????????????111?????0101011")

		// CUSTOM2            ("b?????????????????000?????1011011")
		// CUSTOM2_RS1        ("b?????????????????010?????1011011")
		// CUSTOM2_RS1_RS2    ("b?????????????????011?????1011011")
		// CUSTOM2_RD         ("b?????????????????100?????1011011")
		// CUSTOM2_RD_RS1     ("b?????????????????110?????1011011")
		// CUSTOM2_RD_RS1_RS2 ("b?????????????????111?????1011011")

		// CUSTOM3            ("b?????????????????000?????1111011")
		// CUSTOM3_RS1        ("b?????????????????010?????1111011")
		// CUSTOM3_RS1_RS2    ("b?????????????????011?????1111011")
		// CUSTOM3_RD         ("b?????????????????100?????1111011")
		// CUSTOM3_RD_RS1     ("b?????????????????110?????1111011")
		// CUSTOM3_RD_RS1_RS2 ("b?????????????????111?????1111011")


	default: TODO();
	}

	goto loop;
}
