Refactor controller runtime code generation
This commit is contained in:
parent
d32b1697a6
commit
6639ab0437
14 changed files with 1813 additions and 396 deletions
20
include/library/assembler-intrinsics-dummy.hpp
Normal file
20
include/library/assembler-intrinsics-dummy.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef _library__assembler_intrinsics_dymmy__hpp__included__
|
||||
#define _library__assembler_intrinsics_dymmy__hpp__included__
|
||||
|
||||
#include "assembler.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace assembler_intrinsics
|
||||
{
|
||||
struct dummyarch
|
||||
{
|
||||
dummyarch(assembler::assembler& _a);
|
||||
//Label:
|
||||
void label(assembler::label& l);
|
||||
private:
|
||||
assembler::assembler& a;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
204
include/library/assembler-intrinsics-i386.hpp
Normal file
204
include/library/assembler-intrinsics-i386.hpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
#ifndef _library__assembler_intrinsics_i386__hpp__included__
|
||||
#define _library__assembler_intrinsics_i386__hpp__included__
|
||||
|
||||
#include "assembler.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace assembler_intrinsics
|
||||
{
|
||||
struct I386
|
||||
{
|
||||
struct ref;
|
||||
struct sib_scale_off_intermediate;
|
||||
struct sib_scale_intermediate;
|
||||
struct reg
|
||||
{
|
||||
explicit reg(uint8_t _val);
|
||||
ref operator[](int32_t off) const;
|
||||
sib_scale_intermediate operator*(uint8_t scale) const;
|
||||
ref operator[](const reg r) const;
|
||||
ref operator[](sib_scale_intermediate r) const;
|
||||
ref operator[](sib_scale_off_intermediate r) const;
|
||||
sib_scale_off_intermediate operator+(int32_t off) const;
|
||||
bool hbit();
|
||||
uint8_t lbits();
|
||||
uint8_t num();
|
||||
bool valid(bool amd64);
|
||||
bool is_none();
|
||||
private:
|
||||
friend class ref;
|
||||
friend class sib_scale_intermediate;
|
||||
friend class sib_scale_off_intermediate;
|
||||
uint8_t val;
|
||||
};
|
||||
const static reg reg_none;
|
||||
const static reg reg_ax;
|
||||
const static reg reg_bx;
|
||||
const static reg reg_cx;
|
||||
const static reg reg_dx;
|
||||
const static reg reg_bp; //Don't use in 8-bit mode.
|
||||
const static reg reg_sp; //Don't use in 8-bit mode.
|
||||
const static reg reg_si; //Don't use in 8-bit mode.
|
||||
const static reg reg_di; //Don't use in 8-bit mode.
|
||||
const static reg reg_r8;
|
||||
const static reg reg_r9;
|
||||
const static reg reg_r10;
|
||||
const static reg reg_r11;
|
||||
const static reg reg_r12;
|
||||
const static reg reg_r13;
|
||||
const static reg reg_r14;
|
||||
const static reg reg_r15;
|
||||
const static reg reg_rip;
|
||||
|
||||
struct sib_scale_off_intermediate
|
||||
{
|
||||
sib_scale_off_intermediate(reg idx, uint8_t scale, int32_t offset);
|
||||
private:
|
||||
friend class reg;
|
||||
friend class ref;
|
||||
reg index;
|
||||
uint8_t scale;
|
||||
int32_t offset;
|
||||
};
|
||||
struct sib_scale_intermediate
|
||||
{
|
||||
sib_scale_intermediate(reg idx, uint8_t scale);
|
||||
sib_scale_off_intermediate operator+(int32_t off) const;
|
||||
private:
|
||||
friend class reg;
|
||||
friend class ref;
|
||||
reg index;
|
||||
uint8_t scale;
|
||||
};
|
||||
struct low
|
||||
{
|
||||
low(bool _need_amd64, uint8_t _rex_prefix, std::vector<uint8_t> _ref);
|
||||
bool need_amd64();
|
||||
bool has_rex();
|
||||
uint8_t rex_prefix();
|
||||
std::vector<uint8_t> bytes();
|
||||
void emit_rex(assembler::assembler& a);
|
||||
void emit_bytes(assembler::assembler& a);
|
||||
private:
|
||||
bool needs_amd64;
|
||||
uint8_t rex;
|
||||
std::vector<uint8_t> mref;
|
||||
};
|
||||
struct ref
|
||||
{
|
||||
ref();
|
||||
ref(reg r);
|
||||
ref(sib_scale_intermediate r);
|
||||
ref(sib_scale_off_intermediate r);
|
||||
low operator()(reg r, bool set_size_flag, bool amd64);
|
||||
void emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1);
|
||||
void emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1,
|
||||
uint8_t op2);
|
||||
private:
|
||||
friend class sib_scale_intermediate;
|
||||
friend class sib_scale_off_intermediate;
|
||||
friend class reg;
|
||||
static ref reg_off(reg r, int32_t off = 0);
|
||||
static ref rip_off(int32_t off = 0);
|
||||
static ref sib(reg base, reg index, uint8_t scale = 1, int32_t off = 0);
|
||||
bool needs_amd64;
|
||||
uint8_t rex;
|
||||
std::vector<uint8_t> mref;
|
||||
};
|
||||
I386(assembler::assembler& _a, bool _amd64);
|
||||
//Is amd64?
|
||||
bool is_amd64();
|
||||
//Is i386?
|
||||
bool is_i386();
|
||||
//Get word size.
|
||||
uint8_t wordsize();
|
||||
//Label:
|
||||
void label(assembler::label& l);
|
||||
//PUSH NWORD <reg>
|
||||
void push_reg(reg r);
|
||||
//POP NWORD <reg>
|
||||
void pop_reg(reg r);
|
||||
//MOV NWORD <reg>,<regmem>
|
||||
void mov_reg_regmem(reg r, ref mem);
|
||||
//LEA <reg>,<mem>
|
||||
void lea_reg_mem(reg r, ref mem);
|
||||
//XOR NWORD <reg>,<regmem>
|
||||
void xor_reg_regmem(reg r, ref mem);
|
||||
//MOV BYTE <regmem>, imm
|
||||
void mov_regmem_imm8(ref mem, uint8_t imm);
|
||||
//ADD BYTE <regmem>, imm
|
||||
void add_regmem_imm8(ref mem, uint8_t imm);
|
||||
//MOV DWORD <regmem>, imm
|
||||
void mov_regmem_imm32(ref mem, uint32_t imm);
|
||||
//MOV WORD <regmem>, <reg>
|
||||
void mov_regmem_reg16(ref mem, reg r);
|
||||
//MOV WORD <reg>, <regmem>
|
||||
void mov_reg_regmem16(reg r, ref mem);
|
||||
//MOV BYTE <reg>, <regmem>
|
||||
void mov_reg_regmem8(reg r, ref mem);
|
||||
//MOV BYTE <regmem>, <reg>
|
||||
void mov_regmem_reg8(ref mem, reg r);
|
||||
//OR BYTE <regmem>, imm
|
||||
void or_regmem_imm8(ref mem, uint8_t imm);
|
||||
//AND BYTE <regmem>, imm
|
||||
void and_regmem_imm8(ref mem, uint8_t imm);
|
||||
//TEST BYTE <regmem>, imm
|
||||
void test_regmem_imm8(ref mem, uint8_t imm);
|
||||
//CMP BYTE <regmem>, imm
|
||||
void cmp_regmem_imm8(ref mem, uint8_t imm);
|
||||
//CMP NWORD <regmem>, imm
|
||||
void cmp_regmem_imm(ref mem, int32_t imm);
|
||||
//SHL NWORD <regmem>, imm
|
||||
void shl_regmem_imm(ref mem, uint8_t imm);
|
||||
//SHL BYTE <regmem>, imm
|
||||
void shl_regmem_imm8(ref mem, uint8_t imm);
|
||||
//AND NWORD <regmem>, imm
|
||||
void and_regmem_imm(ref mem, int32_t imm);
|
||||
//ADD NWORD <regmem>, imm
|
||||
void add_regmem_imm(ref mem, int32_t imm);
|
||||
//ADD NWORD <reg>,<regmem>
|
||||
void add_reg_regmem(reg r, ref mem);
|
||||
//INC NWORD <regmem>
|
||||
void inc_regmem(ref mem);
|
||||
//MOV NWORD <reg>, <addr>.
|
||||
void mov_reg_addr(reg r, assembler::label& l);
|
||||
//MOV NWORD <reg>, imm.
|
||||
void mov_reg_imm(reg r, size_t imm);
|
||||
//NEG BYTE <regmem>
|
||||
void neg_regmem8(ref mem);
|
||||
//OR BYTE <regmem>, <reg>
|
||||
void or_regmem_reg8(ref mem, reg r);
|
||||
//CALL <addr>
|
||||
void call_addr(assembler::label& l);
|
||||
//CALL <regmem>
|
||||
void call_regmem(ref mem);
|
||||
//SETNZ <regmem>
|
||||
void setnz_regmem(ref mem);
|
||||
//JMP <regmem>
|
||||
void jmp_regmem(ref mem);
|
||||
//JNZ SHORT <label>
|
||||
void jnz_short(assembler::label& l);
|
||||
//JZ SHORT <label>
|
||||
void jz_short(assembler::label& l);
|
||||
//JZ SHORT <label>
|
||||
void jz_long(assembler::label& l);
|
||||
//JMP SHORT <label>
|
||||
void jmp_short(assembler::label& l);
|
||||
//JMP LONG <label>
|
||||
void jmp_long(assembler::label& l);
|
||||
//JAE SHORT <label>
|
||||
void jae_short(assembler::label& l);
|
||||
//JAE LONG <label>
|
||||
void jae_long(assembler::label& l);
|
||||
//RET
|
||||
void ret();
|
||||
//Write address constant.
|
||||
void address(assembler::label& l);
|
||||
private:
|
||||
assembler::assembler& a;
|
||||
bool amd64;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -72,6 +72,17 @@ private:
|
|||
int offset;
|
||||
};
|
||||
|
||||
class label_list
|
||||
{
|
||||
public:
|
||||
operator label&();
|
||||
operator label*();
|
||||
label& external(void* addr);
|
||||
|
||||
private:
|
||||
std::list<label> labels;
|
||||
};
|
||||
|
||||
void i386_reloc_rel8(uint8_t* location, size_t target, size_t source);
|
||||
void i386_reloc_rel16(uint8_t* location, size_t target, size_t source);
|
||||
void i386_reloc_rel32(uint8_t* location, size_t target, size_t source);
|
||||
|
@ -162,6 +173,7 @@ struct assembler
|
|||
void align(size_t multiple);
|
||||
void pad(size_t amount);
|
||||
size_t size();
|
||||
void dump(const std::string& basename, const std::string& name, void* base, std::map<std::string, void*> map);
|
||||
std::map<std::string, void*> flush(void* base);
|
||||
private:
|
||||
struct reloc
|
||||
|
|
|
@ -81,6 +81,16 @@ inline bool read_button_value(const char* buf, size_t& idx) throw()
|
|||
*/
|
||||
short read_axis_value(const char* buf, size_t& idx) throw();
|
||||
|
||||
/**
|
||||
* Write axis value.
|
||||
*
|
||||
* Parameter buf: The buffer to write to.
|
||||
* Parameter _v: The axis value.
|
||||
* Returns: Number of bytes written.
|
||||
*/
|
||||
size_t write_axis_value(char* buf, short _v);
|
||||
|
||||
|
||||
/**
|
||||
* Skip whitespace.
|
||||
*
|
||||
|
|
246
include/library/controller-parse-asmgen.hpp
Normal file
246
include/library/controller-parse-asmgen.hpp
Normal file
|
@ -0,0 +1,246 @@
|
|||
#ifndef _library__controller_parse_asmgen__hpp__included__
|
||||
#define _library__controller_parse_asmgen__hpp__included__
|
||||
|
||||
#include "assembler.hpp"
|
||||
|
||||
namespace codegen
|
||||
{
|
||||
//Emit function prologue for serialization function. This also Initializes the read position to start of
|
||||
//serialization buffer.
|
||||
//
|
||||
//The serialization function takes three parameters. In order:
|
||||
// - Dummy pointer (to be ignored).
|
||||
// - Readonly controller state (pointer).
|
||||
// - serialization output buffer (pointer).
|
||||
//
|
||||
template<class T> void emit_serialize_prologue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit code to serialize a button.
|
||||
//
|
||||
//The button is emitted to current write position as specified character if set, '.' if not set. Advances write
|
||||
//position by one byte.
|
||||
//
|
||||
//Parameters:
|
||||
// - offset: The byte in controller state the button bit is in (0-based).
|
||||
// - mask: Bitmask for the button bit.
|
||||
// - ch: The character to use for writing pressed button.
|
||||
//
|
||||
template<class T> void emit_serialize_button(T& a, assembler::label_list& labels, int32_t offset, uint8_t mask,
|
||||
uint8_t ch);
|
||||
|
||||
//Emit code to serialize an axis.
|
||||
//
|
||||
//Call write_axis_value() in order to serialize the axis. Advance write position by indicated number of bytes.
|
||||
//
|
||||
//Parameters:
|
||||
// - offset: The low byte of axis in controller state (the high byte is at <offset>+1). This field is signed.
|
||||
//
|
||||
template<class T> void emit_serialize_axis(T& a, assembler::label_list& labels, int32_t offset);
|
||||
|
||||
//Emit code to write a pipe sign ('|').
|
||||
//
|
||||
//Emit a '|' to current write position. Advance write position by one byte.
|
||||
//
|
||||
template<class T> void emit_serialize_pipe(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit function epilogue for serialization function.
|
||||
//
|
||||
//Serialization function returns the number of bytes in serialized output. The return type is size_t.
|
||||
//
|
||||
template<class T> void emit_serialize_epilogue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit function prologue for deserialization function. This also initializes the read position to start of
|
||||
//serialization buffer.
|
||||
//
|
||||
//The serialization function takes three parameters. In order:
|
||||
// - Dummy pointer (to be ignored).
|
||||
// - controller state to be modified (pointer).
|
||||
// - readonly serialization input buffer (pointer).
|
||||
//
|
||||
template<class T> void emit_deserialize_prologue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit code to clear the controller state.
|
||||
//
|
||||
//Parameters:
|
||||
// - size: The number of bytes in controller state.
|
||||
template<class T> void emit_deserialize_clear_storage(T& a, assembler::label_list& labels, int32_t size);
|
||||
|
||||
//Emit code to deserialize button.
|
||||
//
|
||||
//- If the current read position has '|', jump to <next_pipe> without advancing or modifying controller state.
|
||||
//- If the current read position has '\r', \n' or '\0', jump to <end_deserialize> without advancing or modifying
|
||||
// controller state.
|
||||
//- If the current read position has anything except '.' or ' ', set the corresponding bit in controller state.
|
||||
//- If the first two cases do not apply, advance read position by one byte.
|
||||
//
|
||||
// Parameters:
|
||||
// - offset: The byte offset in controller state the button bit is in (0-based).
|
||||
// - mask: Bitmask for the button bit.
|
||||
// - next_pipe: Label to jump to when finding '|'.
|
||||
// - end_deserialize: Label to jump to when finding end of input.
|
||||
//
|
||||
template<class T> void emit_deserialize_button(T& a, assembler::label_list& labels, int32_t offset,
|
||||
uint8_t mask, assembler::label& next_pipe, assembler::label& end_deserialize);
|
||||
|
||||
//Emit code to deserialize axis.
|
||||
//
|
||||
//Call read_axis_value() in order to deserialize the value. Advance read position by indicated number of bytes.
|
||||
//
|
||||
// Parameters:
|
||||
// - offset: The low byte of axis in controller state (the high byte is at <offset>+1). This field is signed.
|
||||
//
|
||||
template<class T> void emit_deserialize_axis(T& a, assembler::label_list& labels, int32_t offset);
|
||||
|
||||
//Emit code to skip until next pipe ('|')
|
||||
//
|
||||
//While current read position does not contain '|', '\r', '\n' nor '\0', advance current read position.
|
||||
//If '|' is encountered, jump to <next_pipe>. If '\r', '\n' or '\0' is encountered, jump to <deserialize_end>.
|
||||
//
|
||||
// Parameters:
|
||||
// - next_pipe: Address to jump to if '|' is found.
|
||||
// - deserialize_end: Address to jump to if '\r', '\n' or '\0' is found.
|
||||
//
|
||||
template<class T> void emit_deserialize_skip_until_pipe(T& a, assembler::label_list& labels,
|
||||
assembler::label& next_pipe, assembler::label& deserialize_end);
|
||||
|
||||
//Emit code to advance read position by one byte.
|
||||
//
|
||||
//Advance the read position by one byte. This is used to skip '|' bytes.
|
||||
//
|
||||
template<class T> void emit_deserialize_skip(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit code to arrange deserialization function to return DESERIALIZE_SPECIAL_BLANK instead of byte count.
|
||||
//
|
||||
//This changes the return value of deserialization function to DESERIALIZE_SPECIAL_BLANK.
|
||||
//
|
||||
template<class T> void emit_deserialize_special_blank(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit function epilogue for serialization function.
|
||||
//
|
||||
//Deserialization function returns the number of bytes in serialized input (or the special value
|
||||
//DESERIALIZE_SPECIAL_BLANK). The return type is size_t.
|
||||
//
|
||||
template<class T> void emit_deserialize_epilogue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit function prologue for Read function.
|
||||
//
|
||||
//The read function takes four parameters. In order:
|
||||
// - Dummy pointer (to be ignored).
|
||||
// - controller state to be queried.
|
||||
// - controller number (unsigned)
|
||||
// - control index number (unsigned)
|
||||
//
|
||||
//Initialze pending return value to 0.
|
||||
//
|
||||
template<class T> void emit_read_prologue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit function epilogue for read function.
|
||||
//
|
||||
//The read function returns the pending return value (short):
|
||||
// - 0 => Invalid control, or released button.
|
||||
// - 1 => Pressed button.
|
||||
// - (other) => Axis value.
|
||||
//
|
||||
template<class T> void emit_read_epilogue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit dispatch code for read/write function.
|
||||
//
|
||||
//Read dispatch table immediately after the code generated by this and jump to obtained address. The table layout
|
||||
//is controls from low to high number, controllers from low to high number. Each controller has 2^<ilog2controls>
|
||||
//entries.
|
||||
//
|
||||
// Parameters:
|
||||
// - controllers: Number of controllers.
|
||||
// - ilog2controls: Two's logarithm of number of controls per controller (rounded up).
|
||||
// - end: Label pointing to code causing read/write function to return.
|
||||
//
|
||||
template<class T> void emit_read_dispatch(T& a, assembler::label_list& labels,
|
||||
unsigned controllers, unsigned ilog2controls, assembler::label& end);
|
||||
|
||||
//Emit a jump pointer.
|
||||
//
|
||||
//Emits a jump pointer read by emit_read_dispatch() and returns reference to the pointer target.
|
||||
//
|
||||
// Return value:
|
||||
// - The newly created label to code this jumps to.
|
||||
//
|
||||
template<class T> assembler::label& emit_read_label(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit a specfied jump pointer.
|
||||
//
|
||||
//Emits a jump pointer read by emit_read_dispatch() pointing to specified label.
|
||||
//
|
||||
// Parameters:
|
||||
// - b: The code to jump to.
|
||||
//
|
||||
template<class T> void emit_read_label_bad(T& a, assembler::label_list& labels, assembler::label& b);
|
||||
|
||||
//Emit code to read button value.
|
||||
//
|
||||
//If the specified bit is set, set pending return value to 1. Then jump to <end>.
|
||||
//
|
||||
// Parameters:
|
||||
// - l: Label for the code fragment itself (this needs to be defined).
|
||||
// - end: Code to return from the read function.
|
||||
// - offset: Byte offset within controller state (0-based).
|
||||
// - mask: Bitmask for the button bit.
|
||||
//
|
||||
template<class T> void emit_read_button(T& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset, uint8_t mask);
|
||||
|
||||
//Emit code to read axis value.
|
||||
//
|
||||
//Set pending return value to value in specified location. Then jump to <end>.
|
||||
//
|
||||
// Parameters:
|
||||
// - l: Label for the code fragment itself (this needs to be defined).
|
||||
// - end: Code to return from the read function.
|
||||
// - offset: Low byte offset in controller state (high byte is at <offset>+1). Signed.
|
||||
//
|
||||
template<class T> void emit_read_axis(T& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset);
|
||||
|
||||
//Emit function prologue for Write function.
|
||||
//
|
||||
//The write function takes five parameters. In order:
|
||||
// - Dummy pointer (to be ignored).
|
||||
// - controller state to be queried.
|
||||
// - controller number (unsigned)
|
||||
// - control index number (unsigned)
|
||||
// - value to write (short).
|
||||
//
|
||||
template<class T> void emit_write_prologue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit epilogue for write function.
|
||||
//
|
||||
//Write function does not return anything.
|
||||
//
|
||||
template<class T> void emit_write_epilogue(T& a, assembler::label_list& labels);
|
||||
|
||||
//Emit code to write button value.
|
||||
//
|
||||
//If the value to write is nonzero, set specified bit to 1, otherwise set it to 0. Then jump to <end>.
|
||||
//
|
||||
// Parameters:
|
||||
// - l: Label for the code fragment itself (this needs to be defined).
|
||||
// - end: Code to return from the write function.
|
||||
// - offset: Byte offset within controller state (0-based).
|
||||
// - mask: Bitmask for the button bit.
|
||||
//
|
||||
template<class T> void emit_write_button(T& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset, uint8_t mask);
|
||||
|
||||
//Emit code to write axis value.
|
||||
//
|
||||
//Write the value to write to specified location. Then jump to <end>.
|
||||
//
|
||||
// Parameters:
|
||||
// - l: Label for the code fragment itself (this needs to be defined).
|
||||
// - end: Code to return from the write function.
|
||||
// - offset: Low byte offset in controller state (high byte is at <offset>+1). Signed.
|
||||
//
|
||||
template<class T> void emit_write_axis(T& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -52,7 +52,7 @@ private:
|
|||
static size_t _serialize(const port_type* _this, const unsigned char* buffer, char* textbuf);
|
||||
static size_t _deserialize(const port_type* _this, unsigned char* buffer, const char* textbuf);
|
||||
void make_dynamic_blocks();
|
||||
void make_routines(assembler::assembler& a, std::list<assembler::label>& labels);
|
||||
void make_routines(assembler::assembler& a, assembler::label_list& labels);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,8 +8,8 @@ __all__.files: $(CORES_FILES)
|
|||
lua ../genfilelist.lua $^ >$@
|
||||
cat $(CORES_FLAGS) >$(ALLFLAGS)
|
||||
|
||||
make-ports.exe: make-ports.cpp ../library/json.cpp ../library/utf8.cpp ../library/string.cpp ../library/controller-parse.cpp ../library/controller-data.cpp ../library/sha256.cpp ../library/assembler.cpp ../library/hex.cpp ../library/eatarg.cpp ../library/int24.cpp ../library/binarystream.cpp ../library/integer-pool.cpp
|
||||
$(HOSTCC) -g -std=gnu++0x -I../../include/library -o $@ $^ -lboost_regex$(HOST_BOOST_POSTFIX) -lboost_system$(HOST_BOOST_POSTFIX) -Wall
|
||||
make-ports.exe: make-ports.cpp ../library/json.cpp ../library/utf8.cpp ../library/string.cpp ../library/controller-parse.cpp ../library/controller-data.cpp ../library/sha256.cpp ../library/assembler.cpp ../library/hex.cpp ../library/eatarg.cpp ../library/int24.cpp ../library/binarystream.cpp ../library/integer-pool.cpp
|
||||
$(HOSTCC) -g -std=gnu++0x -I../../include/library -o $@ $^ -lboost_regex$(HOST_BOOST_POSTFIX) -lboost_system$(HOST_BOOST_POSTFIX) -Wall -DNO_ASM_GENERATION
|
||||
|
||||
bsnes-legacy/$(ALLFILES): forcelook make-ports.exe
|
||||
$(MAKE) -C bsnes-legacy
|
||||
|
|
16
src/library/assembler-intrinsics-dummy.cpp
Normal file
16
src/library/assembler-intrinsics-dummy.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "assembler-intrinsics-dummy.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace assembler_intrinsics
|
||||
{
|
||||
dummyarch::dummyarch(assembler::assembler& _a)
|
||||
: a(_a)
|
||||
{
|
||||
}
|
||||
|
||||
//Label:
|
||||
void dummyarch::label(assembler::label& l)
|
||||
{
|
||||
a._label(l);
|
||||
}
|
||||
}
|
555
src/library/assembler-intrinsics-i386.cpp
Normal file
555
src/library/assembler-intrinsics-i386.cpp
Normal file
|
@ -0,0 +1,555 @@
|
|||
#include "assembler-intrinsics-i386.hpp"
|
||||
#include "serialization.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace assembler_intrinsics
|
||||
{
|
||||
const I386::reg I386::reg_none(255);
|
||||
const I386::reg I386::reg_ax(0);
|
||||
const I386::reg I386::reg_bx(3);
|
||||
const I386::reg I386::reg_cx(1);
|
||||
const I386::reg I386::reg_dx(2);
|
||||
const I386::reg I386::reg_bp(5);
|
||||
const I386::reg I386::reg_sp(4);
|
||||
const I386::reg I386::reg_si(6);
|
||||
const I386::reg I386::reg_di(7);
|
||||
const I386::reg I386::reg_r8(8);
|
||||
const I386::reg I386::reg_r9(9);
|
||||
const I386::reg I386::reg_r10(10);
|
||||
const I386::reg I386::reg_r11(11);
|
||||
const I386::reg I386::reg_r12(12);
|
||||
const I386::reg I386::reg_r13(13);
|
||||
const I386::reg I386::reg_r14(14);
|
||||
const I386::reg I386::reg_r15(15);
|
||||
const I386::reg I386::reg_rip(254);
|
||||
|
||||
const I386::reg i386_r0(0);
|
||||
const I386::reg i386_r1(1);
|
||||
const I386::reg i386_r2(2);
|
||||
const I386::reg i386_r3(3);
|
||||
const I386::reg i386_r4(4);
|
||||
const I386::reg i386_r5(5);
|
||||
const I386::reg i386_r6(6);
|
||||
const I386::reg i386_r7(7);
|
||||
|
||||
I386::reg::reg(uint8_t _val)
|
||||
{
|
||||
val = _val;
|
||||
}
|
||||
|
||||
I386::ref I386::reg::operator[](int32_t off) const
|
||||
{
|
||||
if(val < 16)
|
||||
return ref::reg_off(*this, off);
|
||||
else if(val == 254)
|
||||
return I386::ref::rip_off(off);
|
||||
else
|
||||
throw std::runtime_error("Bad register offset-base");
|
||||
}
|
||||
|
||||
I386::sib_scale_intermediate I386::reg::operator*(uint8_t scale) const
|
||||
{
|
||||
return sib_scale_intermediate(*this, scale);
|
||||
}
|
||||
|
||||
I386::ref I386::reg::operator[](const reg r) const
|
||||
{
|
||||
return ref::sib(*this, r, 1);
|
||||
}
|
||||
|
||||
I386::ref I386::reg::operator[](sib_scale_intermediate r) const
|
||||
{
|
||||
return ref::sib(*this, r.index, r.scale);
|
||||
}
|
||||
|
||||
I386::ref I386::reg::operator[](sib_scale_off_intermediate r) const
|
||||
{
|
||||
return ref::sib(*this, r.index, r.scale, r.offset);
|
||||
}
|
||||
|
||||
I386::sib_scale_off_intermediate I386::reg::operator+(int32_t off) const
|
||||
{
|
||||
return sib_scale_off_intermediate(*this, 1, off);
|
||||
}
|
||||
|
||||
bool I386::reg::hbit() { return (val & 8); }
|
||||
uint8_t I386::reg::lbits() { return val & 7; }
|
||||
uint8_t I386::reg::num() { return val & 15; }
|
||||
bool I386::reg::valid(bool amd64) { return (val < (amd64 ? 16 : 8)); }
|
||||
bool I386::reg::is_none() { return (val == 255); }
|
||||
|
||||
I386::sib_scale_off_intermediate::sib_scale_off_intermediate(reg _idx, uint8_t _scale, int32_t _offset)
|
||||
: index(_idx), scale(_scale), offset(_offset)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
I386::sib_scale_intermediate::sib_scale_intermediate(reg _idx, uint8_t _scale)
|
||||
: index(_idx), scale(_scale)
|
||||
{
|
||||
}
|
||||
|
||||
I386::sib_scale_off_intermediate I386::sib_scale_intermediate::operator+(int32_t off) const
|
||||
{
|
||||
return sib_scale_off_intermediate(index, scale, off);
|
||||
}
|
||||
|
||||
|
||||
void check_register(I386::reg r, bool amd64)
|
||||
{
|
||||
if(!r.valid(amd64))
|
||||
throw std::runtime_error("Illegal register");
|
||||
}
|
||||
|
||||
void I386::label(assembler::label& l)
|
||||
{
|
||||
a._label(l);
|
||||
}
|
||||
|
||||
I386::low::low(bool _need_amd64, uint8_t _rex_prefix, std::vector<uint8_t> _ref)
|
||||
: needs_amd64(_need_amd64), rex(_rex_prefix), mref(_ref)
|
||||
{
|
||||
if(rex) needs_amd64 = true;
|
||||
}
|
||||
bool I386::low::need_amd64() { return needs_amd64; }
|
||||
bool I386::low::has_rex() { return (rex != 0); }
|
||||
uint8_t I386::low::rex_prefix() { return rex; }
|
||||
std::vector<uint8_t> I386::low::bytes() { return mref; }
|
||||
|
||||
void I386::low::emit_rex(assembler::assembler& a)
|
||||
{
|
||||
if(has_rex()) a(rex);
|
||||
}
|
||||
|
||||
void I386::low::emit_bytes(assembler::assembler& a)
|
||||
{
|
||||
for(auto i : mref) a(i);
|
||||
}
|
||||
|
||||
I386::ref::ref()
|
||||
{
|
||||
}
|
||||
|
||||
I386::ref::ref(reg r)
|
||||
{
|
||||
if(!r.valid(true))
|
||||
throw std::runtime_error("Illegal register");
|
||||
needs_amd64 = r.hbit();
|
||||
rex = r.hbit() ? 1 : 0;
|
||||
mref.push_back(0xC0 + r.lbits());
|
||||
}
|
||||
|
||||
I386::ref::ref(sib_scale_intermediate r)
|
||||
{
|
||||
*this = sib(reg_none, r.index, r.scale);
|
||||
}
|
||||
|
||||
I386::ref::ref(sib_scale_off_intermediate r)
|
||||
{
|
||||
*this = sib(reg_none, r.index, r.scale, r.offset);
|
||||
}
|
||||
|
||||
I386::ref I386::ref::reg_off(reg r, int32_t off)
|
||||
{
|
||||
if(!r.valid(true))
|
||||
throw std::runtime_error("Illegal register");
|
||||
bool need_off = (off != 0);
|
||||
bool need_loff = (off < -128 || off > 127);
|
||||
I386::ref x;
|
||||
x.needs_amd64 = r.hbit();
|
||||
x.rex = r.hbit() ? 1 : 0;
|
||||
if(r.lbits() == 5)
|
||||
need_off = true; //EBP and R13 always need offset.
|
||||
uint8_t rtype = need_loff ? 2 : (need_off ? 1 : 0);
|
||||
if(r.lbits() == 4) {
|
||||
//SIB is required for these.
|
||||
x.mref.push_back(0x04 + rtype * 0x40);
|
||||
x.mref.push_back(0x24);
|
||||
} else {
|
||||
x.mref.push_back(r.lbits() + rtype * 0x40);
|
||||
}
|
||||
size_t bytes = x.mref.size();
|
||||
if(need_loff) {
|
||||
x.mref.resize(bytes + 4);
|
||||
serialization::s32l(&x.mref[bytes], off);
|
||||
} else if(need_off) {
|
||||
x.mref.resize(bytes + 1);
|
||||
serialization::s8l(&x.mref[bytes], off);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
I386::ref I386::ref::rip_off(int32_t off)
|
||||
{
|
||||
I386::ref x;
|
||||
x.needs_amd64 = true;
|
||||
x.mref.resize(5);
|
||||
x.mref[0] = 0x05;
|
||||
serialization::s32l(&x.mref[1], off);
|
||||
x.rex = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
I386::ref I386::ref::sib(reg base, reg index, uint8_t scale, int32_t off)
|
||||
{
|
||||
I386::ref x;
|
||||
if(!base.is_none() && !base.valid(true))
|
||||
throw std::runtime_error("Illegal base in SIB");
|
||||
if((!index.is_none() && !index.valid(true)) || index.num() == 4)
|
||||
throw std::runtime_error("Illegal index in SIB");
|
||||
uint8_t ss = 0;
|
||||
switch(scale) {
|
||||
case 1: ss = 0; break;
|
||||
case 2: ss = 1; break;
|
||||
case 4: ss = 2; break;
|
||||
case 8: ss = 3; break;
|
||||
default: throw std::runtime_error("Illegal scale in SIB");
|
||||
};
|
||||
|
||||
bool need_off = (off != 0);
|
||||
bool need_loff = (off < -128 || off > 127);
|
||||
if(base.is_none()) {
|
||||
//Base is the offset.
|
||||
x.mref.push_back(0x04); //SIB coming, no offset
|
||||
x.mref.push_back(ss * 64 + index.lbits() + 5); //Base is "EBP".
|
||||
need_loff = true;
|
||||
} else {
|
||||
if(base.num() == 5)
|
||||
need_off = true; //This always needs offset.
|
||||
uint8_t rtype = need_loff ? 2 : (need_off ? 1 : 0);
|
||||
x.mref.push_back(0x04 + rtype * 0x40); //SIB coming.
|
||||
x.mref.push_back(ss * 64 + index.lbits() * 8 + base.lbits());
|
||||
}
|
||||
x.rex = 0;
|
||||
if(!base.is_none()) x.rex |= (base.hbit() ? 1 : 0);
|
||||
if(!index.is_none()) x.rex |= (index.hbit() ? 2 : 0);
|
||||
x.needs_amd64 = (x.rex != 0);
|
||||
size_t bytes = x.mref.size();
|
||||
if(need_loff) {
|
||||
x.mref.resize(bytes + 4);
|
||||
serialization::s32l(&x.mref[bytes], off);
|
||||
} else if(need_off) {
|
||||
x.mref.resize(bytes + 1);
|
||||
serialization::s8l(&x.mref[bytes], off);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
I386::low I386::ref::operator()(reg r, bool set_size_flag, bool amd64)
|
||||
{
|
||||
check_register(r, amd64);
|
||||
auto c = mref;
|
||||
c[0] |= r.lbits() * 8;
|
||||
uint8_t _rex = rex;
|
||||
if(r.hbit()) _rex+=4; //Set R
|
||||
if(set_size_flag && amd64) _rex+=8; //Set S if needed.
|
||||
if(_rex) _rex+=0x40; //Ret rex prefix bits.
|
||||
if((_rex || needs_amd64) && !amd64)
|
||||
throw std::runtime_error("Illegal memory reference for i386");
|
||||
return low(needs_amd64, _rex, c);
|
||||
}
|
||||
|
||||
void I386::ref::emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1)
|
||||
{
|
||||
auto xref = (*this)(r, set_size_flag, amd64);
|
||||
xref.emit_rex(a);
|
||||
a(op1);
|
||||
xref.emit_bytes(a);
|
||||
}
|
||||
|
||||
void I386::ref::emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1,
|
||||
uint8_t op2)
|
||||
{
|
||||
auto xref = (*this)(r, set_size_flag, amd64);
|
||||
xref.emit_rex(a);
|
||||
a(op1, op2);
|
||||
xref.emit_bytes(a);
|
||||
}
|
||||
|
||||
I386::I386(assembler::assembler& _a, bool _amd64)
|
||||
: a(_a), amd64(_amd64)
|
||||
{
|
||||
}
|
||||
|
||||
bool I386::is_amd64()
|
||||
{
|
||||
return amd64;
|
||||
}
|
||||
|
||||
//Is i386?
|
||||
bool I386::is_i386()
|
||||
{
|
||||
return !amd64;
|
||||
}
|
||||
|
||||
//Get word size.
|
||||
uint8_t I386::wordsize()
|
||||
{
|
||||
return amd64 ? 8 : 4;
|
||||
}
|
||||
|
||||
//PUSH NWORD <reg>
|
||||
void I386::push_reg(reg r)
|
||||
{
|
||||
check_register(r, amd64);
|
||||
if(amd64 && r.hbit()) a(0x41);
|
||||
a(0x50 + r.lbits());
|
||||
}
|
||||
//POP NWORD <reg>
|
||||
void I386::pop_reg(reg r)
|
||||
{
|
||||
check_register(r, amd64);
|
||||
if(amd64 && r.hbit()) a(0x41);
|
||||
a(0x58 + r.lbits());
|
||||
}
|
||||
//MOV NWORD <reg>,<regmem>
|
||||
void I386::mov_reg_regmem(reg r, I386::ref mem)
|
||||
{
|
||||
mem.emit(a, true, amd64, r, 0x8B);
|
||||
}
|
||||
//XOR NWORD <reg>,<regmem>
|
||||
void I386::xor_reg_regmem(reg r, I386::ref mem)
|
||||
{
|
||||
mem.emit(a, true, amd64, r, 0x33);
|
||||
}
|
||||
|
||||
//LEA <reg>,<mem>
|
||||
void I386::lea_reg_mem(reg r, I386::ref mem)
|
||||
{
|
||||
mem.emit(a, true, amd64, r, 0x8D);
|
||||
}
|
||||
|
||||
//AND NWORD <regmem>, imm
|
||||
void I386::and_regmem_imm(I386::ref mem, int32_t imm)
|
||||
{
|
||||
mem.emit(a, true, amd64, i386_r4, 0x81);
|
||||
a(assembler::vle<int32_t>(imm));
|
||||
}
|
||||
|
||||
//SHL NWORD <regmem>, imm
|
||||
void I386::shl_regmem_imm(I386::ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, true, amd64, i386_r4, 0xC1);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//SHL BYTE <regmem>, imm
|
||||
void I386::shl_regmem_imm8(ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, true, amd64, i386_r4, 0xC0);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//ADD NWORD <regmem>, imm
|
||||
void I386::add_regmem_imm(I386::ref mem, int32_t imm)
|
||||
{
|
||||
mem.emit(a, true, amd64, i386_r0, 0x81);
|
||||
a(assembler::vle<int32_t>(imm));
|
||||
}
|
||||
|
||||
//ADD NWORD <reg>,<regmem>
|
||||
void I386::add_reg_regmem(reg r, I386::ref mem)
|
||||
{
|
||||
mem.emit(a, true, amd64, r, 0x03);
|
||||
}
|
||||
|
||||
//MOV BYTE <regmem>, imm
|
||||
void I386::mov_regmem_imm8(I386::ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r0, 0xC6);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//MOV DWORD <regmem>, imm
|
||||
void I386::mov_regmem_imm32(I386::ref mem, uint32_t imm)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r0, 0xC7);
|
||||
a(assembler::vle<uint32_t>(imm));
|
||||
}
|
||||
|
||||
//MOV NWORD <reg>, imm.
|
||||
void I386::mov_reg_imm(reg r, size_t imm)
|
||||
{
|
||||
check_register(r, amd64);
|
||||
if(amd64) a(0x48 | (r.hbit() ? 1 : 0));
|
||||
a(0xB8 + r.lbits());
|
||||
a(assembler::vle<size_t>(imm));
|
||||
}
|
||||
|
||||
//CMP NWORD <regmem>, imm
|
||||
void I386::cmp_regmem_imm(I386::ref mem, int32_t imm)
|
||||
{
|
||||
mem.emit(a, true, amd64, i386_r7, 0x81);
|
||||
a(assembler::vle<int32_t>(imm));
|
||||
}
|
||||
|
||||
//MOV WORD <regmem>, <reg>
|
||||
void I386::mov_regmem_reg16(I386::ref mem, reg r)
|
||||
{
|
||||
a(0x66);
|
||||
mem.emit(a, false, amd64, r, 0x89);
|
||||
}
|
||||
|
||||
//MOV BYTE <regmem>, <reg>
|
||||
void I386::mov_regmem_reg8(ref mem, reg r)
|
||||
{
|
||||
mem.emit(a, false, amd64, r, 0x88);
|
||||
}
|
||||
|
||||
//MOV WORD <reg>, <regmem>
|
||||
void I386::mov_reg_regmem16(reg r, I386::ref mem)
|
||||
{
|
||||
a(0x66);
|
||||
mem.emit(a, false, amd64, r, 0x8B);
|
||||
}
|
||||
|
||||
//MOV BYTE <reg>, <regmem>
|
||||
void I386::mov_reg_regmem8(reg r, ref mem)
|
||||
{
|
||||
mem.emit(a, false, amd64, r, 0x8A);
|
||||
}
|
||||
|
||||
//OR BYTE <regmem>, imm
|
||||
void I386::or_regmem_imm8(I386::ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r1, 0x80);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//AND BYTE <regmem>, imm
|
||||
void I386::and_regmem_imm8(I386::ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r4, 0x80);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//INC NWORD <regmem>
|
||||
void I386::inc_regmem(I386::ref mem)
|
||||
{
|
||||
mem.emit(a, true, amd64, i386_r0, 0xFF);
|
||||
}
|
||||
|
||||
//TEST BYTE <regmem>, imm
|
||||
void I386::test_regmem_imm8(I386::ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r0, 0xF6);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//CMP BYTE <regmem>, imm
|
||||
void I386::cmp_regmem_imm8(I386::ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r7, 0x80);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//MOV NWORD <reg>, <addr>.
|
||||
void I386::mov_reg_addr(reg r, assembler::label& l)
|
||||
{
|
||||
if(amd64) a(0x48 + (r.hbit() ? 1 : 0));
|
||||
a(0xB8 + r.lbits());
|
||||
address(l);
|
||||
}
|
||||
|
||||
//NEG BYTE <regmem>
|
||||
void I386::neg_regmem8(ref mem)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r3, 0xF6);
|
||||
}
|
||||
|
||||
//ADD BYTE <regmem>, imm
|
||||
void I386::add_regmem_imm8(ref mem, uint8_t imm)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r0, 0x80);
|
||||
a(imm);
|
||||
}
|
||||
|
||||
//OR BYTE <regmem>, <reg>
|
||||
void I386::or_regmem_reg8(ref mem, reg r)
|
||||
{
|
||||
mem.emit(a, false, amd64, r, 0x08);
|
||||
}
|
||||
|
||||
//CALL <addr>
|
||||
void I386::call_addr(assembler::label& l)
|
||||
{
|
||||
if(amd64) {
|
||||
call_regmem(reg_rip[2]);
|
||||
a(0xEB, 0x08); //JMP +8
|
||||
address(l);
|
||||
} else {
|
||||
a(0xE8, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
|
||||
}
|
||||
}
|
||||
|
||||
//CALL <regmem>
|
||||
void I386::call_regmem(I386::ref mem)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r2, 0xFF);
|
||||
}
|
||||
|
||||
//JMP <regmem>
|
||||
void I386::jmp_regmem(I386::ref mem)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r4, 0xFF);
|
||||
}
|
||||
|
||||
//SETNZ <regmem>
|
||||
void I386::setnz_regmem(I386::ref mem)
|
||||
{
|
||||
mem.emit(a, false, amd64, i386_r0, 0x0F, 0x95);
|
||||
}
|
||||
|
||||
//JNZ SHORT <label>
|
||||
void I386::jnz_short(assembler::label& l)
|
||||
{
|
||||
a(0x75, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
|
||||
}
|
||||
|
||||
//JZ SHORT <label>
|
||||
void I386::jz_short(assembler::label& l)
|
||||
{
|
||||
a(0x74, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
|
||||
}
|
||||
|
||||
//JMP SHORT <label>
|
||||
void I386::jmp_short(assembler::label& l)
|
||||
{
|
||||
a(0xEB, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
|
||||
}
|
||||
|
||||
//JMP LONG <label>
|
||||
void I386::jmp_long(assembler::label& l)
|
||||
{
|
||||
a(0xE9, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
|
||||
}
|
||||
|
||||
//JZ LONG <label>
|
||||
void I386::jz_long(assembler::label& l)
|
||||
{
|
||||
a(0x0F, 0x84, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
|
||||
}
|
||||
|
||||
//JAE SHORT <label>
|
||||
void I386::jae_short(assembler::label& l)
|
||||
{
|
||||
a(0x73, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
|
||||
}
|
||||
|
||||
//JAE LONG <label>
|
||||
void I386::jae_long(assembler::label& l)
|
||||
{
|
||||
a(0x0F, 0x83, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
|
||||
}
|
||||
|
||||
void I386::ret()
|
||||
{
|
||||
a(0xC3);
|
||||
}
|
||||
|
||||
void I386::address(assembler::label& l)
|
||||
{
|
||||
a(assembler::relocation_tag((amd64 ? assembler::i386_reloc_abs64 : assembler::i386_reloc_abs32), l),
|
||||
assembler::pad_tag(wordsize()));
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
#include "assembler.hpp"
|
||||
#include "serialization.hpp"
|
||||
#include "string.hpp"
|
||||
#include "hex.hpp"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
#define _USE_BSD
|
||||
#include <unistd.h>
|
||||
|
@ -18,6 +21,25 @@
|
|||
|
||||
namespace assembler
|
||||
{
|
||||
label_list::operator label&()
|
||||
{
|
||||
labels.push_back(label());
|
||||
return labels.back();
|
||||
}
|
||||
|
||||
label_list::operator label*()
|
||||
{
|
||||
labels.push_back(label());
|
||||
return &labels.back();
|
||||
}
|
||||
|
||||
label& label_list::external(void* addr)
|
||||
{
|
||||
labels.push_back(label());
|
||||
labels.back() = label(addr);
|
||||
return labels.back();
|
||||
}
|
||||
|
||||
assembler::assembler()
|
||||
{
|
||||
}
|
||||
|
@ -92,6 +114,26 @@ std::map<std::string, void*> assembler::flush(void* base)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void assembler::dump(const std::string& basename, const std::string& name, void* base,
|
||||
std::map<std::string, void*> map)
|
||||
{
|
||||
static unsigned x = 0;
|
||||
std::string realbase = (stringfmt() << basename << "." << (x++)).str();
|
||||
//Dump symbol map.
|
||||
std::ofstream symbols1(realbase + ".syms");
|
||||
symbols1 << hex::to<size_t>((size_t)base, false) << " __base(" << name << ")" << std::endl;
|
||||
for(auto i : map)
|
||||
symbols1 << hex::to<size_t>((size_t)i.second, false) << " " << i.first << std::endl;
|
||||
symbols1 << hex::to<size_t>((size_t)base + data.size(), false) << " __end" << std::endl;
|
||||
symbols1.close();
|
||||
|
||||
//Dump generated binary.
|
||||
std::ofstream symbols2(realbase + ".bin", std::ios::binary);
|
||||
for(unsigned i = 0; i < data.size(); i++)
|
||||
symbols2 << ((char*)base)[i];
|
||||
symbols2.close();
|
||||
}
|
||||
|
||||
void i386_reloc_rel8(uint8_t* location, size_t target, size_t source)
|
||||
{
|
||||
int8_t d = target - source - 1;
|
||||
|
|
|
@ -227,6 +227,20 @@ short read_axis_value(const char* buf, size_t& idx) throw()
|
|||
return static_cast<short>(numval);
|
||||
}
|
||||
|
||||
size_t write_axis_value(char* buf, short _v)
|
||||
{
|
||||
int v = _v;
|
||||
size_t r = 0;
|
||||
buf[r++] = ' ';
|
||||
if(v < 0) { buf[r++] = '-'; v = -v; }
|
||||
if(v >= 10000) buf[r++] = '0' + (v / 10000 % 10);
|
||||
if(v >= 1000) buf[r++] = '0' + (v / 1000 % 10);
|
||||
if(v >= 100) buf[r++] = '0' + (v / 100 % 10);
|
||||
if(v >= 10) buf[r++] = '0' + (v / 10 % 10);
|
||||
buf[r++] = '0' + (v % 10);
|
||||
return r;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
port_type_set& dummytypes()
|
||||
|
|
135
src/library/controller-parse-dummy.cpp
Normal file
135
src/library/controller-parse-dummy.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "controller-parse-asmgen.hpp"
|
||||
#include "assembler-intrinsics-dummy.hpp"
|
||||
|
||||
using namespace assembler_intrinsics;
|
||||
|
||||
namespace codegen
|
||||
{
|
||||
template<> void emit_serialize_prologue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_serialize_button(dummyarch& a, assembler::label_list& labels, int32_t offset, uint8_t mask,
|
||||
uint8_t ch)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_serialize_axis(dummyarch& a, assembler::label_list& labels, int32_t offset)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_serialize_pipe(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_serialize_epilogue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_prologue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_clear_storage(dummyarch& a, assembler::label_list& labels, int32_t size)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_button(dummyarch& a, assembler::label_list& labels, int32_t offset,
|
||||
uint8_t mask, assembler::label& next_pipe, assembler::label& end_deserialize)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_axis(dummyarch& a, assembler::label_list& labels, int32_t offset)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_skip_until_pipe(dummyarch& a, assembler::label_list& labels,
|
||||
assembler::label& next_pipe, assembler::label& deserialize_end)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_skip(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_special_blank(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_epilogue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_read_prologue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_read_epilogue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_read_dispatch(dummyarch& a, assembler::label_list& labels,
|
||||
unsigned controllers, unsigned ilog2controls, assembler::label& end)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> assembler::label& emit_read_label(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_read_label_bad(dummyarch& a, assembler::label_list& labels, assembler::label& b)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_read_button(dummyarch& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset, uint8_t mask)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_read_axis(dummyarch& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_write_prologue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_write_epilogue(dummyarch& a, assembler::label_list& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_write_button(dummyarch& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset, uint8_t mask)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
|
||||
template<> void emit_write_axis(dummyarch& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
}
|
407
src/library/controller-parse-i386.cpp
Normal file
407
src/library/controller-parse-i386.cpp
Normal file
|
@ -0,0 +1,407 @@
|
|||
#include "controller-data.hpp"
|
||||
#include "controller-parse-asmgen.hpp"
|
||||
#include "assembler-intrinsics-i386.hpp"
|
||||
|
||||
using namespace assembler_intrinsics;
|
||||
|
||||
namespace codegen
|
||||
{
|
||||
int calc_mask_bit(uint8_t mask)
|
||||
{
|
||||
switch(mask) {
|
||||
case 0x01: return 0;
|
||||
case 0x02: return 1;
|
||||
case 0x04: return 2;
|
||||
case 0x08: return 3;
|
||||
case 0x10: return 4;
|
||||
case 0x20: return 5;
|
||||
case 0x40: return 6;
|
||||
case 0x80: return 7;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
template<> void emit_serialize_prologue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Variable assignments:
|
||||
//SI: State block.
|
||||
//DX: Output buffer.
|
||||
//AX: Write position counter.
|
||||
|
||||
//On amd64, the ABI places the parameters of serialize() into those registers, but on i386, the parameters
|
||||
//are passed on stack. So load the parameters into correct registers. Also, save the registers beforehand.
|
||||
if(a.is_i386()) {
|
||||
a.push_reg(I386::reg_si);
|
||||
a.push_reg(I386::reg_dx);
|
||||
a.mov_reg_regmem(I386::reg_si, I386::reg_sp[16]);
|
||||
a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[20]);
|
||||
}
|
||||
//Set write position to 0.
|
||||
a.xor_reg_regmem(I386::reg_ax, I386::reg_ax);
|
||||
}
|
||||
|
||||
template<> void emit_serialize_button(I386& a, assembler::label_list& labels, int32_t offset, uint8_t mask,
|
||||
uint8_t ch)
|
||||
{
|
||||
//Test the bit with specified mask on specified position. If set, set CL to 1, otherwise to 0.
|
||||
a.test_regmem_imm8(I386::reg_si[offset], mask);
|
||||
a.setnz_regmem(I386::reg_cx);
|
||||
//Negate CL. This causes CL to become 0xFF or 0x00.
|
||||
a.neg_regmem8(I386::reg_cx);
|
||||
//AND CL with ch-'.' and add '.'. This results in ch if CL was 0xFF and '.' if CL was 0x00.
|
||||
a.and_regmem_imm8(I386::reg_cx, ch - '.');
|
||||
a.add_regmem_imm8(I386::reg_cx, '.');
|
||||
//Write the byte to write position and advance write position.
|
||||
a.mov_regmem_reg8(I386::reg_dx[I386::reg_ax], I386::reg_cx);
|
||||
a.inc_regmem(I386::reg_ax);
|
||||
}
|
||||
|
||||
template<> void emit_serialize_axis(I386& a, assembler::label_list& labels, int32_t offset)
|
||||
{
|
||||
//Get reference to write_axis_value() routine.
|
||||
assembler::label& axisprint = labels.external((void*)write_axis_value);
|
||||
|
||||
//Save the state registers.
|
||||
if(a.is_i386()) a.push_reg(I386::reg_di);
|
||||
a.push_reg(I386::reg_si);
|
||||
a.push_reg(I386::reg_dx);
|
||||
a.push_reg(I386::reg_ax);
|
||||
//Load the axis value from controller state (i386/amd64 is LE, so endianess is right).
|
||||
a.mov_reg_regmem16(I386::reg_si, I386::reg_si[offset]);
|
||||
//The high bits of SI are junk. Mask those off. This will become the second parameter of axis_print().
|
||||
a.and_regmem_imm(I386::reg_si, 0xFFFF);
|
||||
//Set first parameter of axis_print() to current write position.
|
||||
a.lea_reg_mem(I386::reg_di, I386::reg_dx[I386::reg_ax]);
|
||||
//If on i386, push the parameters into stack, as parameters are passed there.
|
||||
if(a.is_i386()) a.push_reg(I386::reg_si);
|
||||
if(a.is_i386()) a.push_reg(I386::reg_di);
|
||||
//Call the axis_print() routine.
|
||||
a.call_addr(axisprint);
|
||||
//If on i386, clean up the passed parameters.
|
||||
if(a.is_i386()) a.add_regmem_imm(I386::reg_sp, 8);
|
||||
//Restore the state. We restore ax to cx, so we preserve the return value of axis_print().
|
||||
a.pop_reg(I386::reg_cx);
|
||||
a.pop_reg(I386::reg_dx);
|
||||
a.pop_reg(I386::reg_si);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_di);
|
||||
//Add the old pointer to return value of axis_print() and make that the new pointer. Addition is commutative,
|
||||
//so new = old + delta and new = delta + old are the same.
|
||||
a.add_reg_regmem(I386::reg_ax, I386::reg_cx);
|
||||
}
|
||||
|
||||
template<> void emit_serialize_pipe(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Write '|' to write position and adavance write position.
|
||||
a.mov_regmem_imm8(I386::reg_dx[I386::reg_ax], '|');
|
||||
a.inc_regmem(I386::reg_ax);
|
||||
}
|
||||
|
||||
template<> void emit_serialize_epilogue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//If on i386, restore the saved registers.
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_dx);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_si);
|
||||
//Exit routine. The pointer in AX is correct for return value.
|
||||
a.ret();
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_prologue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Variable assignments:
|
||||
//SI: State block.
|
||||
//DX: Input buffer.
|
||||
//AX: read position counter.
|
||||
|
||||
//On amd64, the ABI places the parameters of deserialize() into those registers, but on i386, the parameters
|
||||
//are passed on stack. So load the parameters into correct registers. Also, save the registers beforehand.
|
||||
if(a.is_i386()) {
|
||||
a.push_reg(I386::reg_si);
|
||||
a.push_reg(I386::reg_dx);
|
||||
a.mov_reg_regmem(I386::reg_si, I386::reg_sp[16]);
|
||||
a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[20]);
|
||||
}
|
||||
//Set read position to 0.
|
||||
a.xor_reg_regmem(I386::reg_ax, I386::reg_ax);
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_clear_storage(I386& a, assembler::label_list& labels, int32_t size)
|
||||
{
|
||||
int32_t i;
|
||||
//Write four zero bytes at once if there is space for so.
|
||||
for(i = 0; i < size - 3; i += 4)
|
||||
a.mov_regmem_imm32(I386::reg_si[i], 0);
|
||||
//Write the rest one byte at a time.
|
||||
for(; i < size; i++)
|
||||
a.mov_regmem_imm8(I386::reg_si[i], 0);
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_button(I386& a, assembler::label_list& labels, int32_t offset,
|
||||
uint8_t mask, assembler::label& next_pipe, assembler::label& end_deserialize)
|
||||
{
|
||||
assembler::label& clear_button = labels;
|
||||
|
||||
//Load the next input byte into CL.
|
||||
a.mov_reg_regmem8(I386::reg_cx, I386::reg_dx[I386::reg_ax]);
|
||||
//Check for special values '|', '\r', '\n', '\0'.
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '|');
|
||||
a.jz_long(next_pipe);
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '\r');
|
||||
a.jz_long(end_deserialize);
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '\n');
|
||||
a.jz_long(end_deserialize);
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '\0');
|
||||
a.jz_long(end_deserialize);
|
||||
//Check for released values ' ' and '.'. If found, skip setting the bit.
|
||||
a.cmp_regmem_imm8(I386::reg_cx, ' ');
|
||||
a.jz_short(clear_button);
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '.');
|
||||
a.jz_short(clear_button);
|
||||
//Pressed. Set the button bit.
|
||||
a.or_regmem_imm8(I386::reg_si[offset], mask);
|
||||
a.label(clear_button);
|
||||
//Advance the read pointer.
|
||||
a.inc_regmem(I386::reg_ax);
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_axis(I386& a, assembler::label_list& labels, int32_t offset)
|
||||
{
|
||||
//Get reference to read_axis_value() routine.
|
||||
assembler::label& axisread = labels.external((void*)read_axis_value);
|
||||
|
||||
//Save status registers.
|
||||
if(a.is_i386()) a.push_reg(I386::reg_di);
|
||||
a.push_reg(I386::reg_si);
|
||||
a.push_reg(I386::reg_dx);
|
||||
a.push_reg(I386::reg_ax);
|
||||
//Set first parameter of read_axis_value() to start of serialization input buffer.
|
||||
//Set the second parameter to be reference to saved read counter on stack. On i386, push the parameters
|
||||
//to proper places.
|
||||
a.mov_reg_regmem(I386::reg_di, I386::reg_dx);
|
||||
a.mov_reg_regmem(I386::reg_si, I386::reg_sp);
|
||||
if(a.is_i386()) a.push_reg(I386::reg_si);
|
||||
if(a.is_i386()) a.push_reg(I386::reg_di);
|
||||
//Call read_axis_value().
|
||||
a.call_addr(axisread);
|
||||
//Clean up the paraemters from stack (i386 only).
|
||||
if(a.is_i386()) a.add_regmem_imm(I386::reg_sp, 8);
|
||||
//Temporarily put the return value into CX.
|
||||
a.mov_reg_regmem(I386::reg_cx, I386::reg_ax);
|
||||
//Restore the status registers. Note that AX (read position) has been modified by reference.
|
||||
a.pop_reg(I386::reg_ax);
|
||||
a.pop_reg(I386::reg_dx);
|
||||
a.pop_reg(I386::reg_si);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_di);
|
||||
//Write the axis value into controller state.
|
||||
a.mov_regmem_reg16(I386::reg_si[offset], I386::reg_cx);
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_skip_until_pipe(I386& a, assembler::label_list& labels, assembler::label& next_pipe, assembler::label& deserialize_end)
|
||||
{
|
||||
assembler::label& loop = labels;
|
||||
//While...
|
||||
a.label(loop);
|
||||
//Load next character.
|
||||
a.mov_reg_regmem8(I386::reg_cx, I386::reg_dx[I386::reg_ax]);
|
||||
//If it is '|', handle as pipe.
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '|');
|
||||
a.jz_long(next_pipe);
|
||||
//If it is '\r', \n' or '\0', handle as end of input.
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '\r');
|
||||
a.jz_long(deserialize_end);
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '\n');
|
||||
a.jz_long(deserialize_end);
|
||||
a.cmp_regmem_imm8(I386::reg_cx, '\0');
|
||||
a.jz_long(deserialize_end);
|
||||
//Not interesting: Advance read position and go back to loop.
|
||||
a.inc_regmem(I386::reg_ax);
|
||||
a.jmp_short(loop);
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_skip(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Increment the read position to skip the byte.
|
||||
a.inc_regmem(I386::reg_ax);
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_special_blank(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//AX is the pending return value. Set that.
|
||||
a.mov_reg_imm(I386::reg_ax, DESERIALIZE_SPECIAL_BLANK);
|
||||
}
|
||||
|
||||
template<> void emit_deserialize_epilogue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Restore the saved registers on i386.
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_dx);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_si);
|
||||
//Exit routine. The pointer in AX is correct for return value (changed to DESERIALIZE_SPECIAL_BLANK if
|
||||
//needed).
|
||||
a.ret();
|
||||
}
|
||||
|
||||
template<> void emit_read_prologue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Variable assignments:
|
||||
//SI: State block.
|
||||
//DX: Controller number
|
||||
//CX: Button index.
|
||||
//AX: Pending return value.
|
||||
|
||||
//Save registers if i386 and load the parameters from stack into variables.
|
||||
if(a.is_i386()) {
|
||||
a.push_reg(I386::reg_si);
|
||||
a.push_reg(I386::reg_dx);
|
||||
a.push_reg(I386::reg_cx);
|
||||
a.mov_reg_regmem(I386::reg_si, I386::reg_sp[20]);
|
||||
a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[24]);
|
||||
a.mov_reg_regmem(I386::reg_cx, I386::reg_sp[28]);
|
||||
}
|
||||
//Zero out the pending return value.
|
||||
a.xor_reg_regmem(I386::reg_ax, I386::reg_ax);
|
||||
}
|
||||
|
||||
template<> void emit_read_epilogue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Restore the saved registers on i386.
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_cx);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_dx);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_si);
|
||||
//Exit routine. AX is the pending return.
|
||||
a.ret();
|
||||
}
|
||||
|
||||
template<> void emit_read_dispatch(I386& a, assembler::label_list& labels,
|
||||
unsigned controllers, unsigned ilog2controls, assembler::label& end)
|
||||
{
|
||||
//Get reference to table after dispatch code.
|
||||
assembler::label& table = labels;
|
||||
//Is the controller number out of range? If yes, jump to epilogue (pending return register is 0).
|
||||
a.cmp_regmem_imm(I386::reg_dx, controllers);
|
||||
a.jae_long(end);
|
||||
//Is the control number out of range? If yes, jump to epilogue (pending return register is 0).
|
||||
a.cmp_regmem_imm(I386::reg_cx, 1 << ilog2controls);
|
||||
a.jae_long(end);
|
||||
//The numbers are in range. Compute 1-D index as controller * (2^ilog2controls) + control into DX.
|
||||
a.shl_regmem_imm(I386::reg_dx, ilog2controls);
|
||||
a.add_reg_regmem(I386::reg_dx, I386::reg_cx);
|
||||
//Load base address of jump table to CX.
|
||||
a.mov_reg_addr(I386::reg_cx, table);
|
||||
//Jump to entry in position DX in table (doing words to bytes conversion).
|
||||
a.jmp_regmem(I386::reg_cx[I386::reg_dx * a.wordsize()]);
|
||||
//The table comes after this routine.
|
||||
a.label(table);
|
||||
}
|
||||
|
||||
template<> assembler::label& emit_read_label(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Get reference to whatever code is called.
|
||||
assembler::label& l = labels;
|
||||
//Write address entry.
|
||||
a.address(l);
|
||||
//Return the newly created reference.
|
||||
return l;
|
||||
}
|
||||
|
||||
template<> void emit_read_label_bad(I386& a, assembler::label_list& labels, assembler::label& b)
|
||||
{
|
||||
//Write address entry for specified label.
|
||||
a.address(b);
|
||||
}
|
||||
|
||||
template<> void emit_read_button(I386& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset, uint8_t mask)
|
||||
{
|
||||
//Declare the label jumping into this code.
|
||||
a.label(l);
|
||||
//The pending return is 0, so we can just set low byte of it to 1 if button bit is set.
|
||||
a.test_regmem_imm8(I386::reg_si[offset], mask);
|
||||
a.setnz_regmem(I386::reg_ax); //Really AL.
|
||||
//Jump to epilogue.
|
||||
a.jmp_long(end);
|
||||
}
|
||||
|
||||
template<> void emit_read_axis(I386& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset)
|
||||
{
|
||||
//Declare the label jumping into this code.
|
||||
a.label(l);
|
||||
//Just read the axis value. i386/amd64 is LE, so endianess is correct.
|
||||
a.mov_reg_regmem16(I386::reg_ax, I386::reg_si[offset]);
|
||||
//Jump to epilogue.
|
||||
a.jmp_long(end);
|
||||
}
|
||||
|
||||
template<> void emit_write_prologue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Variable assignments:
|
||||
//SI: State block.
|
||||
//DX: Controller number
|
||||
//CX: Button index.
|
||||
//AX: Value to write.
|
||||
|
||||
//Save registers if i386 and load the parameters from stack into variables.
|
||||
if(a.is_i386()) {
|
||||
a.push_reg(I386::reg_si);
|
||||
a.push_reg(I386::reg_dx);
|
||||
a.push_reg(I386::reg_cx);
|
||||
a.push_reg(I386::reg_ax);
|
||||
a.mov_reg_regmem(I386::reg_si, I386::reg_sp[24]);
|
||||
a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[28]);
|
||||
a.mov_reg_regmem(I386::reg_cx, I386::reg_sp[32]);
|
||||
a.mov_reg_regmem(I386::reg_ax, I386::reg_sp[36]);
|
||||
} else {
|
||||
//On amd64, the fifth parameter is in r8. Copy it to ax to be consistent with i386 case.
|
||||
a.mov_reg_regmem(I386::reg_ax, I386::reg_r8);
|
||||
}
|
||||
}
|
||||
|
||||
template<> void emit_write_epilogue(I386& a, assembler::label_list& labels)
|
||||
{
|
||||
//Restore saved registers on i386.
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_ax);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_cx);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_dx);
|
||||
if(a.is_i386()) a.pop_reg(I386::reg_si);
|
||||
//Return. This function has no return value.
|
||||
a.ret();
|
||||
}
|
||||
|
||||
template<> void emit_write_button(I386& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset, uint8_t mask)
|
||||
{
|
||||
//See if mask can be done as shift.
|
||||
int mask_bit = calc_mask_bit(mask);
|
||||
//Label jumping to this code.
|
||||
a.label(l);
|
||||
//Is the write value nonzero? If not, set AL to 1 (otherwise set to 0).
|
||||
//We don't need write value after this, so freely trash it.
|
||||
a.cmp_regmem_imm(I386::reg_ax, 0);
|
||||
a.setnz_regmem(I386::reg_ax);
|
||||
//Set AL to be mask if write value was nonzero.
|
||||
if(mask_bit >= 0) {
|
||||
//Shift the 0 or 1 from LSB to correct place.
|
||||
a.shl_regmem_imm8(I386::reg_ax, mask_bit);
|
||||
} else {
|
||||
//Negate the number to 0xFF or 0x00 and then mask the bits not in mask, giving either mask or 0.
|
||||
a.neg_regmem8(I386::reg_ax);
|
||||
a.and_regmem_imm8(I386::reg_ax, mask);
|
||||
}
|
||||
//Clear the bits in mask.
|
||||
a.and_regmem_imm8(I386::reg_si[offset], ~mask);
|
||||
//If needed, set the bits back.
|
||||
a.or_regmem_reg8(I386::reg_si[offset], I386::reg_ax);
|
||||
//Jump to epilogue.
|
||||
a.jmp_long(end);
|
||||
}
|
||||
|
||||
template<> void emit_write_axis(I386& a, assembler::label_list& labels, assembler::label& l,
|
||||
assembler::label& end, int32_t offset)
|
||||
{
|
||||
//Label jumping to this code.
|
||||
a.label(l);
|
||||
//Write the write value to axis value. i386/amd64 is LE, so endianess is correct.
|
||||
a.mov_regmem_reg16(I386::reg_si[offset], I386::reg_ax);
|
||||
//Jump to epilogue.
|
||||
a.jmp_long(end);
|
||||
}
|
||||
}
|
|
@ -2,10 +2,15 @@
|
|||
#include "assembler.hpp"
|
||||
#include "controller-data.hpp"
|
||||
#include "controller-parse.hpp"
|
||||
#include "controller-parse-asmgen.hpp"
|
||||
#include "assembler-intrinsics-dummy.hpp"
|
||||
#include "assembler-intrinsics-i386.hpp"
|
||||
#include "json.hpp"
|
||||
#include "string.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace assembler_intrinsics;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string quote(const std::string& s)
|
||||
|
@ -728,13 +733,17 @@ size_t port_type_generic::port_size(const JSON::node& root, const std::string& p
|
|||
void port_type_generic::make_dynamic_blocks()
|
||||
{
|
||||
try {
|
||||
std::list<assembler::label> labels;
|
||||
assembler::label_list labels;
|
||||
assembler::assembler a;
|
||||
make_routines(a, labels);
|
||||
|
||||
assembler::dynamic_code* c;
|
||||
dyncode_block = c = new assembler::dynamic_code(a.size());
|
||||
auto m = a.flush(c->pointer());
|
||||
if(getenv("PTG_DUMP_DYNAMIC")) {
|
||||
const char* basename = getenv("PTG_DUMP_DYNAMIC");
|
||||
a.dump(basename, controller_info->hname, c->pointer(), m);
|
||||
}
|
||||
c->commit();
|
||||
if(m.count("read"))
|
||||
read = (short(*)(const port_type*, const unsigned char*, unsigned, unsigned))m["read"];
|
||||
|
@ -755,431 +764,178 @@ void port_type_generic::make_dynamic_blocks()
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ARCH_IS_I386
|
||||
assembler::label& alloc_label(std::list<assembler::label>& v)
|
||||
void port_type_generic::make_routines(assembler::assembler& a, assembler::label_list& labels)
|
||||
{
|
||||
v.push_back(assembler::label());
|
||||
return v.back();
|
||||
}
|
||||
|
||||
static bool i386_amd64()
|
||||
{
|
||||
size_t r;
|
||||
//One can freely return without doing nothing.
|
||||
#ifndef NO_ASM_GENERATION
|
||||
#if defined(ARCH_IS_I386)
|
||||
size_t amd64_flag;
|
||||
asm volatile(
|
||||
//On i386, this is DEC EAX, MOV EAX, 0, NOP, NOP, NOP, NOP
|
||||
//On amd64, this is MOV RAX, 0x9090909000000000
|
||||
".byte 0x48\n"
|
||||
".byte 0xB8, 0, 0, 0, 0\n"
|
||||
".byte 0x90, 0x90, 0x90, 0x90\n"
|
||||
: "=a"(r));
|
||||
return (r != 0);
|
||||
}
|
||||
: "=a"(amd64_flag));
|
||||
I386 as(a, amd64_flag != 0);
|
||||
#define DEFINED_ASSEBLER
|
||||
#endif
|
||||
|
||||
size_t axis_print(char* buf, short _v)
|
||||
{
|
||||
int v = _v;
|
||||
size_t r = 0;
|
||||
buf[r++] = ' ';
|
||||
if(v < 0) { buf[r++] = '-'; v = -v; }
|
||||
if(v >= 10000) buf[r++] = '0' + (v / 10000 % 10);
|
||||
if(v >= 1000) buf[r++] = '0' + (v / 1000 % 10);
|
||||
if(v >= 100) buf[r++] = '0' + (v / 100 % 10);
|
||||
if(v >= 10) buf[r++] = '0' + (v / 10 % 10);
|
||||
buf[r++] = '0' + (v % 10);
|
||||
return r;
|
||||
}
|
||||
//Backup assembler that causes this to error out.
|
||||
#ifndef DEFINED_ASSEBLER
|
||||
dummyarch as(a);
|
||||
#endif
|
||||
|
||||
enum regnum { REG_AX, REG_CX, REG_DX, REG_BX, REG_SP, REG_BP, REG_SI, REG_DI };
|
||||
enum modval { MOD_NO_OFF, MOD_OFF_1, MOD_OFF_4, MOD_REG };
|
||||
enum specials { RM_SIB = 4, INDEX_NONE = 4, SCALE_1 = 0, SCALE_2 = 1, SCALE_4 = 2, SCALE_8 = 3};
|
||||
|
||||
void mov_reg_reg(assembler::assembler& a, uint8_t dest, uint8_t src, bool amd64)
|
||||
{
|
||||
if(amd64) a(0x48);
|
||||
a(0x8B, assembler::i386_modrm(dest, MOD_REG, src));
|
||||
}
|
||||
|
||||
void xor_ax_ax(assembler::assembler& a, bool amd64)
|
||||
{
|
||||
if(amd64) a(0x48);
|
||||
a(0x31, assembler::i386_modrm(REG_AX, MOD_REG, REG_AX));
|
||||
}
|
||||
|
||||
void inc_ax(assembler::assembler& a, bool amd64)
|
||||
{
|
||||
if(amd64) a(0x48);
|
||||
a(0xFF, assembler::i386_modrm(0, MOD_REG, REG_AX));
|
||||
}
|
||||
|
||||
void testb_si_offset_imm(assembler::assembler& a, size_t offset, uint8_t mask)
|
||||
{
|
||||
if(offset > 127)
|
||||
a(0xF6, assembler::i386_modrm(0, MOD_OFF_4, REG_SI), assembler::vle<int32_t>(offset), mask);
|
||||
else
|
||||
a(0xF6, assembler::i386_modrm(0, MOD_OFF_1, REG_SI), (uint8_t)offset, mask);
|
||||
}
|
||||
|
||||
void movb_si_offset(assembler::assembler& a, size_t offset, uint8_t reg)
|
||||
{
|
||||
if(offset > 127)
|
||||
a(0x8A, assembler::i386_modrm(reg, MOD_OFF_4, REG_SI), assembler::vle<int32_t>(offset));
|
||||
else
|
||||
a(0x8A, assembler::i386_modrm(reg, MOD_OFF_1, REG_SI), (uint8_t)offset);
|
||||
}
|
||||
|
||||
void movzbl_si_offset(assembler::assembler& a, size_t offset, uint8_t reg)
|
||||
{
|
||||
if(offset > 127)
|
||||
a(0x0F, 0xB6, assembler::i386_modrm(reg, MOD_OFF_4, REG_SI), assembler::vle<int32_t>(offset));
|
||||
else
|
||||
a(0x0F, 0xB6, assembler::i386_modrm(reg, MOD_OFF_1, REG_SI), (uint8_t)offset);
|
||||
}
|
||||
|
||||
void load_cx_imm(assembler::assembler& a, int64_t val, bool amd64)
|
||||
{
|
||||
if(amd64) a(0x48);
|
||||
a(0xC7, assembler::i386_modrm(0, MOD_REG, REG_CX), assembler::vle<int32_t>(val));
|
||||
}
|
||||
|
||||
void orb_si_offset_imm(assembler::assembler& a, size_t offset, uint8_t mask)
|
||||
{
|
||||
if(offset > 127)
|
||||
a(0x80, assembler::i386_modrm(1, MOD_OFF_4, REG_SI), assembler::vle<int32_t>(offset), mask);
|
||||
else
|
||||
a(0x80, assembler::i386_modrm(1, MOD_OFF_1, REG_SI), (uint8_t)offset, mask);
|
||||
}
|
||||
|
||||
void jnz_near(assembler::assembler& a, assembler::label& l)
|
||||
{
|
||||
a(0x75, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
|
||||
}
|
||||
|
||||
void jz_near(assembler::assembler& a, assembler::label& l)
|
||||
{
|
||||
a(0x74, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
|
||||
}
|
||||
|
||||
void jmp_near(assembler::assembler& a, assembler::label& l)
|
||||
{
|
||||
a(0xEB, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
|
||||
}
|
||||
|
||||
void mov_dxPax_imm(assembler::assembler& a, uint8_t val)
|
||||
{
|
||||
a(0xC6, assembler::i386_modrm(0, MOD_NO_OFF, RM_SIB), assembler::i386_sib(REG_DX, REG_AX, SCALE_1), val);
|
||||
}
|
||||
|
||||
void movb_dxPax_reg(assembler::assembler& a, uint8_t reg)
|
||||
{
|
||||
a(0x88, assembler::i386_modrm(reg, MOD_NO_OFF, RM_SIB), assembler::i386_sib(REG_DX, REG_AX, SCALE_1));
|
||||
}
|
||||
|
||||
void cmp_dxPax_imm(assembler::assembler& a, uint8_t val)
|
||||
{
|
||||
a(0x80, assembler::i386_modrm(7, MOD_NO_OFF, RM_SIB), assembler::i386_sib(REG_DX, REG_AX, SCALE_1), val);
|
||||
}
|
||||
|
||||
void andb_reg_imm(assembler::assembler& a, uint8_t reg, uint8_t val)
|
||||
{
|
||||
a(0x80, assembler::i386_modrm(4, MOD_REG, reg), val);
|
||||
}
|
||||
|
||||
void andd_reg_imm(assembler::assembler& a, uint8_t reg, uint8_t val)
|
||||
{
|
||||
a(0x83, assembler::i386_modrm(4, MOD_REG, reg), val);
|
||||
}
|
||||
|
||||
void cmpb_reg_imm(assembler::assembler& a, uint8_t reg, uint8_t val)
|
||||
{
|
||||
a(0x80, assembler::i386_modrm(7, MOD_REG, reg), val);
|
||||
}
|
||||
|
||||
void cmpd_reg_imm(assembler::assembler& a, uint8_t reg, uint8_t val)
|
||||
{
|
||||
a(0x83, assembler::i386_modrm(7, MOD_REG, reg), val);
|
||||
}
|
||||
|
||||
void addb_reg_imm(assembler::assembler& a, uint8_t reg, uint8_t val)
|
||||
{
|
||||
a(0x80, assembler::i386_modrm(0, MOD_REG, reg), val);
|
||||
}
|
||||
|
||||
void addd_reg_imm(assembler::assembler& a, uint8_t reg, uint8_t val)
|
||||
{
|
||||
a(0x83, assembler::i386_modrm(0, MOD_REG, reg), val);
|
||||
}
|
||||
|
||||
void sbbb_reg_reg(assembler::assembler& a, uint8_t dst, uint8_t src)
|
||||
{
|
||||
a(0x18, assembler::i386_modrm(src, MOD_REG, dst));
|
||||
}
|
||||
|
||||
void sbbd_reg_reg(assembler::assembler& a, uint8_t dst, uint8_t src)
|
||||
{
|
||||
a(0x19, assembler::i386_modrm(src, MOD_REG, dst));
|
||||
}
|
||||
|
||||
void mov_si_offset_si_word(assembler::assembler& a, size_t offset, bool amd64)
|
||||
{
|
||||
if(offset > 127)
|
||||
a(0x66, 0x8B, assembler::i386_modrm(REG_SI, MOD_OFF_4, REG_SI), assembler::vle<int32_t>(offset));
|
||||
else
|
||||
a(0x66, 0x8B, assembler::i386_modrm(REG_SI, MOD_OFF_1, REG_SI), offset);
|
||||
if(amd64) a(0x48);
|
||||
a(0x81, assembler::i386_modrm(4, MOD_REG, REG_SI), assembler::vle<int32_t>(0xFFFF));
|
||||
}
|
||||
|
||||
void lea_load_out_addr_di(assembler::assembler& a, bool amd64)
|
||||
{
|
||||
if(amd64) a(0x48);
|
||||
a(0x8D, assembler::i386_modrm(REG_DI, MOD_NO_OFF, RM_SIB), assembler::i386_sib(REG_DX, REG_AX, SCALE_1));
|
||||
}
|
||||
|
||||
void add_ax_cx(assembler::assembler& a, bool amd64)
|
||||
{
|
||||
if(amd64) a(0x48);
|
||||
a(0x01, assembler::i386_modrm(REG_CX, MOD_REG, REG_AX));
|
||||
}
|
||||
|
||||
void call_label(assembler::assembler& a, assembler::label& l, uint8_t amd64_tmpreg, bool amd64)
|
||||
{
|
||||
if(amd64) {
|
||||
a(0x48 | ((amd64_tmpreg&8) ? 1 : 0), 0xB8 | (amd64_tmpreg&7),
|
||||
assembler::relocation_tag(assembler::i386_reloc_abs64, l), assembler::pad_tag(8),
|
||||
0x40 | ((amd64_tmpreg&8) ? 1 : 0), 0xFF, assembler::i386_modrm(2, MOD_REG, (amd64_tmpreg&7)));
|
||||
} else
|
||||
a(0xE8, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
|
||||
}
|
||||
|
||||
void push_gp(assembler::assembler& a, uint8_t reg)
|
||||
{
|
||||
a(0x50 + (reg&7));
|
||||
}
|
||||
|
||||
void pop_gp(assembler::assembler& a, uint8_t reg)
|
||||
{
|
||||
a(0x58 + (reg&7));
|
||||
}
|
||||
|
||||
void add_esp_imm(assembler::assembler& a, int8_t val)
|
||||
{
|
||||
a(0x83, assembler::i386_modrm(0, MOD_REG, REG_SP), val);
|
||||
}
|
||||
|
||||
void load_gp_esp_n(assembler::assembler& a, uint8_t reg, int8_t offset)
|
||||
{
|
||||
a(0x8B, assembler::i386_modrm(reg, MOD_OFF_1, RM_SIB), assembler::i386_sib(REG_SP, INDEX_NONE, SCALE_1),
|
||||
offset);
|
||||
}
|
||||
|
||||
void mov_si_offset_imm(assembler::assembler& a, int32_t offset, uint8_t val)
|
||||
{
|
||||
if(offset > 127)
|
||||
a(0xC6, assembler::i386_modrm(0, MOD_OFF_4, REG_SI), assembler::vle<int32_t>(offset), val);
|
||||
else
|
||||
a(0xC6, assembler::i386_modrm(0, MOD_OFF_1, REG_SI), offset, val);
|
||||
}
|
||||
|
||||
void mov_ax_imm(assembler::assembler& a, size_t val, bool amd64)
|
||||
{
|
||||
if(amd64) a(0x48, 0xB8, assembler::vle<uint64_t>(val));
|
||||
else a(0xB8, assembler::vle<uint32_t>(val));
|
||||
}
|
||||
|
||||
void mov_si_offset_word(assembler::assembler& a, int32_t offset, uint8_t reg)
|
||||
{
|
||||
if(offset > 127)
|
||||
a(0x66, 0x89, assembler::i386_modrm(reg, MOD_OFF_4, REG_SI), assembler::vle<int32_t>(offset));
|
||||
else
|
||||
a(0x66, 0x89, assembler::i386_modrm(reg, MOD_OFF_1, REG_SI), offset);
|
||||
}
|
||||
|
||||
void ret(assembler::assembler& a)
|
||||
{
|
||||
a(0xC3);
|
||||
}
|
||||
|
||||
void port_type_generic::make_routines(assembler::assembler& a, std::list<assembler::label>& labels)
|
||||
{
|
||||
bool amd64 = i386_amd64();
|
||||
assembler::label& axisprint = (alloc_label(labels) = assembler::label((void*)axis_print));
|
||||
assembler::label& axisread = (alloc_label(labels) = assembler::label((void*)read_axis_value));
|
||||
a._label(alloc_label(labels), "serialize");
|
||||
if(!amd64) {
|
||||
//Emit extra prologue for serialize.
|
||||
push_gp(a, REG_SI);
|
||||
push_gp(a, REG_DX);
|
||||
load_gp_esp_n(a, REG_SI, 16);
|
||||
load_gp_esp_n(a, REG_DX, 20);
|
||||
}
|
||||
//SI: State block.
|
||||
//DX: Output buffer.
|
||||
//AX: Counter.
|
||||
xor_ax_ax(a, amd64);
|
||||
a._label(labels, "serialize");
|
||||
codegen::emit_serialize_prologue(as, labels);
|
||||
for(auto& i : serialize_instructions) {
|
||||
switch(i.type) {
|
||||
case 0: { //Button.
|
||||
/*
|
||||
This is really bad:
|
||||
movb_si_offset(a, i.offset, REG_CX);
|
||||
andb_reg_imm(a, REG_CX, i.mask);
|
||||
cmpb_reg_imm(a, REG_CX, i.mask);
|
||||
sbbb_reg_reg(a, REG_CX, REG_CX);
|
||||
andb_reg_imm(a, REG_CX, 0x2E - i.character);
|
||||
addb_reg_imm(a, REG_CX, i.character);
|
||||
movb_dxPax_reg(a, REG_CX);
|
||||
//Better but still bad.
|
||||
movzbl_si_offset(a, i.offset, REG_CX);
|
||||
andd_reg_imm(a, REG_CX, i.mask);
|
||||
cmpd_reg_imm(a, REG_CX, i.mask);
|
||||
sbbd_reg_reg(a, REG_CX, REG_CX);
|
||||
andd_reg_imm(a, REG_CX, 0x2E - i.character);
|
||||
addd_reg_imm(a, REG_CX, i.character);
|
||||
movb_dxPax_reg(a, REG_CX);
|
||||
*/
|
||||
assembler::label& button_set = alloc_label(labels);
|
||||
assembler::label& end_char = alloc_label(labels);
|
||||
testb_si_offset_imm(a, i.offset, i.mask);
|
||||
jnz_near(a, button_set);
|
||||
mov_dxPax_imm(a, '.');
|
||||
jmp_near(a, end_char);
|
||||
a._label(button_set);
|
||||
mov_dxPax_imm(a, i.character);
|
||||
a._label(end_char);
|
||||
|
||||
inc_ax(a, amd64);
|
||||
case 0: //Button.
|
||||
codegen::emit_serialize_button(as, labels, i.offset, i.mask, i.character);
|
||||
break;
|
||||
}
|
||||
case 1: //Axis
|
||||
if(!amd64) push_gp(a, REG_DI);
|
||||
push_gp(a, REG_SI);
|
||||
push_gp(a, REG_DX);
|
||||
push_gp(a, REG_AX);
|
||||
mov_si_offset_si_word(a, i.offset, amd64);
|
||||
lea_load_out_addr_di(a, amd64);
|
||||
if(!amd64) push_gp(a, REG_SI);
|
||||
if(!amd64) push_gp(a, REG_DI);
|
||||
call_label(a, axisprint, 11, amd64);
|
||||
if(!amd64) add_esp_imm(a, 0x8);
|
||||
pop_gp(a, REG_CX); //POP CX instead of AX so we can add the return value.
|
||||
pop_gp(a, REG_DX);
|
||||
pop_gp(a, REG_SI);
|
||||
if(!amd64) pop_gp(a, REG_DI);
|
||||
add_ax_cx(a, amd64);
|
||||
codegen::emit_serialize_axis(as, labels, i.offset);
|
||||
break;
|
||||
case 2: //Pipe character
|
||||
case 3:
|
||||
mov_dxPax_imm(a, '|');
|
||||
inc_ax(a, amd64);
|
||||
codegen::emit_serialize_pipe(as, labels);
|
||||
break;
|
||||
case 4: //End or nothing
|
||||
case 5:
|
||||
if(!amd64) pop_gp(a, REG_DX);
|
||||
if(!amd64) pop_gp(a, REG_SI);
|
||||
ret(a);
|
||||
codegen::emit_serialize_epilogue(as, labels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
a._label(alloc_label(labels), "deserialize");
|
||||
if(!amd64) {
|
||||
//Emit extra prologue for deserialize.
|
||||
push_gp(a, REG_SI);
|
||||
push_gp(a, REG_DX);
|
||||
load_gp_esp_n(a, REG_SI, 16);
|
||||
load_gp_esp_n(a, REG_DX, 20);
|
||||
}
|
||||
//SI: State block.
|
||||
//DX: Output buffer.
|
||||
//AX: Counter.
|
||||
xor_ax_ax(a, amd64);
|
||||
for(size_t i = 0; i < storage_size; i++)
|
||||
mov_si_offset_imm(a, i, 0);
|
||||
|
||||
a._label(labels, "deserialize");
|
||||
assembler::label& dend = labels;
|
||||
assembler::label* dpipe = labels;
|
||||
codegen::emit_deserialize_prologue(as, labels);
|
||||
codegen::emit_deserialize_clear_storage(as, labels, storage_size);
|
||||
for(auto& i : serialize_instructions) {
|
||||
switch(i.type) {
|
||||
case 0: { //Button.
|
||||
assembler::label& no_progress = alloc_label(labels);
|
||||
assembler::label& clear_button = alloc_label(labels);
|
||||
cmp_dxPax_imm(a, '|');
|
||||
jz_near(a, no_progress);
|
||||
cmp_dxPax_imm(a, '\r');
|
||||
jz_near(a, no_progress);
|
||||
cmp_dxPax_imm(a, '\n');
|
||||
jz_near(a, no_progress);
|
||||
cmp_dxPax_imm(a, '\0');
|
||||
jz_near(a, no_progress);
|
||||
cmp_dxPax_imm(a, ' ');
|
||||
jz_near(a, clear_button);
|
||||
cmp_dxPax_imm(a, '.');
|
||||
jz_near(a, clear_button);
|
||||
orb_si_offset_imm(a, i.offset, i.mask);
|
||||
a._label(clear_button);
|
||||
inc_ax(a, amd64);
|
||||
a._label(no_progress);
|
||||
case 0: //Button.
|
||||
codegen::emit_deserialize_button(as, labels, i.offset, i.mask, *dpipe, dend);
|
||||
break;
|
||||
}
|
||||
case 1: //Axis
|
||||
if(!amd64) push_gp(a, REG_DI);
|
||||
push_gp(a, REG_SI);
|
||||
push_gp(a, REG_DX);
|
||||
push_gp(a, REG_AX);
|
||||
mov_reg_reg(a, REG_DI, REG_DX, amd64);
|
||||
mov_reg_reg(a, REG_SI, REG_SP, amd64);
|
||||
if(!amd64) push_gp(a, REG_SI);
|
||||
if(!amd64) push_gp(a, REG_DI);
|
||||
call_label(a, axisread, 11, amd64);
|
||||
if(!amd64) add_esp_imm(a, 0x8);
|
||||
mov_reg_reg(a, REG_CX, REG_AX, amd64);
|
||||
pop_gp(a, REG_AX);
|
||||
pop_gp(a, REG_DX);
|
||||
pop_gp(a, REG_SI);
|
||||
if(!amd64) pop_gp(a, REG_DI);
|
||||
mov_si_offset_word(a, i.offset, REG_CX);
|
||||
codegen::emit_deserialize_axis(as, labels, i.offset);
|
||||
break;
|
||||
case 2: //Pipe character 1
|
||||
break; //Do nothing.
|
||||
case 3: //Pipe character 2
|
||||
case 4: { //Pipe character 3
|
||||
assembler::label& not_last_pipe = alloc_label(labels);
|
||||
assembler::label& goto_out = alloc_label(labels);
|
||||
assembler::label& loop = alloc_label(labels);
|
||||
a._label(loop);
|
||||
cmp_dxPax_imm(a, '|');
|
||||
jz_near(a, goto_out);
|
||||
cmp_dxPax_imm(a, '\r');
|
||||
jz_near(a, goto_out);
|
||||
cmp_dxPax_imm(a, '\n');
|
||||
jz_near(a, goto_out);
|
||||
cmp_dxPax_imm(a, '\0');
|
||||
jz_near(a, goto_out);
|
||||
inc_ax(a, amd64);
|
||||
jmp_near(a, loop);
|
||||
a._label(goto_out);
|
||||
if(i.type == 3) {
|
||||
cmp_dxPax_imm(a, '|');
|
||||
jnz_near(a, not_last_pipe);
|
||||
inc_ax(a, amd64);
|
||||
a._label(not_last_pipe);
|
||||
} else {
|
||||
if(!amd64) pop_gp(a, REG_DX);
|
||||
if(!amd64) pop_gp(a, REG_SI);
|
||||
ret(a);
|
||||
}
|
||||
case 3: //Pipe character 2. We redefine dpipe to point to next pipe or end.
|
||||
codegen::emit_deserialize_skip_until_pipe(as, labels, *dpipe, dend);
|
||||
as.label(*dpipe);
|
||||
dpipe = labels;
|
||||
codegen::emit_deserialize_skip(as, labels);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
mov_ax_imm(a, DESERIALIZE_SPECIAL_BLANK, amd64);
|
||||
if(!amd64) pop_gp(a, REG_DX);
|
||||
if(!amd64) pop_gp(a, REG_SI);
|
||||
ret(a);
|
||||
case 4: //Pipe character 3. Also note that we need to place dpipe label here.
|
||||
codegen::emit_deserialize_skip_until_pipe(as, labels, *dpipe, dend);
|
||||
as.label(*dpipe);
|
||||
as.label(dend);
|
||||
codegen::emit_deserialize_epilogue(as, labels);
|
||||
break;
|
||||
case 5: //Nothing.
|
||||
codegen::emit_deserialize_special_blank(as, labels);
|
||||
as.label(*dpipe);
|
||||
as.label(dend);
|
||||
codegen::emit_deserialize_epilogue(as, labels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
void port_type_generic::make_routines(assembler::assembler& a, std::list<assembler::label>& labels)
|
||||
{
|
||||
throw std::runtime_error("ASM on this arch not supported");
|
||||
}
|
||||
uint32_t ilog2controls = 0;
|
||||
for(size_t i = 0; i < controller_info->controllers.size(); i++) {
|
||||
while((1U << ilog2controls) < controller_info->controllers[i].buttons.size())
|
||||
ilog2controls++;
|
||||
}
|
||||
uint32_t mcontrols = 1 << ilog2controls;
|
||||
|
||||
a._label(labels, "read");
|
||||
codegen::emit_read_prologue(as, labels);
|
||||
assembler::label& rend = labels;
|
||||
codegen::emit_read_dispatch(as, labels, controller_info->controllers.size(), ilog2controls, rend);
|
||||
//Emit the jump table.
|
||||
std::vector<assembler::label*> xlabels;
|
||||
for(size_t i = 0; i < controller_info->controllers.size(); i++) {
|
||||
size_t cnt = controller_info->controllers[i].buttons.size();
|
||||
for(size_t j = 0; j < cnt; j++) {
|
||||
auto& c = indexinfo[indexbase[i] + j];
|
||||
switch(c.type) {
|
||||
case 0:
|
||||
codegen::emit_read_label_bad(as, labels, rend);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
xlabels.push_back(&codegen::emit_read_label(as, labels));
|
||||
break;
|
||||
};
|
||||
}
|
||||
for(size_t j = cnt; j < mcontrols; j++)
|
||||
codegen::emit_read_label_bad(as, labels, rend);
|
||||
}
|
||||
//Emit Routines.
|
||||
size_t lidx = 0;
|
||||
for(size_t i = 0; i < controller_info->controllers.size(); i++) {
|
||||
size_t cnt = controller_info->controllers[i].buttons.size();
|
||||
for(size_t j = 0; j < cnt; j++) {
|
||||
auto& c = indexinfo[indexbase[i] + j];
|
||||
switch(c.type) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
codegen::emit_read_button(as, labels, *xlabels[lidx++], rend, c.offset, c.mask);
|
||||
break;
|
||||
case 2:
|
||||
codegen::emit_read_axis(as, labels, *xlabels[lidx++], rend, c.offset);
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
a._label(rend);
|
||||
codegen::emit_read_epilogue(as, labels);
|
||||
|
||||
a._label(labels, "write");
|
||||
codegen::emit_write_prologue(as, labels);
|
||||
assembler::label& wend = labels;
|
||||
//Read routines here are the same as write routines.
|
||||
codegen::emit_read_dispatch(as, labels, controller_info->controllers.size(), ilog2controls, wend);
|
||||
xlabels.clear();
|
||||
for(size_t i = 0; i < controller_info->controllers.size(); i++) {
|
||||
size_t cnt = controller_info->controllers[i].buttons.size();
|
||||
for(size_t j = 0; j < cnt; j++) {
|
||||
auto& c = indexinfo[indexbase[i] + j];
|
||||
switch(c.type) {
|
||||
case 0:
|
||||
codegen::emit_read_label_bad(as, labels, wend);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
xlabels.push_back(&codegen::emit_read_label(as, labels));
|
||||
break;
|
||||
};
|
||||
}
|
||||
for(size_t j = cnt; j < mcontrols; j++)
|
||||
codegen::emit_read_label_bad(as, labels, wend);
|
||||
}
|
||||
//Emit Routines.
|
||||
lidx = 0;
|
||||
for(size_t i = 0; i < controller_info->controllers.size(); i++) {
|
||||
size_t cnt = controller_info->controllers[i].buttons.size();
|
||||
for(size_t j = 0; j < cnt; j++) {
|
||||
auto& c = indexinfo[indexbase[i] + j];
|
||||
switch(c.type) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
codegen::emit_write_button(as, labels, *xlabels[lidx++], wend, c.offset, c.mask);
|
||||
break;
|
||||
case 2:
|
||||
codegen::emit_write_axis(as, labels, *xlabels[lidx++], wend, c.offset);
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
a._label(wend);
|
||||
codegen::emit_write_epilogue(as, labels);
|
||||
#endif
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue