This commit is contained in:
parent
2cfaf8b898
commit
6007426eb3
@ -1,54 +1,12 @@
|
|||||||
# Generated from CLion C/C++ Code Style settings
|
BasedOnStyle: GNU
|
||||||
---
|
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
||||||
Language: Cpp
|
|
||||||
BasedOnStyle: LLVM
|
|
||||||
AlignConsecutiveAssignments: false
|
|
||||||
AlignConsecutiveDeclarations: false
|
|
||||||
AlignOperands: false
|
|
||||||
AlignTrailingComments: false
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
BraceWrapping:
|
|
||||||
AfterCaseLabel: true
|
|
||||||
AfterClass: true
|
|
||||||
AfterControlStatement: true
|
|
||||||
AfterEnum: true
|
|
||||||
AfterFunction: true
|
|
||||||
AfterNamespace: true
|
|
||||||
AfterStruct: true
|
|
||||||
AfterUnion: true
|
|
||||||
AfterExternBlock: false
|
|
||||||
BeforeCatch: true
|
|
||||||
BeforeElse: true
|
|
||||||
BeforeLambdaBody: true
|
|
||||||
BeforeWhile: true
|
|
||||||
SplitEmptyFunction: true
|
|
||||||
SplitEmptyRecord: true
|
|
||||||
SplitEmptyNamespace: true
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BreakConstructorInitializers: AfterColon
|
|
||||||
BreakConstructorInitializersBeforeComma: false
|
|
||||||
ColumnLimit: 120
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
|
||||||
ContinuationIndentWidth: 2
|
|
||||||
IncludeCategories:
|
|
||||||
- Regex: '^<.*'
|
|
||||||
Priority: 1
|
|
||||||
- Regex: '^".*'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '.*'
|
|
||||||
Priority: 3
|
|
||||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
|
||||||
IndentCaseBlocks: true
|
|
||||||
InsertNewlineAtEOF: false
|
|
||||||
MacroBlockBegin: ''
|
|
||||||
MacroBlockEnd: ''
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
NamespaceIndentation: All
|
|
||||||
PointerAlignment: Left
|
PointerAlignment: Left
|
||||||
SpaceInEmptyParentheses: false
|
UseTab: Never
|
||||||
SpacesInAngles: false
|
IndentWidth: 2
|
||||||
SpacesInConditionalStatement: false
|
ContinuationIndentWidth: 2
|
||||||
SpacesInCStyleCastParentheses: false
|
ConstructorInitializerIndentWidth: 2
|
||||||
SpacesInParentheses: false
|
BreakAfterReturnType: Automatic
|
||||||
TabWidth: 4
|
BreakConstructorInitializers: AfterColon
|
||||||
...
|
PackConstructorInitializers: Never
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
BreakBeforeBraces: Allman
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "vcppd"]
|
||||||
|
path = vcppd
|
||||||
|
url = https://git.shylie.info/shylie/vcppd.git
|
||||||
@ -9,4 +9,5 @@ else()
|
|||||||
set(FOOT_TESTS OFF)
|
set(FOOT_TESTS OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(vcppd)
|
||||||
add_subdirectory(emulator)
|
add_subdirectory(emulator)
|
||||||
|
|||||||
@ -4,11 +4,14 @@ project(foot-emulator)
|
|||||||
|
|
||||||
add_library(foot-emulator src/foot-emulator.cpp include/foot-emulator.h)
|
add_library(foot-emulator src/foot-emulator.cpp include/foot-emulator.h)
|
||||||
target_include_directories(foot-emulator PUBLIC include)
|
target_include_directories(foot-emulator PUBLIC include)
|
||||||
|
target_link_libraries(foot-emulator PUBLIC vcppd)
|
||||||
|
set_target_properties(foot-emulator PROPERTIES CXX_STANDARD 20)
|
||||||
|
|
||||||
find_package(SDL3 REQUIRED CONFIG)
|
find_package(SDL3 REQUIRED CONFIG)
|
||||||
|
|
||||||
add_executable(foot-emulator-cli src/main.cpp)
|
add_executable(foot-emulator-cli src/main.cpp)
|
||||||
target_link_libraries(foot-emulator-cli PUBLIC foot-emulator SDL3::SDL3)
|
target_link_libraries(foot-emulator-cli PUBLIC foot-emulator SDL3::SDL3)
|
||||||
|
set_target_properties(foot-emulator-cli PROPERTIES CXX_STANDARD 20)
|
||||||
|
|
||||||
if(FOOT_TESTS)
|
if(FOOT_TESTS)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|||||||
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vcppd/vcppd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace foot
|
namespace foot
|
||||||
@ -26,6 +28,7 @@ struct Operand
|
|||||||
class Device
|
class Device
|
||||||
{
|
{
|
||||||
friend class Emulator;
|
friend class Emulator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~Device() = default;
|
virtual ~Device() = default;
|
||||||
|
|
||||||
@ -39,7 +42,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t* assigned_memory_base;
|
uint32_t* assigned_memory_base;
|
||||||
uint32_t assigned_memory_size;
|
uint32_t assigned_memory_size;
|
||||||
|
|
||||||
unsigned int update_frequency;
|
unsigned int update_frequency;
|
||||||
};
|
};
|
||||||
@ -52,7 +55,7 @@ public:
|
|||||||
static constexpr uint8_t RA = 29;
|
static constexpr uint8_t RA = 29;
|
||||||
static constexpr uint8_t SP = 28;
|
static constexpr uint8_t SP = 28;
|
||||||
|
|
||||||
Emulator();
|
explicit Emulator(bool should_trace = false);
|
||||||
|
|
||||||
void run(const std::vector<uint32_t>& program);
|
void run(const std::vector<uint32_t>& program);
|
||||||
void run(const std::string& filename);
|
void run(const std::string& filename);
|
||||||
@ -80,6 +83,10 @@ private:
|
|||||||
std::vector<std::pair<std::unique_ptr<Device>, unsigned int>> devices;
|
std::vector<std::pair<std::unique_ptr<Device>, unsigned int>> devices;
|
||||||
uint32_t mapped_base;
|
uint32_t mapped_base;
|
||||||
bool keep_running;
|
bool keep_running;
|
||||||
|
std::unique_ptr<std::ofstream> vcd_file;
|
||||||
|
std::unique_ptr<std::ostream> null_stream;
|
||||||
|
vcppd::Vcd vcd;
|
||||||
|
bool should_trace;
|
||||||
|
|
||||||
void run_internal();
|
void run_internal();
|
||||||
void read_instruction();
|
void read_instruction();
|
||||||
@ -109,8 +116,11 @@ private:
|
|||||||
uint8_t simd_elements() const;
|
uint8_t simd_elements() const;
|
||||||
uint8_t shift() const;
|
uint8_t shift() const;
|
||||||
|
|
||||||
uint32_t apply_many_unary(uint32_t a, uint32_t (Emulator::*fn)(uint32_t a) const) const;
|
uint32_t apply_many_unary(uint32_t a,
|
||||||
uint32_t apply_many_binary(uint32_t a, uint32_t b, uint32_t (Emulator::*fn)(uint32_t a, uint32_t b) const) const;
|
uint32_t (Emulator::*fn)(uint32_t a) const) const;
|
||||||
|
uint32_t apply_many_binary(uint32_t a, uint32_t b,
|
||||||
|
uint32_t (Emulator::*fn)(uint32_t a, uint32_t b)
|
||||||
|
const) const;
|
||||||
|
|
||||||
uint32_t op_arithmetic_negate(uint32_t a) const;
|
uint32_t op_arithmetic_negate(uint32_t a) const;
|
||||||
uint32_t op_logical_negate(uint32_t a) const;
|
uint32_t op_logical_negate(uint32_t a) const;
|
||||||
@ -127,4 +137,4 @@ private:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif//FOOT_EMULATOR
|
#endif // FOOT_EMULATOR
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
#include "foot-emulator.h"
|
#include "foot-emulator.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <elfio/elfio.hpp>
|
#include <elfio/elfio.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace foot
|
namespace foot
|
||||||
{
|
{
|
||||||
@ -10,13 +9,15 @@ namespace foot
|
|||||||
Operand::Operand(uint8_t bits) :
|
Operand::Operand(uint8_t bits) :
|
||||||
addressing_mode(static_cast<AddressingMode>((bits & 0b01100000) >> 5)),
|
addressing_mode(static_cast<AddressingMode>((bits & 0b01100000) >> 5)),
|
||||||
register_index(bits & 0b00011111)
|
register_index(bits & 0b00011111)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Device::Device(uint16_t size, unsigned int update_frequency) :
|
Device::Device(uint16_t size, unsigned int update_frequency) :
|
||||||
assigned_memory_base(nullptr),
|
assigned_memory_base(nullptr),
|
||||||
assigned_memory_size(size),
|
assigned_memory_size(size),
|
||||||
update_frequency(update_frequency)
|
update_frequency(update_frequency)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t& Device::memory(uint16_t index)
|
uint32_t& Device::memory(uint16_t index)
|
||||||
{
|
{
|
||||||
@ -27,11 +28,54 @@ uint32_t& Device::memory(uint16_t index)
|
|||||||
return assigned_memory_base[index];
|
return assigned_memory_base[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
Emulator::Emulator() :
|
Emulator::Emulator(bool should_trace) :
|
||||||
memory{},
|
memory{},
|
||||||
registers{},
|
registers{},
|
||||||
configuration(0),
|
configuration(0),
|
||||||
mapped_base(0x00010000)
|
mapped_base(0x00010000),
|
||||||
|
vcd_file(should_trace ? std::make_unique<std::ofstream>("trace.vcd")
|
||||||
|
: nullptr),
|
||||||
|
null_stream(should_trace ? nullptr
|
||||||
|
: std::make_unique<std::ostream>(nullptr)),
|
||||||
|
vcd(vcppd::Builder(should_trace ? *vcd_file : *null_stream)
|
||||||
|
.scope("CPU")
|
||||||
|
.scope("REGISTERS")
|
||||||
|
.trace("0", registers[0])
|
||||||
|
.trace("1", registers[1])
|
||||||
|
.trace("2", registers[2])
|
||||||
|
.trace("3", registers[3])
|
||||||
|
.trace("4", registers[4])
|
||||||
|
.trace("5", registers[5])
|
||||||
|
.trace("6", registers[6])
|
||||||
|
.trace("7", registers[7])
|
||||||
|
.trace("8", registers[8])
|
||||||
|
.trace("9", registers[9])
|
||||||
|
.trace("10", registers[10])
|
||||||
|
.trace("11", registers[11])
|
||||||
|
.trace("12", registers[12])
|
||||||
|
.trace("13", registers[13])
|
||||||
|
.trace("14", registers[14])
|
||||||
|
.trace("15", registers[15])
|
||||||
|
.trace("16", registers[16])
|
||||||
|
.trace("17", registers[17])
|
||||||
|
.trace("18", registers[18])
|
||||||
|
.trace("19", registers[19])
|
||||||
|
.trace("20", registers[20])
|
||||||
|
.trace("21", registers[21])
|
||||||
|
.trace("22", registers[22])
|
||||||
|
.trace("23", registers[23])
|
||||||
|
.trace("24", registers[24])
|
||||||
|
.trace("25", registers[25])
|
||||||
|
.trace("26", registers[26])
|
||||||
|
.trace("27", registers[27])
|
||||||
|
.trace("28", registers[28])
|
||||||
|
.trace("29", registers[29])
|
||||||
|
.trace("30", registers[30])
|
||||||
|
.trace("31", registers[31])
|
||||||
|
.unscope()
|
||||||
|
.unscope()
|
||||||
|
.build()),
|
||||||
|
should_trace(should_trace)
|
||||||
{
|
{
|
||||||
registers[SP] = 0xFFFF; // stack pointer
|
registers[SP] = 0xFFFF; // stack pointer
|
||||||
}
|
}
|
||||||
@ -47,13 +91,21 @@ void Emulator::run(const std::string& filename)
|
|||||||
{
|
{
|
||||||
ELFIO::elfio reader;
|
ELFIO::elfio reader;
|
||||||
|
|
||||||
if (!reader.load(filename)) { return; }
|
if (!reader.load(filename))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& segment : reader.segments)
|
for (const auto& segment : reader.segments)
|
||||||
{
|
{
|
||||||
if (segment->get_type() != ELFIO::PT_LOAD) { continue; }
|
if (segment->get_type() != ELFIO::PT_LOAD)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(reinterpret_cast<char*>(memory.data()) + segment->get_virtual_address(), segment->get_data(), segment->get_file_size());
|
memcpy(reinterpret_cast<char*>(memory.data())
|
||||||
|
+ segment->get_virtual_address(),
|
||||||
|
segment->get_data(), segment->get_file_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
registers[PC] = reader.get_entry() / 4;
|
registers[PC] = reader.get_entry() / 4;
|
||||||
@ -68,10 +120,7 @@ void Emulator::map_device(std::unique_ptr<Device>&& device)
|
|||||||
devices.emplace_back(std::move(device), 0);
|
devices.emplace_back(std::move(device), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t& Emulator::memory_at(uint32_t address)
|
uint32_t& Emulator::memory_at(uint32_t address) { return memory[address]; }
|
||||||
{
|
|
||||||
return memory[address];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t& Emulator::decode_operand(Operand operand)
|
uint32_t& Emulator::decode_operand(Operand operand)
|
||||||
{
|
{
|
||||||
@ -88,20 +137,22 @@ uint32_t& Emulator::decode_operand(Operand operand)
|
|||||||
return memory[registers[operand.register_index]++];
|
return memory[registers[operand.register_index]++];
|
||||||
|
|
||||||
case Operand::AddressingMode::Indirect:
|
case Operand::AddressingMode::Indirect:
|
||||||
return memory[registers[operand.register_index]];
|
return memory[registers[operand.register_index]];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return dummy_value;
|
return dummy_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t& Emulator::register_at(uint8_t index)
|
uint32_t& Emulator::register_at(uint8_t index) { return registers[index]; }
|
||||||
{
|
|
||||||
return registers[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Emulator::run_instruction()
|
void Emulator::run_instruction()
|
||||||
{
|
{
|
||||||
|
if (should_trace)
|
||||||
|
{
|
||||||
|
vcd.tick();
|
||||||
|
}
|
||||||
|
|
||||||
read_instruction();
|
read_instruction();
|
||||||
|
|
||||||
if (!should_run())
|
if (!should_run())
|
||||||
@ -114,7 +165,10 @@ void Emulator::run_instruction()
|
|||||||
|
|
||||||
for (int iteration = 0; iteration < loop_count; iteration++)
|
for (int iteration = 0; iteration < loop_count; iteration++)
|
||||||
{
|
{
|
||||||
if (rep) { registers[LC] = iteration; }
|
if (rep)
|
||||||
|
{
|
||||||
|
registers[LC] = iteration;
|
||||||
|
}
|
||||||
|
|
||||||
switch ((instruction & 0x0F000000) >> 24)
|
switch ((instruction & 0x0F000000) >> 24)
|
||||||
{
|
{
|
||||||
@ -204,7 +258,10 @@ void Emulator::run_instruction()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rep) { registers[LC] = loop_count; }
|
if (rep)
|
||||||
|
{
|
||||||
|
registers[LC] = loop_count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::run_internal()
|
void Emulator::run_internal()
|
||||||
@ -229,12 +286,15 @@ void Emulator::run_internal()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vcd_file)
|
||||||
|
{
|
||||||
|
vcd_file->flush();
|
||||||
|
vcd_file->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::read_instruction()
|
void Emulator::read_instruction() { instruction = memory[registers[PC]++]; }
|
||||||
{
|
|
||||||
instruction = memory[registers[PC]++];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Emulator::CNST()
|
void Emulator::CNST()
|
||||||
{
|
{
|
||||||
@ -262,7 +322,7 @@ void Emulator::CMPR()
|
|||||||
|
|
||||||
void Emulator::BWNG()
|
void Emulator::BWNG()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = ~a;
|
d = ~a;
|
||||||
@ -270,7 +330,7 @@ void Emulator::BWNG()
|
|||||||
|
|
||||||
void Emulator::ARNG()
|
void Emulator::ARNG()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_unary(a, &Emulator::op_arithmetic_negate);
|
d = apply_many_unary(a, &Emulator::op_arithmetic_negate);
|
||||||
@ -278,7 +338,7 @@ void Emulator::ARNG()
|
|||||||
|
|
||||||
void Emulator::LONG()
|
void Emulator::LONG()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_unary(a, &Emulator::op_logical_negate);
|
d = apply_many_unary(a, &Emulator::op_logical_negate);
|
||||||
@ -286,7 +346,7 @@ void Emulator::LONG()
|
|||||||
|
|
||||||
void Emulator::CONF()
|
void Emulator::CONF()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = configuration;
|
d = configuration;
|
||||||
@ -295,7 +355,7 @@ void Emulator::CONF()
|
|||||||
|
|
||||||
void Emulator::COPY()
|
void Emulator::COPY()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = a;
|
d = a;
|
||||||
@ -303,8 +363,8 @@ void Emulator::COPY()
|
|||||||
|
|
||||||
void Emulator::BWOR()
|
void Emulator::BWOR()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = a | b;
|
d = a | b;
|
||||||
@ -312,8 +372,8 @@ void Emulator::BWOR()
|
|||||||
|
|
||||||
void Emulator::BAND()
|
void Emulator::BAND()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = a & b;
|
d = a & b;
|
||||||
@ -321,8 +381,8 @@ void Emulator::BAND()
|
|||||||
|
|
||||||
void Emulator::BXOR()
|
void Emulator::BXOR()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = a ^ b;
|
d = a ^ b;
|
||||||
@ -330,8 +390,8 @@ void Emulator::BXOR()
|
|||||||
|
|
||||||
void Emulator::ZRSH()
|
void Emulator::ZRSH()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_pad_zero_right_shift);
|
d = apply_many_binary(a, b, &Emulator::op_pad_zero_right_shift);
|
||||||
@ -339,8 +399,8 @@ void Emulator::ZRSH()
|
|||||||
|
|
||||||
void Emulator::SRSH()
|
void Emulator::SRSH()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_signed_right_shift);
|
d = apply_many_binary(a, b, &Emulator::op_signed_right_shift);
|
||||||
@ -348,8 +408,8 @@ void Emulator::SRSH()
|
|||||||
|
|
||||||
void Emulator::ZLSH()
|
void Emulator::ZLSH()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_pad_zero_left_shift);
|
d = apply_many_binary(a, b, &Emulator::op_pad_zero_left_shift);
|
||||||
@ -357,8 +417,8 @@ void Emulator::ZLSH()
|
|||||||
|
|
||||||
void Emulator::CLSH()
|
void Emulator::CLSH()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_circular_left_shift);
|
d = apply_many_binary(a, b, &Emulator::op_circular_left_shift);
|
||||||
@ -366,8 +426,8 @@ void Emulator::CLSH()
|
|||||||
|
|
||||||
void Emulator::ADDI()
|
void Emulator::ADDI()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_add);
|
d = apply_many_binary(a, b, &Emulator::op_add);
|
||||||
@ -375,8 +435,8 @@ void Emulator::ADDI()
|
|||||||
|
|
||||||
void Emulator::SUBT()
|
void Emulator::SUBT()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_sub);
|
d = apply_many_binary(a, b, &Emulator::op_sub);
|
||||||
@ -384,8 +444,8 @@ void Emulator::SUBT()
|
|||||||
|
|
||||||
void Emulator::MULT()
|
void Emulator::MULT()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_mul);
|
d = apply_many_binary(a, b, &Emulator::op_mul);
|
||||||
@ -393,8 +453,8 @@ void Emulator::MULT()
|
|||||||
|
|
||||||
void Emulator::DIVI()
|
void Emulator::DIVI()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_div);
|
d = apply_many_binary(a, b, &Emulator::op_div);
|
||||||
@ -402,8 +462,8 @@ void Emulator::DIVI()
|
|||||||
|
|
||||||
void Emulator::MODU()
|
void Emulator::MODU()
|
||||||
{
|
{
|
||||||
uint32_t a = decode_operand(instruction & 0x000000FF);
|
uint32_t a = decode_operand(instruction & 0x000000FF);
|
||||||
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
uint32_t b = decode_operand((instruction & 0x0000FF00) >> 8);
|
||||||
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
uint32_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
|
||||||
|
|
||||||
d = apply_many_binary(a, b, &Emulator::op_mod);
|
d = apply_many_binary(a, b, &Emulator::op_mod);
|
||||||
@ -426,78 +486,85 @@ bool Emulator::should_run() const
|
|||||||
return status_register == Status::Equal;
|
return status_register == Status::Equal;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
return status_register == Status::Greater || status_register == Status::Equal;
|
return status_register == Status::Greater
|
||||||
|
|| status_register == Status::Equal;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
return status_register == Status::Greater;
|
return status_register == Status::Greater;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
return status_register == Status::Less || status_register == Status::Greater;
|
return status_register == Status::Less
|
||||||
|
|| status_register == Status::Greater;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Emulator::repeats() const
|
bool Emulator::repeats() const { return instruction & 0x10000000; }
|
||||||
{
|
|
||||||
return instruction & 0x10000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Emulator::simd_elements() const
|
uint8_t Emulator::simd_elements() const
|
||||||
{
|
{
|
||||||
uint8_t bits = 0;
|
uint8_t bits = 0;
|
||||||
if (instruction & (1 << 7)) { bits |= 0b01; }
|
if (instruction & (1 << 7))
|
||||||
if (instruction & (1 << 23)) { bits |= 0b10; }
|
{
|
||||||
|
bits |= 0b01;
|
||||||
|
}
|
||||||
|
if (instruction & (1 << 23))
|
||||||
|
{
|
||||||
|
bits |= 0b10;
|
||||||
|
}
|
||||||
|
|
||||||
return 1 << bits;
|
return 1 << bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Emulator::shift() const
|
uint8_t Emulator::shift() const { return configuration & 0x0000001F; }
|
||||||
{
|
|
||||||
return configuration & 0x0000001F;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Emulator::apply_many_unary(uint32_t a, uint32_t (Emulator::*fn)(uint32_t a) const) const
|
uint32_t Emulator::apply_many_unary(uint32_t a,
|
||||||
|
uint32_t (Emulator::*fn)(uint32_t a)
|
||||||
|
const) const
|
||||||
{
|
{
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
|
|
||||||
for (int i = 0; i < simd_elements(); i++)
|
for (int i = 0; i < simd_elements(); i++)
|
||||||
{
|
{
|
||||||
uint32_t simd_width_mask = ((uint64_t(1) << (32 / simd_elements())) - 1);
|
uint32_t simd_width_mask = ((uint64_t(1) << (32 / simd_elements())) - 1);
|
||||||
uint32_t simd_element_a = (a >> (i * 32 / simd_elements())) & simd_width_mask;
|
uint32_t simd_element_a
|
||||||
uint32_t simd_result = ((this->*fn)(simd_element_a) & simd_width_mask) << (i * 32 / simd_elements());
|
= (a >> (i * 32 / simd_elements())) & simd_width_mask;
|
||||||
|
uint32_t simd_result = ((this->*fn)(simd_element_a)&simd_width_mask)
|
||||||
|
<< (i * 32 / simd_elements());
|
||||||
result |= simd_result;
|
result |= simd_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Emulator::apply_many_binary(uint32_t a, uint32_t b, uint32_t (Emulator::*fn)(uint32_t a, uint32_t b) const) const
|
uint32_t Emulator::apply_many_binary(uint32_t a, uint32_t b,
|
||||||
|
uint32_t (Emulator::*fn)(uint32_t a,
|
||||||
|
uint32_t b)
|
||||||
|
const) const
|
||||||
{
|
{
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
|
|
||||||
for (int i = 0; i < simd_elements(); i++)
|
for (int i = 0; i < simd_elements(); i++)
|
||||||
{
|
{
|
||||||
uint32_t simd_width_mask = ((uint64_t(1) << (32 / simd_elements())) - 1);
|
uint32_t simd_width_mask = ((uint64_t(1) << (32 / simd_elements())) - 1);
|
||||||
uint32_t simd_element_a = (a >> (i * 32 / simd_elements())) & simd_width_mask;
|
uint32_t simd_element_a
|
||||||
uint32_t simd_element_b = (b >> (i * 32 / simd_elements())) & simd_width_mask;
|
= (a >> (i * 32 / simd_elements())) & simd_width_mask;
|
||||||
uint32_t simd_result = ((this->*fn)(simd_element_a, simd_element_b) & simd_width_mask) << (i * 32 / simd_elements());
|
uint32_t simd_element_b
|
||||||
|
= (b >> (i * 32 / simd_elements())) & simd_width_mask;
|
||||||
|
uint32_t simd_result
|
||||||
|
= ((this->*fn)(simd_element_a, simd_element_b) & simd_width_mask)
|
||||||
|
<< (i * 32 / simd_elements());
|
||||||
result |= simd_result;
|
result |= simd_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Emulator::op_arithmetic_negate(uint32_t a) const
|
uint32_t Emulator::op_arithmetic_negate(uint32_t a) const { return -a; }
|
||||||
{
|
|
||||||
return -a;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Emulator::op_logical_negate(uint32_t a) const
|
uint32_t Emulator::op_logical_negate(uint32_t a) const { return !a; }
|
||||||
{
|
|
||||||
return !a;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Emulator::op_pad_zero_right_shift(uint32_t a, uint32_t b) const
|
uint32_t Emulator::op_pad_zero_right_shift(uint32_t a, uint32_t b) const
|
||||||
{
|
{
|
||||||
@ -519,15 +586,9 @@ uint32_t Emulator::op_circular_left_shift(uint32_t a, uint32_t b) const
|
|||||||
return (a << b) | (a >> (32 - b));
|
return (a << b) | (a >> (32 - b));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Emulator::op_add(uint32_t a, uint32_t b) const
|
uint32_t Emulator::op_add(uint32_t a, uint32_t b) const { return a + b; }
|
||||||
{
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Emulator::op_sub(uint32_t a, uint32_t b) const
|
uint32_t Emulator::op_sub(uint32_t a, uint32_t b) const { return a - b; }
|
||||||
{
|
|
||||||
return a - b;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Emulator::op_mul(uint32_t a, uint32_t b) const
|
uint32_t Emulator::op_mul(uint32_t a, uint32_t b) const
|
||||||
{
|
{
|
||||||
@ -545,4 +606,4 @@ uint32_t Emulator::op_mod(uint32_t a, uint32_t b) const
|
|||||||
return int32_t(a) % int32_t(b);
|
return int32_t(a) % int32_t(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
#include "foot-emulator.h"
|
#include "foot-emulator.h"
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -26,7 +24,7 @@ public:
|
|||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,16 +34,14 @@ public:
|
|||||||
MemoryViewDevice(SDL_Renderer* render, uint8_t width, uint8_t height) :
|
MemoryViewDevice(SDL_Renderer* render, uint8_t width, uint8_t height) :
|
||||||
Device(width * height, 10000),
|
Device(width * height, 10000),
|
||||||
render(render),
|
render(render),
|
||||||
texture(SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, width, height)),
|
texture(SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA32,
|
||||||
|
SDL_TEXTUREACCESS_STREAMING, width, height)),
|
||||||
width(width),
|
width(width),
|
||||||
height(height)
|
height(height)
|
||||||
{
|
{
|
||||||
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
||||||
}
|
}
|
||||||
~MemoryViewDevice()
|
~MemoryViewDevice() { SDL_DestroyTexture(texture); }
|
||||||
{
|
|
||||||
SDL_DestroyTexture(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool update() override
|
bool update() override
|
||||||
{
|
{
|
||||||
@ -100,9 +96,10 @@ int main(int argc, char** argv)
|
|||||||
SDL_Renderer* render;
|
SDL_Renderer* render;
|
||||||
|
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
SDL_CreateWindowAndRenderer("foot emulator", WIDTH * 8, HEIGHT * 8, 0, &window, &render);
|
SDL_CreateWindowAndRenderer("foot emulator", WIDTH * 8, HEIGHT * 8, 0,
|
||||||
|
&window, &render);
|
||||||
|
|
||||||
foot::Emulator emu;
|
foot::Emulator emu(true);
|
||||||
emu.map_device(std::make_unique<PrintDevice>());
|
emu.map_device(std::make_unique<PrintDevice>());
|
||||||
emu.map_device(std::make_unique<MemoryViewDevice>(render, WIDTH, HEIGHT));
|
emu.map_device(std::make_unique<MemoryViewDevice>(render, WIDTH, HEIGHT));
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ function(test name)
|
|||||||
add_executable(foot-emulator-test-${name} ${name}.cpp test-common.h)
|
add_executable(foot-emulator-test-${name} ${name}.cpp test-common.h)
|
||||||
target_link_libraries(foot-emulator-test-${name} PUBLIC foot-emulator)
|
target_link_libraries(foot-emulator-test-${name} PUBLIC foot-emulator)
|
||||||
add_test(NAME ${name} COMMAND foot-emulator-test-${name})
|
add_test(NAME ${name} COMMAND foot-emulator-test-${name})
|
||||||
|
set_target_properties(foot-emulator-test-${name} PROPERTIES CXX_STANDARD 20)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
test(cnst-instruction)
|
test(cnst-instruction)
|
||||||
@ -23,4 +24,4 @@ test(addi-instruction)
|
|||||||
test(subt-instruction)
|
test(subt-instruction)
|
||||||
test(mult-instruction)
|
test(mult-instruction)
|
||||||
test(divi-instruction)
|
test(divi-instruction)
|
||||||
test(modu-instruction)
|
test(modu-instruction)
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x09200220 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x09200220 });
|
||||||
if (!check(0x00004569, emu.register_at(0))) { return 1; }
|
if (!check(0x00004569, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -11,7 +11,10 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x01200220 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x01200220 });
|
||||||
if (!check(0xFFFFBA99, emu.register_at(0))) { return 1; }
|
if (!check(0xFFFFBA99, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -23,7 +26,10 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x01A002A0 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x01A002A0 });
|
||||||
if (!check(0x0000CBA9, emu.register_at(0))) { return 1; }
|
if (!check(0x0000CBA9, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -15,8 +15,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x00212345, 0x03202021 });
|
foot::Emulator emu
|
||||||
if (!check(0x00004567 & 0x00002345, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00204567, 0x00212345, 0x03202021 });
|
||||||
|
if (!check(0x00004567 & 0x00002345, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -11,7 +11,10 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x01200120 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x01200120 });
|
||||||
if (!check(0xFFFFBA98, emu.register_at(0))) { return 1; }
|
if (!check(0xFFFFBA98, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -15,8 +15,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x00212345, 0x02202021 });
|
foot::Emulator emu
|
||||||
if (!check(0x00004567 | 0x00002345, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00204567, 0x00212345, 0x02202021 });
|
||||||
|
if (!check(0x00004567 | 0x00002345, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -15,8 +15,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x00212345, 0x04202021 });
|
foot::Emulator emu
|
||||||
if (!check(0x00004567 ^ 0x00002345, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00204567, 0x00212345, 0x04202021 });
|
||||||
|
if (!check(0x00004567 ^ 0x00002345, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x08201820 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x08201820 });
|
||||||
if (!check(0x67000045, emu.register_at(0))) { return 1; }
|
if (!check(0x67000045, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -7,7 +7,10 @@ int main(int argc, char** argv)
|
|||||||
// imm - 0xFFFF
|
// imm - 0xFFFF
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instruction(0x0020FFFF);
|
foot::Emulator emu = run_instruction(0x0020FFFF);
|
||||||
if (!check(0xFFFF, emu.register_at(0))) { return 1; }
|
if (!check(0xFFFF, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -19,7 +22,10 @@ int main(int argc, char** argv)
|
|||||||
// imm = 0x7777
|
// imm = 0x7777
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x0020FFFF, 0x00407777 });
|
foot::Emulator emu = run_instructions({ 0x0020FFFF, 0x00407777 });
|
||||||
if (!check(0x7777, emu.memory_at(0xFFFF))) { return 1; }
|
if (!check(0x7777, emu.memory_at(0xFFFF)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -34,10 +40,16 @@ int main(int argc, char** argv)
|
|||||||
foot::Emulator emu = run_instructions({ 0x003E0010, 0x10601234 });
|
foot::Emulator emu = run_instructions({ 0x003E0010, 0x10601234 });
|
||||||
for (int i = 0; i < 0x10; i++)
|
for (int i = 0; i < 0x10; i++)
|
||||||
{
|
{
|
||||||
if (!check(0x1234, emu.memory_at(i))) { failed = true; }
|
if (!check(0x1234, emu.memory_at(i)))
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failed) { return 1; }
|
if (failed)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200005, 0x0C200220 });
|
foot::Emulator emu = run_instructions({ 0x00200005, 0x0C200220 });
|
||||||
if (!check(0x00000002, emu.register_at(0))) { return 1; }
|
if (!check(0x00000002, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -28,8 +31,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200007, 0x01210207, 0x0C202120 });
|
foot::Emulator emu
|
||||||
if (!check(0xFFFFFFFF, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00200007, 0x01210207, 0x0C202120 });
|
||||||
|
if (!check(0xFFFFFFFF, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARNG
|
// ARNG
|
||||||
@ -45,8 +52,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({0x01200207, 0x01210207, 0x0C202120 });
|
foot::Emulator emu
|
||||||
if (!check(0x1, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x01200207, 0x01210207, 0x0C202120 });
|
||||||
|
if (!check(0x1, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -66,9 +77,14 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200002, 0x00210001, 0x01000401, 0x0C202120 });
|
foot::Emulator emu
|
||||||
|
= run_instructions({ 0x00200002, 0x00210001, 0x01000401, 0x0C202120 });
|
||||||
// 1 / 0.5 = 2
|
// 1 / 0.5 = 2
|
||||||
if (!check(2 << 1 /* shift 1 for 31.1 fixed-point format */, emu.register_at(0))) { return 1; }
|
if (!check(2 << 1 /* shift 1 for 31.1 fixed-point format */,
|
||||||
|
emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -11,7 +11,10 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x01200320 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x01200320 });
|
||||||
if (!check(0x00000000, emu.register_at(0))) { return 1; }
|
if (!check(0x00000000, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LONG
|
// LONG
|
||||||
@ -19,7 +22,10 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instruction(0x01200320);
|
foot::Emulator emu = run_instruction(0x01200320);
|
||||||
if (!check(0x00000001, emu.register_at(0))) { return 1; }
|
if (!check(0x00000001, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -31,9 +37,11 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200230, 0x01A003A0 });
|
foot::Emulator emu = run_instructions({ 0x00200230, 0x01A003A0 });
|
||||||
if (!check(0x11111001, emu.register_at(0))) { return 1; }
|
if (!check(0x11111001, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200005, 0x0D200220 });
|
foot::Emulator emu = run_instructions({ 0x00200005, 0x0D200220 });
|
||||||
if (!check(0x1, emu.register_at(0))) { return 1; }
|
if (!check(0x1, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -28,8 +31,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200005, 0x01210202, 0x0D202120 });
|
foot::Emulator emu
|
||||||
if (!check(0x1, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00200005, 0x01210202, 0x0D202120 });
|
||||||
|
if (!check(0x1, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARNG
|
// ARNG
|
||||||
@ -45,8 +52,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x01200202, 0x00210005, 0x0D202120 });
|
foot::Emulator emu
|
||||||
if (!check(0xFFFFFFFE, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x01200202, 0x00210005, 0x0D202120 });
|
||||||
|
if (!check(0xFFFFFFFE, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -66,9 +77,13 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200005, 0x00210003, 0x01000401, 0x0D202120 });
|
foot::Emulator emu
|
||||||
|
= run_instructions({ 0x00200005, 0x00210003, 0x01000401, 0x0D202120 });
|
||||||
|
|
||||||
if (!check(0x2, emu.register_at(0))) { return 1; }
|
if (!check(0x2, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200003, 0x0B200220 });
|
foot::Emulator emu = run_instructions({ 0x00200003, 0x0B200220 });
|
||||||
if (!check(0x0006, emu.register_at(0))) { return 1; }
|
if (!check(0x0006, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -28,8 +31,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200007, 0x01210207, 0x0B202120 });
|
foot::Emulator emu
|
||||||
if (!check(0xFFFFFFCF, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00200007, 0x01210207, 0x0B202120 });
|
||||||
|
if (!check(0xFFFFFFCF, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARNG
|
// ARNG
|
||||||
@ -45,8 +52,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x01200207, 0x01210207, 0x0B202120 });
|
foot::Emulator emu
|
||||||
if (!check(0x31, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x01200207, 0x01210207, 0x0B202120 });
|
||||||
|
if (!check(0x31, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -66,9 +77,14 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00200007, 0x00210007, 0x01000401, 0x0B202120 });
|
foot::Emulator emu
|
||||||
|
= run_instructions({ 0x00200007, 0x00210007, 0x01000401, 0x0B202120 });
|
||||||
// 3.5 * 3.5 = 12.25 -- truncated to 12 with 31.1 fixed-point format
|
// 3.5 * 3.5 = 12.25 -- truncated to 12 with 31.1 fixed-point format
|
||||||
if (!check(12 << 1 /* shift 1 for 31.1 fixed-point format */, emu.register_at(0))) { return 1; }
|
if (!check(12 << 1 /* shift 1 for 31.1 fixed-point format */,
|
||||||
|
emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x06200220 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x06200220 });
|
||||||
if (!check(0x4567 >> 2, emu.register_at(0))) { return 1; }
|
if (!check(0x4567 >> 2, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARNG
|
// ARNG
|
||||||
@ -24,7 +27,10 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x01200204, 0x06200220 });
|
foot::Emulator emu = run_instructions({ 0x01200204, 0x06200220 });
|
||||||
if (!check((-2) >> 2, emu.register_at(0))) { return 1; }
|
if (!check((-2) >> 2, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -40,8 +46,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00208686, 0x00211122, 0x06A021A0 });
|
foot::Emulator emu
|
||||||
if (!check(0x4321, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00208686, 0x00211122, 0x06A021A0 });
|
||||||
|
if (!check(0x4321, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x0A200220 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x0A200220 });
|
||||||
if (!check(0x4565, emu.register_at(0))) { return 1; }
|
if (!check(0x4565, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -28,8 +31,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x01210202, 0x0A202120 });
|
foot::Emulator emu
|
||||||
if (!check(0x4569, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00204567, 0x01210202, 0x0A202120 });
|
||||||
|
if (!check(0x4569, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
#include <foot-emulator.h>
|
#include <foot-emulator.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
inline foot::Emulator run_instruction(uint32_t instruction)
|
inline foot::Emulator run_instruction(uint32_t instruction)
|
||||||
@ -12,7 +11,8 @@ inline foot::Emulator run_instruction(uint32_t instruction)
|
|||||||
return emu;
|
return emu;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline foot::Emulator run_instructions(const std::vector<uint32_t>& instructions)
|
inline foot::Emulator
|
||||||
|
run_instructions(const std::vector<uint32_t>& instructions)
|
||||||
{
|
{
|
||||||
foot::Emulator emu;
|
foot::Emulator emu;
|
||||||
for (int i = 0; i < instructions.size(); i++)
|
for (int i = 0; i < instructions.size(); i++)
|
||||||
@ -32,7 +32,8 @@ inline bool check(uint32_t expected, uint32_t actual)
|
|||||||
{
|
{
|
||||||
if (actual != expected)
|
if (actual != expected)
|
||||||
{
|
{
|
||||||
std::cout << std::hex << "Expected " << expected << ", got " << actual << ".\n";
|
std::cout << std::hex << "Expected " << expected << ", got " << actual
|
||||||
|
<< ".\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#include "test-common.h"
|
#include "test-common.h"
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x07200220 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x07200220 });
|
||||||
if (!check(0x0001159C, emu.register_at(0))) { return 1; }
|
if (!check(0x0001159C, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -28,10 +31,13 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204343, 0x00211122, 0x07A021A0 });
|
foot::Emulator emu
|
||||||
if (!check(0x860C, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00204343, 0x00211122, 0x07A021A0 });
|
||||||
|
if (!check(0x860C, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,10 @@ int main(int argc, char** argv)
|
|||||||
// b - 2, Immediate
|
// b - 2, Immediate
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204567, 0x05200220 });
|
foot::Emulator emu = run_instructions({ 0x00204567, 0x05200220 });
|
||||||
if (!check(0x1159, emu.register_at(0))) { return 1; }
|
if (!check(0x1159, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNST
|
// CNST
|
||||||
@ -28,8 +31,12 @@ int main(int argc, char** argv)
|
|||||||
// a - 0, Direct
|
// a - 0, Direct
|
||||||
// b - 1, Direct
|
// b - 1, Direct
|
||||||
{
|
{
|
||||||
foot::Emulator emu = run_instructions({ 0x00204343, 0x00211122, 0x05A021A0 });
|
foot::Emulator emu
|
||||||
if (!check(0x2110, emu.register_at(0))) { return 1; }
|
= run_instructions({ 0x00204343, 0x00211122, 0x05A021A0 });
|
||||||
|
if (!check(0x2110, emu.register_at(0)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
15
foot.script
15
foot.script
@ -7,5 +7,18 @@ ENTRY(entry);
|
|||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
. = 0x0;
|
.text :
|
||||||
|
{
|
||||||
|
*(.text)
|
||||||
|
*(.text.*)
|
||||||
|
_etext = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : AT(_etext)
|
||||||
|
{
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata.*)
|
||||||
|
*(.data)
|
||||||
|
*(.data.*)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
vcppd
Submodule
1
vcppd
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 83ff5b450cf5590d222aeb13e4dbd3f7fcf47f63
|
||||||
Loading…
x
Reference in New Issue
Block a user