If you sell a flight computer and you want advice about porting RL to it please get in touch with me at paul@taniwha.com.
You could also port the interpreter to execute code out of EEPROM (although it might be slow) so long as your machine has enough SRAM in it for global variables and stack.
The file in the standard source distribution sim.c is a C compilable loader and interpreter interpreter for the .x files - if you have a C compiler for your machine you are more than half way there (otherwise you may need to translate the interpreter it into assembly code). You will need to mek some changes:
In my case I translated the code into some somewhat threaded code - many of the primitives are calls to subroutines - this is mainly to save space.
Feel free to copy as much of this code as is usefull to you.
The byte code machine has a number of 'registers' (they don't have to be mapped to real registers although the first 4 are really good candidates for doing so):
Notes Opcode value description
P_HALT 0 halt
cu2 P_UBYTE_L 1 load a local unsigned byte
the byte at (FP+N2) is 0 extended into TOS
TOS = *(unsigned char *)(FP+N2)
cu2 P_UBYTE_G 2 load a global unsigned byte
the byte at (GP+N2) is 0 extended into TOS
TOS = *(unsigned char *)(GP+N2)
wb P_J_NE 3 jump if TOS ne 0 to PC+N2+1
wb P_J_EQ 4 jump if TOS eq 0 to PC+N2+1
w P_GE 5 TOS = POP() >= TOS
w P_GT 6 TOS = POP() > TOS
w P_LE 7 TOS = POP() <= TOS
w P_LT 8 TOS = POP() < TOS
w P_NE 9 TOS = POP() != TOS
w P_EQ 10 TOS = POP() == TOS
w P_NOT 11 TOS = !TOS
w P_SUB 12 TOS = POP() - TOS
w P_ADD 13 TOS = POP() + TOS
w P_MUL 14 TOS = POP() * TOS (signed)
w P_DIV 15 TOS = POP() / TOS (signed)
w P_MOD 16 TOS = POP() % TOS - undefined TOS is 0
w P_OR 17 TOS = POP() | TOS
w P_XOR 18 TOS = POP() ^ TOS
w P_AND 19 TOS = POP() & TOS
w P_SHL 20 TOS = POP() << TOS
w P_SHR 21 TOS = POP() >> TOS
b P_JMP 22 jump unconditionally to PC+N2+1
w P_COMP 23 TOS = ~TOS
bw P_CALL 24 call subroutine
TOS = PC+3
PC = PC+N2+1
w P_CALLI 25 call-indirect subroutine (from TOS - TOS is popped)
tmp = TOS
TOS = PC+3
PC = tmp
2w P_COPYN 26 N+1 words are popped from TOS, old TOS is pushed back on
SP += N2
2w P_CUTN 27 N words are popped from TOS
SP += N2
2w P_CONST 28 TOS gets a constant value
TOS = N2
2w P_ADDR_L 29 TOS gets the address of a local value (2 byte offset)
TOS = FP+N2
2w P_ADDR_G 30 TOS gets the address of a global value (2 byte offset)
TOS = GP+N2
2c P_BYTE_L 31 TOS gets a local byte value (2 byte offset) [sign extended]
TOS = *(char *)(FP+N2)
2c P_BYTE_G 32 TOS gets a global byte value (2 byte offset) [sign extended]
TOS = *(char *)(GP+N2)
2w P_WORD_L 33 TOS gets a local word value (2 byte offset)
TOS = *(short *)(FP+N2)
2w P_WORD_G 34 TOS gets a global word value (2 byte offset)
TOS = *(short *)(GP+N2)
2c P_SBYTE_L 35 store a local byte value (2 byte offset)
*(char *)(FP+N2) = TOS
2c P_SBYTE_G 36 store a global byte value (2 byte offset)
*(char *)(GP+N2) = TOS
2w P_SWORD_L 37 store a local word value (2 byte offset)
*(short *)(FP+N2) = TOS
2w P_SWORD_G 38 store a global word value (2 byte offset)
*(short *)(GP+N2) = TOS
c2 P_LOAD_B 39 tos is replaced by byte from indirection of old tos value [sign extended]
TOS = *(char *)(TOS+N2)
w2 P_LOAD_W 40 tos is replaced by word from indirection of old tos value
TOS = *(short *)(TOS+N2)
w P_DUP 41 push top of stack TOS is duplicated onto stack
PUSH(TOS)
c2 P_STORE_B 42 *tos = tos-1 (byte) tos/tos-1 are discarded
*(char *)(tos+N2) = POP()
c2 P_STORE_W 43 *tos = tos-1 (word) tos/tos-1 are discarded
*(word *)(tos+N2) = POP()
wb P_ADDR_P 44 TOS gets address of a code address (relative) (2 byte offset)
TOS = PC+N2+1
w P_ZERO 45 TOS gets 0
TOS = 0
w P_ONE 46 TOS gets 1
TOS = 1
w2 P_ENTER 47 allocates space for local variables (subtracts sp by 2 byte offset)
PUSH(TOS);
if (N2 != 0xffff) {
PUSH(FP);
SP -= N2;
FP = SP;
}
w2 P_RET 48 recovers space for local variables and returns from subroutine (subtracts sp by 2 byte offset)
if (N2 != 0xffff) {
SP += N2;
FP = POP();
}
PC = POP();
w2 P_ADDR_S 49 push the address of a string (2 byte offset)
TOS = SSP+N2
P_POLL 50 call into environment for busy work - this is the core of the
system support - see below for more info
TIME = RTIME
w P_INTERRUPT 51 push true if interrupt N has occured (and clear the flag)(2 byte number)
if (INT[N2]) {
INT[N2]=0;
TOS=1;
} else {
TOS = 0;
}
ul P_SAVE_TIME 52 saves current time
SAVED_TIME=TIME
TIME_FLAG=1
l4 P_CMP_TIME 53 pushes true if current time is N > than the last saved one (2 byte constant)
if (TIME_FLAG && TIME >= (SAVED_TIME+N4)) { // unsigned long math here
TOS = 1;
TIME_FLAG = 0;
} else {
TOS = 0;
}
w P_INPUT 54 pushes false if no input char available, and the char true it it is
if (input_available) {
TOS = getchar();
} else {
TOS = 0;
}
w P_OUTPUT 55 pushes truth value - true if we can send a character without stalling
TOS = output_possible
w P_LAUNCH 56 pushes truth value - true if we have launched
if (!LAUNCH_FOUND and LAUNCHED) {
TOS = 1;
LAUNCH_FOUND = 1;
} else {
TOS = 0;
}
w P_ARM 57 TOS is passed to the arming mechanism
do_arm(TOS);
w P_SAFE 58 TOS is passed to the safing mechanism
do_safe(TOS);
w P_FIRE 59 TOS is passed to activate a pyro channel
do_fire(TOS);
ul P_TIME 60 pushes absolute time
TOS = TIME
ul P_LTIME 61 pushes time since launch event
TOS = LTIME
w P_SET_LOG_FULL 62 pops TOS is log full value
LOG_FULL = TOP
wb P_SET_POLL 63 pops TOS is log address value
LOG_POLL = PC+N2+1
w P_BEEP 64 TOS passed to control the beeper (if any)
do_beep(TOS)
w P_PSTR 65 TOS used as the address of a string to print
printf("%s", TOS);
w P_PHEX 66 TOS used as a hex value to print
printf("%04x", TOS);
l P_PVAL 67 TOS used as a decimal value to print
printf("%d", TOS);
w P_PCHR 68 TOS used as a character value to print
putchar(TOS)
w P_EESAVE 69 3 TOS values saves data to eeprom tos is eeprom address, tos-1 main mem address tos-2 is count
do_eesave(TOS, POP(), POP())
w P_EELOAD 70 3 TOS values loads data from eeprom tos is main mem address, tos-1 eeprom address tos-2 is count
do_eeload(TOS, POP(), POP())
w P_GET 71 get value from external source (tos gives value and is replaced with old one)
TOS = get(TOS);
w P_SET 72 set value to external source (tos is the value tos-1 points to the source, both are removed)
set(TOS, POP());
w P_POP 73 discard TOS
TOS = POP()
l P_GE_L 74 TOS = POP() >= TOS
l P_GT_L 75 TOS = POP() <= TOS
l P_LE_L 76 TOS = POP() > TOS
l P_LT_L 77 TOS = POP() <= TOS
l P_NE_L 78 TOS = POP() < TOS
l P_EQ_L 79 TOS = POP() == TOS
l P_NOT_L 80 TOS = POP() != TOS
l P_SUB_L 81 TOS = POP() - TOS
l P_ADD_L 82 TOS = POP() + TOS
l P_MUL_L 83 TOS = POP() * TOS
l P_DIV_L 84 TOS = POP() / TOS
l P_MOD_L 85 TOS = POP() % TOS
l P_OR_L 86 TOS = POP() | TOS
l P_XOR_L 87 TOS = POP() ^ TOS
l P_AND_L 88 TOS = POP() & TOS
l P_SHL_L 89 TOS = POP() << TOS
l P_SHR_L 90 TOS = POP() >> TOS
l P_COMP_L 91 TOS = ~TOS
l4 P_CONST_L 92 TOS = N4
l2 P_LONG_L 93 load long local
TOS = *(long *)(FP+N2)
l2 P_LONG_G 94 load long global
TOS = *(long *)(GP+N2)
l2 P_SLONG_L 95 store long local
*(long *)(FP+N2) = TOS
l2 P_SLONG_G 96 store long global
*(long *)(GP+N2) = TOS
l2 P_LOAD_L 97 load long
TOS = *(long *)(TOS+N2)
l P_DUP_L 98 push a long
PUSH(TOS)
l2 P_STORE_L 99 store long
*(long *)(TOS+N2) = POP()
l P_ZERO_L 100 load long 0
TOS = 0;
l P_ONE_L 101 load long 1
TOS = 1
l P_WIDEN 102 widen signed from short to long
TOS = TOS&0xffff|(TOS&0x8000?0xffff0000:0)
w P_SHORTEN 103 shorten from long to short
TOS = TOS&0xffff
l P_PVALU 104 TOS used as an unsigned decimal value to print
printf("%u", TOS);
l P_PHEXL 105 print a long hex value from TOS
printf("%08x", TOS)
l P_POP_L 106 discard the long TOS
TOS = POP()
w2 P_EQ_C 107 test for equality
TOS = TOS == N2
l4 P_EQ_L_C 108 test for equality (LONG)
TOS = TOS == N4
w2 P_ADD_C 109 add constant
TOS = TOS + N2
l4 P_ADD_L_C 110 add constant long
TOS = TOS + N4
w P_INDEX2 111 index by 2
TOS = TOS<<1
w P_INDEX4 112 index by 4
TOS = TOS<<2
w P_LSHORTEN 113 preserve a long logical value in a 16-bit
TOS = TOS|(TOS>>16)
ul P_RTIME 114 get real time
TOS = RTIME
w P_LOG_FULL 115 get the log full state
TOS = LOG_FULL
w P_LOG_BASE 116 get the log base address (see below)
TOS = LOG_BASE
w P_LOG_END 117 get the log end address (see below)
TOS = LOG_END
wbX P_JEQV 118 jump if TOS == constant
if (TOS == XN2)
PC += NN2
lbY P_JEQV_L 119 jump if TOS == constant
if (TOS == XN4)
PC += NN2
wbX P_JNEV 120 jump if TOS != constant
if (TOS != XN2)
PC += NN2
lbY P_JNEV_L 121 jump if TOS != constant
if (TOS != XN4)
PC += NN2
l P_WIDENU 122 unsigned widen from short to long
TOS = TOS&0xffff
uc2 P_LOAD_UB 123 tos is replaced by byte from indirection of old tos value [zero extend
ed]
TOS = *(unsigned char *)(TOS+N2)
P_FOUND_LAUNCH 124 a launch event was detected
LAUNCHED = 1
uw P_MULU 125 unsigned multiply
TOS = POP()*TOS
uw P_DIVU 126 unsigned deivide
TOS = POP()/TOS
ul P_MULU_L 127 unsigned multiply
TOS = POP()*TOS
ul P_DIVU_L 128 unsigned divide
TOS = POP()/TOS
uw P_GEU 129 unsigned compare
TOS = POP >= TOP
ul P_GEU_L 130 unsigned compare
TOS = POP >= TOP
uw P_GTU 131 unsigned compare
TOS = POP > TOP
ul P_GTU_L 132 unsigned compare
TOS = POP > TOP
uw P_LEU 133 unsigned compare
TOS = POP <= TOP
ul P_LEU_L 134 unsigned compare
TOS = POP <= TOP
uw P_LTU 135 unsigned compare
TOS = POP < TOP
ul P_LTU_L 136 unsigned compare
TOS = POP < TOP
wb P_SET_TRAMPOLINE 137 set trampoline return address (see below)
TRAMPOLINE = PC+N2+1
wb P_SET_EXTRA 138 set extra callback routine (see below)
_EXTRA = PC+N2+1
ul P_STIME 139 get time we last did a P_SAVE_TIME
TOS = SAVED_TIME
Key to Notes:
2 - opcode is followed by a 2 byte constant
4 - opcode is followed by a 4 byte constant
b - opcode is followed by a 2 byte constant relative to the PC+1
X - opcode is followed by a 2 byte constant after a 'b' branch field
Y - opcode is followed by a 4 byte constant after a 'b' branch field
u - operation involves unsigned math or zero-extension
c - operation is onbyte quantities
w - operation is on 2-byte quantities (stack PUSH/POP operations inc/dec the stack by 2)
l - operation is on 4-byte quantities (stack PUSH/POP operations inc/dec the stack by 2)
In addition the logging code needs to be called out of the P_POLL and a number of the loggable action operations (P_SET, P_FIRE, P_ARM and P_SAFE).
(examples of the following are in the simulator source).
The following byte codes include the addresses of code that should be saved away - they will be executed at the very beginning of the program P_SET_POLL, P_SET_TRAMPOLINE and P_SET_EXTRA.
In your P_POLL routine after you copy RTIME to TIME you must check to see if a P_SET_POLL value was given, you call the routine pointed to by P_SET_POLL:
P_POLL: TIME = RTIME
if (SET_POLL != 0) {
TOS = PC+1;
PC = SET_POLL;
}
For the P_SET, P_FIRE, P_ARM and P_SAFE byte codes after you perform the operation you must
push PC+1 followed by the parameters in reverse order on to the stack (if the only have 1
push a 0 forst) followed by a code identifying the routine, the set the return
address (TOS) to the TRAMPOLINE value (this is a pointer to the code that will clean
up the stack for you as you exit) and jump to the SET_EXTRA address:
P_FIRE: do fire stuff
if (SET_EXTRA) {
PUSH(PC+1);
PUSH(0);
PUSH(TOS);
PUSH(0x43);
TOS = SET_TRAMPOLINE;
PC = SET_EXTRA;
}
P_ARM: do arm stuff
if (SET_EXTRA) {
PUSH(PC+1);
PUSH(0);
PUSH(TOS);
PUSH(0x44);
TOS = SET_TRAMPOLINE;
PC = SET_EXTRA;
}
P_SAFE: do fire stuff
if (SET_EXTRA) {
PUSH(PC+1);
PUSH(0);
PUSH(TOS);
PUSH(0x45);
TOS = SET_TRAMPOLINE;
PC = SET_EXTRA;
}
P_SET: NTOS = POP()
do fire stuff
if (SET_EXTRA) {
PUSH(PC+1);
PUSH(NTOS);
PUSH(TOS);
PUSH(0x46);
TOS = SET_TRAMPOLINE;
PC = SET_EXTRA;
}
(Note: all pushes are 2 byte pushes)
Intel hex formatted data consists of lines of data, each starts with a : (or in this cate !) followed by a string of pairs of hex digits - consisting of the folllwing in order:
The last record in the file is special it's flag field has the value 1 and it has the C structure (from code.h):
struct code_hdr {
unsigned char magic; // always 0xc5
unsigned char cmd; // 0 - do nothing, just load
// 1 - run
// 2 - save
unsigned char dest; // save set to save in
unsigned char code_size[2]; // number of bytes of code
unsigned char string_size[2]; // number of bytes of strings
unsigned char global_size[2]; // number of bytes of globals
unsigned char stack_size[2]; // number of bytes of stack
};
the values in the last 4 fields are actually 16-bit integers - stored little-endian.At the begining of the program if the program was loaded at address BASE then PC will be set to BASE, SSP to BASE+code_size, GP to BASE+code_size+string_size.
stack_size is the number of bytes of stack to allocate - everything between the top of memory-stack_size and BASE+code_size+string_size+global_size is free for use as the system log.