#include <iostream>

#define MAX_REG 4

/*
instruction numbers:
	0 - Halts
	1 - Loads
	2 - Add
	3 - Print
Program Structure
 **bits:
	15 - 12  -> Instruction Number
	11 - 8 	 -> Register to use
	7  - 0   -> Immediate Value
*/
template<typename U>
void print( U && a )
{
	std::cout << a << std::endl;
}

template< typename T, typename ...U>
void print( T && a, U &&...b )
{
	std::cout << a;
	print( std::forward<U>( b )... );
}

template< typename T, typename ...U>
void emitLn( T && a, U &&...b )
{
	print( std::forward<T>( a ), std::forward<U>( b )... );
}

int main()
{
	int vm_registers[MAX_REG] = { 0 };
	int const program_code[] = { 0x109F, 0x11C8, 0x2201, 0x3200, 0x0000 };
	int instruction_number, reg_0, reg_1, reg_2;

	int instruction_pointer = 0, immediate_value = 0;
	auto fetch = [&instruction_pointer, &program_code]{
		return program_code[instruction_pointer++];
	};
	auto decode = [&]( uint16_t instrNumber ) mutable -> void {
		instruction_number = ( instrNumber & 0xF000 ) >> 12;
		reg_0 = ( instrNumber & 0xF00 ) >> 8;
		reg_1 = ( instrNumber & 0xF0 ) >> 4;
		reg_2 = ( instrNumber & 0xF );
		immediate_value = ( instrNumber & 0xFF );
	};
	auto eval = [&]() mutable -> void{
		switch( instruction_number ){
			case 0:
				emitLn( "halt" );
				break;
			case 1:
				emitLn( "loadi r", reg_0, " #", immediate_value );
				vm_registers[reg_0] = immediate_value;
				break;
			case 2:
				emitLn( "add r", reg_0, " r", reg_1, " r", reg_2 );
				vm_registers[reg_0] = vm_registers[reg_1] + vm_registers[reg_2];
				break;
			case 3:
				emitLn( "print r", reg_0, "; //" , vm_registers[reg_0] );
				break;
		}
	};
	int const code_count = sizeof( program_code ) / sizeof( int );

	[&]()->void{
		while( instruction_pointer < code_count ){
			auto instrNumber = fetch();
			decode( instrNumber );
			eval();
		}
	}();
	return 0;
}