APGsembly: Difference between revisions

From LifeWiki
Jump to navigation Jump to search
m mention * and ZZ syntactic shortcuts.
Add description for MUL action
 
(23 intermediate revisions by 9 users not shown)
Line 1: Line 1:
The '''general purpose calculator''' ('''GPC''') is a way to implement computational tasks inside Conway's Game of Life. It's Turing complete and programmed by a special-purpose programming language called '''APGsembly'''. The first computational Life patterns using this technology were constructed by [[Adam P. Goucher]] in 2010. These [[pi calculator]] and [[phi calculator]] patterns, and a related pattern programmed to grow at [[Osqrtlogt|O(sqrt(log(t)))]], had all the same basic general-purpose components that make up the GPC.
The '''general purpose calculator''' ('''GPC''') is a way to implement computational tasks inside Conway's Game of Life. It is [[Turing complete]] and programmed by a special-purpose programming language called '''APGsembly'''. The first computational Life patterns using this technology were constructed by [[Adam P. Goucher]] in 2009 and 2010. The [[Spartan universal computer-constructor]], [[pi calculator]] and [[phi calculator]] patterns, and a related pattern programmed to grow at [[Osqrtlogt|O(sqrt(log(t)))]], had all the same basic general-purpose components that make up the GPC.


Goucher's original compiler/editor was written in DarkBasic, which became difficult to run on modern computers. In 2019 a Golly Python-script compiler was written by [[Dave Greene]] to create a Conway's Life pattern corresponding to APGsembly code, and a visual emulator and debugger was written by [[Michael Simkin]] and Dave Greene. At that time the logic circuitry was adjusted to include a standard set of components, and the resulting "GPC" was shown to be capable of supporting any of the three programs (pi, phi, or Osqrtlogt).
Goucher's original compiler/editor was written in DarkBasic, which became difficult to run on modern computers. In 2019 a Golly Python-script compiler was written by [[Dave Greene]] to create a Conway's Life pattern corresponding to APGsembly code, and a visual emulator and debugger was written by [[Michael Simkin]] and Dave Greene. At that time the logic circuitry was adjusted to include a standard set of components, and the resulting "GPC" was shown to be capable of supporting any of the three programs (pi, phi, or Osqrtlogt). In 2024, branoc created a version of the compiler and emulator based on the updated specifications from ''[[Conway's Game of Life: Mathematics and Construction]]'' textbook.<ref name="post199807" />


The base GPC pattern includes just the logic and memory circuitry; an additional subpattern representing an APGsembly program should be attached to a GPC to produce a complete calculator, printer, or other functional pattern.  The additional subpattern is basically a finite-state machine.  Each line in an APGsembly program corresponds to a state, and there are JUMP instructions for each possible return value (Z or NZ), pointing to the next state that the machine should enter. Each line of code can trigger one or more commands, or "actions", and it's important to make sure that each line includes exactly one action that returns a zero/nonzero value.  
The base GPC pattern includes just the logic and memory circuitry; an additional subpattern representing an APGsembly program should be attached to a GPC to produce a complete calculator, printer, or other functional pattern.  The additional subpattern is basically a finite-state machine.  Each line in an APGsembly program corresponds to a state, and there are JUMP instructions for each possible return value (Z or NZ), pointing to the next state that the machine should enter. Each line of code can trigger one or more commands, or "actions", and it's important to make sure that each line includes exactly one action that returns a zero/nonzero value.  
Line 7: Line 7:
== Overview ==
== Overview ==


The GPC includes a ticking clock in the form of two synchronized glider guns of period 2^20, 2^22 or higher, depending on what components are being used (see '''SQ''' below). These guns emit a glider each time an operation has been executed with a return value of either Z or NZ, represented by a glider emitted on one of two possible output [[lane]]s, as a result of the most recent output-producing action. These actions are triggered by an input glider emitted by the calculator program pattern, sent into special computational units on specific lanes to trigger a specific action (ADD, SUB, WRITE, READ, INC, etc.) defined by the APGsembly code.  
The GPC includes a ticking clock in the form of two synchronized glider guns of period 2^17, 2^18 or higher, depending on the size of the program. These guns emit a glider each time an action has been executed with a return value of either Z or NZ, represented by a glider emitted on one of two possible output [[lane]]s, as a result of the most recent output-producing action. These actions are triggered by an input glider emitted by the calculator program pattern, sent into special computational units on specific lanes to trigger a specific action (ADD, SUB, WRITE, READ, INC, etc.) defined by the APGsembly code.  


Thus the GPC has four major physical components:  
Thus the GPC has four major physical components:  
Line 14: Line 14:
# Program code.
# Program code.
# Computational unit array.
# Computational unit array.
# Printer and SQ
# Printer and B2D.


== The computational cycle ==
== The computational cycle ==
Line 25: Line 25:


APGsembly code consists of a list of actions to be performed for each ''state ID'', for each possible return value (Z or NZ). The program consists of lines of code; each state ID corresponds to two lines of code, one for Z and one for NZ.
APGsembly code consists of a list of actions to be performed for each ''state ID'', for each possible return value (Z or NZ). The program consists of lines of code; each state ID corresponds to two lines of code, one for Z and one for NZ.
#The program must start with INITIAL, and will halt if it ever reaches a state where none of the actions return a Z/NZ response.
#The program must start with INITIAL, and will halt if it ever executes HALT_OUT (or HALT) action.
#If the program is written according the usual rules (i.e. a single return signal per line), it will run indefinitely.
#Two or more actions with Z/NZ responses for a single state are technically illegal, and will likely (though not inevitably) cause chaotic explosions in the calculator pattern.
#Two or more actions with Z/NZ responses for a single state are technically illegal, and will likely (though not inevitably) cause chaotic explosions in the calculator pattern.


Each line of code consists of 4 parts, separated by semicolons and optional whitespace:
Each line of code consists of 4 parts, separated by semicolons and optional whitespace:


'''StateID; Z/NZ; NextStateID; Op1, Op2, Op3, etc.'''  
'''StateID; Z/NZ; NextStateID; Action1, Action2, Action3, etc.'''  


#'''StateID.''' Each line's first element is a state ID.  This can be thought of as a line number, except that pairs of successive lines usually share the same state ID.
#'''StateID.''' Each line's first element is a state ID.  This can be thought of as a line number, except that pairs of successive lines usually share the same state ID.
#'''Z/NZ.''' Each line's second element is either Z or NZ. Only the line matching the Z or NZ return value from the previous cycle will actually be executed.
#'''Z/NZ.''' Each line's second element is either Z or NZ. Only the line matching the Z or NZ return value from the previous cycle will actually be executed.
#'''NextStateID''' Each line's third element defines the state that will be executed on the next clock tick, if that line is executed.
#'''NextStateID.''' Each line's third element defines the state that will be executed on the next clock tick, if that line is executed.
#'''Op.''' Each line's fourth element defines a comma-separated list of operations, or "actions", to be executed. These actions are inputs to the computational units. Some actions will not return anything and only change the state of the computational unit; some will return either Z or NZ depending on the internal state of a particular unit. It is the programmer's task to make sure there is exactly one return value.
#'''Action.''' Each line's fourth element defines a comma-separated list of actions to be executed. These actions are inputs to the computational units. Some actions will not return anything and only change the state of the computational unit; some will return either Z or NZ depending on the internal state of a particular unit. It is the programmer's task to make sure there is exactly one return value.


'''Example:'''
'''Example:'''


#COMPONENTS B0
  INITIAL; Z; A2; NOP
  INITIAL; Z; A2; NOP
  INITIAL; NZ; A2; NOP
  INITIAL; NZ; A2; NOP
  A1; Z; A2; INC T0
  A1; Z; A2; INC B0, NOP
  A1; NZ; A2; INC T0
  A1; NZ; A2; INC B0, NOP
  A2; Z; A1; READ T0, SET T0
  A2; Z; A1; SET B0, NOP
  A2; NZ; A1; READ T0, SET T0
  A2; NZ; A1; SET B0, NOP


This loads the T0 register with an increasing number of '1' bits. Notice that each state ID '''INITIAL''', '''A1''', and '''A2''' appears twice in the program, once for each possible Z return and once for each NZ return.
This loads the B0 register with an increasing number of '1' bits. Notice that each state ID '''INITIAL''', '''A1''', and '''A2''' appears twice in the program, once for each possible Z return and once for each NZ return.


A syntactic shortcut added in the new compiler is the use of "*" to mean "both Z and NZ", and "ZZ" to mean "only Z is possible" for a given state.
A syntactic shortcut added in the new compiler is the use of "*" to mean "both Z and NZ", and "ZZ" to mean "only Z is possible" for a given state. The above sample program could be written more compactly as
 
#COMPONENTS B0
INITIAL; ZZ; A2; NOP
A1; *; A2; INC B0, NOP
A2; *; A1; SET B0, NOP


== Computational units ==
== Computational units ==
Line 56: Line 61:


# ''NOP'' - The NOP action sends a Z output directly, and takes no other action.
# ''NOP'' - The NOP action sends a Z output directly, and takes no other action.
# ''R'' - sliding block register (holds a single integer value, stored in unary)
# ''HALT'' - The HALT_OUT action halts the entire computation and emits a glider. The HALT action halts the entire computation without emitting a glider.
# ''T'' - binary string register (holds an arbitrary-length string of binary digits)
# ''U'' - sliding block register (holds a single integer value, stored in unary)
# ''B'' - binary string register (holds an arbitrary-length string of binary digits)
# ''ADD'' - adder
# ''ADD'' - adder
# ''SUB'' - subtractor
# ''SUB'' - subtractor
# ''MUL'' - ... we're not sure how to use this, but it shows up in the pi and phi calculator code, apparently as a binary-to-decimal converter.
# ''MUL'' - multiplier
# ''SQ'' - a two-dimensional (''square'') array of data.  
# ''B2D'' - a two-dimensional binary register.  


The calculator can have an arbitrary number of R, T, ADD, SUB, and MUL units, a single SQ unit, and a single digit or character printer.
The calculator can have an arbitrary number of U, B, ADD, SUB, and MUL units, one B2D unit, and one digit printer or character printer.  Future versions of the compiler/emulator will support other components, such as an arbitrary number of fixed-width B2D units.


===R: Sliding block===
Before 2021, "U" was written as "R" (short for "register"), and "B2D" was written as "SQ" (short for "square"). The "T" (short for "tape") unit was an older version of the binary register, and differed in some ways from the B unit.<ref name="rename2021" />


* Sliding block registers in APGsembly code are denoted by keywords starting with the letter R -- for example, R0, R1, R2, etc.
=== U: Sliding block register ===
* A sliding block register has very simple logic. It represents a single number, and all you can do with it is INC (increase by 1) and TDEC (test and decrease by 1).<br />


'''R: Operations:'''
* Sliding block registers in APGsembly code are denoted by keywords starting with the letter U, short for "unary" -- for example, U0, U1, U2, etc.
* A sliding block register has very simple logic. It represents a single number, and all you can do with it is INC (increase by 1) and TDEC (test and decrease by 1).


* ''INC Rx'' will increase the register by 1. No return signal.
'''U Actions:'''
* ''TDEC Rx'' will decrease the register by 1. Returns NZ if the register > 0, otherwise Z.


===T: Binary string register===
* ''INC Ux'' will increase the register by 1. No return signal.
* ''TDEC Ux'' will decrease the register by 1. Returns NZ if the register > 0, otherwise Z.


* Binary string registers in APGsembly code are denoted by keywords starting with T, short for "tape" -- for example, T0, T1, T2, etc.
=== B: Binary string register ===
* A binary string register has a "reading head" that can be moved forward and backward along the tape with INC and DEC, just like a sliding block register.
* A binary string register can store arbitrarily-large binary strings. A program can only retrieve one bit at a time using READ command, followed by SET or RESET actions.  SET and RESET might be more clearly named "WRITE 1" and "WRITE 0".


'''T: Operations'''
* Binary string registers in APGsembly code are denoted by keywords starting with B, short for "binary" -- for example, B0, B1, B2, etc.
* A binary string register has a "reading head" that can be moved forward and backward along the tape with INC and TDEC, just like a sliding block register.
* A binary string register can store arbitrarily-large binary strings. A program can only retrieve one bit at a time using READ command. SET might be more clearly named "WRITE 1", but SET is the terms used in existing APGsembly.


* ''INC Tx'' increases the position of reading head. Returns Z unless the space is empty then it returns NZ.
'''B Actions'''
* ''DEC Tx'' decreases the position of reading head. Returns NZ unless at 0.
* ''READ Tx'' return the state of the binary string value located at reading head. Returns Z if the value is 0 and NZ if 1.
* ''SET Tx'' sets the state of the binary string to 1. Must be after READ operation which deletes the state. No return signal.
* ''RESET Tx'' setse state of the binary string to 0. Must be after READ operation which deletes the state. No return signal.


===ADD: adder===
* ''INC Bx'' increases the position of reading head. No return signal.
* ''TDEC Bx'' decreases the position of reading head. Returns NZ unless at 0.
* ''READ Bx'' returns the state of the binary string value located at reading head and then sets it equal to 0. Returns Z if the value is 0 and NZ if 1.
* ''SET Bx'' sets the state of the current binary bit to 1 (breaks if the cell is set to 1). No return signal.


One should think about the adder as having two bits A and B. Each of them has 0 or 1 option, and each of the options is an input to the adder. A input is just changing the state of the Adder unit, and B input returns the result.
=== ADD: adder ===


* ''A0'' is ignored case because there is nothing to change.
One should think about the adder as having a 2-bit number. An A input just changes the state of the adder unit, whereas a B input returns the result.
* ''A1'' is a valid input
 
* ''B0'', ''B1'' should come after A was initialized.
* ''A0'' is not implemented as an input, because there is nothing to change.
* The adder returns (Ax + By + Memory) % 2
* The adder stores Memory = (Ax + By + Prev Memory >= 2)
* The memory bit is always stored in the unit when existent.
* If you not sure about the state of the adder just send B0 and you can be sure the Memory is cleaned.


'''ADD Actions'''
'''ADD Actions'''
* ''ADD A1''
* ''ADD A1'' increments the number (modulo 4). No return signal.
* ''ADD B0''
 
* ''ADD B1''
{| class="wikitable"
! Current !! Next
|-
| 00 || 01
|-
| 01 || 10
|-
| 10 || 11
|-
| 11 || 00
|}
 
* ''ADD B0'' right-shifts the number. Returns Z if the value before the action is even and NZ if odd.
 
{| class="wikitable"
! Current !! Next !! Output
|-
| 00 || 00 || Z
|-
| 01 || 00 || NZ
|-
| 10 || 01 || Z
|-
| 11 || 01 || NZ
|}
 
* ''ADD B1'' increments and then right-shifts the number. Returns Z if the value before the action is odd and NZ if even.
 
{| class="wikitable"
! Current !! Next !! Output
|-
| 00 || 00 || NZ
|-
| 01 || 01 || Z
|-
| 10 || 01 || NZ
|-
| 11 || 00 || Z
|}


===SUB: subtractor===
=== SUB: subtractor ===


The subtractor works exactly as adder, just for subtracting two numbers bit by bit keeping in memory 0 or 1.  
The subtractor works same as the adder, but using 2-bit two's complement arithmetic.


'''SUB Actions'''
'''SUB Actions'''
* ''SUB A1''
* ''SUB A1'' increments the number (modulo 4). No return signal.
* ''SUB B0''
 
* ''SUB B1''
{| class="wikitable"
! Current !! Next
|-
| 00 || 01
|-
| 01 || 10
|-
| 10 || 11
|-
| 11 || 00
|}
 
* ''SUB B0'' right-shifts the number. Returns Z if the value before the action is even and NZ if odd.
 
{| class="wikitable"
! Current !! Next !! Output
|-
| 00 || 00 || Z
|-
| 01 || 00 || NZ
|-
| 10 || 11 || Z
|-
| 11 || 11 || NZ
|}
 
* ''SUB B1'' decrements and then right-shifts the number. Returns Z if the value before the action is odd and NZ if even.
 
{| class="wikitable"
! Current !! Next !! Output
|-
| 00 || 11 || NZ
|-
| 01 || 00 || Z
|-
| 10 || 00 || NZ
|-
| 11 || 11 || Z
|}


===MUL===
=== MUL ===


MUL keeps inside a register a number between 0 and 10. MUL 0 divides the number by 2, and MUL 1 divides it by 2 and adds 5. As mentioned we're not yet sure how exactly it is used, but it's involved with binary-to-decimal conversion in the pi and phi calculator programs.
MUL keeps a number between 0 and 10 in an internal register. MUL 0 divides the number by 2 (i.e., bit shifts it by 1 bit), and MUL 1 adds 10 and ''then'' divides the number by 2. The point of doing this is that performing MUL commands in succession can be used to multiply binary numbers one bit at a time, with the internal memory of the MUL component keeping track of future carry bits. This is used in the digit extraction step of the [[pi calculator|pi]] and [[phi calculator]] programs.


'''MUL Actions'''
'''MUL Actions'''
* ''MUL 0''
* ''MUL 0'' divides the number by 2. Returns Z if the value before the action is even and NZ if odd.
* ''MUL 1''
* ''MUL 1'' adds 10 and then divides the number by 2. Returns Z if the value before the action is even and NZ if odd.


===SQ: a two dimensional array ===
=== B2D: 2d binary register ===


* The SQ register is starting with SQ. For example INC SQ, DEC SQ, READ SQ etc. in APGsembly.
* B2D is two-dimensional binary register and is unbounded in positive X and Y directions.
* The SQ has two arms X and Y. Each arm can be moved with INC and DEC. For example INC SQX, DEC SQY.
* B2D can be used as memory array as well as 2d plotter.
* A program can retrieve a bit located at (X, Y) by READ command. Unlike T where you must follow with SET, RESET the SQ will erase the (X, Y) bit only if it was existing. It also has only SET SQ command.
* B2D register commands contain the string "B2D" in APGsembly code -- e.g., INC B2DX, TDEC B2DX, READ B2D, etc.
* This SQ unit's internal circuitrly operates fairly slowly. If an APGsembly program makes use of this component, a clock gun of at least period 2^22 is required.
* The B2D has two arms X and Y. Each arm can be moved with INC and TDEC -- e.g., INC B2DX, TDEC B2DY.
* A program can retrieve a bit located at (X, Y) via the READ B2D command. READ B2D will always set the (X, Y) bit back to 0 (empty space). This means there is no need for a RESET B2D command, so only SET B2D is implemented.


'''SQ Operations'''
'''B2D Actions'''
* ''INC SQX'' increases the position of the X arm. No return signal.
* ''INC B2DX'' increases the position of the X arm. No return signal.
* ''INC SQY'' increases the position of the Y arm. No return signal.
* ''INC B2DY'' increases the position of the Y arm. No return signal.
* ''DEC SQX'' decreases the position of X arm, or if it's at 0 already, keeps it there. Returns Z if already at 0, otherwise NZ.
* ''TDEC B2DX'' decreases the position of X arm, or if it's at 0 already, keeps it there. Returns Z if already at 0, otherwise NZ.
* ''DEC SQY'' decreases the position of Y arm, or if it's at 0 already, keeps it there. Returns Z if already at 0, otherwise NZ.
* ''TDEC B2DY'' decreases the position of Y arm, or if it's at 0 already, keeps it there. Returns Z if already at 0, otherwise NZ.
* ''READ SQ'' returns the state of the binary value located at (X, Y). Returns Z if the current state is 0 (empty), otherwise NZ.
* ''READ B2D'' returns the state of the binary value located at (X, Y) and then sets it equal to 0. Returns Z if the value is 0 (empty), otherwise NZ.
* ''SET SQ'' sets the state at (X, Y) to 1 (breaks if the cell is set to 1). No return signal.
* ''SET B2D'' sets the state at (X, Y) to 1 (breaks if the cell is set to 1). No return signal.


===Printer===
=== Digit printer ===


The printer can print "." and digits 0-9. No return signal. <br />
The ''digit printer'' can print "." and digits 0-9. No return signal.


The command is<br />
'''Digit printer actions'''
''OUTPUT x''
* ''OUTPUT x'', for x = ., 0, 1, 2, 3, 4, 5, 6, 7, 8, or 9.


== Further details ==  
== Further details ==  
* For further discussion and examples, or to ask questions, see [https://conwaylife.com/forums/viewtopic.php?f=2&t=4196 this conwaylife.com forum thread].
* [[Conway's Game of Life: Mathematics and Construction]], Chapter 9: Universal Computation
* [https://conwaylife.com/forums/viewtopic.php?p=221972#p221972 A compiler and emulator] written in Lua for use in Golly.
* [https://github.com/dvgrn/b3s23life/tree/main/calculator A compiler and debugger] for use in Golly. This is an old (1.0) version.
* [https://rei1024.github.io/apgsembly-emulator/ An emulator] that works in a web browser.


For further discussion and examples, or to ask questions, see [https://www.conwaylife.com/forums/viewtopic.php?f=2&t=4196&sid=3194b56e4730fe694ebecca64e511a69 this conwaylife.com forum thread].
== References ==
<references>
<ref name="post199807">{{LinkForumThread
|format    = ref
|p          = 199807
|title      = Re: Smaller Pi Calculator Challenge
|author    = branoc
|date      = December 14, 2024
|accessdate = June 29, 2025
}}</ref>
<ref name="rename2021">{{cite web
|url=https://github.com/rei1024/apgsembly-emulator/issues/1
|title=alternate identifier for R, T, and SQ units
|author=Dave Greene
|website=GitHub
|date=November 20, 2020
|accessdate = June 29, 2025
}}</ref>
</references>


[https://github.com/dvgrn/b3s23life/tree/master/calculator Here] you can download the compiler and debugger.
[[Category:Everything else]]
[[Category:Universal computation]]

Latest revision as of 13:45, 19 April 2026

The general purpose calculator (GPC) is a way to implement computational tasks inside Conway's Game of Life. It is Turing complete and programmed by a special-purpose programming language called APGsembly. The first computational Life patterns using this technology were constructed by Adam P. Goucher in 2009 and 2010. The Spartan universal computer-constructor, pi calculator and phi calculator patterns, and a related pattern programmed to grow at O(sqrt(log(t))), had all the same basic general-purpose components that make up the GPC.

Goucher's original compiler/editor was written in DarkBasic, which became difficult to run on modern computers. In 2019 a Golly Python-script compiler was written by Dave Greene to create a Conway's Life pattern corresponding to APGsembly code, and a visual emulator and debugger was written by Michael Simkin and Dave Greene. At that time the logic circuitry was adjusted to include a standard set of components, and the resulting "GPC" was shown to be capable of supporting any of the three programs (pi, phi, or Osqrtlogt). In 2024, branoc created a version of the compiler and emulator based on the updated specifications from Conway's Game of Life: Mathematics and Construction textbook.[1]

The base GPC pattern includes just the logic and memory circuitry; an additional subpattern representing an APGsembly program should be attached to a GPC to produce a complete calculator, printer, or other functional pattern. The additional subpattern is basically a finite-state machine. Each line in an APGsembly program corresponds to a state, and there are JUMP instructions for each possible return value (Z or NZ), pointing to the next state that the machine should enter. Each line of code can trigger one or more commands, or "actions", and it's important to make sure that each line includes exactly one action that returns a zero/nonzero value.

Overview

The GPC includes a ticking clock in the form of two synchronized glider guns of period 2^17, 2^18 or higher, depending on the size of the program. These guns emit a glider each time an action has been executed with a return value of either Z or NZ, represented by a glider emitted on one of two possible output lanes, as a result of the most recent output-producing action. These actions are triggered by an input glider emitted by the calculator program pattern, sent into special computational units on specific lanes to trigger a specific action (ADD, SUB, WRITE, READ, INC, etc.) defined by the APGsembly code.

Thus the GPC has four major physical components:

  1. Clock.
  2. Program code.
  3. Computational unit array.
  4. Printer and B2D.

The computational cycle

Every 2^N generations, a glider gun initiates an action based on the output from the previous cycle. The gun waits long enough that an output has most likely returned. If no output has in fact appeared (this can happen when retrieving data from a very long data tape, for example) the gun waits for another cycle and checks again, using a universal regulator mechanism.

  • The return signal is always a one-bit output value, either Z or NZ.
  • Each cycle is expected to return exactly one such output.

Program code

APGsembly code consists of a list of actions to be performed for each state ID, for each possible return value (Z or NZ). The program consists of lines of code; each state ID corresponds to two lines of code, one for Z and one for NZ.

  1. The program must start with INITIAL, and will halt if it ever executes HALT_OUT (or HALT) action.
  2. Two or more actions with Z/NZ responses for a single state are technically illegal, and will likely (though not inevitably) cause chaotic explosions in the calculator pattern.

Each line of code consists of 4 parts, separated by semicolons and optional whitespace:

StateID; Z/NZ; NextStateID; Action1, Action2, Action3, etc.

  1. StateID. Each line's first element is a state ID. This can be thought of as a line number, except that pairs of successive lines usually share the same state ID.
  2. Z/NZ. Each line's second element is either Z or NZ. Only the line matching the Z or NZ return value from the previous cycle will actually be executed.
  3. NextStateID. Each line's third element defines the state that will be executed on the next clock tick, if that line is executed.
  4. Action. Each line's fourth element defines a comma-separated list of actions to be executed. These actions are inputs to the computational units. Some actions will not return anything and only change the state of the computational unit; some will return either Z or NZ depending on the internal state of a particular unit. It is the programmer's task to make sure there is exactly one return value.

Example:

#COMPONENTS B0
INITIAL; Z; A2; NOP
INITIAL; NZ; A2; NOP
A1; Z; A2; INC B0, NOP
A1; NZ; A2; INC B0, NOP
A2; Z; A1; SET B0, NOP
A2; NZ; A1; SET B0, NOP

This loads the B0 register with an increasing number of '1' bits. Notice that each state ID INITIAL, A1, and A2 appears twice in the program, once for each possible Z return and once for each NZ return.

A syntactic shortcut added in the new compiler is the use of "*" to mean "both Z and NZ", and "ZZ" to mean "only Z is possible" for a given state. The above sample program could be written more compactly as

#COMPONENTS B0
INITIAL; ZZ; A2; NOP
A1; *; A2; INC B0, NOP
A2; *; A1; SET B0, NOP

Computational units

There are currently 6 types of logic units, though it is possible to add more. Each type of logic unit has one or more actions, triggered by glider inputs on specific lanes.

  1. NOP - The NOP action sends a Z output directly, and takes no other action.
  2. HALT - The HALT_OUT action halts the entire computation and emits a glider. The HALT action halts the entire computation without emitting a glider.
  3. U - sliding block register (holds a single integer value, stored in unary)
  4. B - binary string register (holds an arbitrary-length string of binary digits)
  5. ADD - adder
  6. SUB - subtractor
  7. MUL - multiplier
  8. B2D - a two-dimensional binary register.

The calculator can have an arbitrary number of U, B, ADD, SUB, and MUL units, one B2D unit, and one digit printer or character printer. Future versions of the compiler/emulator will support other components, such as an arbitrary number of fixed-width B2D units.

Before 2021, "U" was written as "R" (short for "register"), and "B2D" was written as "SQ" (short for "square"). The "T" (short for "tape") unit was an older version of the binary register, and differed in some ways from the B unit.[2]

U: Sliding block register

  • Sliding block registers in APGsembly code are denoted by keywords starting with the letter U, short for "unary" -- for example, U0, U1, U2, etc.
  • A sliding block register has very simple logic. It represents a single number, and all you can do with it is INC (increase by 1) and TDEC (test and decrease by 1).

U Actions:

  • INC Ux will increase the register by 1. No return signal.
  • TDEC Ux will decrease the register by 1. Returns NZ if the register > 0, otherwise Z.

B: Binary string register

  • Binary string registers in APGsembly code are denoted by keywords starting with B, short for "binary" -- for example, B0, B1, B2, etc.
  • A binary string register has a "reading head" that can be moved forward and backward along the tape with INC and TDEC, just like a sliding block register.
  • A binary string register can store arbitrarily-large binary strings. A program can only retrieve one bit at a time using READ command. SET might be more clearly named "WRITE 1", but SET is the terms used in existing APGsembly.

B Actions

  • INC Bx increases the position of reading head. No return signal.
  • TDEC Bx decreases the position of reading head. Returns NZ unless at 0.
  • READ Bx returns the state of the binary string value located at reading head and then sets it equal to 0. Returns Z if the value is 0 and NZ if 1.
  • SET Bx sets the state of the current binary bit to 1 (breaks if the cell is set to 1). No return signal.

ADD: adder

One should think about the adder as having a 2-bit number. An A input just changes the state of the adder unit, whereas a B input returns the result.

  • A0 is not implemented as an input, because there is nothing to change.

ADD Actions

  • ADD A1 increments the number (modulo 4). No return signal.
Current Next
00 01
01 10
10 11
11 00
  • ADD B0 right-shifts the number. Returns Z if the value before the action is even and NZ if odd.
Current Next Output
00 00 Z
01 00 NZ
10 01 Z
11 01 NZ
  • ADD B1 increments and then right-shifts the number. Returns Z if the value before the action is odd and NZ if even.
Current Next Output
00 00 NZ
01 01 Z
10 01 NZ
11 00 Z

SUB: subtractor

The subtractor works same as the adder, but using 2-bit two's complement arithmetic.

SUB Actions

  • SUB A1 increments the number (modulo 4). No return signal.
Current Next
00 01
01 10
10 11
11 00
  • SUB B0 right-shifts the number. Returns Z if the value before the action is even and NZ if odd.
Current Next Output
00 00 Z
01 00 NZ
10 11 Z
11 11 NZ
  • SUB B1 decrements and then right-shifts the number. Returns Z if the value before the action is odd and NZ if even.
Current Next Output
00 11 NZ
01 00 Z
10 00 NZ
11 11 Z

MUL

MUL keeps a number between 0 and 10 in an internal register. MUL 0 divides the number by 2 (i.e., bit shifts it by 1 bit), and MUL 1 adds 10 and then divides the number by 2. The point of doing this is that performing MUL commands in succession can be used to multiply binary numbers one bit at a time, with the internal memory of the MUL component keeping track of future carry bits. This is used in the digit extraction step of the pi and phi calculator programs.

MUL Actions

  • MUL 0 divides the number by 2. Returns Z if the value before the action is even and NZ if odd.
  • MUL 1 adds 10 and then divides the number by 2. Returns Z if the value before the action is even and NZ if odd.

B2D: 2d binary register

  • B2D is two-dimensional binary register and is unbounded in positive X and Y directions.
  • B2D can be used as memory array as well as 2d plotter.
  • B2D register commands contain the string "B2D" in APGsembly code -- e.g., INC B2DX, TDEC B2DX, READ B2D, etc.
  • The B2D has two arms X and Y. Each arm can be moved with INC and TDEC -- e.g., INC B2DX, TDEC B2DY.
  • A program can retrieve a bit located at (X, Y) via the READ B2D command. READ B2D will always set the (X, Y) bit back to 0 (empty space). This means there is no need for a RESET B2D command, so only SET B2D is implemented.

B2D Actions

  • INC B2DX increases the position of the X arm. No return signal.
  • INC B2DY increases the position of the Y arm. No return signal.
  • TDEC B2DX decreases the position of X arm, or if it's at 0 already, keeps it there. Returns Z if already at 0, otherwise NZ.
  • TDEC B2DY decreases the position of Y arm, or if it's at 0 already, keeps it there. Returns Z if already at 0, otherwise NZ.
  • READ B2D returns the state of the binary value located at (X, Y) and then sets it equal to 0. Returns Z if the value is 0 (empty), otherwise NZ.
  • SET B2D sets the state at (X, Y) to 1 (breaks if the cell is set to 1). No return signal.

Digit printer

The digit printer can print "." and digits 0-9. No return signal.

Digit printer actions

  • OUTPUT x, for x = ., 0, 1, 2, 3, 4, 5, 6, 7, 8, or 9.

Further details

References

  1. branoc (December 14, 2024). Re: Smaller Pi Calculator Challenge (discussion thread) at the ConwayLife.com forums
  2. Dave Greene (November 20, 2020). "alternate identifier for R, T, and SQ units". Retrieved on June 29, 2025.