Initial commit
This commit is contained in:
commit
67fb02c3e7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/cmake-build-debug
|
||||
/.idea
|
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.17)
|
||||
project(rrcSmall)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
add_executable(rrcSmall src/main.cpp src/ImageFormat.cpp src/ImageFormat.h)
|
24
COPYING
Normal file
24
COPYING
Normal file
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2020, rrcSmall FM10K-Documentation Contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# rrcSmall
|
||||
This is a tool used to interact with and decode FM10K Non-Volatile Memory images.
|
||||
|
||||
Currently it extracts configuration values from image, and decode / execute the state machine partially to try to reach every executable code present on image.
|
1302
src/ImageFormat.cpp
Normal file
1302
src/ImageFormat.cpp
Normal file
|
@ -0,0 +1,1302 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2020, rrcSmall FM10K-Documentation Contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <fstream>
|
||||
#include "ImageFormat.h"
|
||||
|
||||
ImageFormat ImageFormat::fromBytes(const std::vector<uint8_t>& imageBytes, bool prefetchInitialZone) {
|
||||
ImageFormat image;
|
||||
|
||||
image.baseImage = imageBytes;
|
||||
|
||||
uint32_t offset = 0;
|
||||
|
||||
uint8_t spiConfig = imageBytes[offset++];
|
||||
image.header.reserved = (spiConfig) & 0b111;
|
||||
image.header.speed = static_cast<HeaderSpeed>((spiConfig >> 3) & 0b111);
|
||||
image.header.mode = static_cast<HeaderMode>((spiConfig >> 6) & 0b11);
|
||||
|
||||
image.header.baseAddress = imageBytes[offset++] << 16;
|
||||
image.header.baseAddress |= imageBytes[offset++] << 8;
|
||||
image.header.baseAddress |= imageBytes[offset++];
|
||||
|
||||
printf("SPEED: 0x%02x MODE: 0x%02x BOOT: 0x%08x\n", image.header.speed, image.header.mode, image.header.baseAddress);
|
||||
|
||||
|
||||
offset = CFG_HEADER;
|
||||
image.cfgHeader.length = imageBytes[offset++];
|
||||
|
||||
image.cfgHeader.base = imageBytes[offset++] << 16;
|
||||
image.cfgHeader.base |= imageBytes[offset++] << 8;
|
||||
image.cfgHeader.base |= imageBytes[offset++];
|
||||
|
||||
offset = image.cfgHeader.base;
|
||||
|
||||
image.cfg.fileFormat = imageBytes[offset++];
|
||||
image.cfg.version = imageBytes[offset++];
|
||||
|
||||
image.cfg.length = imageBytes[offset++] << 8;
|
||||
image.cfg.length |= imageBytes[offset++];
|
||||
image.cfg.length <<= 4;
|
||||
|
||||
|
||||
if(image.cfg.version == 0){
|
||||
if(image.cfg.fileFormat == 1){
|
||||
offset = image.cfgHeader.base + CFG_LENGTH;
|
||||
uint32_t imageSize = image.cfg.length;
|
||||
uint32_t bytesCnt = 0;
|
||||
|
||||
while (bytesCnt < imageSize){
|
||||
std::string currentEntry;
|
||||
uint8_t character;
|
||||
do{
|
||||
character = imageBytes[offset++];
|
||||
++bytesCnt;
|
||||
if(character != '\n' && character != 0xFF){
|
||||
currentEntry += character;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if(!currentEntry.empty()){
|
||||
image.bootConfig.push_back(currentEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> branches;
|
||||
|
||||
/*if(prefetchInitialZone){
|
||||
offset = image.header.baseAddress;
|
||||
|
||||
Instruction::Command lastOp;
|
||||
do{
|
||||
auto instruction = Instruction::fromBytes(offset, image.baseImage);
|
||||
offset = instruction.endAddress;
|
||||
lastOp = instruction.getCommand();
|
||||
|
||||
if(instruction.getCommand() != Instruction::Command::NOP){
|
||||
auto nextBranches = instruction.getPossibleBranches();
|
||||
branches.insert(branches.end(), nextBranches.begin(), nextBranches.end());
|
||||
image.instructions.push_back(std::move(instruction));
|
||||
}
|
||||
} while (lastOp != Instruction::Command::END && lastOp != Instruction::Command::NOP);
|
||||
}else{*/
|
||||
branches.push_back(image.header.baseAddress);
|
||||
//}
|
||||
|
||||
image.decodeAnalyzeInstructionsAt(image.header.baseAddress);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ImageFormat::decodeAnalyzeInstructionsAt(uint32_t offset) {
|
||||
std::queue<AnalysisState> savedStates;
|
||||
std::vector<AnalysisState> branchingStates;
|
||||
|
||||
std::unordered_map<uint32_t, bool> jumpsUsed;
|
||||
|
||||
AnalysisState baseState(offset);
|
||||
|
||||
savedStates.push(baseState);
|
||||
|
||||
std::unordered_map<uint32_t, Instruction> decodedInstructions;
|
||||
|
||||
uint32_t maxUnchangedExecutions = 1000;
|
||||
uint32_t maxTotalExecutions = 20000;
|
||||
|
||||
while(!savedStates.empty()){
|
||||
AnalysisState state = savedStates.front();
|
||||
savedStates.pop();
|
||||
|
||||
uint32_t loopsSinceLastModification = 0;
|
||||
uint32_t absoluteLoops = 0;
|
||||
|
||||
do{
|
||||
|
||||
if(state.current >= 0x100000 || state.current % 4 != 0){
|
||||
break;
|
||||
}else if(decodedInstructions.find(state.current) == decodedInstructions.end()){
|
||||
auto decodedInstruction = Instruction::fromBytes(state.current, baseImage);
|
||||
decodedInstructions[decodedInstruction.address] = decodedInstruction;
|
||||
instructions.emplace_back(decodedInstruction);
|
||||
}
|
||||
|
||||
if(loopsSinceLastModification > 800){
|
||||
//std::cout << "TOO UNCHANGED " << std::hex << state.previous << " -> " << std::hex << state.current << "\n";
|
||||
|
||||
}
|
||||
|
||||
jumpsUsed[state.current] = true;
|
||||
|
||||
state.previous = state.current;
|
||||
const auto& instruction = decodedInstructions[state.current];
|
||||
|
||||
state.current &= 0xfffffffc;
|
||||
|
||||
if(instruction.getCommand() == Instruction::Command::NOP){
|
||||
break;
|
||||
}
|
||||
|
||||
auto possibleBranches = instruction.execute(state);
|
||||
|
||||
if((instruction.getCommand() == Instruction::Command::JUMP || instruction.getCommand() == Instruction::Command::RETURN) && jumpsUsed.find(instruction.endAddress) == jumpsUsed.end()){
|
||||
jumpsUsed[instruction.endAddress] = false;
|
||||
}
|
||||
|
||||
if(state.current == 0){
|
||||
//std::cout << "EXIT DUE TO END " << std::hex << state.previous << " -> " << std::hex << state.current << "\n";
|
||||
break;
|
||||
}
|
||||
|
||||
if(instruction.getCommand() == Instruction::Command::JUMP){
|
||||
auto previousInstruction = findInstructionByAddress(instruction.address - 1);
|
||||
while (previousInstruction != nullptr){
|
||||
const Instruction& prevInstructionRef = *previousInstruction;
|
||||
if(previousInstruction->getCommand() == Instruction::Command::WRITE){
|
||||
if(
|
||||
(
|
||||
previousInstruction->parameters[0] == (uint32_t)Instruction::KnownRegisters::MGMT_SCRATCH_1
|
||||
|| (previousInstruction->parameters[0] >= (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_START && previousInstruction->parameters[0] < (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_END)
|
||||
)
|
||||
&& previousInstruction->parameters.size() == 2
|
||||
){ //This is commonly used before jumps to mark return values or switch statements
|
||||
if(jumpsUsed.find(previousInstruction->parameters[1]) == jumpsUsed.end()){
|
||||
jumpsUsed[previousInstruction->parameters[1]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
previousInstruction = findInstructionByAddress(previousInstruction->address - 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(const auto& branch : possibleBranches){
|
||||
AnalysisState newState(state);
|
||||
newState.current = branch.first & 0xfffffffc;
|
||||
for(const auto& entry : branch.second){
|
||||
newState.setRegister(entry.first, entry.second);
|
||||
}
|
||||
|
||||
bool stateExists = false;
|
||||
for(const auto& previousState : branchingStates){
|
||||
if(newState.previous == previousState.previous && newState.current == previousState.current){
|
||||
stateExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!stateExists){
|
||||
loopsSinceLastModification = 0;
|
||||
savedStates.push(newState);
|
||||
branchingStates.push_back(std::move(newState));
|
||||
}
|
||||
}
|
||||
} while (loopsSinceLastModification++ < maxUnchangedExecutions && absoluteLoops++ < maxTotalExecutions);
|
||||
|
||||
if(savedStates.empty()){
|
||||
for(auto& visited : jumpsUsed){
|
||||
if(!visited.second && visited.first >= offset && visited.first <= 0x100000 && visited.first % 4 == 0){
|
||||
baseState.current = visited.first;
|
||||
savedStates.push(baseState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Next state, branched states left: " << std::dec << savedStates.size() << /*", executed states: " << std::dec << createdStates.size() <<*/ ", total instructions decoded: " << std::dec << decodedInstructions.size() << "\n";
|
||||
}
|
||||
struct by_address {
|
||||
bool operator()(const ImageFormat::Instruction &a, const ImageFormat::Instruction &b) const {
|
||||
return a.address < b.address;
|
||||
}
|
||||
};
|
||||
|
||||
std::sort(instructions.begin(), instructions.end(), by_address());
|
||||
}
|
||||
|
||||
void ImageFormat::decodeInstructionsAt(const std::vector<uint32_t>& offset) {
|
||||
std::vector<uint32_t> branches;
|
||||
|
||||
std::vector<std::pair<uint32_t,uint32_t>> possibleBranches;
|
||||
|
||||
branches.insert(branches.end(), offset.begin(), offset.end());
|
||||
|
||||
while (!branches.empty()){
|
||||
uint32_t currentBranchOffset = branches.back();
|
||||
branches.pop_back();
|
||||
|
||||
if(findInstructionByAddress(currentBranchOffset) != nullptr){
|
||||
continue;
|
||||
}
|
||||
|
||||
//std::cout << "BRANCH ADDR 0x" << std::hex << std::setw(8) << std::setfill('0') << currentBranchOffset << "\n";
|
||||
|
||||
if(currentBranchOffset < baseImage.size()){
|
||||
auto instruction = Instruction::fromBytes(currentBranchOffset, baseImage);
|
||||
|
||||
if(instruction.getCommand() != Instruction::Command::NOP){
|
||||
auto nextBranches = instruction.getPossibleBranches();
|
||||
|
||||
if(instruction.getCommand() == Instruction::Command::JUMP){ //Do additional checks to find plausible RETURN jump values
|
||||
auto previousInstruction = findInstructionByAddress(instruction.address - 1);
|
||||
while (previousInstruction != nullptr){
|
||||
const Instruction& prevInstructionRef = *previousInstruction;
|
||||
if(previousInstruction->getCommand() == Instruction::Command::WRITE){
|
||||
if(
|
||||
(
|
||||
previousInstruction->parameters[0] == (uint32_t)Instruction::KnownRegisters::MGMT_SCRATCH_1
|
||||
|| (previousInstruction->parameters[0] >= (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_START && previousInstruction->parameters[0] < (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_END)
|
||||
)
|
||||
&& previousInstruction->parameters.size() == 2
|
||||
){ //This is commonly used before jumps to mark return values or switch statements
|
||||
bool registerReturnExists = false;
|
||||
for (const auto& ins : instructions){
|
||||
if(ins.getCommand() == Instruction::Command::RETURN && ins.parameters[0] == previousInstruction->parameters[0]){
|
||||
registerReturnExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(registerReturnExists){
|
||||
branches.push_back(previousInstruction->parameters[1]);
|
||||
}else{
|
||||
possibleBranches.emplace_back(previousInstruction->parameters[0], previousInstruction->parameters[1]);
|
||||
}
|
||||
}
|
||||
|
||||
previousInstruction = findInstructionByAddress(previousInstruction->address - 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if(instruction.getCommand() == Instruction::Command::RETURN){ //Find possible values, and if used, add to list to check
|
||||
for(auto& entry : possibleBranches){
|
||||
if(entry.first == instruction.parameters[0]){
|
||||
branches.push_back(entry.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
branches.insert(branches.end(), nextBranches.begin(), nextBranches.end());
|
||||
instructions.push_back(std::move(instruction));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct by_address {
|
||||
bool operator()(const ImageFormat::Instruction &a, const ImageFormat::Instruction &b) const {
|
||||
return a.address < b.address;
|
||||
}
|
||||
};
|
||||
|
||||
std::sort(instructions.begin(), instructions.end(), by_address());
|
||||
|
||||
}
|
||||
|
||||
ImageFormat::Instruction ImageFormat::Instruction::fromBytes(uint32_t offset, const std::vector<uint8_t>& bytes) {
|
||||
Instruction instruction;
|
||||
|
||||
instruction.address = offset;
|
||||
|
||||
instruction.command = bytes[offset++];
|
||||
|
||||
switch (instruction.getCommand()) {
|
||||
|
||||
case Command::WRITE:
|
||||
{
|
||||
uint8_t len = 1 + (instruction.command >> 2) & 0b1111;
|
||||
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
while (len-- > 0){
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Command::COPY:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::LOAD:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
//This prevents a value of 0 from underflowing below
|
||||
int32_t count = bytes[offset++] << 16;
|
||||
count |= bytes[offset++] << 8;
|
||||
count |= bytes[offset++];
|
||||
instruction.parameters.push_back(count);
|
||||
|
||||
while (count-- > 0){
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Command::INIT:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::CALC:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::CALC_IMM:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::BRANCH:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::POLL:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
offset++;
|
||||
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::LOOP:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::JUMP:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::RETURN:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::SET:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
|
||||
arg = bytes[offset++] << 24;
|
||||
arg |= bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::WAIT:
|
||||
{
|
||||
uint32_t arg;
|
||||
arg = bytes[offset++] << 16;
|
||||
arg |= bytes[offset++] << 8;
|
||||
arg |= bytes[offset++];
|
||||
instruction.parameters.push_back(arg);
|
||||
}
|
||||
break;
|
||||
case Command::END:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
case Command::NOP:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
while ((offset % 4) != 0){
|
||||
offset++;
|
||||
}
|
||||
|
||||
instruction.endAddress = offset;
|
||||
|
||||
return instruction;
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>> ImageFormat::Instruction::execute(AnalysisState& state) const {
|
||||
std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>> branches;
|
||||
|
||||
switch (getCommand()) {
|
||||
case Command::WRITE:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
|
||||
uint32_t index = 0;
|
||||
for (auto it = parameters.begin() + 1; it != parameters.end(); ++it) {
|
||||
state.setRegister(memoryOffset + parameters[0] + index, *it);
|
||||
index++;
|
||||
}
|
||||
|
||||
state.current = endAddress;
|
||||
}
|
||||
break;
|
||||
case Command::COPY:
|
||||
{
|
||||
uint32_t memoryOffsetAA = state.getAddressOffset(command & 0b11);
|
||||
uint32_t memoryOffsetBB = state.getAddressOffset((command >> 2) & 0b11);
|
||||
uint8_t words = (1 + ((command >> 4) & 0b11)) * 4;
|
||||
|
||||
for (uint32_t index = 0; index < words; ++index) {
|
||||
state.setRegister(memoryOffsetAA + parameters[0] + index, state.getRegister(memoryOffsetBB + parameters[1] + index));
|
||||
}
|
||||
|
||||
state.current = endAddress;
|
||||
}
|
||||
break;
|
||||
case Command::LOAD:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
uint8_t increment = ((command >> 2) & 0b1);
|
||||
|
||||
uint32_t index = 0;
|
||||
for (auto it = parameters.begin() + 2; it != parameters.end(); ++it) {
|
||||
state.setRegister(memoryOffset + parameters[0] + index, *it);
|
||||
|
||||
index += increment;
|
||||
}
|
||||
|
||||
state.current = endAddress;
|
||||
}
|
||||
break;
|
||||
case Command::INIT:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
uint8_t increment = ((command >> 2) & 0b1);
|
||||
|
||||
|
||||
for (uint32_t index = 0; index < parameters[1]; ++index) {
|
||||
state.setRegister(memoryOffset + parameters[0] + (increment ? index : 0), state.getRegister(parameters[2] + parameters[3] * index));
|
||||
}
|
||||
|
||||
state.current = endAddress;
|
||||
}
|
||||
break;
|
||||
case Command::CALC:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
uint8_t operation = ((command >> 2) & 0b111);
|
||||
|
||||
uint32_t v_a = state.getRegister(memoryOffset + parameters[1]);
|
||||
uint32_t v_b = state.getRegister(memoryOffset + parameters[2]);
|
||||
|
||||
switch (operation) {
|
||||
case 0:
|
||||
state.setRegister(parameters[0], v_a & v_b);
|
||||
break;
|
||||
case 1:
|
||||
state.setRegister(parameters[0], v_a | v_b);
|
||||
break;
|
||||
case 2:
|
||||
state.setRegister(parameters[0], v_a + v_b);
|
||||
break;
|
||||
case 3:state.setRegister(parameters[0], v_a - v_b);
|
||||
break;
|
||||
case 4:state.setRegister(parameters[0], v_a << v_b);
|
||||
break;
|
||||
case 5:
|
||||
state.setRegister(parameters[0], v_a >> v_b);
|
||||
break;
|
||||
case 6:
|
||||
state.setRegister(parameters[0], (uint32_t)((int32_t)v_a >> v_b));
|
||||
break;
|
||||
case 7:
|
||||
//TODO: Undefined?
|
||||
break;
|
||||
}
|
||||
|
||||
state.current = endAddress;
|
||||
}
|
||||
break;
|
||||
case Command::CALC_IMM:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
uint8_t operation = ((command >> 2) & 0b111);
|
||||
|
||||
uint32_t v_a = state.getRegister(memoryOffset + parameters[1]);
|
||||
uint32_t v_b = parameters[2];
|
||||
|
||||
switch (operation) {
|
||||
case 0:
|
||||
state.setRegister(parameters[0], v_a & v_b);
|
||||
break;
|
||||
case 1:
|
||||
state.setRegister(parameters[0], v_a | v_b);
|
||||
break;
|
||||
case 2:
|
||||
state.setRegister(parameters[0], v_a + v_b);
|
||||
break;
|
||||
case 3:state.setRegister(parameters[0], v_a - v_b);
|
||||
break;
|
||||
case 4:state.setRegister(parameters[0], v_a << v_b);
|
||||
break;
|
||||
case 5:
|
||||
state.setRegister(parameters[0], v_a >> v_b);
|
||||
break;
|
||||
case 6:
|
||||
state.setRegister(parameters[0], (uint32_t)((int32_t)v_a >> v_b));
|
||||
break;
|
||||
case 7:
|
||||
//TODO: Undefined?
|
||||
break;
|
||||
}
|
||||
|
||||
state.current = endAddress;
|
||||
}
|
||||
break;
|
||||
case Command::BRANCH:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
uint8_t equality = (command >> 2) & 0b1;
|
||||
|
||||
/*if((memoryOffset + parameters[3]) % 4 != 0){
|
||||
state.current = 0;
|
||||
} else {*/
|
||||
if((state.getRegister(memoryOffset + parameters[0]) & parameters[2]) == parameters[1]){
|
||||
state.current = equality ? memoryOffset + parameters[3] : endAddress;
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> branchedState;
|
||||
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? ~parameters[1] & parameters[2] : parameters[1]);
|
||||
branches.emplace_back(equality ? endAddress : memoryOffset + parameters[3], std::move(branchedState));
|
||||
}else{
|
||||
state.current = equality ? endAddress : memoryOffset + parameters[3];
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> branchedState;
|
||||
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? parameters[1] : ~parameters[1] & parameters[2]);
|
||||
branches.emplace_back(equality ? memoryOffset + parameters[3] : endAddress, std::move(branchedState));
|
||||
}
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case Command::POLL:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
uint8_t equality = (command >> 2) & 0b1;
|
||||
|
||||
/*if((memoryOffset + parameters[5]) % 4 != 0){
|
||||
state.current = 0;
|
||||
} else {*/
|
||||
if((state.getRegister(memoryOffset + parameters[0]) & parameters[2]) == parameters[1]){
|
||||
state.current = equality ? endAddress : memoryOffset + parameters[5];
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> branchedState;
|
||||
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? parameters[1] : ~parameters[1] & parameters[2]);
|
||||
branches.emplace_back(equality ? memoryOffset + parameters[3] : endAddress, std::move(branchedState));
|
||||
}else{
|
||||
state.current = equality ? memoryOffset + parameters[5] : endAddress;
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> branchedState;
|
||||
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? ~parameters[1] & parameters[2] : parameters[1]);
|
||||
branches.emplace_back(equality ? endAddress : memoryOffset + parameters[3], std::move(branchedState));
|
||||
}
|
||||
//}
|
||||
|
||||
}
|
||||
break;
|
||||
case Command::LOOP:
|
||||
{
|
||||
uint32_t counterAddress = (uint32_t)KnownRegisters::BSM_COUNTER_0 + (command & 0b1);
|
||||
|
||||
/*if(parameters[0] % 4 != 0){
|
||||
state.current = 0;
|
||||
} else {*/
|
||||
if(state.getRegister(counterAddress) == 0){
|
||||
state.current = endAddress;
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> branchedState;
|
||||
branchedState[counterAddress] = 1;
|
||||
branches.emplace_back(parameters[0], std::move(branchedState));
|
||||
}else{
|
||||
state.current = parameters[0];
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> branchedState;
|
||||
branchedState[counterAddress] = 0;
|
||||
branches.emplace_back(endAddress, std::move(branchedState));
|
||||
}
|
||||
//}
|
||||
}
|
||||
break;
|
||||
case Command::JUMP:
|
||||
{
|
||||
/*if(parameters[0] % 4 != 0){
|
||||
state.current = 0;
|
||||
} else {*/
|
||||
state.current = parameters[0];
|
||||
//}
|
||||
}
|
||||
break;
|
||||
case Command::RETURN:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
uint32_t jumpAddress = state.getRegister(memoryOffset + parameters[0]);
|
||||
//std::cout << std::hex << address << ": RETURN @ " << getAddressRegisterName(parameters[0]) << " : 0x" << jumpAddress << "\n";
|
||||
/*if(jumpAddress % 4 != 0){
|
||||
state.current = 0;
|
||||
} else {*/
|
||||
state.current = jumpAddress;
|
||||
//}
|
||||
}
|
||||
break;
|
||||
case Command::SET:
|
||||
{
|
||||
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
|
||||
|
||||
state.setRegister(memoryOffset + parameters[0], (state.getRegister(memoryOffset + parameters[0]) & ~parameters[2]) | parameters[1]);
|
||||
|
||||
state.current = endAddress;
|
||||
}
|
||||
case Command::WAIT:
|
||||
//TODO: do we even wait when simulating?
|
||||
state.current = endAddress;
|
||||
break;
|
||||
case Command::END:
|
||||
case Command::NOP:
|
||||
state.current = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ImageFormat::Instruction::getPossibleBranches() const{
|
||||
std::vector<uint32_t> branches;
|
||||
|
||||
switch (getCommand()) {
|
||||
|
||||
case Command::WRITE:
|
||||
case Command::COPY:
|
||||
case Command::LOAD:
|
||||
case Command::INIT:
|
||||
case Command::CALC:
|
||||
case Command::CALC_IMM:
|
||||
case Command::SET:
|
||||
case Command::WAIT:
|
||||
branches.push_back(endAddress);
|
||||
break;
|
||||
|
||||
case Command::BRANCH:
|
||||
branches.push_back(endAddress);
|
||||
branches.push_back(parameters[3]);
|
||||
break;
|
||||
case Command::POLL:
|
||||
branches.push_back(endAddress);
|
||||
branches.push_back(parameters[5]);
|
||||
break;
|
||||
case Command::LOOP:
|
||||
branches.push_back(endAddress);
|
||||
branches.push_back(parameters[0]);
|
||||
break;
|
||||
case Command::JUMP:
|
||||
branches.push_back(parameters[0]);
|
||||
break;
|
||||
|
||||
case Command::RETURN:
|
||||
break;
|
||||
case Command::END:
|
||||
case Command::NOP:
|
||||
break;
|
||||
}
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
std::string ImageFormat::Instruction::toString() const{
|
||||
std::stringstream op;
|
||||
op << std::hex << std::setw(8) << std::setfill('0') << address << ": ";
|
||||
|
||||
switch (getCommand()) {
|
||||
case Command::WRITE:
|
||||
{
|
||||
uint8_t offset = (command & 0b11);
|
||||
op << "WRITE " << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " = 0x";
|
||||
|
||||
for (auto it = parameters.end() - 1; it != parameters.begin(); --it) {
|
||||
op << std::hex << std::setw(parameters.size() == 2 ? 2 : 8) << std::setfill('0') << *it;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Command::COPY:
|
||||
{
|
||||
uint8_t offsetAA = (command & 0b11);
|
||||
uint8_t offsetBB = ((command >> 2) & 0b11);
|
||||
uint8_t words = (1 + ((command >> 4) & 0b11)) * 4;
|
||||
op << "COPY " << (uint32_t) words << " BYTES FROM " << getAddressRegisterName(parameters[0]);
|
||||
if(offsetAA){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offsetAA << "]";
|
||||
}
|
||||
op << " TO " << getAddressRegisterName(parameters[1]);
|
||||
if(offsetBB){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[1] << ") + BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offsetBB << "]";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Command::LOAD:
|
||||
{
|
||||
uint8_t offset = (command & 0b11);
|
||||
uint8_t increment = ((command >> 2) & 0b1);
|
||||
op << "LOAD " << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
if(parameters[1] > 1 && increment){
|
||||
op << " TO " << getAddressRegisterName(parameters[0] + (parameters[1] - 1) * 4);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] + (parameters[1] - 1) * 4 << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
}
|
||||
op << " INCREMENT " << std::dec << (uint32_t)increment;
|
||||
op << " DATA 0x";
|
||||
|
||||
for (auto it = parameters.begin() + 2; it != parameters.end(); ++it) {
|
||||
op << std::hex << std::setw(8) << std::setfill('0') << *it;
|
||||
}
|
||||
|
||||
if(parameters[1] > 1){
|
||||
op << "\n";
|
||||
|
||||
uint32_t off = 0;
|
||||
for (auto it = parameters.begin() + 2; it != parameters.end(); ++it) {
|
||||
op << " - " << getAddressRegisterName(parameters[0] + (increment ? off++ : 0));
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] + (increment ? off - 1 : 0) << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " = 0x" << std::hex << std::setw(2) << std::setfill('0') << *it << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Command::INIT:
|
||||
{
|
||||
uint8_t offset = (command & 0b11);
|
||||
uint8_t increment = ((command >> 2) & 0b1);
|
||||
op << "INIT " << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " COUNT " << std::hex << std::setw(6) << std::setfill('0') << parameters[1];
|
||||
op << " INCREMENT " << std::dec << (uint32_t)increment;
|
||||
op << " DATA 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2];
|
||||
op << " INC 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[3];
|
||||
}
|
||||
break;
|
||||
case Command::CALC:
|
||||
{
|
||||
uint8_t offset = (command & 0b11);
|
||||
uint8_t operation = ((command >> 2) & 0b111);
|
||||
op << "CALC " << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " = " << getAddressRegisterName(parameters[1]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[1] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " ";
|
||||
switch (operation) {
|
||||
case 0:
|
||||
op << "&";
|
||||
break;
|
||||
case 1:
|
||||
op << "|";
|
||||
break;
|
||||
case 2:
|
||||
op << "+";
|
||||
break;
|
||||
case 3:
|
||||
op << "-";
|
||||
break;
|
||||
case 4:
|
||||
op << "<<";
|
||||
break;
|
||||
case 5:
|
||||
op << ">>>";
|
||||
break;
|
||||
case 6:
|
||||
op << ">>";
|
||||
break;
|
||||
case 7:
|
||||
op << "UNDEFINED";
|
||||
break;
|
||||
}
|
||||
op << " " << getAddressRegisterName(parameters[2]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[2] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Command::CALC_IMM:
|
||||
{
|
||||
uint8_t offset = (command & 0b11);
|
||||
uint8_t operation = ((command >> 2) & 0b111);
|
||||
op << "CALC_IMM " << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " = " << getAddressRegisterName(parameters[1]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[1] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " ";
|
||||
switch (operation) {
|
||||
case 0:
|
||||
op << "&";
|
||||
break;
|
||||
case 1:
|
||||
op << "|";
|
||||
break;
|
||||
case 2:
|
||||
op << "+";
|
||||
break;
|
||||
case 3:
|
||||
op << "-";
|
||||
break;
|
||||
case 4:
|
||||
op << "<<";
|
||||
break;
|
||||
case 5:
|
||||
op << ">>>";
|
||||
break;
|
||||
case 6:
|
||||
op << ">>";
|
||||
break;
|
||||
case 7:
|
||||
op << "UNDEFINED";
|
||||
break;
|
||||
}
|
||||
op << " 0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[2];
|
||||
}
|
||||
break;
|
||||
case Command::BRANCH:
|
||||
{
|
||||
uint8_t equality = ((command >> 2) & 0b1);
|
||||
uint8_t offset = (command & 0b11);
|
||||
op << "BRANCH IF (" << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
//op << " & ~0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
|
||||
op << " & 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
|
||||
if(equality){
|
||||
op << " == ";
|
||||
}else{
|
||||
op << " != ";
|
||||
}
|
||||
op << "0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[1] << " JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[3];
|
||||
}
|
||||
break;
|
||||
case Command::POLL:
|
||||
{
|
||||
uint8_t equality = ((command >> 2) & 0b1);
|
||||
uint8_t offset = (command & 0b11);
|
||||
op << "POLL IF (" << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
//op << " & ~0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
|
||||
op << " & 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
|
||||
if(equality){
|
||||
op << " != ";
|
||||
}else{
|
||||
op << " == ";
|
||||
}
|
||||
op << "0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[1] << " WAIT " << std::dec << parameters[4] << " MAX " << std::dec << parameters[3] << " EXCEED JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[3];
|
||||
}
|
||||
break;
|
||||
case Command::LOOP:
|
||||
{
|
||||
uint8_t counter = (command & 0b1);
|
||||
op << "LOOP IF BSM_COUNTER[" << std::dec << (uint32_t)counter << "] != 0 JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[0];
|
||||
}
|
||||
break;
|
||||
case Command::JUMP:
|
||||
op << "JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[0];
|
||||
break;
|
||||
case Command::RETURN:
|
||||
{
|
||||
|
||||
uint8_t offset = (command & 0b11);
|
||||
op << "RETURN " << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Command::SET:
|
||||
{
|
||||
uint8_t offset = (command & 0b11);
|
||||
op << "SET " << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
op << " = (" << getAddressRegisterName(parameters[0]);
|
||||
if(offset){
|
||||
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
|
||||
}
|
||||
//op << " & ~0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ") | 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[1];
|
||||
op << " & 0x" << std::hex << std::setw(8) << std::setfill('0') << ~parameters[2] << ") | 0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[1];
|
||||
break;
|
||||
}
|
||||
case Command::WAIT:
|
||||
op << "WAIT " << std::dec << parameters[0];
|
||||
break;
|
||||
case Command::END:
|
||||
op << "END";
|
||||
break;
|
||||
case Command::NOP:
|
||||
op << "NOP 0x" << std::hex << std::setw(2) << std::setfill('0') << command;
|
||||
break;
|
||||
}
|
||||
|
||||
return op.str();
|
||||
}
|
||||
|
||||
std::string ImageFormat::Instruction::getRegisterName(ImageFormat::Instruction::KnownRegisters addr) {
|
||||
if((uint32_t)addr >= (uint32_t)KnownRegisters::BSM_SCRATCH_START && (uint32_t)addr <= (uint32_t)KnownRegisters::BSM_SCRATCH_END){
|
||||
std::stringstream s;
|
||||
s << "BSM_SCRATCH[0x" << std::hex << std::setw(3) << std::setfill('0') << (((uint32_t)addr) - (uint32_t)KnownRegisters::BSM_SCRATCH_START) << "]";
|
||||
return s.str();
|
||||
}
|
||||
switch (addr) {
|
||||
case KnownRegisters::FATAL_CODE:
|
||||
return "FATAL_CODE";
|
||||
case KnownRegisters::LAST_FATAL_CODE:
|
||||
return "LAST_FATAL_CODE";
|
||||
case KnownRegisters::SOFT_RESET:
|
||||
return "SOFT_RESET";
|
||||
case KnownRegisters::DEVICE_CFG:
|
||||
return "DEVICE_CFG";
|
||||
case KnownRegisters::RESET_CFG:
|
||||
return "RESET_CFG";
|
||||
case KnownRegisters::WATCHDOG_CFG:
|
||||
return "WATCHDOG_CFG";
|
||||
case KnownRegisters::MGMT_SCRATCH_0:
|
||||
return "MGMT_SCRATCH[0]";
|
||||
case KnownRegisters::MGMT_SCRATCH_1:
|
||||
return "MGMT_SCRATCH[1]";
|
||||
case KnownRegisters::VITAL_PRODUCT_DATA:
|
||||
return "VITAL_PRODUCT_DATA";
|
||||
case KnownRegisters::GLOBAL_INTERRUPT_DETECT:
|
||||
return "GLOBAL_INTERRUPT_DETECT";
|
||||
case KnownRegisters::INTERRUPT_MASK_BSM:
|
||||
return "INTERRUPT_MASK_BSM";
|
||||
case KnownRegisters::CHIP_VERSION:
|
||||
return "CHIP_VERSION";
|
||||
case KnownRegisters::BSM_ARGS:
|
||||
return "BSM_ARGS";
|
||||
case KnownRegisters::BSM_ADDR_OFFSET_0:
|
||||
return "BSM_ADDR_OFFSET[0]";
|
||||
case KnownRegisters::BSM_ADDR_OFFSET_1:
|
||||
return "BSM_ADDR_OFFSET[1]";
|
||||
case KnownRegisters::BSM_ADDR_OFFSET_2:
|
||||
return "BSM_ADDR_OFFSET[2]";
|
||||
case KnownRegisters::BSM_ADDR_OFFSET_3:
|
||||
return "BSM_ADDR_OFFSET[3]";
|
||||
case KnownRegisters::BSM_COUNTER_0:
|
||||
return "BSM_COUNTER[0]";
|
||||
case KnownRegisters::BSM_COUNTER_1:
|
||||
return "BSM_COUNTER[1]";
|
||||
case KnownRegisters::PLL_PCIE_CTRL:
|
||||
return "PLL_PCIE_CTRL";
|
||||
case KnownRegisters::PLL_PCIE_STAT:
|
||||
return "PLL_PCIE_STAT";
|
||||
case KnownRegisters::PCIE_XPLL_CTRL:
|
||||
return "PCIE_XPLL_CTRL";
|
||||
case KnownRegisters::PCIE_CLK_CTRL:
|
||||
return "PCIE_CLK_CTRL";
|
||||
case KnownRegisters::PCIE_CLK_CTRL_2:
|
||||
return "PCIE_CLK_CTRL_2";
|
||||
case KnownRegisters::PLL_EPL_CTRL:
|
||||
return "PLL_EPL_CTRL";
|
||||
case KnownRegisters::PLL_EPL_STAT:
|
||||
return "PLL_EPL_STAT";
|
||||
case KnownRegisters::PLL_FABRIC_CTRL:
|
||||
return "PLL_FABRIC_CTRL";
|
||||
case KnownRegisters::PLL_FABRIC_STAT:
|
||||
return "PLL_FABRIC_STAT";
|
||||
case KnownRegisters::PLL_FABRIC_LOCK:
|
||||
return "PLL_FABRIC_LOCK";
|
||||
case KnownRegisters::SBUS_EPL_CFG:
|
||||
return "SBUS_EPL_CFG";
|
||||
case KnownRegisters::SBUS_EPL_COMMAND:
|
||||
return "SBUS_EPL_COMMAND";
|
||||
case KnownRegisters::SBUS_EPL_REQUEST:
|
||||
return "SBUS_EPL_REQUEST";
|
||||
case KnownRegisters::SBUS_EPL_RESPONSE:
|
||||
return "SBUS_EPL_RESPONSE";
|
||||
case KnownRegisters::SBUS_EPL_SPICO_IN:
|
||||
return "SBUS_EPL_SPICO_IN";
|
||||
case KnownRegisters::SBUS_EPL_SPICO_OUT:
|
||||
return "SBUS_EPL_SPICO_OUT";
|
||||
case KnownRegisters::SBUS_EPL_IP:
|
||||
return "SBUS_EPL_IP";
|
||||
case KnownRegisters::SBUS_EPL_IM:
|
||||
return "SBUS_EPL_IM";
|
||||
case KnownRegisters::PM_CLKOBS_CTRL:
|
||||
return "PM_CLKOBS_CTRL";
|
||||
case KnownRegisters::GPIO_CFG:
|
||||
return "GPIO_CFG";
|
||||
case KnownRegisters::GPIO_DATA:
|
||||
return "GPIO_DATA";
|
||||
case KnownRegisters::GPIO_IP:
|
||||
return "GPIO_IP";
|
||||
case KnownRegisters::GPIO_IM:
|
||||
return "GPIO_IM";
|
||||
case KnownRegisters::SBUS_PCIE_CFG:
|
||||
return "SBUS_PCIE_CFG";
|
||||
case KnownRegisters::SBUS_PCIE_COMMAND:
|
||||
return "SBUS_PCIE_COMMAND";
|
||||
case KnownRegisters::SBUS_PCIE_REQUEST:
|
||||
return "SBUS_PCIE_REQUEST";
|
||||
case KnownRegisters::SBUS_PCIE_RESPONSE:
|
||||
return "SBUS_PCIE_RESPONSE";
|
||||
case KnownRegisters::SBUS_PCIE_SPICO_IN:
|
||||
return "SBUS_PCIE_SPICO_IN";
|
||||
case KnownRegisters::SBUS_PCIE_SPICO_OUT:
|
||||
return "SBUS_PCIE_SPICO_OUT";
|
||||
case KnownRegisters::SBUS_PCIE_IP:
|
||||
return "SBUS_PCIE_IP";
|
||||
case KnownRegisters::SBUS_PCIE_IM:
|
||||
return "SBUS_PCIE_IM";
|
||||
case KnownRegisters::REI_CTRL:
|
||||
return "REI_CTRL";
|
||||
case KnownRegisters::REI_STAT:
|
||||
return "REI_STAT";
|
||||
case KnownRegisters::BIST_CTRL:
|
||||
return "BIST_CTRL";
|
||||
case KnownRegisters::PCIE_CLKMON_RATIO_CFG:
|
||||
return "PCIE_CLKMON_RATIO_CFG";
|
||||
case KnownRegisters::PCIE_CLKMON_TOLERANCE_CFG:
|
||||
return "PCIE_CLKMON_TOLERANCE_CFG";
|
||||
case KnownRegisters::PCIE_CLKMON_DEADLINES_CFG:
|
||||
return "PCIE_CLKMON_DEADLINES_CFG";
|
||||
case KnownRegisters::PCIE_CLK_STAT:
|
||||
return "PCIE_CLK_STAT";
|
||||
case KnownRegisters::PCIE_CLK_IP:
|
||||
return "PCIE_CLK_IP";
|
||||
case KnownRegisters::PCIE_CLK_IM:
|
||||
return "PCIE_CLK_IM";
|
||||
case KnownRegisters::PCIE_WARM_RESET_DELAY:
|
||||
return "PCIE_WARM_RESET_DELAY";
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
std::stringstream s;
|
||||
s << "register[0x" << std::hex << std::setw(6) << std::setfill('0') << (uint32_t)addr << "]";
|
||||
return s.str();
|
||||
}
|
||||
|
||||
uint32_t ImageFormat::AnalysisState::getAddressOffset(uint8_t offsetEntry) const {
|
||||
if(offsetEntry == 0){
|
||||
return 0;
|
||||
}
|
||||
return getRegister((uint32_t)ImageFormat::Instruction::KnownRegisters::BSM_ADDR_OFFSET_0 + offsetEntry);
|
||||
}
|
320
src/ImageFormat.h
Normal file
320
src/ImageFormat.h
Normal file
|
@ -0,0 +1,320 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2020, rrcSmall FM10K-Documentation Contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
class ImageFormat{
|
||||
|
||||
public:
|
||||
|
||||
class AnalysisState{
|
||||
public:
|
||||
std::unordered_map<uint32_t, uint32_t> memory;
|
||||
uint32_t previous;
|
||||
uint32_t current;
|
||||
|
||||
AnalysisState(uint32_t initial) : current(initial), previous(0){
|
||||
|
||||
}
|
||||
AnalysisState(const AnalysisState& oldState) : current(oldState.current), previous(oldState.previous), memory(oldState.memory){
|
||||
|
||||
}
|
||||
|
||||
bool operator==(const AnalysisState &other) const{
|
||||
return current == other.current && memory == other.memory;
|
||||
}
|
||||
|
||||
void setRegister(uint32_t addr, uint32_t value){
|
||||
memory[addr] = value;
|
||||
}
|
||||
|
||||
uint32_t getRegister(uint32_t addr) const{
|
||||
return memory.find(addr) == memory.end() ? 0 : memory.at(addr);
|
||||
}
|
||||
|
||||
uint32_t getAddressOffset(uint8_t offsetEntry) const;
|
||||
};
|
||||
|
||||
class Instruction{
|
||||
public:
|
||||
Instruction(){
|
||||
|
||||
}
|
||||
Instruction(const Instruction& instruction) : address(instruction.address), endAddress(instruction.endAddress), command(instruction.command), parameters(instruction.parameters){
|
||||
|
||||
}
|
||||
enum class KnownRegisters : uint32_t {
|
||||
FATAL_CODE = 0x0,
|
||||
LAST_FATAL_CODE = 0x1,
|
||||
|
||||
SOFT_RESET = 0x3,
|
||||
DEVICE_CFG = 0x4,
|
||||
RESET_CFG = 0x5,
|
||||
WATCHDOG_CFG = 0x6,
|
||||
|
||||
MGMT_SCRATCH_0 = 0x8,
|
||||
MGMT_SCRATCH_1 = MGMT_SCRATCH_0 + 1,
|
||||
|
||||
VITAL_PRODUCT_DATA = 0x304,
|
||||
|
||||
GLOBAL_INTERRUPT_DETECT = 0x400,
|
||||
|
||||
INTERRUPT_MASK_BSM = 0x442,
|
||||
|
||||
CHIP_VERSION = 0x452,
|
||||
|
||||
BSM_SCRATCH_START = 0x800,
|
||||
BSM_SCRATCH_END = BSM_SCRATCH_START + 0x400 - 1,
|
||||
|
||||
BSM_ARGS = 0x000C01,
|
||||
|
||||
BSM_ADDR_OFFSET_0 = 0x000C04,
|
||||
BSM_ADDR_OFFSET_1 = BSM_ADDR_OFFSET_0 + 1,
|
||||
BSM_ADDR_OFFSET_2 = BSM_ADDR_OFFSET_0 + 2,
|
||||
BSM_ADDR_OFFSET_3 = BSM_ADDR_OFFSET_0 + 3,
|
||||
|
||||
BSM_COUNTER_0 = 0x000C08,
|
||||
BSM_COUNTER_1 = BSM_COUNTER_0 + 1,
|
||||
|
||||
BIST_CTRL = 0x000C10,
|
||||
|
||||
REI_CTRL = 0x000C12,
|
||||
REI_STAT = 0x000C13,
|
||||
|
||||
GPIO_CFG = 0x000C15,
|
||||
GPIO_DATA = 0x000C16,
|
||||
GPIO_IP = 0x000C17,
|
||||
GPIO_IM = 0x000C18,
|
||||
|
||||
PLL_PCIE_CTRL = 0x2241,
|
||||
PLL_PCIE_STAT = 0x2242,
|
||||
SBUS_PCIE_CFG = 0x2243,
|
||||
SBUS_PCIE_COMMAND = 0x2244,
|
||||
SBUS_PCIE_REQUEST = 0x2245,
|
||||
SBUS_PCIE_RESPONSE = 0x2246,
|
||||
SBUS_PCIE_SPICO_IN = 0x2247,
|
||||
SBUS_PCIE_SPICO_OUT = 0x2248,
|
||||
SBUS_PCIE_IP = 0x2249,
|
||||
SBUS_PCIE_IM = 0x224a,
|
||||
|
||||
PCIE_XPLL_CTRL = 0x3000,
|
||||
PCIE_CLK_CTRL = 0x3001,
|
||||
PCIE_CLK_CTRL_2 = 0x3002,
|
||||
PCIE_CLKMON_RATIO_CFG = 0x3003,
|
||||
PCIE_CLKMON_TOLERANCE_CFG = 0x3004,
|
||||
PCIE_CLKMON_DEADLINES_CFG = 0x3005,
|
||||
PCIE_CLK_STAT = 0x3006,
|
||||
PCIE_CLK_IP = 0x3007,
|
||||
PCIE_CLK_IM = 0x3008,
|
||||
PCIE_WARM_RESET_DELAY = 0x3009,
|
||||
|
||||
PORTS_MGMT_BASE_ADDRESS = 0xE8000,
|
||||
PLL_EPL_CTRL = PORTS_MGMT_BASE_ADDRESS + 0x0,
|
||||
PLL_EPL_STAT = PORTS_MGMT_BASE_ADDRESS + 0x1,
|
||||
PLL_FABRIC_CTRL = PORTS_MGMT_BASE_ADDRESS + 0x2,
|
||||
PLL_FABRIC_STAT = PORTS_MGMT_BASE_ADDRESS + 0x3,
|
||||
PLL_FABRIC_LOCK = PORTS_MGMT_BASE_ADDRESS + 0x4,
|
||||
SBUS_EPL_CFG = PORTS_MGMT_BASE_ADDRESS + 0x5,
|
||||
SBUS_EPL_COMMAND = PORTS_MGMT_BASE_ADDRESS + 0x6,
|
||||
SBUS_EPL_REQUEST = PORTS_MGMT_BASE_ADDRESS + 0x7,
|
||||
SBUS_EPL_RESPONSE = PORTS_MGMT_BASE_ADDRESS + 0x8,
|
||||
SBUS_EPL_SPICO_IN = PORTS_MGMT_BASE_ADDRESS + 0x9,
|
||||
SBUS_EPL_SPICO_OUT = PORTS_MGMT_BASE_ADDRESS + 0xa,
|
||||
SBUS_EPL_IP = PORTS_MGMT_BASE_ADDRESS + 0xb,
|
||||
SBUS_EPL_IM = PORTS_MGMT_BASE_ADDRESS + 0xc,
|
||||
|
||||
PM_CLKOBS_CTRL = PORTS_MGMT_BASE_ADDRESS + 0x12,
|
||||
|
||||
PCIE_PF_BASE_ADDRESS = 0x100000,
|
||||
|
||||
NOP = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
static std::string getAddressRegisterName(uint32_t addr){
|
||||
return getRegisterName(static_cast<KnownRegisters>(addr));
|
||||
}
|
||||
|
||||
static std::string getRegisterName(KnownRegisters addr);
|
||||
|
||||
enum class Command : uint8_t {
|
||||
WRITE = 0,
|
||||
COPY,
|
||||
LOAD,
|
||||
INIT,
|
||||
CALC,
|
||||
CALC_IMM,
|
||||
BRANCH,
|
||||
POLL,
|
||||
LOOP,
|
||||
JUMP,
|
||||
RETURN,
|
||||
SET,
|
||||
WAIT,
|
||||
END,
|
||||
NOP = 0xFF
|
||||
};
|
||||
|
||||
uint32_t address;
|
||||
uint32_t endAddress;
|
||||
uint32_t command;
|
||||
std::vector<uint32_t> parameters;
|
||||
|
||||
static Instruction fromBytes(uint32_t offset, const std::vector<uint8_t>& bytes);
|
||||
Instruction(uint32_t address, uint32_t command, std::vector<uint32_t> parameters) : address(address), command(command), parameters(std::move(parameters)), endAddress(0){
|
||||
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::vector<uint32_t> getPossibleBranches() const;
|
||||
|
||||
std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>> execute(AnalysisState& state) const;
|
||||
|
||||
Command getCommand() const{
|
||||
if(((command >> 6) & 0b11) == 0b00){
|
||||
return Command::WRITE;
|
||||
}else if(((command >> 6) & 0b11) == 0b01){
|
||||
return Command::COPY;
|
||||
}else if(((command >> 3) & 0b11111) == 0b11000){
|
||||
return Command::LOAD;
|
||||
}else if(((command >> 3) & 0b11111) == 0b11001){
|
||||
return Command::INIT;
|
||||
}else if(((command >> 5) & 0b111) == 0b100){
|
||||
return Command::CALC;
|
||||
}else if(((command >> 5) & 0b111) == 0b101){
|
||||
return Command::CALC_IMM;
|
||||
}else if(((command >> 3) & 0b11111) == 0b11010){
|
||||
return Command::BRANCH;
|
||||
}else if(((command >> 3) & 0b11111) == 0b11011){
|
||||
return Command::POLL;
|
||||
}else if(((command >> 1) & 0b1111111) == 0b1110000){
|
||||
return Command::LOOP;
|
||||
}else if(command == 0b11101000){
|
||||
return Command::JUMP;
|
||||
}else if(((command >> 2) & 0b111111) == 0b111100){
|
||||
return Command::RETURN;
|
||||
}else if(((command >> 2) & 0b111111) == 0b111110){
|
||||
return Command::SET;
|
||||
}else if(command == 0xFE){
|
||||
return Command::WAIT;
|
||||
}else if(command == 0xFF){
|
||||
return Command::END;
|
||||
}
|
||||
|
||||
return Command::NOP;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum class HeaderSpeed : uint8_t {
|
||||
SPEED_390_KHZ = 0,
|
||||
SPEED_780_KHZ,
|
||||
SPEED_1560_KHZ,
|
||||
SPEED_3125_KHZ,
|
||||
SPEED_6250_KHZ,
|
||||
SPEED_12500_KHZ,
|
||||
SPEED_25000_KHZ,
|
||||
SPEED_50000_KHZ
|
||||
};
|
||||
|
||||
enum class HeaderMode : uint8_t {
|
||||
MODE_SINGLE = 0,
|
||||
MODE_DUAL,
|
||||
MODE_QUAD,
|
||||
MODE_SINGLE_FAST
|
||||
};
|
||||
|
||||
|
||||
static ImageFormat fromBytes(const std::vector<uint8_t>& image, bool prefetchInitialZone = false);
|
||||
|
||||
void decodeInstructionsAt(const std::vector<uint32_t>& offset);
|
||||
void decodeAnalyzeInstructionsAt(uint32_t offset);
|
||||
|
||||
const Instruction* findInstructionByAddress(uint32_t addr){
|
||||
for(const auto& instruction : instructions){
|
||||
if((instruction.endAddress == 0 && addr == instruction.address ) || (instruction.endAddress != 0 && addr >= instruction.address && addr < instruction.endAddress)){
|
||||
return &instruction;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImageFormat(){
|
||||
|
||||
}
|
||||
|
||||
const auto& getHeader() const{
|
||||
return header;
|
||||
}
|
||||
|
||||
const auto& getInstructions() const{
|
||||
return instructions;
|
||||
}
|
||||
|
||||
const auto& getBaseImage() const{
|
||||
return baseImage;
|
||||
}
|
||||
|
||||
const auto& getBootConfig() const{
|
||||
return bootConfig;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct {
|
||||
uint8_t reserved : 3;
|
||||
ImageFormat::HeaderSpeed speed : 3;
|
||||
ImageFormat::HeaderMode mode : 2;
|
||||
uint32_t baseAddress : 24;
|
||||
} header;
|
||||
|
||||
static const int CFG_HEADER = 0x4 << 2;
|
||||
static const int CFG_LENGTH = 16;
|
||||
|
||||
struct {
|
||||
uint8_t length : 8;
|
||||
uint32_t base : 24;
|
||||
} cfgHeader;
|
||||
|
||||
struct {
|
||||
uint8_t fileFormat : 8;
|
||||
uint8_t version : 8;
|
||||
uint16_t length : 16;
|
||||
} cfg;
|
||||
|
||||
std::vector<std::string> bootConfig;
|
||||
|
||||
std::vector<Instruction> instructions;
|
||||
|
||||
std::vector<uint8_t> baseImage;
|
||||
};
|
92
src/main.cpp
Normal file
92
src/main.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2020, rrcSmall FM10K-Documentation Contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "ImageFormat.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if(argc < 2){
|
||||
std::cout << "Usage: " << argv[0] << " nvmImage.bin > decoded.txt\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
std::vector<uint8_t> v{0x00, 0x04, 0x01, 0x04, 0x00, 0x00, 0x00, 0x19};
|
||||
for (uint8_t &c: v)
|
||||
std::cout << std::hex << (uint32_t) c << "\n";
|
||||
auto instruction = ImageFormat::Instruction::fromBytes(0, v);
|
||||
std::cout << instruction.toString();
|
||||
return 0;
|
||||
*/
|
||||
|
||||
std::ifstream image(argv[1]);
|
||||
if(image.is_open()){
|
||||
std::vector<uint8_t> bytes;
|
||||
while(!image.eof()){
|
||||
bytes.push_back(image.get());
|
||||
}
|
||||
auto imageObject = ImageFormat::fromBytes(bytes, false);
|
||||
|
||||
for(auto& k : imageObject.getBootConfig()){
|
||||
std::cout << k << "\n";
|
||||
}
|
||||
|
||||
uint32_t prevAddress = 0;
|
||||
for(const auto& instruction : imageObject.getInstructions()){
|
||||
if(instruction.address < prevAddress){
|
||||
std::cout << "====== DECODE ERROR? ========== " << std::hex << std::setw(8) << std::setfill('0') << prevAddress << " - " << std::hex << std::setw(8) << std::setfill('0') << instruction.address << "\n";
|
||||
}else if((instruction.address - prevAddress) >= 4 && (instruction.address - prevAddress) < 1024){
|
||||
for(uint32_t addr = prevAddress; addr < instruction.address; addr += 4){
|
||||
std::stringstream op;
|
||||
std::string printable;
|
||||
op << std::hex << std::setw(8) << std::setfill('0') << addr << ": ";
|
||||
for(uint32_t i = 0; i < 4; ++i){
|
||||
op << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)bytes[addr + i];
|
||||
if(bytes[addr + i] >= 0x20 && bytes[addr + i] < 0x7F){
|
||||
printable += bytes[addr + i];
|
||||
}else{
|
||||
printable += ".";
|
||||
}
|
||||
}
|
||||
op << " " << printable << "\n";
|
||||
|
||||
std::cout << op.str();
|
||||
}
|
||||
}else if((instruction.address - prevAddress) >= 1024){
|
||||
std::cout << "================ " << std::hex << std::setw(8) << std::setfill('0') << prevAddress << " - " << std::hex << std::setw(8) << std::setfill('0') << instruction.address << "\n";
|
||||
}
|
||||
std::cout << instruction.toString() << "\n";
|
||||
prevAddress = instruction.endAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue