#include<cstdlib>
#include<cstdint>
#include<cstddef>
#include<cstdio>
#include<initializer_list>
namespace test{
enum class
opcode
{
nop,
addi,
ori,
lui,
lt,
brz,
brnz,
exi,
};
struct nop{};
struct exi{};
struct
addi
{
uint8_t r1;
uint8_t r2;
int16_t imm;
};
struct
lui
{
uint8_t r1;
uint16_t imm;
};
struct
ori
{
uint8_t r1;
uint8_t r2;
uint16_t imm;
};
struct
lt
{
uint8_t r1;
uint8_t r2;
uint8_t r3;
};
struct
brz
{
uint8_t r1;
int16_t offset;
};
struct
brnz
{
uint8_t r1;
int16_t offset;
};
struct
inst_code
{
uint32_t m_data=0;
static constexpr uint32_t to_code(opcode c) noexcept{return static_cast<int>(c)<<26;}
public:
constexpr inst_code() noexcept{}
constexpr inst_code(test::addi addi) noexcept:
m_data(to_code(opcode::addi)|(addi.r1<<21)|(addi.r2<<16)|(addi.imm&0xFFFF)){}
constexpr inst_code(test::ori ori) noexcept:
m_data(to_code(opcode::ori)|(ori.r1<<21)|(ori.r2<<16)|(ori.imm&0xFFFF)){}
constexpr inst_code(test::lui lui) noexcept:
m_data(to_code(opcode::lui)|(lui.r1<<21)|(lui.imm&0xFFFF)){}
constexpr inst_code(test::lt lt) noexcept:
m_data(to_code(opcode::lt)|(lt.r1<<21)|(lt.r2<<16)|lt.r3){}
constexpr inst_code(test::brz brz) noexcept:
m_data(to_code(opcode::brz)|(brz.r1<<21)|(brz.offset&0xFFFF)){}
constexpr inst_code(test::brnz brnz) noexcept:
m_data(to_code(opcode::brnz)|(brnz.r1<<21)|(brnz.offset&0xFFFF)){}
constexpr inst_code(test::exi exi) noexcept:
m_data(to_code(opcode::exi)){}
constexpr opcode get_opcode() const noexcept
{
return static_cast<opcode>(m_data>>26);
}
constexpr uint8_t get_r1() const noexcept{return (m_data>>21)&0x1F;}
constexpr uint8_t get_r2() const noexcept{return (m_data>>16)&0x1F;}
constexpr uint8_t get_r3() const noexcept{return (m_data )&0x1F;}
constexpr uint16_t get_imm_u16() const noexcept{return (m_data&0xFFFF);}
constexpr int16_t get_imm_s16() const noexcept{return (m_data&0xFFFF);}
};
class
register_set
{
static constexpr int number_of_registers = 32;
int32_t m_registers[number_of_registers] = {0};
public:
int32_t get(uint8_t i ) const noexcept{return m_registers[i] ;}
void put(uint8_t i, int32_t v) noexcept{ m_registers[i] = v;}
void addi(uint8_t dst, uint8_t src, int16_t imm) noexcept
{
m_registers[dst] = m_registers[src]+imm;
}
void ori(uint8_t dst, uint8_t src, uint16_t imm) noexcept
{
m_registers[dst] = m_registers[src]|imm;
}
void lui(uint8_t dst, int16_t imm) noexcept
{
m_registers[dst] = imm<<16;
}
void lt(uint8_t dst, uint8_t src1, uint8_t src2) noexcept
{
m_registers[dst] = (m_registers[src1] < m_registers[src2])? 1:0;
}
};
class
memory_space
{
static constexpr size_t data_size = 256;
uint8_t m_data[data_size];
public:
memory_space() noexcept{}
memory_space(std::initializer_list<inst_code> ls) noexcept{assign(ls);}
void assign(std::initializer_list<inst_code> ls) noexcept
{
auto p = reinterpret_cast<inst_code*>(&m_data[0]);
for(auto icode: ls)
{
*p++ = icode;
}
}
inst_code fetch(uint32_t pc) const noexcept{return *reinterpret_cast<const inst_code*>(&m_data[pc&~3]);}
size_t size() const noexcept{return data_size;}
uint8_t& get_u8( uint32_t address) noexcept{return m_data[address];}
int8_t& get_s8( uint32_t address) noexcept{return reinterpret_cast< int8_t&>(m_data[address ]);}
uint16_t& get_u16(uint32_t address) noexcept{return reinterpret_cast<uint16_t&>(m_data[address&~1]);}
int16_t& get_s16(uint32_t address) noexcept{return reinterpret_cast< int16_t&>(m_data[address&~1]);}
uint32_t& get_u32(uint32_t address) noexcept{return reinterpret_cast<uint32_t&>(m_data[address&~3]);}
int32_t& get_s32(uint32_t address) noexcept{return reinterpret_cast< int32_t&>(m_data[address&~3]);}
};
class
vm
{
uint32_t m_pc=0;
register_set* m_register_set=nullptr;
memory_space* m_memory_space=nullptr;
bool m_is_exited=false;
public:
vm(register_set& regset, memory_space& memsp) noexcept:
m_register_set(®set), m_memory_space(&memsp){}
bool is_exited() const noexcept{return m_is_exited;}
uint32_t get_pc() const noexcept{return m_pc;}
void step() noexcept
{
auto inst = m_memory_space->fetch(m_pc);
m_pc += 4;
switch(inst.get_opcode())
{
case(opcode::nop):
break;
case(opcode::addi):
m_register_set->addi(inst.get_r1(),inst.get_r2(),inst.get_imm_s16());
break;
case(opcode::ori):
m_register_set->ori(inst.get_r1(),inst.get_r2(),inst.get_imm_u16());
break;
case(opcode::lui):
m_register_set->lui(inst.get_r1(),inst.get_imm_u16());
break;
case(opcode::lt):
m_register_set->lt(inst.get_r1(),inst.get_r2(),inst.get_r3());
break;
case(opcode::brz):
if(m_register_set->get(inst.get_r1()) == 0)
{
m_pc += inst.get_imm_s16()<<2;
}
break;
case(opcode::brnz):
if(m_register_set->get(inst.get_r1()))
{
m_pc += inst.get_imm_s16()<<2;
}
break;
case(opcode::exi):
m_is_exited = true;
break;
default:
printf("[%d]",static_cast<int>(inst.get_opcode()));
}
}
};
}
int
main(int argc, char** argv)
{
using namespace test;
bool pr = (argc > 1);
test::memory_space mem({
inst_code(addi{1,0,0}),
inst_code(lui{2,0xFF}),
inst_code(ori{2,2,0xFFFF}),
inst_code(lt{3,1,2}),
inst_code(brz{3,2}),
inst_code(addi{1,1,1}),
inst_code(brz{0,-4}),
inst_code(exi{}),
});
test::register_set regset;
test::vm vm(regset,mem);
while(!vm.is_exited())
{
if(pr){printf("%8d",vm.get_pc());}
vm.step();
if(pr){printf(" %d\n",regset.get(1));}
}
return 0;
}