Skip to content

Commit caa752a

Browse files
committed
Start documentation
Get started on the documentation (largely unformatted, lots of gaps still) Unionize some stuct fields
1 parent f24a2d7 commit caa752a

File tree

4 files changed

+146
-27
lines changed

4 files changed

+146
-27
lines changed

README.md

+109-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,116 @@
11
# TIS-100
2-
A TIS-100 emulator, for the wonderful Zachtronics game of the same name. Uses TIS-100 save file format.
2+
A TIS-100 emulator, for the wonderful Zachtronics game of the same name. Uses TIS-100 save file format for code; the layout/specification is a proprietary format.
33

4-
None of the others available did quite what I wanted, so I'm going to try my hand at making my own.
4+
None of the other emulators available did quite what I wanted, so I'm going to try my hand at making my own.
5+
6+
My goals with this emulator are not to re-implement the game, but to mold this design into something more like a niche, but usable programming language.
7+
There is no verification of outputs against a predefined list; that is the duty of the programmer.
8+
The variety of input and output styles will be expanded from the simple number lists and graphical display of the game, including ASCII input and output.
9+
The layout and arrangement of nodes will be modifiable, and (in the long term) more node types will be added, such as a RAM node.
510

611
This is all very WIP, so a bunch of things aren't implemented. However, the instruction set should be functional
7-
and complete. Also no documentation yet, sorry. It's on my short list.
12+
and complete. Also no real documentation yet, sorry. It's on my short list. Draft below.
13+
14+
===
815

916
(useful links for dev'ment:)
17+
http://www.zachtronics.com/images/TIS-100P%20Reference%20Manual.pdf
1018
https://alandesmet.github.io/TIS-100-Hackers-Guide/
19+
20+
===
21+
22+
## TIS Assembly Language
23+
24+
### The official manual
25+
The official TIS-100 manual is available from the Zachtronics website here: http://www.zachtronics.com/images/TIS-100P%20Reference%20Manual.pdf
26+
If you are new to TIS, this is the best place to start whether you intend to play the game, or use this emulator.
27+
28+
### Differences, deviations, addendums and errata
29+
(additional details, especially where this deviates from the maunal, and details on how the save file works)
30+
Deviations between the manual and this emulator include:
31+
- HCF still exits the system immediately, but the emulator attempts a clean exit.
32+
33+
One thing to be aware of is that, like the game, compute nodes are numbered left to right, top to bottom, indexed from zero to n-1, where n is the number of compute nodes. Not all nodes must be present.
34+
When counting, nodes other than compute nodes are skipped.
35+
36+
## TIS Configuration
37+
38+
### Components of a TIS:
39+
The majority of a TIS is made of several parts called nodes; each node is of a specific type that does a specific job.
40+
These nodes are arranged in a rectangle, and each node can communicate with its four immediate neighbors. (Border nodes will have fewer neighbors.)
41+
42+
The most common node is a execution, or compute node. This node type can hold up to 15 instructions, and those instructions are run on a loop until termination.
43+
Each node has internal registers to store data, and port registers to communicate with its neighbors. See the documentation in the official manual for more details.
44+
45+
Another node type is the stack memory node. This node can store up to 15 numbers in a stack; this stack may be accessed by all four neighbors. See the documentation in the official manual for more details.
46+
47+
If a node is detected to be damaged, it will be disabled and therefore will not interact with neighboring nodes.
48+
49+
The top row of nodes may have an input pseudo-node as their upward neighbor. These pseudo-nodes provide data from an external source.
50+
See the TIS Input/Output section below for more details on available input pseudo-node types.
51+
52+
Likewise, the bottom row of nodes may have an output pseudo-node as their downward neighbor. These pseudo-nodes provide data to an external destination.
53+
See the TIS Input/Output section below for more details on available output pseudo-node types.
54+
55+
### The default configuration
56+
The default configuration of a TIS is 3 rows of 4 nodes each. All twelve nodes will be configured as compute nodes.
57+
A simple ASCII input from stdin will be made available to the upper-left node, and a simple ASCII output will be made available to the lower-right node.
58+
The configurations encountered in the game differ only slightly to this; exhibiting different node types and input/output style and indexes.
59+
60+
Assuming the TIS assembly code is in 'code.tisasm', this is the simplest method of running the emulator:
61+
$ tis code.tisasm
62+
63+
### Changing the size of the default layout
64+
If two numbers are provided on the command line, the configuration will be initialized to a different size that corresponds to the given rows and columns.
65+
All other aspects will remain default; all nodes will be compute nodes, and the upper-left input and lower-right output willl remain.
66+
67+
Using these rows and columns arguments might look like this, for two rows and 5 columns:
68+
$ tis code.tisasm 2 5
69+
70+
### Defining a custom layout
71+
For maximum control, use a layout file or string. A file is recommended, but the layout may be provided as a quoted string instead with the -l flag.
72+
73+
The first three elements of a layout file are required, and all elements in a layout file are separated by whitespace (space, tab, newline), with one exception that will be covered below.
74+
75+
The first two elements are the rows and columns of the layout. The third element is a map describing what type each node is.
76+
A total of rows*columns characters are then read, skipping whitespace (this is the exception to the whitespace-delimiter rule). The valid node types are (case insensitive):
77+
- C - compute node; T21 Basic Execution Node
78+
- M or S - stack node; T30 Stack Memory Node (Not yet implemented)
79+
- R - ram node; T31 Random Access Memory Node (Not yet implemented)
80+
- D - damaged/disabled node; (Not yet implemented)
81+
82+
A sample layout may be:
83+
5 4
84+
CCSD
85+
CCCC
86+
CCCC
87+
CCCC
88+
CSCC
89+
Which is 5 rows and 4 columns with two stack nodes and one disabled node listed.
90+
91+
Following the layout details are (optional) definitions of the various input and output pseudo-nodes. Input nodes start with the token I<n> where n is the index. I0 is the first column, I3 is the fourth, etc.
92+
Output pseudo-nodes are similar, but start with the token O<n> instead. If one of these tokens is encountered, it is assumed that the previous definition is complete. These definitions need not be in any specific order,
93+
and if there are conflicts, the later definitions will override the earlier ones. Be warned: this may leak memory or file handles if not used with care.
94+
95+
A sample layout, including some input/output definitions may look like this:
96+
2 3
97+
CCS
98+
CCC
99+
I0 NUMERIC numbers.txt
100+
O0 NUMERIC - 32
101+
O2 ASCII -
102+
103+
The details for each type of input and output pseudo node are described below, in the TIS Input/Output section.
104+
105+
Assuming that the file 'layout.tiscfg' contains the above configuration, the command line might look something like this:
106+
$ tis code.tisasm layout.tiscfg
107+
108+
Since whitespace is the delimiter, this layout produces the same result:
109+
2 3 CCSCCC I0 NUMERIC numbers.txt O0 NUMERIC - 32 O2 ASCII -
110+
111+
It is less readable, but more suitable for using as a string on the command line with the -l flag. Note the quotes around the layout string.
112+
$ tis code.tisasm -l "2 3 CCSCCC I0 NUMERIC numbers.txt O0 NUMERIC - 32 O2 ASCII -"
113+
114+
## TIS Input/Output
115+
116+
(describe the various options for IO, both original and new)

tis.c

+9-9
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,13 @@ int init_layout(tis_t* tis, char* layoutfile, int layoutmode) {
172172
if(strcasecmp(buf, "STDIN") == 0 ||
173173
strcasecmp(buf, "-") == 0) {
174174
debug("Set I%zu to use stdin\n", index);
175-
tis->inputs[index]->file = stdin; // TODO make sure this doesn't already have a file
175+
tis->inputs[index]->file.file = stdin; // TODO make sure this doesn't already have a file
176176
} else {
177177
debug("Set I%zu to use file %.*s\n", index, BUFSIZE, buf);
178-
if((tis->inputs[index]->file = fopen(buf, "r")) == NULL) { // TODO register file for later close?
178+
if((tis->inputs[index]->file.file = fopen(buf, "r")) == NULL) {
179179
error("Unable to open %.*s for reading\n", BUFSIZE, buf); // TODO what to do about this? error out?
180180
}
181-
register_file_handle(tis->inputs[index]->file);
181+
register_file_handle(tis->inputs[index]->file.file);
182182
}
183183
} else {
184184
// TODO node type not implemented? internal error?
@@ -192,7 +192,7 @@ int init_layout(tis_t* tis, char* layoutfile, int layoutmode) {
192192
} else if(strcasecmp(buf, "NUMERIC") == 0) {
193193
debug("Set O%zu to NUMERIC mode\n", index);
194194
tis->outputs[index]->type = TIS_IO_TYPE_IOSTREAM_NUMERIC;
195-
tis->outputs[index]->sep = -1;
195+
tis->outputs[index]->file.sep = -1;
196196
} else {
197197
goto skip_io_token;
198198
}
@@ -201,19 +201,19 @@ int init_layout(tis_t* tis, char* layoutfile, int layoutmode) {
201201
if(strcasecmp(buf, "STDOUT") == 0 ||
202202
strcasecmp(buf, "-") == 0) {
203203
debug("Set O%zu to use stdout\n", index);
204-
tis->outputs[index]->file = stdout; // TODO make sure this doesn't already have a file
204+
tis->outputs[index]->file.file = stdout; // TODO make sure this doesn't already have a file
205205
} else if(strcasecmp(buf, "STDERR") == 0) {
206206
debug("Set O%zu to use stderr\n", index);
207-
tis->outputs[index]->file = stderr;
207+
tis->outputs[index]->file.file = stderr;
208208
} else if(tis->outputs[index]->type == TIS_IO_TYPE_IOSTREAM_NUMERIC &&
209-
sscanf(buf, "%d", &(tis->outputs[index]->sep)) == 1) {
209+
sscanf(buf, "%d", &(tis->outputs[index]->file.sep)) == 1) {
210210
// nothing to do
211211
} else {
212212
debug("Set O%zu to use file %.*s\n", index, BUFSIZE, buf);
213-
if((tis->outputs[index]->file = fopen(buf, "a")) == NULL) { // TODO register file for later close?
213+
if((tis->outputs[index]->file.file = fopen(buf, "a")) == NULL) {
214214
error("Unable to open %.*s for writing\n", BUFSIZE, buf); // TODO what to do about this? error out?
215215
}
216-
register_file_handle(tis->outputs[index]->file);
216+
register_file_handle(tis->outputs[index]->file.file);
217217
}
218218
} else {
219219
// TODO node type not implemented? internal error?

tis_io.c

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ tis_op_result_t input(tis_io_node_t* io, int* value) {
1010
int in = EOF;
1111
switch(io->type) {
1212
case TIS_IO_TYPE_IOSTREAM_ASCII:
13-
if((in = fgetc(io->file)) == EOF) {
13+
if((in = fgetc(io->file.file)) == EOF) {
1414
return TIS_OP_RESULT_READ_WAIT;
1515
}
1616
*value = clamp(in);
1717
break;
1818
case TIS_IO_TYPE_IOSTREAM_NUMERIC:
19-
if(fscanf(io->file, " %d ", &in) != 1) {
19+
if(fscanf(io->file.file, " %d ", &in) != 1) {
2020
return TIS_OP_RESULT_READ_WAIT;
2121
}
2222
*value = clamp(in);
@@ -41,17 +41,17 @@ tis_op_result_t output(tis_io_node_t* io, int value) {
4141
int out = EOF;
4242
switch(io->type) {
4343
case TIS_IO_TYPE_IOSTREAM_ASCII:
44-
if((out = fputc(value, io->file)) == EOF) {
44+
if((out = fputc(value, io->file.file)) == EOF) {
4545
//return TIS_OP_RESULT_WRITE_WAIT; // TODO what should I do here?
4646
}
4747
break;
4848
case TIS_IO_TYPE_IOSTREAM_NUMERIC:
49-
if(io->sep >= 0) {
50-
if(fprintf(io->file, "%d%c", value, io->sep) > 0) {
49+
if(io->file.sep >= 0) {
50+
if(fprintf(io->file.file, "%d%c", value, io->file.sep) > 0) {
5151
//return TIS_OP_RESULT_WRITE_WAIT; // TODO what should I do here?
5252
}
5353
} else {
54-
if(fprintf(io->file, "%d", value) > 0) {
54+
if(fprintf(io->file.file, "%d", value) > 0) {
5555
//return TIS_OP_RESULT_WRITE_WAIT; // TODO what should I do here?
5656
}
5757
}

tis_types.h

+22-9
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ typedef enum tis_io_type {
120120
TIS_IO_TYPE_IOSTREAM_ASCII,
121121
TIS_IO_TYPE_IOSTREAM_NUMERIC,
122122
TIS_IO_TYPE_OSTREAM_IMAGE,
123-
TIS_IO_TYPE_IGENERATOR_ALGEBRAIC,
124-
TIS_IO_TYPE_IGENERATOR_CONSTANT,
125-
TIS_IO_TYPE_IGENERATOR_CYCLIC,
126-
TIS_IO_TYPE_IGENERATOR_RANDOM,
127-
TIS_IO_TYPE_IGENERATOR_SEQUENCE,
123+
TIS_IO_TYPE_IGENERATOR_LIST, // echo given numbers once
124+
TIS_IO_TYPE_IGENERATOR_CYCLIC, // repeat given numbers forever
125+
TIS_IO_TYPE_IGENERATOR_RANDOM, // on the interval specified, or -999..999 by default
126+
TIS_IO_TYPE_IGENERATOR_ALGEBRAIC, // need scale, start value and increment (scale is not necessary here, but keep for consistency)
127+
TIS_IO_TYPE_IGENERATOR_GEOMETRIC, // need scale, start value and multiplier
128+
TIS_IO_TYPE_IGENERATOR_HARMONIC, // need scale, start value and increment (reciprocal of ALGEBRAIC)
129+
TIS_IO_TYPE_IGENERATOR_OEIS, // grab the b-file to a temp file, then read like NUMERIC? (make this compile-out-able if so)
128130
} tis_io_type_t;
129131

130132
/*
@@ -155,8 +157,10 @@ typedef struct tis_node {
155157
size_t row;
156158
size_t col;
157159
char* name; // optional (no equivalent in-game)
158-
tis_op_t* code[15]; // up to 15 lines of code (used by compute)
159-
int data[15]; // up to 15 cells for data (used by memory)
160+
union {
161+
tis_op_t* code[15]; // up to 15 lines of code (used by compute)
162+
int data[15]; // up to 15 cells for data (used by memory)
163+
};
160164
int acc; // (used by compute)
161165
int bak; // (used by compute)
162166
int writebuf; // (used by compute)
@@ -169,8 +173,17 @@ typedef struct tis_node {
169173
typedef struct tis_io_node {
170174
tis_io_type_t type;
171175
char* name; // optional
172-
FILE* file; // TODO union this for other type that don't use a FILE?
173-
int sep; // negative is none, otherwise cast to char
176+
union {
177+
struct {
178+
FILE* file;
179+
int sep; // negative is none, otherwise cast to char
180+
} file;
181+
struct {
182+
int current; // current is unscaled and (in the case of HARMONIC) unreciprocated
183+
int scale; // scaling before casting to int and clamping
184+
int arg; // either increment or multiplier
185+
} seq;
186+
};
174187
} tis_io_node_t;
175188

176189
typedef struct tis {

0 commit comments

Comments
 (0)