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.