As a little fun side project, and to gain some more experience, I'm currently trying to create something similar to TIS-100, but with HTML and Javascript. TIS-100 is basically a very simple, very limited assembly interpreter, designed as a programming puzzle.
There's a fixed number of nodes, all of which simulating mini computers running their own code. I have the programing for the instruction set close to finished and the nodes generally work. That is, as long as each of them stays self-contained. That means all the logic has been implemented and can be used already, as long as there's no read from, or write to one of the connections.
Each node has two connections to it's direct neighbors, both vertical and horizontal. One of them is the output (writing to the neighbor) and the other for input (reading from the neighbor).
+----+ +----+ +----+
| No | -> | No | -> | No |
| 1 | <- | 2 | <- | 3 |
+----+ +----+ +----+
^ | ^ | ^ |
| v | v | v
+----+ +----+ +----+
| No | -> | No | -> | No |
| 4 | <- | 5 | <- | 6 |
+----+ +----+ +----+
The connections kind of act like named pipes in unix. The execution of a node halts, until the program on the other end of the connection reads from it.
For now, the user has to press a button to run the next instruction. All nodes run simultaneously, and I just call each node's runNextInstruction()
method when clicking the button.
When getting to a read or write instruction, it calls either readFromConnection(direction)
or writeToConnection(direction)
. This is what I've got so far:
TIS_Node.prototype.readFromConnection = function(direction) {
this.ioMode = TIS_ioMode.READ;
this.lastDirection = direction;
var connection = this._connections[direction];
console.log(connection.ioMode);
};
All it does for now, is setting the node's state from IDLE
to READ
, which will prevent the executeNextInstruction()
method to execute any other instructions. Precisely, readFromConnection
is getting called like below:
// Gets called when a MOV instruction is called.
// Syntax: MOV SOURCE, DEST
TIS_Node.prototype.action_MOV = function(command) {
var components = command.split(",");
var source = components[0].trim();
var destination = components[1].trim();
source = this.getValue(source);
switch (destination) {
case "ACC":
this.acc = source;
break;
case "NIL":
// Discard
break;
// ...
}
};
TIS_Node.prototype.getValue = function(address) {
switch (address.trim().toUpperCase()) {
case "LEFT":
return this.readFromConnection(TIS_Direction.LEFT);
case "RIGHT":
return this.readFromConnection(TIS_Direction.RIGHT);
// ...
default:
return address;
}
};
The problem I have is getting my head around how to do it. My first naive idea was to just wait in javascript itself until the connection's ioMode
changes to WRITE
, but that probably won't work and I couldn't even imagine how.
My second idea was just to set the own ioMode
to READ
and then call a method such as receiveInputValue(direction, value)
from the other node when it executes the write instruction. Still, I can't think about how to implement this and still having the correct destination written to (a value can be written to the node's accumulator ACC
, discarded via NIL
or just handed through to another connection).
On another note:
While everything is fast enough that it seems the nodes run simultaneously, they actually don't. Node 1's runNextInstruction
gets called one step in a forEach loop before Node's method. This would also ruin my results. Imagine the following: Node 1 wants to read from node 2. In the exact same time (theoretically) node 2 changes from IDLE to WRITE and wants to hand node 1 the value. Because node 1 de facto gets executed before node 2, when it tried to read the value, node 2 was still in IDLE, thus started waiting.
Is there any easy way around these issues? And can you give me some clues as to how to go on from here?