MPCE, Massive Parallel Computer Emulator - documentation

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.

Processor description:

I/O and interrupt subsystem:

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:

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).

Additional commands:

Bad things usually aren't detected so be careful and don't do things like call mpce-log on connected channels.