| ... | ... |
@@ -1,7 +1,9 @@ |
| 1 | 1 |
.POSIX: |
| 2 | 2 |
|
| 3 | 3 |
.PHONY: all |
| 4 |
-all: |
|
| 4 |
+all: um |
|
| 5 |
+ |
|
| 6 |
+.PHONY: test |
|
| 5 | 7 |
|
| 6 | 8 |
# Remove `.gitignore`d files interactively. |
| 7 | 9 |
.PHONY: clean |
| ... | ... |
@@ -34,3 +36,11 @@ sandmark-output.txt um.um: |
| 34 | 36 |
curl -sSL $(URL)/$@ > $@ |
| 35 | 37 |
um-spec.txt: |
| 36 | 38 |
curl -sSL $(URL)/$@ | awk 'sub(/ *$$/, "") || 1;' > $@ |
| 39 |
+ |
|
| 40 |
+um: um.cpp |
|
| 41 |
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) um.cpp $(LDLIBS) -o $@ |
|
| 42 |
+ |
|
| 43 |
+test: um sandmark.umz sandmark-output.txt |
|
| 44 |
+ { time ./um sandmark.umz; } \
|
|
| 45 |
+ | tee /dev/tty \ |
|
| 46 |
+ | diff -q sandmark-output.txt - |
| ... | ... |
@@ -38,6 +38,31 @@ These files are all committed but also added to `.gitignore` and removed by |
| 38 | 38 |
[`sandmark-output.txt`]: sandmark-output.txt |
| 39 | 39 |
[`um-spec.txt`]: um-spec.txt |
| 40 | 40 |
|
| 41 |
+### Mostly Vetted Platterer (MVP) |
|
| 42 |
+ |
|
| 43 |
+I have completed the first version of the implementation, see [`um.cpp`][]. |
|
| 44 |
+ |
|
| 45 |
+It is as pure as I can make it. If someone wants a concise, readable, |
|
| 46 |
+programmatic description of the spec this is the version they should look at |
|
| 47 |
+(though reading the spec "blind" is a pleasure). |
|
| 48 |
+ |
|
| 49 |
+The `um()` function is 45 self-contained lines. 65 lines total in the file, the |
|
| 50 |
+difference being `#include`s and a 11 line `main` to deal with the ugliness of |
|
| 51 |
+the outside world. Note that guardrails are only present in `main`, if the |
|
| 52 |
+scroll itself has bugs there might be [UB][] galore (which is in spec). |
|
| 53 |
+ |
|
| 54 |
+I have added a `um` [Makefile][] target, which is a dependency of the default |
|
| 55 |
+`all` target: |
|
| 56 |
+ |
|
| 57 |
+```sh |
|
| 58 |
+make um |
|
| 59 |
+# or simply |
|
| 60 |
+make |
|
| 61 |
+``` |
|
| 62 |
+ |
|
| 63 |
+[`um.cpp`]: um.cpp |
|
| 64 |
+[UB]: https://en.wikipedia.org/wiki/Undefined_behavior |
|
| 65 |
+ |
|
| 41 | 66 |
## License |
| 42 | 67 |
|
| 43 | 68 |
Licensed under the [ISC License][] unless otherwise noted, see the |
| 44 | 69 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,65 @@ |
| 1 |
+#include <array> |
|
| 2 |
+#include <cstdint> |
|
| 3 |
+#include <cstdio> |
|
| 4 |
+#include <cstdlib> |
|
| 5 |
+#include <fstream> |
|
| 6 |
+#include <iostream> |
|
| 7 |
+#include <vector> |
|
| 8 |
+ |
|
| 9 |
+void um(std::istream & scroll, std::istream & i, std::ostream & o) |
|
| 10 |
+{
|
|
| 11 |
+ std::unitbuf(o); |
|
| 12 |
+ using Chr = std::uint8_t; |
|
| 13 |
+ using Plt = std::uint32_t; |
|
| 14 |
+ auto reg = std::array<Plt, 8>(); |
|
| 15 |
+ auto arr = std::vector<std::vector<Plt>>(1); |
|
| 16 |
+ auto fng = Plt(0); |
|
| 17 |
+ auto hlt = false; |
|
| 18 |
+ auto alloc = [&](Plt c) { arr.emplace_back(c); return(Plt)arr.size()-1; };
|
|
| 19 |
+ auto aband = [&](Plt c) { arr[c] = decltype(arr)::value_type(); };
|
|
| 20 |
+ #define PACK(IND) ((Plt)(Chr)ch[IND] << 8*(3-IND)) |
|
| 21 |
+ for (char ch[4]; scroll.read(ch, 4);) |
|
| 22 |
+ arr[0].push_back(PACK(0) | PACK(1) | PACK(2) | PACK(3)); |
|
| 23 |
+ #undef PACK |
|
| 24 |
+ while (!hlt) |
|
| 25 |
+ {
|
|
| 26 |
+ #define UNPACK(POS, SIZ) ((plt >> POS) & ((Plt(1) << SIZ) - 1)) |
|
| 27 |
+ auto plt = arr[0][fng++]; |
|
| 28 |
+ auto num = UNPACK(28, 4); |
|
| 29 |
+ auto val = UNPACK( 0, 25); |
|
| 30 |
+ auto & a = reg[UNPACK( 6, 3)]; |
|
| 31 |
+ auto & b = reg[UNPACK( 3, 3)]; |
|
| 32 |
+ auto & c = reg[UNPACK( 0, 3)]; |
|
| 33 |
+ auto & d = reg[UNPACK(25, 3)]; |
|
| 34 |
+ #undef UNPACK |
|
| 35 |
+ switch (num) |
|
| 36 |
+ {
|
|
| 37 |
+ case 0: a = c ? b : a; break; // CMOVE |
|
| 38 |
+ case 1: a = arr[b][c]; break; // LOAD |
|
| 39 |
+ case 2: arr[a][b] = c; break; // STOR |
|
| 40 |
+ case 3: a = b + c; break; // ADD |
|
| 41 |
+ case 4: a = b * c; break; // MUL |
|
| 42 |
+ case 5: a = b / c; break; // DIV |
|
| 43 |
+ case 6: a = ~(b & c); break; // NAND |
|
| 44 |
+ case 7: hlt = true; break; // HALT |
|
| 45 |
+ case 8: b = alloc(c); break; // ALLOC |
|
| 46 |
+ case 9: aband(c); break; // ABAND |
|
| 47 |
+ case 10: o.put(c); break; // PUT |
|
| 48 |
+ case 11: c = i.get(); break; // GET |
|
| 49 |
+ case 12: arr[0] = arr[b]; fng = c; break; // EXEC |
|
| 50 |
+ case 13: d = val; break; // VALUE |
|
| 51 |
+ } |
|
| 52 |
+ } |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+int main(int argc, char * argv[]) |
|
| 56 |
+{
|
|
| 57 |
+ if (argc != 2) |
|
| 58 |
+ return std::fputs("Usage: um <scroll>\n", stderr), EXIT_FAILURE;
|
|
| 59 |
+ auto scroll = std::ifstream(argv[1], std::ios::binary); |
|
| 60 |
+ if (scroll.fail()) |
|
| 61 |
+ return std::perror("Failed to read scroll"), EXIT_FAILURE;
|
|
| 62 |
+ if (scroll.peek(), scroll.eof()) |
|
| 63 |
+ return std::fputs("Empty scroll\n", stderr), EXIT_FAILURE;
|
|
| 64 |
+ um(scroll, std::cin, std::cout); |
|
| 65 |
+} |