This program emulates network of simple independent processors. Processors communicate with their neighbours with buffered data channels. Each processor has local memory, input and output data channels and interrupt system.
This documentation is very brief so don't hesitate to ask about every detail.
Each processor has interrupt table (at begining of memory - for each interrupt one word). Each input channel of processor corresponds to one interrupt. Processor can be in three states - normal, interrupt and sleep. In sleep state processor is waiting. If there are some data in one of input channels and processor is in sleep state, state is changed to normal. If there are some data in one of input channels and that channel has enabled interrupt (corresponding item in interrupt table is non-zero), state is changed to interrupt - IP is stored to SIP (Shadow IP), new IP is loaded from corresponding item in interrupt table and interrupt flag is set. Instruction 'rfi' (in iterrupt state) restores old IP (from SIP) and sets state to normal. Instruction 'slp' in normal state sets sleep state.
Data channels are (logically) 1 byte wide, has latency 8 cycles (variable latency in proc.c) and buffer size 8 bytes (BUFF_SIZE in proc.h). Data output to channels not connected to another processor is signaled (and can be logged to file).
MPCE processors use their own assembly language which is similar to MIPS's one. It is translated by assembler command 'asm'. Every line is divided (by ' ') to tokens and first token determinates kind of line. There are four kinds of lines:
Instruction set is divided to 9 groups by format of parameters:
group format of parameters examples ------------------------------------------------------------------------ 0 - rfi, slp, dump 1 R1 move, jmp, chnl, wrt 2 R1 R2 mul, div 3 R1 imm bbr 4 RD RS imm load, store, in, out 5 RD RS1 RS2 / RD RS imm alu w/o (mul, div) 6 rel branch 7 R1 rel branch 8 R1 R2 rel branch
R? are register paramerers ($0 - $31), imm and rel are numeric constants. Instructions from group 5 has two formats - third parameter can be register or numeric constant. Numeric constant can be direct number or named constant (as created by label or variable definition). Difference between imm and rel constants is in translation of named constant. In imm named constant is translated to its value but in rel to difference of its value and actual position (so labels are translated to their relative value). Numeric constants are 16 bit long.
Instructions with asterisk (*) exist in signed and unsigned variant. In standard way they are signed but you can add 'u' after instruction name (for example add and addu) for unsigned variant.
name group meaning ------------------------------------------------------------------------ add 5* RD = RS1 + RS2 or RD = RS + imm sub 5* RD = RS1 - RS2 or RD = RS - imm slt 5* RD = (RS1 < RS2) ? 1 : 0 or RD = (RS < imm) ? 1 : 0 mul 2 HI = upper_word (R1 * R2); LO = lower_word (R1 * R2) div 2 LO = R1 / R2; HI = R1 % R2 and 5 RD = RS1 & RS2 or RD = RS & imm or 5 RD = RS1 | RS2 or RD = RS | imm xor 5 RD = RS1 ^ RS2 or RD = RS ^ imm nor 5 RD = ~(RS1 | RS2) or RS = ~(RS | imm) sll 5 RD = RS1 << RS2 or RD = RS << imm srl 5 RD = RS1 >> RS2 or RD = RS >> imm (logical shift - unsigned) sra 5 RD = RS1 >> RS2 or RD = RS >> imm (arithmetic shift - signed) lb 4* RD = memory (RS + imm) // 1 byte lh 4* RD = memory (RS + imm) // 1 halfword lw 4 RD = memory (RS + imm) // 1 word sb 4 memory (RD + imm) = RS // 1 byte sh 4 memory (RD + imm) = RS // 1 halfword sw 4 memory (RD + imm) = RS // 1 word mfhi 1 R1 = HI mflo 1 R1 = LO mthi 1 HI = R1 mtlo 1 LO = R1 beq 8 if (R1 == R2) branch (imm) bne 8 if (R1 != R2) branch (imm) bgez 7 if (R1 >= 0) branch (imm) bgtz 7 if (R1 > 0) branch (imm) blez 7 if (R1 <= 0) branch (imm) bltz 7 if (R1 < 0) branch (imm) bof 6 if (overflow_flag) branch (imm) bno 6 if (! overflow_flag) branch (imm) bbr 3 branch (R1 << imm) jmp 1 jump (R1) rfi 0 return from interrupt slp 0 sleep chnl 1 R1 = number of channel with waiting data, else -1 in 4 RD = channel (RS + imm) // 1 byte, if no data then -1 out 4 while (channel_full (RD + imm) wait; channel (RD + imm) = RS // 1 byte wrt 1* write_debug_message (R1) dump 0 dump processor status jump (x) is $31 = new_IP; new_IP = x branch (x) is jump (actual_IP + x) So jump $31 can be used as return from subroutine.
After execution 'mpce' starts emulator and accepts commands from command line. Emulator runs asynchronously so user can call commands during emulation (without stopping of emulation). Commands are in Scheme (Guile) language with some additional functions. File specified as first parameter is loaded and interpreted as commands even before start of emulator. Processors and connections can be added even during run of emulator but cannot be removed. Program should be stopped with CTRL-D (EOF).
Bad things usually aren't detected so be careful and don't do things like call mpce-log on connected channels.