Little Endian ordering is now ensured for stack based and register
based operations e.g. PUSH now pushes datums in LE ordering onto the
stack, POP pops data on the stack, converting from LE ordering to host
ordering.
More functions in the runtime are now macro defined, so there's less
code while still maintaining the lookup table.
--------------------------------------------------------------------------------
What LE ordering means for actual source code is that I utilise the
convert_*_to_* functions for PUSH and POP. All other data oriented
instructions are completely internal to the system and any data
storage must be in Little Endian. So MOV, PUSH-REGISTER and DUP can
all directly memcpy the data around the system without needing to
consider endian at all.
Macros were a necessity after I felt the redefinition of push for 4
different data types was too much. Most functions are essentially
copies for each datatype. Lisp macros would make this so easy :(
OP_HALT = 1 now. This commit also adjusts the error checking in
inst_read_bytecode.
The main reasoning behind this is when other platforms or applications
target the AVM: whenever a new opcode may be added, the actual binary
for OP_HALT changes (as a result of how C enums work).
Say your application targets commit alpha of AVM. OP_HALT is, say,
98. In commit beta, AVM is updated with a new opcode so OP_HALT is
changed to 99 (due to the new opcode being placed before OP_HALT). If
your application builds a binary for AVM version alpha and AVM version
beta is used instead, OP_HALT will be interpreted as another
instruction, which can lead to undefined behaviour.
This can be hard to debug, so here I've made the decision to try and
not place new opcodes in between old ones; new ones will always be
placed *before* NUMBER_OF_OPCODES.
Instead of using a linked list, which is incredibly fragmented, a
vector keeps all pointers together. Keeps all our stuff together and
in theory we should have less cache misses when deleting pages.
It does introduce the issue of fragmenting, where if we allocate and
then delete many times a lot of the heap vector will be empty so
traversal will be over a ton of useless stuff.
Copied the code from stack overflow without thinking about it. The
first byte in little endian order should always be LSB so I construct
a more contrived example (0xFFFF0000) which should make it easier to
detect what the first byte is considered on the machine. If it's 0
then the LSB is the first byte hence little endian, otherwise it's big
endian.
On a greater note: Don't never copy no code from stack overflow, bro.
I went up there at 11 o'clock last night trynna get me some code.
Bro, I copied that shit, woke up, my motherfucking LITTLE_ENDIAN
detection don't work. Explain, bro.
While it helped with understanding to use unions as a safe way to
access the underlying bits, this shift based mechanism actually makes
more sense at a glance, particularly by utilising WORD_NTH_BYTE