i have created a very simple Computer using the new Combinators.
It's basically a Harvard Architecture with 4 registers (reg3-reg0, with reg0 being the instruction pointer), 160 Words of ROM, 80 Words of RAM and a few 7 Segment displays for IO. At the moment it runs at 2 Instructions per second but one might be able to speed it up a little bit by reducing some propagation delays.
This is the implemented instruction set:
Code: Select all
reset reg3 = reg2 = reg1 = reg0 = 0
nop
load0 #imm reg0 = #imm
load1 #imm reg1 = #imm
load2 #imm reg2 = #imm
load3 #imm reg3 = #imm
swap01 reg0 <-> reg1
swap02 reg0 <-> reg2
swap03 reg0 <-> reg3
swap12 reg1 <-> reg2
swap13 reg1 <-> reg3
swap23 reg2 <-> reg3
add reg1 = reg1 + reg2
sub reg1 = reg1 - reg2
mul reg1 = reg1 * reg2
div reg1 = reg1 / reg2
rem reg1 = reg1 % reg2
jz #addr if reg1 == 0 then reg0 = #addr
jnz #addr if reg1 != 0 then reg0 = #addr
jlz #addr if reg1 < 0 then reg0 = #addr
jgz #addr if reg1 > 0 then reg0 = #addr
sti #addr RAM[#addr] = reg1
st RAM[reg3] = reg1
ldi #addr reg1 = RAM[#addr]
ld reg1 = RAM[reg3]
inc reg1 = reg1 + 1
dec reg1 = reg1 - 1
Code: Select all
/c local source = "
load1 3\n
sti 2\n
load1 19\n
sti 4\n
loop1:\n
ldi 2\n
sti 1001\n
load2 2\n
div\n
sti 3\n
loop2:\n
ldi 3\n
dec\n
sti 3\n
jz prime\n
inc\n
swap12\n
ldi 2\n
rem\n
jnz loop2\n
load0 notprime\n
prime:\n
ldi 4\n\n
inc\n
sti 4\n
swap13\n
ldi 2\n
st\n
sti 1000\n
notprime:\n
ldi 2\n
inc\n
inc\n
sti 2\n
load0 loop1\n
";
local instructions = {
reset = { a=0 },
nop = { a=1 },
load0 = { a=2, args=1 },
load1 = { a=3, args=1 },
load2 = { a=4, args=1 },
load3 = { a=5, args=1 },
swap01 = { a=6 },
swap02 = { a=7 },
swap03 = { a=8 },
swap12 = { a=9 },
swap13 = { a=10 },
swap23 = { a=11 },
add = { a=12 },
sub = { a=13 },
mul = { a=14 },
div = { a=15 },
rem = { a=16 },
jz = { a=17, args=1 },
jnz = { a=18, args=1 },
jlz = { a=19, args=1 },
jgz = { a=20, args=1 },
sti = { a=21, args=1 },
st = { a=22 },
ldi = { a=23, args=1 },
ld = { a=24 },
inc = { a=25 },
dec = { a=26 }
};
local code = {};
local labels = {};
function myprint(s)
print(s);
game.player.print(s);
end;
function assembleSource(pass)
local currentAddr = 0;
for line in string.gmatch(source, "[^\n]+") do
items = {}
for item in string.gmatch(line, "[^%s]+") do
table.insert(items, item);
end;
while #items > 0 do
local item = items[1];
table.remove(items, 1);
if item then
if string.sub(item, 1, 1) == ";" then
items = {};
item = nil;
end;
end;
if item then
local labelEnd = string.find(item, ":");
if labelEnd then
local labelName = string.sub(item, 1, labelEnd-1);
if string.len(labelName) < 1 then error("invalid label"); end;
if pass == 1 then
if labels[labelName] then error("duplicate label"); end;
labels[labelName] = currentAddr;
end;
item = nil;
end;
end;
if item then
local instruction = instructions[item];
if not instruction then
error("invalid instruction: " .. item);
end;
local arguments = {};
if instruction.args then
for i=1,instruction.args,1 do
local argument = items[1];
if not argument then error("missing argument"); end;
table.remove(items, 1);
table.insert(arguments, argument);
end;
end;
if pass == 2 then
c = {};
table.insert(c, instruction.a);
while #arguments > 0 do
local argument = arguments[1];
table.remove(arguments, 1);
if tonumber(argument) then
table.insert(c, tonumber(argument));
else
if not labels[argument] then error("unknown label:" .. argument); end;
table.insert(c, labels[argument]);
end;
end;
code[currentAddr] = c;
end;
currentAddr = currentAddr + 1;
end;
end;
end;
end;
function setResetEntity(entity, active)
local cond = entity.get_circuit_condition(1);
for i=1,15,1 do cond.parameters[i] = { signal={type="item"}, count=1, index=i }; end;
if active then cond.parameters[1] = { signal={type="virtual", name="signal-red"}, count=1, index=1 }; end;
entity.set_circuit_condition(1, cond);
end;
function setReset(reset, active)
local entities = game.player.surface.find_entities_filtered({area={{reset.x,reset.y},{reset.x,reset.y}}, name="constant-combinator"});
if not entities or #entities < 1 then error("no constant-combinator at " .. reset.x .. " " .. reset.y); end;
if #entities ~= 1 then error("found more than one constant-combinator at " .. reset.x .. " " .. reset.y); end;
for index, entity in ipairs(entities) do
setResetEntity(entity, active);
end;
end;
function setInstructionEntity(entity, a, b, c)
local cond = entity.get_circuit_condition(1);
for i=1,15,1 do cond.parameters[i] = { signal={type="item"}, count=1, index=i }; end;
if a then cond.parameters[1] = { signal={type="virtual", name="signal-A"}, count=a, index=1 }; end;
if b then cond.parameters[2] = { signal={type="virtual", name="signal-B"}, count=b, index=2 }; end;
if c then cond.parameters[3] = { signal={type="virtual", name="signal-C"}, count=c, index=3 }; end;
entity.set_circuit_condition(1, cond);
end;
function setInstruction(memory, addr, a, b, c)
if addr >= memory.rows * memory.columns then error("invalid memory address " .. addr); end;
local x = memory.x0 + math.floor(addr / memory.rows) * memory.xstride;
local y = memory.y0 + math.fmod(addr, memory.rows) * memory.ystride;
local entities = game.player.surface.find_entities_filtered({area={{x,y},{x,y}}, name="constant-combinator"});
if not entities or #entities < 1 then error("no constant-combinator at " .. x .. " " .. y); end;
if #entities ~= 1 then error("found more than one constant-combinator at " .. x .. " " .. y); end;
for index, entity in ipairs(entities) do
setInstructionEntity(entity, a, b, c);
end;
end;
function clearMemory(memory)
local max = memory.rows * memory.columns - 1;
for i=0,max,1 do
setInstruction(memory, i, 0, 0, 0);
end;
end;
function fillMemory(memory)
local numInstructions = 0;
for addr, code in pairs(code) do
local a = code[1];
local b = code[2];
local c = code[3];
setInstruction(memory, addr, a, b, c);
numInstructions = numInstructions + 1;
end;
return numInstructions;
end;
local RST = {
x = -166.5,
y = 398.5
};
local ROM = {
x0 = -242.5,
y0 = 422.5,
xstride = 4,
ystride = 1,
rows = 20,
columns = 8
};
setReset(RST, true);
myprint("Assembling source ...");
assembleSource(1);
assembleSource(2);
clearMemory(ROM);
local numInstructions = fillMemory(ROM);
myprint("Done. Assembled " .. numInstructions .. " instructions.");
This is a simple program that calculates prime numbers:
Code: Select all
; memory map:
; @2 = number under investigation
; @3 = current divisor
; @4 = memory address for next prime number
load1 3
sti 2
load1 19
sti 4
loop1:
ldi 2
sti 1001
load2 2
div
sti 3
loop2:
ldi 3
dec
sti 3
jz prime
inc
swap12
ldi 2
rem
jnz loop2
load0 notprime
prime:
ldi 4
inc
sti 4
swap13
ldi 2
st
sti 1000
notprime:
ldi 2
inc
inc
sti 2
load0 loop1
And finally a picture:
http://i.imgur.com/YG2eTT9.jpg
The entire machine is pretty modular, all the modules are connected via a central bus running red and green lines. The red line holds all the "current" values like the register values and the instruction stored at the memory address inside reg0. On the other hand the green line holds all the "new" values, like the new values for all the registers that will be written into the register file during the next clock cycle. The four seven segment displays at the top directly show the current values of all the registers (reg3, reg2, reg1, reg0 from left to right), so the right display always shows the address of the next instruction. The two displays on the right however are hooked into the RAM module and can be addressed using the special addresses 1000 and 1001, there are also three constant combinators that can be used as inputs (1003-1005).
Here's the save file if anyone wants to check it out (you might need to install some mods to be able to load it, see picture):
http://i.imgur.com/CTjwrW9.png
Here are some pictures of my base:
http://i.imgur.com/8mFKBxL.jpg
http://i.imgur.com/htbRFFJ.jpg
http://i.imgur.com/0rm3Zs0.jpg
http://i.imgur.com/XXlIOEH.jpg
That's all i have for now, i'm looking forward to your feedback