more refactoring, still less exceptions: os
This commit is contained in:
parent
da69725697
commit
e90ef447fc
|
@ -1,26 +1,30 @@
|
|||
//---
|
||||
// fxos.os: Operating system models and primitives
|
||||
//---------------------------------------------------------------------------//
|
||||
// 1100101 |_ mov #0, r4 __ //
|
||||
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
|
||||
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
|
||||
// |_ base# + offset |_| /_\_\___/__/ //
|
||||
//---------------------------------------------------------------------------//
|
||||
// fxos/os: Operating system models and primitives
|
||||
//
|
||||
// This header describes a structure called [OS] which can be attached to a
|
||||
// virtual space and gives information about the operating system.
|
||||
//
|
||||
// The OS structure can only be created for virtual spaces that have a mapping
|
||||
// over ROM and if the OS is recognised. The OS structure requires the whole OS
|
||||
// code to be mapped and cannot provide partial information. If a mapping is
|
||||
// added to a virtual space, any existing OS structure is discarded.
|
||||
// code to be mapped and cannot provide partial information.
|
||||
//
|
||||
// To access OS information for a virtual space, you should use
|
||||
// VirtualSpace::os_analysis(), which caches results and doesn't raise
|
||||
// exceptions.
|
||||
// To access OS information, use VirtualSpace::os_analysis() which caches
|
||||
// results instead of building OS objects directly.
|
||||
//---
|
||||
|
||||
#ifndef LIBFXOS_OS_H
|
||||
#define LIBFXOS_OS_H
|
||||
#ifndef FXOS_OS_H
|
||||
#define FXOS_OS_H
|
||||
|
||||
#include <fxos/util/Addressable.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
|
@ -29,78 +33,79 @@ class VirtualSpace;
|
|||
class OS
|
||||
{
|
||||
public:
|
||||
/* Create an OS interface for this virtual space. If there is no data
|
||||
loaded in ROM, this raises an exception. */
|
||||
OS(VirtualSpace &space);
|
||||
/* Create an OS interface for this virtual space. If there is no data
|
||||
loaded in ROM or the OS can't be identified, the type os OS is set to
|
||||
UNKNOWN and no information is provided. */
|
||||
OS(VirtualSpace &space);
|
||||
|
||||
/* Type of OS, determined at construction */
|
||||
enum Type { FX, CG };
|
||||
Type type;
|
||||
/* Type of OS, determined at construction */
|
||||
enum Type { UNKNOWN, FX, CG };
|
||||
Type type;
|
||||
|
||||
/* Bootcode timestamp and checksum */
|
||||
Addressable<std::string> bootcode_timestamp;
|
||||
Addressable<uint32_t> bootcode_checksum;
|
||||
/* Bootcode timestamp and checksum */
|
||||
Addressable<std::string> bootcode_timestamp;
|
||||
Addressable<uint32_t> bootcode_checksum;
|
||||
|
||||
/* OS version, serial number, timestamp and checksum */
|
||||
Addressable<std::string> version;
|
||||
Addressable<std::string> serial_number;
|
||||
Addressable<std::string> timestamp;
|
||||
Addressable<uint32_t> checksum;
|
||||
/* OS version, serial number, timestamp and checksum */
|
||||
Addressable<std::string> version;
|
||||
Addressable<std::string> serial_number;
|
||||
Addressable<std::string> timestamp;
|
||||
Addressable<uint32_t> checksum;
|
||||
|
||||
/* Separated version components */
|
||||
int version_major, version_minor, version_patch;
|
||||
/* Recomputed checksum */
|
||||
uint32_t computed_checksum;
|
||||
/* Separated version components */
|
||||
int version_major, version_minor, version_patch;
|
||||
/* Recomputed checksum */
|
||||
uint32_t computed_checksum;
|
||||
|
||||
/* Get number of syscalls */
|
||||
int syscall_count() const noexcept;
|
||||
/* Get a syscall entry */
|
||||
uint32_t syscall(int id) const;
|
||||
/* Find a syscall entry. Returns -1 if syscall is not found */
|
||||
int find_syscall(uint32_t entry) const noexcept;
|
||||
/* Get address of syscall table */
|
||||
uint32_t syscall_table_address() const noexcept;
|
||||
/* Get number of syscalls */
|
||||
int syscall_count() const noexcept;
|
||||
/* Get a syscall entry */
|
||||
uint32_t syscall(int id) const;
|
||||
/* Find a syscall entry. Returns -1 if syscall is not found */
|
||||
int find_syscall(uint32_t entry) const noexcept;
|
||||
/* Get address of syscall table */
|
||||
uint32_t syscall_table_address() const noexcept;
|
||||
|
||||
/* Tests against the OS version */
|
||||
bool version_lt(int major, int minor, int patch) const noexcept;
|
||||
bool version_le(int major, int minor, int patch) const noexcept;
|
||||
bool version_gt(int major, int minor, int patch) const noexcept;
|
||||
bool version_ge(int major, int minor, int patch) const noexcept;
|
||||
/* Tests against the OS version */
|
||||
bool version_lt(int major, int minor, int patch) const noexcept;
|
||||
bool version_le(int major, int minor, int patch) const noexcept;
|
||||
bool version_gt(int major, int minor, int patch) const noexcept;
|
||||
bool version_ge(int major, int minor, int patch) const noexcept;
|
||||
|
||||
/* Footer address, or -1 if not found */
|
||||
uint32_t footer;
|
||||
/* Number of langdata entries */
|
||||
int langdata;
|
||||
/* Footer address, or -1 if not found */
|
||||
uint32_t footer;
|
||||
/* Number of langdata entries */
|
||||
int langdata;
|
||||
|
||||
private:
|
||||
/* Virtual space being analyzed */
|
||||
VirtualSpace &m_space;
|
||||
/* Virtual space being analyzed */
|
||||
VirtualSpace &m_space;
|
||||
|
||||
/* Parse the OS header. This should be the first analysis function to
|
||||
be called, because it determines the type of model (ie. fx9860g vs
|
||||
fxcg50) thus the location of the syscall table and many more
|
||||
important parameters. */
|
||||
void parse_header();
|
||||
/* Parse the OS header. This should be the first analysis function to
|
||||
be called, because it determines the type of model (ie. fx9860g vs
|
||||
fxcg50) thus the location of the syscall table and many more
|
||||
important parameters. */
|
||||
void parse_header();
|
||||
|
||||
/* Locate and parse the syscall table. */
|
||||
void parse_syscall_table();
|
||||
/* Locate and parse the syscall table. */
|
||||
void parse_syscall_table();
|
||||
|
||||
/* Locate and parse the footer. */
|
||||
void parse_footer();
|
||||
/* Locate and parse the footer. */
|
||||
void parse_footer();
|
||||
|
||||
/* Compute OS checkum .*/
|
||||
uint32_t compute_checksum() const;
|
||||
/* Compute OS checkum .*/
|
||||
uint32_t compute_checksum() const;
|
||||
|
||||
//---
|
||||
// OS information
|
||||
//---
|
||||
//---
|
||||
// OS information
|
||||
//---
|
||||
|
||||
/* Syscall table, in order of syscall IDs */
|
||||
std::vector<uint32_t> m_syscall_table;
|
||||
/* Bimap converse, syscalls sorted by address */
|
||||
std::map<uint32_t,int> m_syscall_addresses;
|
||||
/* Syscall table, in order of syscall IDs */
|
||||
std::vector<uint32_t> m_syscall_table;
|
||||
/* Bimap converse, syscalls sorted by address */
|
||||
std::map<uint32_t,int> m_syscall_addresses;
|
||||
};
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
||||
#endif /* LIBFXOS_OS_H */
|
||||
#endif /* FXOS_OS_H */
|
||||
|
|
|
@ -1,9 +1,22 @@
|
|||
//---
|
||||
// fxos.semantics: Analyzed data types and locations (OS semantics)
|
||||
// fxos/semantics: High-level data types and values, and location tracking
|
||||
//
|
||||
// TODO: This is a work in progress, not really tested/integrated yet.
|
||||
//
|
||||
// This header is intended to provide the tools needed to analyze disassembled
|
||||
// code. The main idea is to assign to some *location* and *type* and possibly
|
||||
// a *value*.
|
||||
//
|
||||
// The locations considered are defined by RelConst elements. The data types
|
||||
// are 8-bit, 16-bit and 32-bit integers, arrays, strings, bit fields and
|
||||
// structures.
|
||||
//
|
||||
// Notes:
|
||||
// - Aliasing is a huge issue which might result in incorrect analysis. (!)
|
||||
// - The current interface is inefficient and cumbersome. I don't like it
|
||||
//---
|
||||
|
||||
#ifndef LIBFXOS_SEMANTICS_H
|
||||
#define LIBFXOS_SEMANTICS_H
|
||||
#ifndef FXOS_SEMANTICS_H
|
||||
#define FXOS_SEMANTICS_H
|
||||
|
||||
#include <fxos/lang.h>
|
||||
#include <fxos/ai/RelConst.h>
|
||||
|
@ -30,41 +43,41 @@ class DataType;
|
|||
/* Base type: common information for all types (mixin) */
|
||||
struct BaseType
|
||||
{
|
||||
/* Type size in bytes, as would be returned by sizeof(). Must be 1, 2
|
||||
or 4 for integral types and bit fields. Cannot be 0 because all
|
||||
considered types are fixed-size and finite. */
|
||||
size_t size;
|
||||
/* Type alignment, can only be 1, 2 or 4 */
|
||||
size_t align;
|
||||
/* Type size in bytes, as would be returned by sizeof(). Must be 1, 2
|
||||
or 4 for integral types and bit fields. Cannot be 0 because all
|
||||
considered types are fixed-size and finite. */
|
||||
size_t size;
|
||||
/* Type alignment, can only be 1, 2 or 4 */
|
||||
size_t align;
|
||||
};
|
||||
|
||||
/* Integer type; of byte, word or longword size. Plus signedness. This kind is
|
||||
so small that it is enumerated. */
|
||||
struct IntegerType: public BaseType
|
||||
{
|
||||
static DataType const *u8, *i8, *u16, *i16, *u32, *i32;
|
||||
static DataType const *u8, *i8, *u16, *i16, *u32, *i32;
|
||||
|
||||
IntegerType(size_t _size, bool _issigned) {
|
||||
size = align = _size;
|
||||
issigned = _issigned;
|
||||
}
|
||||
IntegerType(size_t _size, bool _issigned) {
|
||||
size = align = _size;
|
||||
issigned = _issigned;
|
||||
}
|
||||
|
||||
/* Whether the type is signed */
|
||||
bool issigned;
|
||||
/* Whether the type is signed */
|
||||
bool issigned;
|
||||
};
|
||||
|
||||
/* Bit fields over bytes, words or longwords. This should satisfy the invariant
|
||||
that the sum of the field sizes is equal to the type size. */
|
||||
struct BitfieldType: public BaseType
|
||||
{
|
||||
/* Fields must have positive size; the name might be empty. */
|
||||
using Field = std::pair<std::string, int>;
|
||||
/* Fields must have positive size; the name might be empty. */
|
||||
using Field = std::pair<std::string, int>;
|
||||
|
||||
std::string name;
|
||||
std::vector<Field> fields;
|
||||
std::string name;
|
||||
std::vector<Field> fields;
|
||||
|
||||
/* Get field by name (throws if not found) */
|
||||
Field named_field(std::string name) const;
|
||||
/* Get field by name (throws if not found) */
|
||||
Field named_field(std::string name) const;
|
||||
};
|
||||
|
||||
/* Homogeneous fixed-size arrays. The number of elements cannot be set to
|
||||
|
@ -72,60 +85,60 @@ struct BitfieldType: public BaseType
|
|||
should equal the size of the array type. */
|
||||
struct ArrayType: public BaseType
|
||||
{
|
||||
struct DataType *object_type;
|
||||
int elements;
|
||||
struct DataType *object_type;
|
||||
int elements;
|
||||
};
|
||||
|
||||
/* Fixed-length string. Size must be positive. */
|
||||
struct StringType: public BaseType
|
||||
{
|
||||
int size;
|
||||
/* Whether string stops at first NUL, or must account for all
|
||||
characters up to the size regardless of NULs */
|
||||
bool nul_terminated;
|
||||
int size;
|
||||
/* Whether string stops at first NUL, or must account for all
|
||||
characters up to the size regardless of NULs */
|
||||
bool nul_terminated;
|
||||
};
|
||||
|
||||
/* Heterogeneous structure types. */
|
||||
struct StructType: public BaseType
|
||||
{
|
||||
/* Fields can be of any type since all are fixed-size. */
|
||||
using Field = std::pair<std::string, DataType>;
|
||||
/* Fields can be of any type since all are fixed-size. */
|
||||
using Field = std::pair<std::string, DataType>;
|
||||
|
||||
std::string name;
|
||||
std::vector<Field> fields;
|
||||
std::string name;
|
||||
std::vector<Field> fields;
|
||||
};
|
||||
|
||||
/* Sum-type-style union. Basically a variant with NAMES. Thank you. */
|
||||
class DataType
|
||||
{
|
||||
public:
|
||||
/* Variant identifier (think of it as a named sum type) */
|
||||
enum DataKind { Integer=0, Bitfield=1, Array=2, String=3, Struct=4 };
|
||||
DataKind kind() const noexcept;
|
||||
/* Variant identifier (think of it as a named sum type) */
|
||||
enum DataKind { Integer=0, Bitfield=1, Array=2, String=3, Struct=4 };
|
||||
DataKind kind() const noexcept;
|
||||
|
||||
/* Common properties */
|
||||
size_t size() const noexcept;
|
||||
size_t align() const noexcept;
|
||||
/* Common properties */
|
||||
size_t size() const noexcept;
|
||||
size_t align() const noexcept;
|
||||
|
||||
/* Access to type-specific data. Exactly one of these can be accessed,
|
||||
depending on the type kind. */
|
||||
/* Access to type-specific data. Exactly one of these can be accessed,
|
||||
depending on the type kind. */
|
||||
|
||||
IntegerType const &integer() const;
|
||||
BitfieldType const &bitfield() const;
|
||||
ArrayType const &array() const;
|
||||
StringType const &string() const;
|
||||
StructType const &structs() const;
|
||||
IntegerType const &integer() const;
|
||||
BitfieldType const &bitfield() const;
|
||||
ArrayType const &array() const;
|
||||
StringType const &string() const;
|
||||
StructType const &structs() const;
|
||||
|
||||
/* Converting constructors from any of these types */
|
||||
/* Converting constructors from any of these types */
|
||||
|
||||
DataType(IntegerType t): v(t) {}
|
||||
DataType(BitfieldType t): v(t) {}
|
||||
DataType(ArrayType t): v(t) {}
|
||||
DataType(StringType t): v(t) {}
|
||||
DataType(StructType t): v(t) {}
|
||||
DataType(IntegerType t): v(t) {}
|
||||
DataType(BitfieldType t): v(t) {}
|
||||
DataType(ArrayType t): v(t) {}
|
||||
DataType(StringType t): v(t) {}
|
||||
DataType(StructType t): v(t) {}
|
||||
|
||||
private:
|
||||
std::variant<IntegerType,BitfieldType,ArrayType,StringType,StructType> v;
|
||||
std::variant<IntegerType,BitfieldType,ArrayType,StringType,StructType> v;
|
||||
};
|
||||
|
||||
//---
|
||||
|
@ -137,37 +150,37 @@ private:
|
|||
|
||||
struct DataValue
|
||||
{
|
||||
/* Each byte in the array is stored on an int16_t so that uninitialized
|
||||
bytes can be found and diagnosed. */
|
||||
DataType const *type;
|
||||
std::vector<int16_t> mem;
|
||||
/* Each byte in the array is stored on an int16_t so that uninitialized
|
||||
bytes can be found and diagnosed. */
|
||||
DataType const *type;
|
||||
std::vector<int16_t> mem;
|
||||
|
||||
/* Create value with no memory and no type */
|
||||
DataValue();
|
||||
/* Create value with uninitialized memory for that data type */
|
||||
DataValue(DataType const *type);
|
||||
/* Create value with no memory and no type */
|
||||
DataValue();
|
||||
/* Create value with uninitialized memory for that data type */
|
||||
DataValue(DataType const *type);
|
||||
|
||||
/* Check whether the value is fully defined and initialized */
|
||||
bool defined() const {
|
||||
return std::find(mem.begin(), mem.end(), -1) == mem.end();
|
||||
}
|
||||
operator bool() const {
|
||||
return defined();
|
||||
}
|
||||
/* Check whether the value is fully defined and initialized */
|
||||
bool defined() const {
|
||||
return std::find(mem.begin(), mem.end(), -1) == mem.end();
|
||||
}
|
||||
operator bool() const {
|
||||
return defined();
|
||||
}
|
||||
|
||||
/* Checks that the access is correct and fits within the value. */
|
||||
void access(size_t offset, size_t size) const;
|
||||
/* Read data from the value. Access must be 1, 2 or 4 bytes (possibly
|
||||
unaligned) and must be in bounds. */
|
||||
uint32_t read(size_t offset, size_t size) const;
|
||||
/* Write data. Access must be 1, 2 or 4 bytes and in bounds. */
|
||||
void write(size_t offset, size_t size, uint32_t contents);
|
||||
/* Checks that the access is correct and fits within the value. */
|
||||
void access(size_t offset, size_t size) const;
|
||||
/* Read data from the value. Access must be 1, 2 or 4 bytes (possibly
|
||||
unaligned) and must be in bounds. */
|
||||
uint32_t read(size_t offset, size_t size) const;
|
||||
/* Write data. Access must be 1, 2 or 4 bytes and in bounds. */
|
||||
void write(size_t offset, size_t size, uint32_t contents);
|
||||
|
||||
/* Retrieve value as uin32_t - only valid for Integer types */
|
||||
uint32_t uinteger() const;
|
||||
/* Retrieve value as uin32_t - only valid for Integer types */
|
||||
uint32_t uinteger() const;
|
||||
|
||||
/* Byte-based string representation */
|
||||
std::string str() const noexcept;
|
||||
/* Byte-based string representation */
|
||||
std::string str() const noexcept;
|
||||
};
|
||||
|
||||
//---
|
||||
|
@ -182,4 +195,4 @@ using Location = RelConst;
|
|||
|
||||
} /* namespace FxOS */
|
||||
|
||||
#endif /* LIBFXOS_SEMANTICS_H */
|
||||
#endif /* FXOS_SEMANTICS_H */
|
||||
|
|
|
@ -1,68 +1,61 @@
|
|||
//---
|
||||
// fxos.symbols: User-defined symbols for OS objects
|
||||
//---------------------------------------------------------------------------//
|
||||
// 1100101 |_ mov #0, r4 __ //
|
||||
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
|
||||
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
|
||||
// |_ base# + offset |_| /_\_\___/__/ //
|
||||
//---------------------------------------------------------------------------//
|
||||
// fxos/symbols: User-extensible naming scheme for OS objects
|
||||
//
|
||||
// This header provides tools to define symbols, ie. names attached to fixed or
|
||||
// symbolic addresses. Currently supported:
|
||||
// - Address: the name maps to a fixed 32-bit virtual address.
|
||||
// - Syscall: the name maps to a syscall number resolved by OS analysis.
|
||||
//
|
||||
// The SymbolTable structure manages a set of symbols, usually all the symbols
|
||||
// in a given virtual space.
|
||||
//---
|
||||
|
||||
#ifndef LIBFXOS_SYMBOLS_H
|
||||
#define LIBFXOS_SYMBOLS_H
|
||||
#ifndef FXOS_SYMBOLS_H
|
||||
#define FXOS_SYMBOLS_H
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fxos/os.h>
|
||||
|
||||
namespace FxOS
|
||||
{
|
||||
|
||||
class VirtualSpace;
|
||||
namespace FxOS {
|
||||
|
||||
/* A named symbol that can be substituted to literal values in the code. */
|
||||
struct Symbol
|
||||
{
|
||||
/* Syscall: The value is a syscall number. The syscall number for an
|
||||
address is determined by querying the OS object.
|
||||
Address: The value is a fixed 32-bit virtual address. */
|
||||
enum Type { Syscall=1, Address=2 };
|
||||
enum Type { Syscall=1, Address=2 };
|
||||
|
||||
enum Type type;
|
||||
uint32_t value;
|
||||
enum Type type;
|
||||
uint32_t value;
|
||||
|
||||
/* Symbol name, no particular conventions */
|
||||
std::string name;
|
||||
/* Symbol name, no particular conventions */
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/* A symbol table, essentially a set of symbols loaded from the same file */
|
||||
class SymbolTable
|
||||
/* A symbol table, usually the set of symbols of a virtual space */
|
||||
struct SymbolTable
|
||||
{
|
||||
public:
|
||||
/* Construct a new symbol table for the specified OS and MPU; the two
|
||||
constraints apply simultaneously. For both, an empty string
|
||||
represents no constraint. */
|
||||
SymbolTable(std::string os="", std::string mpu="");
|
||||
SymbolTable();
|
||||
|
||||
/* Allow move but disable copy */
|
||||
SymbolTable(SymbolTable const &other) = delete;
|
||||
SymbolTable(SymbolTable &&other) = default;
|
||||
/* Allow move but disable copy */
|
||||
SymbolTable(SymbolTable const &other) = delete;
|
||||
SymbolTable(SymbolTable &&other) = default;
|
||||
|
||||
std::string table_name;
|
||||
std::vector<Symbol> symbols;
|
||||
std::string table_name;
|
||||
std::vector<Symbol> symbols;
|
||||
|
||||
/* Validate constraints to see if table is usable */
|
||||
bool is_usable_on(OS &os) const noexcept;
|
||||
bool is_usable_on(VirtualSpace &space) const noexcept;
|
||||
/* Add a symbol to the table */
|
||||
void add(Symbol s);
|
||||
/* Query a value for a certain type of symbol */
|
||||
std::optional<std::string> query(Symbol::Type type, uint32_t value)
|
||||
const;
|
||||
/* Lookup the symbol behind a given name */
|
||||
std::optional<Symbol> lookup(std::string name) const;
|
||||
|
||||
private:
|
||||
std::string m_os_constraint;
|
||||
std::string m_mpu_constraint;
|
||||
/* Add a symbol to the table */
|
||||
void add(Symbol s);
|
||||
/* Query a value for a certain type of symbol */
|
||||
std::optional<std::string> query(Symbol::Type type, uint32_t value) const;
|
||||
/* Lookup the symbol behind a given name */
|
||||
std::optional<Symbol> lookup(std::string name) const;
|
||||
};
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
||||
#endif /* LIBFXOS_SYMBOLS_H */
|
||||
#endif /* FXOS_SYMBOLS_H */
|
||||
|
|
364
lib/load-asm.l
364
lib/load-asm.l
|
@ -1,4 +1,11 @@
|
|||
%{
|
||||
//---------------------------------------------------------------------------//
|
||||
// 1100101 |_ mov #0, r4 __ //
|
||||
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
|
||||
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
|
||||
// |_ base# + offset |_| /_\_\___/__/ //
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#include <fxos/lang.h>
|
||||
#include <fxos/disassembly.h>
|
||||
#include <fxos/util/format.h>
|
||||
|
@ -12,43 +19,43 @@ static char *yylval;
|
|||
|
||||
/* Argument tokens */
|
||||
enum Token {
|
||||
/* Instruction pattern and mnemonic */
|
||||
PATTERN = 1, MNEMONIC,
|
||||
/* General-purpose registers */
|
||||
R0, RN, RM,
|
||||
/* Banked registers */
|
||||
R0_BANK, R1_BANK, R2_BANK, R3_BANK, R4_BANK, R5_BANK, R6_BANK, R7_BANK,
|
||||
/* Control registers */
|
||||
SR, PR, GBR, VBR, DBR, SSR, SPC, SGR, MACH, MACL,
|
||||
/* PC-relative jumps and displacements (with 4-alignment correction) */
|
||||
JUMP8, JUMP12, AT_DPC,
|
||||
/* PC-relative address access (without memory access) */
|
||||
DPC,
|
||||
/* Immediate operands */
|
||||
IMM,
|
||||
/* Memory access with post-increment and pre-decrement */
|
||||
AT_RN, AT_RM, AT_RMP, AT_RNP, AT_MRN,
|
||||
/* Structure dereferencing */
|
||||
AT_DRN, AT_DRM, AT_DGBR,
|
||||
/* Array dereferencing */
|
||||
AT_R0RN, AT_R0RM, AT_R0GBR,
|
||||
/* Instruction pattern and mnemonic */
|
||||
PATTERN = 1, MNEMONIC,
|
||||
/* General-purpose registers */
|
||||
R0, RN, RM,
|
||||
/* Banked registers */
|
||||
R0_BANK, R1_BANK, R2_BANK, R3_BANK, R4_BANK, R5_BANK, R6_BANK, R7_BANK,
|
||||
/* Control registers */
|
||||
SR, PR, GBR, VBR, DBR, SSR, SPC, SGR, MACH, MACL,
|
||||
/* PC-relative jumps and displacements (with 4-alignment correction) */
|
||||
JUMP8, JUMP12, AT_DPC,
|
||||
/* PC-relative address access (without memory access) */
|
||||
DPC,
|
||||
/* Immediate operands */
|
||||
IMM,
|
||||
/* Memory access with post-increment and pre-decrement */
|
||||
AT_RN, AT_RM, AT_RMP, AT_RNP, AT_MRN,
|
||||
/* Structure dereferencing */
|
||||
AT_DRN, AT_DRM, AT_DGBR,
|
||||
/* Array dereferencing */
|
||||
AT_R0RN, AT_R0RM, AT_R0GBR,
|
||||
};
|
||||
|
||||
/* Instruction opcode pattern */
|
||||
struct Pattern {
|
||||
/* 16-bit opcode, bits corresponding to arguments are clear */
|
||||
uint16_t bits;
|
||||
/* Position of the arguments */
|
||||
uint8_t n_sh, m_sh, d_sh, i_sh;
|
||||
/* Length of arguments, in bits */
|
||||
uint16_t n_size, m_size, d_size, i_size;
|
||||
/* 16-bit opcode, bits corresponding to arguments are clear */
|
||||
uint16_t bits;
|
||||
/* Position of the arguments */
|
||||
uint8_t n_sh, m_sh, d_sh, i_sh;
|
||||
/* Length of arguments, in bits */
|
||||
uint16_t n_size, m_size, d_size, i_size;
|
||||
};
|
||||
|
||||
/* Current file name */
|
||||
static std::string filename;
|
||||
|
||||
#define err(fmt, ...) \
|
||||
FxOS_log(ERR, "%s:%d: " fmt, filename, yylineno, ##__VA_ARGS__)
|
||||
FxOS_log(ERR, "%s:%d: " fmt, filename, yylineno, ##__VA_ARGS__)
|
||||
|
||||
%}
|
||||
|
||||
|
@ -131,29 +138,27 @@ namespace FxOS {
|
|||
of the [&] operator. But this decoding method is now unused.) */
|
||||
static Pattern make_pattern(char const *code)
|
||||
{
|
||||
Pattern p {};
|
||||
Pattern p {};
|
||||
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
int c = code[i];
|
||||
for(int i = 0; i < 16; i++) {
|
||||
int c = code[i];
|
||||
|
||||
/* Constant bits */
|
||||
if(c == '0' || c == '1')
|
||||
{
|
||||
p.bits = (p.bits << 1) | (c - '0');
|
||||
continue;
|
||||
}
|
||||
/* Constant bits */
|
||||
if(c == '0' || c == '1') {
|
||||
p.bits = (p.bits << 1) | (c - '0');
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Argument bits */
|
||||
p.bits <<= 1;
|
||||
/* Argument bits */
|
||||
p.bits <<= 1;
|
||||
|
||||
if(c == 'n') p.n_sh = 15 - i, p.n_size++;
|
||||
if(c == 'm') p.m_sh = 15 - i, p.m_size++;
|
||||
if(c == 'd') p.d_sh = 15 - i, p.d_size++;
|
||||
if(c == 'i') p.i_sh = 15 - i, p.i_size++;
|
||||
}
|
||||
if(c == 'n') p.n_sh = 15 - i, p.n_size++;
|
||||
if(c == 'm') p.m_sh = 15 - i, p.m_size++;
|
||||
if(c == 'd') p.d_sh = 15 - i, p.d_size++;
|
||||
if(c == 'i') p.i_sh = 15 - i, p.i_size++;
|
||||
}
|
||||
|
||||
return p;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Instantiate an argument token as an fxos language structure.
|
||||
|
@ -163,67 +168,67 @@ static Pattern make_pattern(char const *code)
|
|||
Returns a semantic FxOS::Argument. */
|
||||
static Argument make_arg(int token, int opsize, int m, int n, int d, int i)
|
||||
{
|
||||
using Reg = CpuRegister;
|
||||
static Reg general_purpose[16] = {
|
||||
Reg::R0, Reg::R1, Reg::R2, Reg::R3, Reg::R4, Reg::R5,
|
||||
Reg::R6, Reg::R7, Reg::R8, Reg::R9, Reg::R10, Reg::R11,
|
||||
Reg::R12, Reg::R13, Reg::R14, Reg::R15,
|
||||
};
|
||||
using Reg = CpuRegister;
|
||||
static Reg general_purpose[16] = {
|
||||
Reg::R0, Reg::R1, Reg::R2, Reg::R3, Reg::R4, Reg::R5,
|
||||
Reg::R6, Reg::R7, Reg::R8, Reg::R9, Reg::R10, Reg::R11,
|
||||
Reg::R12, Reg::R13, Reg::R14, Reg::R15,
|
||||
};
|
||||
|
||||
/* Registers rn and rm */
|
||||
CpuRegister Rn = general_purpose[n & 0xf];
|
||||
CpuRegister Rm = general_purpose[m & 0xf];
|
||||
/* Sign extensions of d to 8 and 12 bits */
|
||||
int32_t d8 = (int8_t)d;
|
||||
int32_t d12 = (d & 0x800) ? (int32_t)(d | 0xfffff000) : (d);
|
||||
/* Sign extension of i to 8 bits */
|
||||
int32_t i8 = (int8_t)i;
|
||||
/* Registers rn and rm */
|
||||
CpuRegister Rn = general_purpose[n & 0xf];
|
||||
CpuRegister Rm = general_purpose[m & 0xf];
|
||||
/* Sign extensions of d to 8 and 12 bits */
|
||||
int32_t d8 = (int8_t)d;
|
||||
int32_t d12 = (d & 0x800) ? (int32_t)(d | 0xfffff000) : (d);
|
||||
/* Sign extension of i to 8 bits */
|
||||
int32_t i8 = (int8_t)i;
|
||||
|
||||
switch(token)
|
||||
{
|
||||
case R0: return Argument_Reg(Reg::R0);
|
||||
case RN: return Argument_Reg(Rn);
|
||||
case RM: return Argument_Reg(Rm);
|
||||
case R0_BANK: return Argument_Reg(Reg::R0B);
|
||||
case R1_BANK: return Argument_Reg(Reg::R1B);
|
||||
case R2_BANK: return Argument_Reg(Reg::R2B);
|
||||
case R3_BANK: return Argument_Reg(Reg::R3B);
|
||||
case R4_BANK: return Argument_Reg(Reg::R4B);
|
||||
case R5_BANK: return Argument_Reg(Reg::R5B);
|
||||
case R6_BANK: return Argument_Reg(Reg::R6B);
|
||||
case R7_BANK: return Argument_Reg(Reg::R7B);
|
||||
case SR: return Argument_Reg(Reg::SR);
|
||||
case PR: return Argument_Reg(Reg::PR);
|
||||
case GBR: return Argument_Reg(Reg::GBR);
|
||||
case VBR: return Argument_Reg(Reg::VBR);
|
||||
case DBR: return Argument_Reg(Reg::DBR);
|
||||
case SSR: return Argument_Reg(Reg::SSR);
|
||||
case SPC: return Argument_Reg(Reg::SPC);
|
||||
case SGR: return Argument_Reg(Reg::SGR);
|
||||
case MACH: return Argument_Reg(Reg::MACH);
|
||||
case MACL: return Argument_Reg(Reg::MACL);
|
||||
case JUMP8: return Argument_PcJump(d8 * 2);
|
||||
case JUMP12: return Argument_PcJump(d12 * 2);
|
||||
case DPC: return Argument_PcAddr(d * 4);
|
||||
case IMM: return Argument_Imm(i8);
|
||||
case AT_RN: return Argument_Deref(Rn);
|
||||
case AT_RM: return Argument_Deref(Rm);
|
||||
case AT_RMP: return Argument_PostInc(Rm);
|
||||
case AT_RNP: return Argument_PostInc(Rn);
|
||||
case AT_MRN: return Argument_PreDec(Rn);
|
||||
case AT_DRN: return Argument_StructDeref(d*opsize, opsize, Rn);
|
||||
case AT_DRM: return Argument_StructDeref(d*opsize, opsize, Rm);
|
||||
case AT_DGBR: return Argument_StructDeref(d*opsize, opsize, Reg::GBR);
|
||||
case AT_R0RN: return Argument_ArrayDeref(Reg::R0, Rn);
|
||||
case AT_R0RM: return Argument_ArrayDeref(Reg::R0, Rm);
|
||||
case AT_R0GBR: return Argument_ArrayDeref(Reg::R0, Reg::GBR);
|
||||
switch(token) {
|
||||
case R0: return Argument_Reg(Reg::R0);
|
||||
case RN: return Argument_Reg(Rn);
|
||||
case RM: return Argument_Reg(Rm);
|
||||
case R0_BANK: return Argument_Reg(Reg::R0B);
|
||||
case R1_BANK: return Argument_Reg(Reg::R1B);
|
||||
case R2_BANK: return Argument_Reg(Reg::R2B);
|
||||
case R3_BANK: return Argument_Reg(Reg::R3B);
|
||||
case R4_BANK: return Argument_Reg(Reg::R4B);
|
||||
case R5_BANK: return Argument_Reg(Reg::R5B);
|
||||
case R6_BANK: return Argument_Reg(Reg::R6B);
|
||||
case R7_BANK: return Argument_Reg(Reg::R7B);
|
||||
case SR: return Argument_Reg(Reg::SR);
|
||||
case PR: return Argument_Reg(Reg::PR);
|
||||
case GBR: return Argument_Reg(Reg::GBR);
|
||||
case VBR: return Argument_Reg(Reg::VBR);
|
||||
case DBR: return Argument_Reg(Reg::DBR);
|
||||
case SSR: return Argument_Reg(Reg::SSR);
|
||||
case SPC: return Argument_Reg(Reg::SPC);
|
||||
case SGR: return Argument_Reg(Reg::SGR);
|
||||
case MACH: return Argument_Reg(Reg::MACH);
|
||||
case MACL: return Argument_Reg(Reg::MACL);
|
||||
case JUMP8: return Argument_PcJump(d8 * 2);
|
||||
case JUMP12: return Argument_PcJump(d12 * 2);
|
||||
case DPC: return Argument_PcAddr(d * 4);
|
||||
case IMM: return Argument_Imm(i8);
|
||||
case AT_RN: return Argument_Deref(Rn);
|
||||
case AT_RM: return Argument_Deref(Rm);
|
||||
case AT_RMP: return Argument_PostInc(Rm);
|
||||
case AT_RNP: return Argument_PostInc(Rn);
|
||||
case AT_MRN: return Argument_PreDec(Rn);
|
||||
case AT_DRN: return Argument_StructDeref(d*opsize, opsize, Rn);
|
||||
case AT_DRM: return Argument_StructDeref(d*opsize, opsize, Rm);
|
||||
case AT_DGBR: return Argument_StructDeref(d*opsize, opsize, Reg::GBR);
|
||||
case AT_R0RN: return Argument_ArrayDeref(Reg::R0, Rn);
|
||||
case AT_R0RM: return Argument_ArrayDeref(Reg::R0, Rm);
|
||||
case AT_R0GBR: return Argument_ArrayDeref(Reg::R0, Reg::GBR);
|
||||
|
||||
case AT_DPC:
|
||||
if(!opsize) err("@(disp,pc) must have a size (.w, .l)");
|
||||
return Argument_PcRel(d*opsize, opsize);
|
||||
}
|
||||
case AT_DPC:
|
||||
if(!opsize)
|
||||
err("@(disp,pc) must have a size (.w, .l)");
|
||||
return Argument_PcRel(d*opsize, opsize);
|
||||
}
|
||||
|
||||
throw std::logic_error("lex asm builds args from bad tokens");
|
||||
throw std::logic_error("lex asm builds args from bad tokens");
|
||||
}
|
||||
|
||||
/* Record all the instances of an instruction in the disassembly table.
|
||||
|
@ -235,110 +240,101 @@ static Argument make_arg(int token, int opsize, int m, int n, int d, int i)
|
|||
Generates all the instances of the instruction, then sends them to the
|
||||
disassembler for fast lookup. Returns number of instantiated opcodes. */
|
||||
static int instantiate(struct Pattern p, char const *mnemonic, int argtoken1,
|
||||
int argtoken2)
|
||||
int argtoken2)
|
||||
{
|
||||
int total = 0;
|
||||
int total = 0;
|
||||
|
||||
for(int n = 0; n < (1 << p.n_size); n++)
|
||||
for(int m = 0; m < (1 << p.m_size); m++)
|
||||
for(int d = 0; d < (1 << p.d_size); d++)
|
||||
for(int i = 0; i < (1 << p.i_size); i++)
|
||||
{
|
||||
uint16_t opcode = p.bits;
|
||||
opcode |= (n << p.n_sh);
|
||||
opcode |= (m << p.m_sh);
|
||||
opcode |= (d << p.d_sh);
|
||||
opcode |= (i << p.i_sh);
|
||||
for(int n = 0; n < (1 << p.n_size); n++)
|
||||
for(int m = 0; m < (1 << p.m_size); m++)
|
||||
for(int d = 0; d < (1 << p.d_size); d++)
|
||||
for(int i = 0; i < (1 << p.i_size); i++) {
|
||||
uint16_t opcode = p.bits;
|
||||
opcode |= (n << p.n_sh);
|
||||
opcode |= (m << p.m_sh);
|
||||
opcode |= (d << p.d_sh);
|
||||
opcode |= (i << p.i_sh);
|
||||
|
||||
Instruction ins(mnemonic);
|
||||
ins.opcode = opcode;
|
||||
Instruction ins(mnemonic);
|
||||
ins.opcode = opcode;
|
||||
|
||||
if(argtoken1) {
|
||||
ins.args[0] = make_arg(argtoken1, ins.opsize, m,n,d,i);
|
||||
ins.arg_count = 1;
|
||||
}
|
||||
if(argtoken2) {
|
||||
ins.args[1] = make_arg(argtoken2, ins.opsize, m,n,d,i);
|
||||
ins.arg_count = 2;
|
||||
}
|
||||
if(argtoken1) {
|
||||
ins.args[0] = make_arg(argtoken1, ins.opsize, m,n,d,i);
|
||||
ins.arg_count = 1;
|
||||
}
|
||||
if(argtoken2) {
|
||||
ins.args[1] = make_arg(argtoken2, ins.opsize, m,n,d,i);
|
||||
ins.arg_count = 2;
|
||||
}
|
||||
|
||||
register_instruction(ins);
|
||||
total++;
|
||||
}
|
||||
register_instruction(ins);
|
||||
total++;
|
||||
}
|
||||
|
||||
return total;
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Load an assembly instruction table for the disassembler. */
|
||||
int load_instructions(Buffer const &file)
|
||||
{
|
||||
/* Lex all instructions and fill in the general assembly table */
|
||||
/* Lex all instructions and fill in the general assembly table */
|
||||
|
||||
YY_BUFFER_STATE buf = yy_scan_bytes(file.data.get(), file.size);
|
||||
yylineno = 1;
|
||||
filename = file.path;
|
||||
YY_BUFFER_STATE buf = yy_scan_bytes(file.data.get(), file.size);
|
||||
yylineno = 1;
|
||||
filename = file.path;
|
||||
|
||||
/* Number of instructions lexed */
|
||||
int total = 0;
|
||||
/* Number of instructions lexed */
|
||||
int total = 0;
|
||||
|
||||
/* Instruction information */
|
||||
char *code=nullptr, *mnemonic=nullptr;
|
||||
int argtoken1=0, argtoken2=0;
|
||||
/* Instruction information */
|
||||
char *code=nullptr, *mnemonic=nullptr;
|
||||
int argtoken1=0, argtoken2=0;
|
||||
|
||||
/* Current line */
|
||||
int line = -1;
|
||||
/* Current line */
|
||||
int line = -1;
|
||||
|
||||
while(1)
|
||||
{
|
||||
int t = yylex();
|
||||
while(1) {
|
||||
int t = yylex();
|
||||
|
||||
if(line >= 0 && (yylineno != line || t == PATTERN || t == -1))
|
||||
{
|
||||
/* Finalize current instruction */
|
||||
if(!mnemonic)
|
||||
{
|
||||
err("missing mnemonic at line %d", line);
|
||||
break;
|
||||
}
|
||||
if(line >= 0 && (yylineno != line || t == PATTERN || t == -1)) {
|
||||
/* Finalize current instruction */
|
||||
if(!mnemonic) {
|
||||
err("missing mnemonic at line %d", line);
|
||||
break;
|
||||
}
|
||||
|
||||
Pattern p = make_pattern(code);
|
||||
total += instantiate(p, mnemonic, argtoken1,argtoken2);
|
||||
Pattern p = make_pattern(code);
|
||||
total += instantiate(p, mnemonic, argtoken1,argtoken2);
|
||||
|
||||
if(code) free(code);
|
||||
if(mnemonic) free(mnemonic);
|
||||
}
|
||||
if(t == -1) break;
|
||||
if(code) free(code);
|
||||
if(mnemonic) free(mnemonic);
|
||||
}
|
||||
if(t == -1) break;
|
||||
|
||||
if(t == PATTERN)
|
||||
{
|
||||
code = yylval;
|
||||
line = yylineno;
|
||||
if(t == PATTERN) {
|
||||
code = yylval;
|
||||
line = yylineno;
|
||||
|
||||
mnemonic = nullptr;
|
||||
argtoken1 = 0;
|
||||
argtoken2 = 0;
|
||||
}
|
||||
else if(t == MNEMONIC && !mnemonic)
|
||||
{
|
||||
mnemonic = yylval;
|
||||
}
|
||||
else if(!mnemonic)
|
||||
{
|
||||
err("missing mnemonic at line %d", line);
|
||||
break;
|
||||
}
|
||||
else if(!argtoken1)
|
||||
{
|
||||
argtoken1 = t;
|
||||
}
|
||||
else if(!argtoken2)
|
||||
{
|
||||
argtoken2 = t;
|
||||
}
|
||||
}
|
||||
mnemonic = nullptr;
|
||||
argtoken1 = 0;
|
||||
argtoken2 = 0;
|
||||
}
|
||||
else if(t == MNEMONIC && !mnemonic) {
|
||||
mnemonic = yylval;
|
||||
}
|
||||
else if(!mnemonic) {
|
||||
err("missing mnemonic at line %d", line);
|
||||
break;
|
||||
}
|
||||
else if(!argtoken1) {
|
||||
argtoken1 = t;
|
||||
}
|
||||
else if(!argtoken2) {
|
||||
argtoken2 = t;
|
||||
}
|
||||
}
|
||||
|
||||
yy_delete_buffer(buf);
|
||||
return total;
|
||||
yy_delete_buffer(buf);
|
||||
return total;
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
273
lib/os.cpp
273
lib/os.cpp
|
@ -1,230 +1,227 @@
|
|||
#include <fxos/os.h>
|
||||
#include <fxos/vspace.h>
|
||||
#include <fxos/memory.h>
|
||||
#include <fxos/util/log.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
OS::OS(VirtualSpace &space): m_space {space}
|
||||
OS::OS(VirtualSpace &space): type{UNKNOWN}, m_space{space}
|
||||
{
|
||||
if(!space.covers(0x80000000))
|
||||
throw std::runtime_error("OS requires a target with ROM");
|
||||
if(!space.covers(0x80000000, (256 << 10))) {
|
||||
FxOS_log(ERR, "OS analysis failed: space doesn't have at least 256 kB "
|
||||
"of ROM to analyze from");
|
||||
return;
|
||||
}
|
||||
|
||||
/* OS files are all at least 1 MB large */
|
||||
if(!space.covers(0x80000000, 1000000))
|
||||
throw std::runtime_error("OS requires target with >1MB ROM");
|
||||
/* Detect OS type by heuristic */
|
||||
if(space.read_str(0x80010000, 8).value == "CASIOWIN")
|
||||
this->type = FX;
|
||||
else if(space.read_str(0x80020000, 8).value == "CASIOWIN")
|
||||
this->type = CG;
|
||||
else {
|
||||
FxOS_log(ERR, "OS analysis failed: cannot determine OS type");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Detect OS type by heuristic */
|
||||
if(space.read_str(0x80010000, 8).value == "CASIOWIN")
|
||||
this->type = FX;
|
||||
else if(space.read_str(0x80020000, 8).value == "CASIOWIN")
|
||||
this->type = CG;
|
||||
else
|
||||
throw std::runtime_error("Cannot determine OS type (FX/CG)");
|
||||
|
||||
parse_header();
|
||||
parse_syscall_table();
|
||||
parse_footer();
|
||||
parse_header();
|
||||
parse_syscall_table();
|
||||
parse_footer();
|
||||
}
|
||||
|
||||
void OS::parse_header()
|
||||
{
|
||||
VirtualSpace &s = m_space;
|
||||
VirtualSpace &s = m_space;
|
||||
|
||||
if(this->type == FX)
|
||||
{
|
||||
/* Bootcode timestamp at the very end of the bootcode */
|
||||
this->bootcode_timestamp = s.read_str(0x8000ffb0, 14);
|
||||
this->bootcode_checksum = s.read_u32(0x8000fffc);
|
||||
this->version = s.read_str(0x80010020, 10);
|
||||
this->serial_number = s.read_str(0x8000ffd0, 8);
|
||||
}
|
||||
else if(this->type == CG)
|
||||
{
|
||||
this->bootcode_timestamp = s.read_str(0x8001ffb0, 14);
|
||||
this->bootcode_checksum = s.read_u32(0x8001fffc);
|
||||
this->version = s.read_str(0x80020020, 10);
|
||||
this->serial_number = s.read_str(0x8001ffd0, 8);
|
||||
}
|
||||
if(this->type == FX) {
|
||||
/* Bootcode timestamp at the very end of the bootcode */
|
||||
this->bootcode_timestamp = s.read_str(0x8000ffb0, 14);
|
||||
this->bootcode_checksum = s.read_u32(0x8000fffc);
|
||||
this->version = s.read_str(0x80010020, 10);
|
||||
this->serial_number = s.read_str(0x8000ffd0, 8);
|
||||
}
|
||||
else if(this->type == CG) {
|
||||
this->bootcode_timestamp = s.read_str(0x8001ffb0, 14);
|
||||
this->bootcode_checksum = s.read_u32(0x8001fffc);
|
||||
this->version = s.read_str(0x80020020, 10);
|
||||
this->serial_number = s.read_str(0x8001ffd0, 8);
|
||||
}
|
||||
|
||||
/* Version has format MM.mm.pppp */
|
||||
version_major = std::stoi(this->version.value.substr(0, 2));
|
||||
version_minor = std::stoi(this->version.value.substr(3, 2));
|
||||
version_patch = std::stoi(this->version.value.substr(6, 4));
|
||||
/* Version has format MM.mm.pppp */
|
||||
version_major = std::stoi(this->version.value.substr(0, 2));
|
||||
version_minor = std::stoi(this->version.value.substr(3, 2));
|
||||
version_patch = std::stoi(this->version.value.substr(6, 4));
|
||||
}
|
||||
|
||||
bool OS::version_lt(int major, int minor, int patch) const noexcept
|
||||
{
|
||||
if(this->version_major < major) return true;
|
||||
if(this->version_major > major) return false;
|
||||
if(this->version_minor < minor) return true;
|
||||
if(this->version_minor > minor) return false;
|
||||
return (this->version_patch < patch);
|
||||
if(this->version_major < major) return true;
|
||||
if(this->version_major > major) return false;
|
||||
if(this->version_minor < minor) return true;
|
||||
if(this->version_minor > minor) return false;
|
||||
return (this->version_patch < patch);
|
||||
}
|
||||
|
||||
bool OS::version_le(int major, int minor, int patch) const noexcept
|
||||
{
|
||||
if(this->version_major < major) return true;
|
||||
if(this->version_major > major) return false;
|
||||
if(this->version_minor < minor) return true;
|
||||
if(this->version_minor > minor) return false;
|
||||
return (this->version_patch <= patch);
|
||||
if(this->version_major < major) return true;
|
||||
if(this->version_major > major) return false;
|
||||
if(this->version_minor < minor) return true;
|
||||
if(this->version_minor > minor) return false;
|
||||
return (this->version_patch <= patch);
|
||||
}
|
||||
|
||||
bool OS::version_gt(int major, int minor, int patch) const noexcept
|
||||
{
|
||||
if(this->version_major < major) return false;
|
||||
if(this->version_major > major) return true;
|
||||
if(this->version_minor < minor) return false;
|
||||
if(this->version_minor > minor) return true;
|
||||
return (this->version_patch > patch);
|
||||
if(this->version_major < major) return false;
|
||||
if(this->version_major > major) return true;
|
||||
if(this->version_minor < minor) return false;
|
||||
if(this->version_minor > minor) return true;
|
||||
return (this->version_patch > patch);
|
||||
}
|
||||
|
||||
bool OS::version_ge(int major, int minor, int patch) const noexcept
|
||||
{
|
||||
if(this->version_major < major) return false;
|
||||
if(this->version_major > major) return true;
|
||||
if(this->version_minor < minor) return false;
|
||||
if(this->version_minor > minor) return true;
|
||||
return (this->version_patch >= patch);
|
||||
if(this->version_major < major) return false;
|
||||
if(this->version_major > major) return true;
|
||||
if(this->version_minor < minor) return false;
|
||||
if(this->version_minor > minor) return true;
|
||||
return (this->version_patch >= patch);
|
||||
}
|
||||
|
||||
//---
|
||||
// Syscall resolution
|
||||
// Syscall resolution
|
||||
//---
|
||||
|
||||
int OS::syscall_count() const noexcept
|
||||
{
|
||||
return m_syscall_table.size();
|
||||
return m_syscall_table.size();
|
||||
}
|
||||
|
||||
uint32_t OS::syscall(int id) const
|
||||
{
|
||||
return m_syscall_table[id];
|
||||
return m_syscall_table[id];
|
||||
}
|
||||
|
||||
int OS::find_syscall(uint32_t entry) const noexcept
|
||||
{
|
||||
try {
|
||||
return m_syscall_addresses.at(entry);
|
||||
}
|
||||
catch(std::out_of_range &e) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
return m_syscall_addresses.at(entry);
|
||||
}
|
||||
catch(std::out_of_range &e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t OS::syscall_table_address() const noexcept
|
||||
{
|
||||
uint32_t address = (this->type == FX) ? 0x8001007c : 0x8002007c;
|
||||
return m_space.read_u32(address);
|
||||
uint32_t address = (this->type == FX) ? 0x8001007c : 0x8002007c;
|
||||
return m_space.read_u32(address);
|
||||
}
|
||||
|
||||
void OS::parse_syscall_table()
|
||||
{
|
||||
/* Traverse the syscall table */
|
||||
uint32_t syscall_table = syscall_table_address();
|
||||
int id = 0;
|
||||
/* Traverse the syscall table */
|
||||
uint32_t syscall_table = syscall_table_address();
|
||||
int id = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
uint32_t entry = m_space.read_u32(syscall_table + 4 * id);
|
||||
while(1) {
|
||||
uint32_t entry = m_space.read_u32(syscall_table + 4 * id);
|
||||
|
||||
MemoryRegion const *r = MemoryRegion::region_for(entry);
|
||||
if(!r) break;
|
||||
MemoryRegion const *r = MemoryRegion::region_for(entry);
|
||||
if(!r) break;
|
||||
|
||||
m_syscall_table.push_back(entry);
|
||||
m_syscall_addresses[entry] = id;
|
||||
m_syscall_table.push_back(entry);
|
||||
m_syscall_addresses[entry] = id;
|
||||
|
||||
id++;
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Footer search
|
||||
// Footer search
|
||||
//---
|
||||
|
||||
void OS::parse_footer()
|
||||
{
|
||||
VirtualSpace &s = m_space;
|
||||
VirtualSpace &s = m_space;
|
||||
|
||||
/* Find the footer address (occurrence of "CASIOABSLangdata") */
|
||||
uint32_t start = MemoryRegion::ROM.start;
|
||||
uint32_t end = MemoryRegion::ROM.end;
|
||||
/* Find the footer address (occurrence of "CASIOABSLangdata") */
|
||||
uint32_t start = MemoryRegion::ROM.start;
|
||||
uint32_t end = MemoryRegion::ROM.end;
|
||||
|
||||
this->footer = s.search(start, end, "CASIOABSLangdata", 16);
|
||||
if(this->footer == end)
|
||||
{
|
||||
this->footer = -1;
|
||||
this->timestamp = std::string("");
|
||||
this->langdata = 0;
|
||||
this->computed_checksum = -1;
|
||||
return;
|
||||
}
|
||||
this->footer = s.search(start, end, "CASIOABSLangdata", 16);
|
||||
if(this->footer == end) {
|
||||
this->footer = -1;
|
||||
this->timestamp = std::string("");
|
||||
this->langdata = 0;
|
||||
this->computed_checksum = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t addr = this->footer + 8;
|
||||
this->langdata = 0;
|
||||
uint32_t addr = this->footer + 8;
|
||||
this->langdata = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
void const *entry = s.translate(addr, 8);
|
||||
if(!entry || memcmp(entry, "Langdata", 8) != 0)
|
||||
break;
|
||||
this->langdata++;
|
||||
addr += 0x30;
|
||||
}
|
||||
while(1) {
|
||||
void const *entry = s.translate(addr, 8);
|
||||
if(!entry || memcmp(entry, "Langdata", 8) != 0)
|
||||
break;
|
||||
this->langdata++;
|
||||
addr += 0x30;
|
||||
}
|
||||
|
||||
this->timestamp = s.read_str(addr, 14);
|
||||
this->checksum = s.read_u32(addr + 0x18);
|
||||
this->computed_checksum = this->compute_checksum();
|
||||
this->timestamp = s.read_str(addr, 14);
|
||||
this->checksum = s.read_u32(addr + 0x18);
|
||||
this->computed_checksum = this->compute_checksum();
|
||||
}
|
||||
|
||||
//---
|
||||
// Checksum
|
||||
//---
|
||||
|
||||
static uint32_t accumulate_range(VirtualSpace const &m_space,
|
||||
uint32_t start, uint32_t end)
|
||||
static uint32_t accumulate_range(VirtualSpace const &m_space, uint32_t start,
|
||||
uint32_t end)
|
||||
{
|
||||
uint32_t sum = 0;
|
||||
uint32_t sum = 0;
|
||||
|
||||
/* Read from dynamically-sized bindings; read_u8() would be too slow */
|
||||
while(start < end) {
|
||||
int size;
|
||||
uint8_t *buf = (uint8_t *)m_space.translate_dynamic(start, &size);
|
||||
if(!buf || size <= 0)
|
||||
break;
|
||||
/* Read from dynamically-sized bindings; read_u8() would be too slow */
|
||||
while(start < end) {
|
||||
int size;
|
||||
uint8_t *buf = (uint8_t *)m_space.translate_dynamic(start, &size);
|
||||
if(!buf || size <= 0)
|
||||
return -1;
|
||||
|
||||
if(start + size > end)
|
||||
size = end - start;
|
||||
if(start + size > end)
|
||||
size = end - start;
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
sum += buf[i];
|
||||
for(int i = 0; i < size; i++)
|
||||
sum += buf[i];
|
||||
|
||||
start += size;
|
||||
}
|
||||
start += size;
|
||||
}
|
||||
|
||||
return sum;
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint32_t OS::compute_checksum() const
|
||||
{
|
||||
if(this->type == FX) {
|
||||
return accumulate_range(m_space, 0x80010000, this->checksum.address);
|
||||
}
|
||||
else if(this->type == CG) {
|
||||
if(this->version_lt(2, 2, 0)) {
|
||||
return accumulate_range(m_space, 0x80020000, 0x80b5feb0)
|
||||
+ accumulate_range(m_space,0x80b5fef0, 0x80b5fff8);
|
||||
}
|
||||
else {
|
||||
return accumulate_range(m_space, 0x80020000, 0x80b20000)
|
||||
+ accumulate_range(m_space, this->footer+8,
|
||||
this->checksum.address);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
if(this->type == FX) {
|
||||
return accumulate_range(m_space, 0x80010000, this->checksum.address);
|
||||
}
|
||||
else if(this->type == CG) {
|
||||
if(this->version_lt(2, 2, 0)) {
|
||||
return accumulate_range(m_space, 0x80020000, 0x80b5feb0)
|
||||
+ accumulate_range(m_space,0x80b5fef0, 0x80b5fff8);
|
||||
}
|
||||
else {
|
||||
return accumulate_range(m_space, 0x80020000, 0x80b20000)
|
||||
+ accumulate_range(m_space, this->footer+8,
|
||||
this->checksum.address);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -109,11 +109,10 @@ std::optional<std::string> PrintPass::symquery(Symbol::Type type,
|
|||
for(int i = m_symtables.size() - 1; i >= 0; i--)
|
||||
{
|
||||
SymbolTable const &st = m_symtables[i];
|
||||
if(!st.is_usable_on(m_disasm.space())) continue;
|
||||
if(m_os && !st.is_usable_on(*m_os)) continue;
|
||||
|
||||
auto maybe_str = st.query(type, value);
|
||||
if(maybe_str) return maybe_str;
|
||||
if(maybe_str)
|
||||
return maybe_str;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
|
|
@ -25,52 +25,51 @@ DataType const *IntegerType::i32 = &_i32;
|
|||
|
||||
BitfieldType::Field BitfieldType::named_field(std::string name) const
|
||||
{
|
||||
for(auto &f: fields)
|
||||
{
|
||||
if(f.first == name) return f;
|
||||
}
|
||||
for(auto &f: fields) {
|
||||
if(f.first == name)
|
||||
return f;
|
||||
}
|
||||
|
||||
throw std::logic_error("no such field name in bit field");
|
||||
throw std::logic_error("no such field name in bit field");
|
||||
}
|
||||
|
||||
DataType::DataKind DataType::kind() const noexcept
|
||||
{
|
||||
return (DataKind)v.index();
|
||||
return (DataKind)v.index();
|
||||
}
|
||||
|
||||
size_t DataType::size() const noexcept
|
||||
{
|
||||
switch(kind())
|
||||
{
|
||||
case Integer: return integer().size;
|
||||
case Bitfield: return bitfield().size;
|
||||
case Array: return array().size;
|
||||
case String: return string().size;
|
||||
case Struct: return structs().size;
|
||||
}
|
||||
switch(kind()) {
|
||||
case Integer: return integer().size;
|
||||
case Bitfield: return bitfield().size;
|
||||
case Array: return array().size;
|
||||
case String: return string().size;
|
||||
case Struct: return structs().size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
};
|
||||
|
||||
IntegerType const &DataType::integer() const
|
||||
{
|
||||
return std::get<IntegerType>(v);
|
||||
return std::get<IntegerType>(v);
|
||||
}
|
||||
BitfieldType const &DataType::bitfield() const
|
||||
{
|
||||
return std::get<BitfieldType>(v);
|
||||
return std::get<BitfieldType>(v);
|
||||
}
|
||||
ArrayType const &DataType::array() const
|
||||
{
|
||||
return std::get<ArrayType>(v);
|
||||
return std::get<ArrayType>(v);
|
||||
}
|
||||
StringType const &DataType::string() const
|
||||
{
|
||||
return std::get<StringType>(v);
|
||||
return std::get<StringType>(v);
|
||||
}
|
||||
StructType const &DataType::structs() const
|
||||
{
|
||||
return std::get<StructType>(v);
|
||||
return std::get<StructType>(v);
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -78,92 +77,87 @@ StructType const &DataType::structs() const
|
|||
//---
|
||||
|
||||
DataValue::DataValue():
|
||||
type {nullptr}
|
||||
type {nullptr}
|
||||
{
|
||||
}
|
||||
|
||||
DataValue::DataValue(DataType const *type):
|
||||
type {type}, mem(type->size(), (int16_t)-1)
|
||||
type {type}, mem(type->size(), (int16_t)-1)
|
||||
{
|
||||
}
|
||||
|
||||
void DataValue::access(size_t offset, size_t size) const
|
||||
{
|
||||
if(size != 1 && size != 2 && size != 4)
|
||||
throw std::logic_error("Invalid simulated access size");
|
||||
if(offset + size > mem.size())
|
||||
throw std::logic_error("Access overflows from data");
|
||||
if(size != 1 && size != 2 && size != 4)
|
||||
throw std::logic_error("Invalid simulated access size");
|
||||
if(offset + size > mem.size())
|
||||
throw std::logic_error("Access overflows from data");
|
||||
}
|
||||
|
||||
uint32_t DataValue::read(size_t offset, size_t size) const
|
||||
{
|
||||
access(offset, size);
|
||||
uint32_t result = 0;
|
||||
access(offset, size);
|
||||
uint32_t result = 0;
|
||||
|
||||
while(size--)
|
||||
{
|
||||
int16_t byte = mem[offset++];
|
||||
if(byte == -1)
|
||||
throw std::logic_error("Read uninitialized value");
|
||||
while(size--) {
|
||||
int16_t byte = mem[offset++];
|
||||
if(byte == -1)
|
||||
throw std::logic_error("Read uninitialized value");
|
||||
|
||||
result = (result << 8) | byte;
|
||||
}
|
||||
result = (result << 8) | byte;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void DataValue::write(size_t offset, size_t size, uint32_t contents)
|
||||
{
|
||||
access(offset, size);
|
||||
access(offset, size);
|
||||
|
||||
offset += size;
|
||||
while(size-- > 0)
|
||||
{
|
||||
mem[--offset] = contents & 0xff;
|
||||
contents >>= 8;
|
||||
}
|
||||
offset += size;
|
||||
while(size-- > 0) {
|
||||
mem[--offset] = contents & 0xff;
|
||||
contents >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DataValue::uinteger() const
|
||||
{
|
||||
if(!type || type->kind() != DataType::Integer)
|
||||
throw std::logic_error("uinteger() on non-int DataValue");
|
||||
if(!type || type->kind() != DataType::Integer)
|
||||
throw std::logic_error("uinteger() on non-int DataValue");
|
||||
|
||||
return read(0, type->size());
|
||||
return read(0, type->size());
|
||||
}
|
||||
|
||||
std::string DataValue::str() const noexcept
|
||||
{
|
||||
std::string result;
|
||||
std::string result;
|
||||
|
||||
switch(type->kind())
|
||||
{
|
||||
/* Format all integers in hexadecimal */
|
||||
case DataType::Integer:
|
||||
return format("0x%0*x", 2*type->size(), read(0,type->size()));
|
||||
switch(type->kind()) {
|
||||
/* Format all integers in hexadecimal */
|
||||
case DataType::Integer:
|
||||
return format("0x%0*x", 2*type->size(), read(0,type->size()));
|
||||
|
||||
/* TODO: Print data values of complex types */
|
||||
case DataType::Bitfield:
|
||||
case DataType::Array:
|
||||
case DataType::String:
|
||||
case DataType::Struct:
|
||||
/* TODO: Print data values of complex types */
|
||||
case DataType::Bitfield:
|
||||
case DataType::Array:
|
||||
case DataType::String:
|
||||
case DataType::Struct:
|
||||
|
||||
/* If the type is not supported, use hexadecimal notation */
|
||||
default:
|
||||
for(size_t i = 0; i < mem.size(); i++)
|
||||
{
|
||||
int16_t byte = mem[i];
|
||||
/* If the type is not supported, use hexadecimal notation */
|
||||
default:
|
||||
for(size_t i = 0; i < mem.size(); i++) {
|
||||
int16_t byte = mem[i];
|
||||
|
||||
if(byte == -1) result += " UND";
|
||||
else result += format(" %02x", byte);
|
||||
}
|
||||
if(byte == -1) result += " UND";
|
||||
else result += format(" %02x", byte);
|
||||
}
|
||||
|
||||
result[0] = '{';
|
||||
result += '}';
|
||||
}
|
||||
result[0] = '{';
|
||||
result += '}';
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -1,49 +1,43 @@
|
|||
//---------------------------------------------------------------------------//
|
||||
// 1100101 |_ mov #0, r4 __ //
|
||||
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
|
||||
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
|
||||
// |_ base# + offset |_| /_\_\___/__/ //
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#include <fxos/symbols.h>
|
||||
#include <fxos/vspace.h>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
SymbolTable::SymbolTable(std::string os, std::string mpu):
|
||||
m_os_constraint(os), m_mpu_constraint(mpu)
|
||||
SymbolTable::SymbolTable()
|
||||
{
|
||||
}
|
||||
|
||||
bool SymbolTable::is_usable_on(OS &os) const noexcept
|
||||
{
|
||||
std::string constraint = (os.type == OS::FX) ? "fx" : "cg";
|
||||
return (m_os_constraint == "" || m_os_constraint == constraint);
|
||||
}
|
||||
|
||||
bool SymbolTable::is_usable_on(VirtualSpace &space) const noexcept
|
||||
{
|
||||
return m_mpu_constraint == "" || space.mpu == "" ||
|
||||
m_mpu_constraint == space.mpu;
|
||||
}
|
||||
|
||||
void SymbolTable::add(Symbol s)
|
||||
{
|
||||
symbols.push_back(s);
|
||||
symbols.push_back(s);
|
||||
}
|
||||
|
||||
std::optional<std::string> SymbolTable::query(Symbol::Type type,
|
||||
uint32_t value) const
|
||||
uint32_t value) const
|
||||
{
|
||||
for(auto &sym: symbols)
|
||||
{
|
||||
if(sym.type == type && sym.value == value) return sym.name;
|
||||
}
|
||||
for(auto &sym: symbols) {
|
||||
if(sym.type == type && sym.value == value)
|
||||
return sym.name;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Symbol> SymbolTable::lookup(std::string name) const
|
||||
{
|
||||
for(auto &sym: symbols)
|
||||
{
|
||||
if(sym.name == name) return sym;
|
||||
}
|
||||
for(auto &sym: symbols) {
|
||||
if(sym.name == name)
|
||||
return sym;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -153,6 +153,9 @@ OS *VirtualSpace::os_analysis(bool force)
|
|||
{
|
||||
if(!m_os || force) {
|
||||
m_os = std::make_unique<OS>(*this);
|
||||
/* We don't keep an OS analysis result that failed */
|
||||
if(m_os->type == OS::UNKNOWN)
|
||||
m_os = nullptr;
|
||||
}
|
||||
return m_os.get();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue