Add stuff
This commit is contained in:
commit
14e7e198ab
|
@ -0,0 +1,90 @@
|
|||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
struct bxPsuedoRng {
|
||||
|
||||
// Constant init state
|
||||
constexpr static std::uint32_t InitState[6] {
|
||||
0xf22d0e56,
|
||||
0x883126e9,
|
||||
0xc624dd2f,
|
||||
0x702c49c,
|
||||
0x9e353f7d,
|
||||
0x6fdf3b64
|
||||
};
|
||||
|
||||
std::uint32_t state[6];
|
||||
|
||||
explicit bxPsuedoRng() {
|
||||
std::memcpy(&this->state[0], &InitState, sizeof(state));
|
||||
}
|
||||
|
||||
void Init(std::uint32_t seed) {
|
||||
state[0] = seed + InitState[0];
|
||||
state[1] = seed + InitState[1];
|
||||
state[2] = seed + InitState[2];
|
||||
state[3] = seed + InitState[3];
|
||||
state[4] = seed + InitState[4];
|
||||
state[5] = seed + InitState[5];
|
||||
}
|
||||
|
||||
std::uint32_t NextInt() {
|
||||
std::uint32_t uVar1;
|
||||
std::uint32_t uVar2;
|
||||
std::uint32_t uVar3;
|
||||
std::uint32_t uVar4;
|
||||
int iVar5;
|
||||
|
||||
uVar3 = this->state[5] + this->state[4];
|
||||
iVar5 = 0;
|
||||
|
||||
if ((uVar3 < this->state[5]) || (uVar3 < this->state[4]))
|
||||
iVar5 = 1;
|
||||
|
||||
uVar2 = this->state[3];
|
||||
|
||||
// TODO: fix it up (I think i nabbed out one cycle.)
|
||||
|
||||
uVar1 = this->state[2];
|
||||
this->state[4] = uVar3;
|
||||
uVar4 = uVar3 + uVar2 + iVar5;
|
||||
|
||||
|
||||
uVar3 = this->state[1];
|
||||
this->state[3] = uVar4;
|
||||
uVar4 = uVar4 + uVar1 + (std::uint32_t)(uVar4 < uVar2);
|
||||
uVar2 = this->state[0];
|
||||
this->state[2] = uVar4;
|
||||
uVar4 = uVar4 + uVar3 + (std::uint32_t)(uVar4 < uVar1);
|
||||
this->state[1] = uVar4;
|
||||
uVar1 = this->state[5] + 1;
|
||||
uVar3 = uVar4 + uVar2 + (std::uint32_t)(uVar4 < uVar3);
|
||||
this->state[5] = uVar1;
|
||||
|
||||
if ((((uVar1 == 0) && (uVar2 = this->state[4] + 1, this->state[4] = uVar2, uVar2 == 0)) &&
|
||||
(uVar2 = this->state[3] + 1, this->state[3] = uVar2, uVar2 == 0)) &&
|
||||
((uVar2 = this->state[2] + 1, this->state[2] = uVar2, uVar2 == 0 &&
|
||||
(uVar2 = this->state[1] + 1, this->state[1] = uVar2, uVar2 == 0)))) {
|
||||
uVar3 = uVar3 + 1;
|
||||
}
|
||||
|
||||
|
||||
this->state[0] = uVar3;
|
||||
|
||||
return this->state[0];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
bxPsuedoRng rng;
|
||||
|
||||
for(int i = 0; i < 1000; ++i) {
|
||||
auto next = rng.NextInt();
|
||||
std::printf("cycle %d: 0x%08x (decimal: %d / %u)\n", i + 1, next, next, next);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// BX code uses the [PJW Hash](https://en.wikipedia.org/wiki/PJW_hash_function) function.
|
||||
uint32_t bxStringHash(const char* in) {
|
||||
uint32_t hash = 0;
|
||||
uint32_t high = 0;
|
||||
|
||||
while(*in) {
|
||||
hash = (hash << 4) + *in++;
|
||||
|
||||
if(high = hash & 0xf0000000)
|
||||
hash ^= high >> 23;
|
||||
|
||||
hash &= ~high;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// Prelimnary Luno Scripting Binary/Compiled Documentation
|
||||
#include <cstdint>
|
||||
|
||||
namespace luno {
|
||||
|
||||
// Some notes:
|
||||
//
|
||||
// - Luno is *not* a VLE. All instructions are 4 bytes long, no matter what.
|
||||
// I assume this was inspired by MIPS? either that,
|
||||
// or easier bytecode. Which is always a good thing for
|
||||
// developers.
|
||||
//
|
||||
// - It's also not very strongly typed. There are 6 types:
|
||||
// Bool(?)
|
||||
// Int
|
||||
// Float
|
||||
// Index (into Table?)
|
||||
// Func
|
||||
// Table (assocative container)
|
||||
|
||||
// Mneomic notation:
|
||||
// [l8]: 8-bit literal
|
||||
// [ri]: register index (alternative 8-bit literal)
|
||||
// [l16]: 16-bit literal (usually an offset to jump to)
|
||||
// [adr]: Address (alternative 16-bit literal)
|
||||
|
||||
enum class eLunoOpcode : std::uint8_t {
|
||||
|
||||
// branching
|
||||
|
||||
JMP = 0x0, // jmp [adr]
|
||||
|
||||
BEZ = 0x1, // bez [ri], [adr]
|
||||
BNZ = 0x2, // bnz [ri], [adr]
|
||||
|
||||
BEQ = 0x3, // beq [ri], [ri], [adr]
|
||||
BNE = 0x4, // bne [ri], [ri], [adr]
|
||||
|
||||
|
||||
// Arithmetic instructions
|
||||
ADD = 0xc, // add [ri],[ri]
|
||||
SUB = 0xd, // sub [ri],[ri]
|
||||
MUL = 0xe, // mul [ri],[ri]
|
||||
DIV = 0xf, // div [ri],[ri]
|
||||
|
||||
// what's 0x10?
|
||||
|
||||
MOD = 0x11, // mod [ri],[ri]
|
||||
|
||||
// Table instructions? I dunno lol
|
||||
|
||||
MAKE_TABLE = 0x14 // mktable [?]
|
||||
|
||||
// Stack instructions are probably here
|
||||
|
||||
|
||||
// not handled in retail Luno VM, so this is probably
|
||||
// a debug halt.
|
||||
HALT = 0x1a // halt
|
||||
|
||||
|
||||
// Call C function from luno.
|
||||
// First [l8] is funcnum, the second is a parameter to give to it?
|
||||
// Could also be a 16-bit integer afterwards?
|
||||
C_CALL = 0x21 // ccall [l8], [l8]
|
||||
};
|
||||
|
||||
// Type repressenting a Luno opcode.
|
||||
union tLunoInstruction {
|
||||
std::uint32_t total_value;
|
||||
|
||||
struct {
|
||||
eLunoOpcode opcode; // 8 bits...
|
||||
std::uint8_t operandBytes[3];
|
||||
};
|
||||
|
||||
// todo: operand decoding
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace luno
|
|
@ -0,0 +1,158 @@
|
|||
// An reverse-engineered implementation of the PRNG used in the SSX games.
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
// only bring this in for the test
|
||||
#ifdef COMPILE_TEST
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#endif
|
||||
|
||||
struct bxPsuedoRng {
|
||||
|
||||
|
||||
/**
|
||||
* The initalization state.
|
||||
* Weirdly, these constants seem to show up in tables
|
||||
* to convert from milliseconds -> NTP fractional time.
|
||||
*
|
||||
* Interesting source for psuedorandom constants.
|
||||
*/
|
||||
constexpr static std::uint32_t InitState[6] {
|
||||
0xf22d0e56,
|
||||
0x883126e9,
|
||||
0xc624dd2f,
|
||||
0x702c49c,
|
||||
0x9e353f7d,
|
||||
0x6fdf3b64
|
||||
};
|
||||
|
||||
|
||||
constexpr explicit bxPsuedoRng() {
|
||||
std::memcpy(this, &InitState[0], sizeof(*this));
|
||||
}
|
||||
|
||||
constexpr explicit bxPsuedoRng(std::uint32_t seed) {
|
||||
Seed(seed);
|
||||
}
|
||||
|
||||
constexpr void Seed(std::uint32_t seed = 0) {
|
||||
state[0] = seed + InitState[0];
|
||||
state[1] = seed + InitState[1];
|
||||
state[2] = seed + InitState[2];
|
||||
state[3] = seed + InitState[3];
|
||||
state[4] = seed + InitState[4];
|
||||
state[5] = seed + InitState[5];
|
||||
}
|
||||
|
||||
|
||||
// These were implemented in the original code so I'm just doing it here to be nice
|
||||
|
||||
constexpr void CopyTo(bxPsuedoRng* dest) {
|
||||
std::memcpy(dest, this, sizeof(*this));
|
||||
}
|
||||
|
||||
constexpr void CopyFrom(bxPsuedoRng* src) {
|
||||
std::memcpy(this, src, sizeof(*this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random number.
|
||||
*/
|
||||
constexpr std::uint32_t NextInt() {
|
||||
std::uint32_t tempOutput = state[4] + state[5];
|
||||
bool startCycleIf = (state[4] + state[5] < state[5]) || (state[4] + state[5] < state[4]);
|
||||
|
||||
// start cycle?
|
||||
std::uint32_t prevState = state[3];
|
||||
state[4] = tempOutput;
|
||||
tempOutput += startCycleIf + prevState;
|
||||
|
||||
std::uint32_t prevState2 = state[2];
|
||||
state[3] = tempOutput;
|
||||
tempOutput += (tempOutput < prevState) + prevState2;
|
||||
|
||||
prevState = state[1];
|
||||
state[2] = tempOutput;
|
||||
tempOutput += (tempOutput < prevState2) + prevState;
|
||||
|
||||
state[1] = tempOutput;
|
||||
tempOutput += state[0] + (tempOutput < prevState);
|
||||
|
||||
// Set output "register" to the output of all the cycles combined,
|
||||
// and add 1 to the final "register".
|
||||
state[0] = tempOutput;
|
||||
|
||||
|
||||
// Make sure the entire state is nonzero, including the output
|
||||
// "register". This really should be cleaned up :(
|
||||
|
||||
state[5]++;
|
||||
if (state[5] + 1 == 0) {
|
||||
auto* ptr = &state[4];
|
||||
*ptr++;
|
||||
if (*ptr == 0) {
|
||||
ptr = &state[3];
|
||||
*ptr++;
|
||||
if (*ptr == 0) {
|
||||
ptr = &state[2];
|
||||
*ptr++;
|
||||
if (*ptr == 0) {
|
||||
ptr = &state[1];
|
||||
*ptr++;
|
||||
if (*ptr == 0) {
|
||||
tempOutput++;
|
||||
state[0] = tempOutput;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The PRNG state, which I call "registers" for simplicity.
|
||||
*
|
||||
* Things I know:
|
||||
* [0] is the output register
|
||||
*/
|
||||
std::uint32_t state[6];
|
||||
};
|
||||
|
||||
|
||||
#ifdef COMPILE_TEST
|
||||
// The following is a simple driver program
|
||||
// which just cycles the PRNG 1000 times.
|
||||
// I use this in a very simple test one-liner I ran while simplifiying/cleaning up the PRNG:
|
||||
//
|
||||
// g++ -std=c++20 -DCOMPILE_TEST bxprng_new.cpp -o bxprng; ./bxprng >prelim; diff working prelim
|
||||
//
|
||||
// The "working" file, attached to this Gist, is the output of simply getting the decompiled code
|
||||
// to even compile. (since I assume that would work without introducing bugs). If you see no output
|
||||
// (besides gcc warnings) from this command, the test passed (prelim file is exactly the same as "working").
|
||||
|
||||
|
||||
int main() {
|
||||
bxPsuedoRng rng;
|
||||
|
||||
// Run 1000 cycles, printing out results as we go.
|
||||
for(int i = 0; i < 1000; ++i) {
|
||||
auto next = rng.NextInt();
|
||||
std::printf("cycle %d: 0x%08x (decimal: %d / %u)\n", i + 1, next, next, next);
|
||||
}
|
||||
|
||||
// The state after 1000 cycles should always match this, since
|
||||
// we don't seed the PRNG.
|
||||
assert(rng.state[0] == 0x9213d468);
|
||||
assert(rng.state[1] == 0x04dfa254);
|
||||
assert(rng.state[2] == 0xddf4b3e5);
|
||||
assert(rng.state[3] == 0x90ef376d);
|
||||
assert(rng.state[4] == 0x9e3cdd49);
|
||||
assert(rng.state[5] == 0x6fdf3f4c);
|
||||
}
|
||||
|
||||
#endif // COMPILE_TEST
|
|
@ -0,0 +1,81 @@
|
|||
// Prelimnary Luno Scripting Binary/Compiled Documentation
|
||||
#include <cstdint>
|
||||
|
||||
namespace luno {
|
||||
|
||||
// Some notes:
|
||||
//
|
||||
// - Luno is *not* a VLE. All instructions are 4 bytes long, no matter what.
|
||||
// I assume this was inspired by MIPS? either that,
|
||||
// or easier bytecode. Which is always a good thing for
|
||||
// developers.
|
||||
//
|
||||
// - It's also not very strongly typed. There are 6 types:
|
||||
// Bool(?)
|
||||
// Int
|
||||
// Float
|
||||
// Index (into Table?)
|
||||
// Func
|
||||
// Table (assocative container)
|
||||
|
||||
// Opcodes come with a mneomic.
|
||||
//
|
||||
// Mneomic operand notation:
|
||||
// [ri]: register index (alternative 8-bit literal)
|
||||
// [l8]: 8-bit literal
|
||||
// [l16]: 16-bit literal (usually an offset to jump to)
|
||||
|
||||
enum class eLunoOpcode : uint8_t {
|
||||
|
||||
// Branch to label
|
||||
JMP = 0x0, // jmp [l16]
|
||||
|
||||
// branch to label if [ri] != 0
|
||||
BNZ = 0x1, // bnz [ri],[l16]
|
||||
|
||||
// branch to label if [ri] == 0
|
||||
BEZ = 0x2, // bez [ri],[l16]
|
||||
|
||||
// TODO: probably more
|
||||
|
||||
// Arithmetic instructions
|
||||
ADD = 0xc, // add [ri],[ri]
|
||||
SUB = 0xd, // sub [ri],[ri]
|
||||
MUL = 0xe, // mul [ri],[ri]
|
||||
DIV = 0xf, // div [ri],[ri]
|
||||
|
||||
// what's 0x10?
|
||||
|
||||
MOD = 0x11, // mod [ri],[ri]
|
||||
|
||||
// Table instructions?
|
||||
MAKE_TABLE = 0x14 // mktable [?]
|
||||
|
||||
// not handled in retail Luno VM, so this is probably
|
||||
// a debug halt.
|
||||
HALT = 0x1a // halt
|
||||
|
||||
|
||||
// Call C function from luno.
|
||||
// First [l8] is funcnum, the second is a parameter to give to it?
|
||||
C_CALL = 0x21 // ccall [l8],[l8]
|
||||
};
|
||||
|
||||
// Type repressenting a Luno opcode.
|
||||
union tLunoInstruction {
|
||||
uint32_t total_value;
|
||||
|
||||
struct {
|
||||
eLunoOpcode opcode; // 8 bits...
|
||||
uint8_t operand_1; // 16
|
||||
uint8_t operand_2; // 24
|
||||
uint8_t operand_3; // 32
|
||||
};
|
||||
|
||||
// todo: operand decoding
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace luno
|
Loading…
Reference in New Issue