+#!/usr/bin/perl
+use v5.14;
+use warnings;
+use lib '.';
+
+use asm;
+
+sub update_leds {
+ my ($x) = @_;
+ route $x + 0, $x + 1, 4;
+ route $x + 1, $x + 2, 3;
+ route $x + 2, $x + 3, 2;
+ route $x + 3, $x + 4, 1;
+}
+
+sub xori {
+ my ($addrA, $imm, $addrC) = @_;
+ storei 42, $imm;
+ xor_ $addrA, 42, $addrC;
+}
+
+sub andi {
+ my ($addrA, $imm, $addrC) = @_;
+ storei 42, $imm;
+ and_ $addrA, 42, $addrC;
+}
+
+# we want everyone to have
+#
+# own quadrant at mem[0]
+# east quadrant at mem[11]
+# south quadrant at mem[12]
+# SE quadrant at mem[13]
+
+my $q_east = 11;
+my $q_south = 12;
+my $q_SE = 13;
+
+chip_select 4, sub { storei 0, 0x247 }; # glider
+chip_select $_, sub { ledi $_ } for 1, 2, 4, 8;
+
+
+update_leds 0;
+
+chip_select 1, sub { route 0, 11 };
+chip_select 2, sub { route 11, 12 };
+chip_select 4, sub { route 12, 13 };
+
+chip_select 2, sub { route 0, 13 };
+chip_select 4, sub { route 13, 12 };
+chip_select 8, sub { route 12, 11 };
+
+chip_select 4, sub { route 0, 11 };
+chip_select 8, sub { route 11, 12 };
+chip_select 1, sub { route 12, 13 };
+
+chip_select 8, sub { route 0, 13 };
+chip_select 1, sub { route 13, 12 };
+chip_select 2, sub { route 12, 11 };
+
+
+sub news_to_mem {
+ my ($dir, $mem) = @_;
+ alu3 alu_zero, alu_select_f, 0, 0, $mem, flag_news($dir), flag_zero
+}
+
+my $and_AB = alu_of_function { $a & $b };
+
+sub compute_neighbour {
+ my ($dest, $dir, $mask, $other) = @_;
+ storei $dest, $mask;
+ alu3 $and_AB, $and_AB, 0, $dest, $dest, flag_zero, flag_zero;
+ news_to_mem $dir, $dest;
+
+ storei $dest + 1, 0xFFFF - $mask;
+ alu3 $and_AB, $and_AB, $other, $dest + 1, $dest + 1, flag_zero, flag_zero;
+ alu3 alu_zero, alus_addAF, $dest, 0, $dest, flag_news($dir), flag_zero
+}
+
+# N neighbour
+compute_neighbour 1, 0, 0x0FFF, $q_south;
+# E neighbour
+compute_neighbour 2, 1, 0xEEEE, $q_east;
+# S neighbour
+compute_neighbour 3, 2, 0xFFF0, $q_south;
+# W neighbour
+compute_neighbour 4, 3, 0x7777, $q_east;
+
+sub compute_diagonal_neighbour {
+ my ($dest, $dir, @masks) = @_;
+ die 'expected exactly 4 @masks' unless @masks == 4;
+ storei $dest, $masks[0];
+ alu3 $and_AB, alu_select_a, 0, $dest, 0, flag_zero, flag_zero;
+ news_to_mem $dir, $dest;
+
+ for my $quad (1 .. 3) {
+ storei $dest + 1, $masks[$quad];
+ alu3 $and_AB, alu_select_a, 10 + $quad, $dest + 1, 10 + $quad, flag_zero, flag_zero;
+ alu3 alu_zero, alus_addAF, $dest, 0, $dest, flag_news($dir), flag_zero;
+ }
+}
+
+#update_leds $q_SE;
+
+# NW neighbour
+compute_diagonal_neighbour 5, 4, 0x0777, 0x0888, 0x7000, 0x8000;
+#update_leds 5;
+# NE neighbour
+compute_diagonal_neighbour 6, 5, 0x0EEE, 0x0111, 0xE000, 0x1000;
+#update_leds 6;
+# SE neighbour
+compute_diagonal_neighbour 7, 6, 0xEEE0, 0x1110, 0x000E, 0x0001;
+#update_leds 7;
+# SW neighbour (DODGY)
+compute_diagonal_neighbour 8, 7, 0x7770, 0x8880, 0x0007, 0x0008;
+#update_leds 8;
+
+sub flag_to_memory {
+ my ($address, $flag) = @_;
+ alu3 alu_zero, alu_select_f, 0, 0, $address, $flag, flag_zero;
+}
+
+# now we add bits mem[1] .. mem[8]
+# result should be mem[1] .. mem[3] (we don't distinguish 8 from 0)
+my $F = 1;
+
+# add 1 and 2, 3 and 4, 5 and 6, 7 and 8
+for my $x (1, 3, 5, 7) {
+ add $x, $x + 1, $x, $F;
+ flag_to_memory $x + 1, $F;
+}
+
+# add 12 and 34, 56 and 78
+for my $x (1, 5) {
+ add $x, $x + 2, $x, $F;
+ addC $x + 1, $x + 3, $x + 1, $F;
+ flag_to_memory $x + 2, $F;
+}
+
+# add 12 and 56
+add 1, 5, 1, $F;
+addC 2, 6, 2, $F;
+# F = F | 3 | 7
+alu3 alu_or, alu_select_a, 3, 7, 3, $F, $F;
+
+# should a new cell be born here? (do we have 3 neighbours?)
+sub alu_birth { alu_of_function {; $a && $b && !$_ } }
+
+# should a living cell survive here? (do we have 2 or 3 neighbours?)
+sub alu_survival { alu_of_function {; $a && !$_ } }
+
+# compute the state of this cell at the next tick
+sub alu_step { alu_of_function {; $_ & ($a | $b) } }
+
+# read from memory 1, and with memory 2 and not F, write into memory 1 (= birth-p)
+alu3 alu_zero, alu_birth, 1, 2, 1, $F, flag_zero;
+# read from memory 2, and with flag not F, write into flag F (= survive-p)
+alu3 alu_survival, alu_select_a, 2, 0, 2, $F, $F;
+# read from memory 0, memory 1, and flag F, write F and (mem0 or mem1) into memory 0
+alu3 alu_zero, alu_step, 0, 1, 0, $F, $F; # also zeroes out flag F