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

#pragma once
#include "kio/kio.h"
#include "Item.h"
#include "Machine.h"


class CpuRiscV : public Item
{
public:
	char* memory;

	// user mode registers:
	uint32 ireg[32] = {0};
	double freg[32];
	uint32 pc;

public:
	struct Rtype
	{
		uint32 n;

		uint f7() {return (n>>25);     }	// :7
		uint r2() {return (n>>20) & 31;}	// :5
		uint r1() {return (n>>15) & 31;}	// :5
		uint f3() {return (n>>12) & 7; }	// :3
		uint rd() {return (n>>7)  & 31;}	// :5
		uint op() {return  n      & 127;}	// :7

		Rtype(uint f7,uint r2, uint r1, uint f3, uint rd, uint o) :
			n((f7<<25) | (r2<<20) | (r1<<15) | (f3<<12) | (rd<<7) | (o)) {}
	};

	struct Itype
	{
		uint32 n;

		int32 ival() {return int32(n) >> 20; } // : 12;
		uint r1() {return (n>>15) & 31;}	// :5
		uint f3() {return (n>>12) & 7; }	// :3
		uint rd() {return (n>>7)  & 31;}	// :5
		uint op() {return  n      & 127;}	// :7

		Itype(int32 n,uint r1,uint f3,uint rd, uint o) :
			n((uint32(n<<20)) | (r1<<15) | (f3<<12) | (rd<<7) | (o)) {}
	};

	struct Stype
	{
		uint32 n;

		static const uint32 mhi = 0xFEu << 25;
		static const uint32 mlo = 0x1Fu << 7;

		int32 hi() { return (int32(n & mhi) >> 20); }	// :7
		uint  r2() { return (n>>20) & 31; }	// :5
		uint  r1() { return (n>>15) & 31; }	// :5
		uint  f3() { return (n>>12) & 7;  }	// :3
		int32 lo() { return (n>>7)  & 31; }	// :5
		uint  op() { return  n      & 127;}	// :7

		int32 ival() { return hi() | lo(); }

		Stype(int32 n, uint r2, uint r1, uint f3, uint o) :
			n( ((uint32(n<<20))&mhi) | (r2<<20) | (r1<<15) | (f3<<12) | ((uint32(n<<7))&mlo) | (o)) {}
	};

	struct Btype
	{
		uint32 n;

		static const uint32 m12 = 1u << 31;
		static const uint32 m11 = 1u << 7;
		static const uint32 mhi = 0x3Fu << 25;
		static const uint32 mlo = 0x0Fu << 8;

		int32 i12() { return int32(n&m12) >> 19; }	// :1
		int32 hi()  { return int32(n&mhi) >> 20; }	// :6
		int32 i11() { return int32(n&m11) << 4; }	// :1
		int32 lo()  { return int32(n&mlo) >> 7; }	// :4

		uint  r2()  { return (n>>20) & 31;  }		// :5
		uint  r1()  { return (n>>15) & 31;  }		// :5
		uint  f3()  { return (n>>12) & 7;   }		// :3
		uint  op()  { return  n      & 127; }		// :7

		int32 ival() { return i12() | hi() | i11() | lo(); }

		Btype(int32 n, uint r2, uint r1, uint f3, uint o) :
			n( (uint32(n<<19)&m12) | (uint32(n<<20)&mhi) | (uint32(n>>4)&m11) | (uint32(n<<7)&mlo) | (r2<<20) | (r1<<15) | (f3<<12) | (o) ) {}
	};

	struct Utype
	{
		uint32 n;

		static const uint32 mhi = 0xFFFFF000;

		uint rd() {return (n>>7)  & 31;}	    // :5
		uint op() {return  n      & 127;}	    // :7

		int32 ival() { return int32(n & mhi); } // :20

		Utype(int32 n, uint rd, uint o) :
			n( (uint32(n)&mhi) | (rd<<7) | (o) ) {}
	};

	struct Jtype
	{
		uint32 n;

		static const uint32 m20 = 1u << 31;
		static const uint32 mhi = 0xFFu << 12;
		static const uint32 m11 = 1u << 20;
		static const uint32 mlo = 0x3FFu << 21;

		int32 i20() { return int32(n&m20) >> 11; }	// :1
		int32 hi()  { return int32(n&mhi);       }	// :6
		int32 i11() { return int32(n&m11) >> 9;  }	// :1
		int32 lo()  { return int32(n&mlo) >> 20; }	// :4

		uint rd() {return (n>>7)  & 31;}	    // :5
		uint op() {return  n      & 127;}	    // :7

		int32 ival() { return i20() | hi() | i11() | lo(); }

		Jtype(int32 n, uint rd, uint o) :
			n( ((uint32(n)<<11)&m20) | ((uint32(n))&mhi) | ((uint32(n)<<9)&m11) | ((uint32(n)<<20)&mlo) | (rd<<9) | (o) ) {}
	};

	union Opcode
	{
		uint32 opcode;
		Rtype rtype;
		Itype itype;
		Stype stype;
		Btype btype;
		Utype utype;
		Jtype jtype;

		Opcode(uint32 o=0) : opcode(o) {}
		Opcode(Rtype t) : rtype(t) {}
		Opcode(Itype t) : itype(t) {}
		Opcode(Stype t) : stype(t) {}
		Opcode(Btype t) : btype(t) {}
		Opcode(Utype t) : utype(t) {}
		Opcode(Jtype t) : jtype(t) {}
	};

	static_assert (sizeof(Opcode) == 4, "");


	CpuRiscV(Machine*, char* memory);
	~CpuRiscV() override;

// Item interface:
	virtual void	powerOn		() override;
	virtual void	reset		(Time) override;
	//virtual bool	input		(uint32, uint32&) override;
	//virtual bool	output		(uint32, uint32) override;

// Run the Cpu:
	int run (uint options=0);

	void setNmi () volatile noexcept {}
	void clearNmi () volatile noexcept {}
};

