Zahlen is a stack-based processor specification discovered by User:Orby during March of 2019 (and revisited during February of 2021). Zahlen is particularly suited toward solving numerical problems in a small amount of space. This page is a work in progress. Many operators are yet undocumented. Please contribute comments and ideas on the discussion page.
- 1 Introduction
- 2 Types
- 3 Opcodes
- 3.1 Unary operators
- 3.2 Trigonometric functions
- 3.3 Binary operators
- 3.4 Matrix manipulation
- 3.5 Subroutines
- 3.6 Ternary operator
- 3.7 Memory operators
- 3.8 Miscellaneous operators
- 4 Examples
- 5 See also
Zahlen is intended for use in code golf competitions involving numbers. Each Zahlen instruction is represented by an 8-bit code. This makes Zahlen instructions more compact, but also restricts the number of available instructions. The codes 0xA0 through 0xFF are reserved for future expansion.
Zahlen operates on two types: logicals and numericals. Logical values are matrices whose cells are 32-bit unsigned integers. Operations performed on logical values are performed bit-wise. Numerical values are matrices whose cells are IEEE 754 double precision floating point values. In this documentation, matrices are enclosed in brackets.
What follows is a comprehensive list of opcodes.
Each unary operator pops a value from the stack which we refer to here as A. The i,j-th cell of A is referred to as ai,j. The value pushed is dependent upon the type of A and the operator selected. Each operation is named after its effect on a numerical.
If A is not 1 x 1 or a1,1 is zero, then the result is undefined.
|Logical||[ 1, 2, ..., a1,1 ] for 1 x 1 matrix A|
|Numerical||[ 1, 2, ..., floor(a1,1) ] for 1 x 1 matrix A|
|Logical||[ ai,j + 1 ]|
|Numerical||[ ai,j + 1 ]|
|Logical||[ ai,j - 1 ]|
|Numerical||[ ai,j - 1 ]|
Alternate pneumonic: not
|Logical||[ not ai,j ]|
|Set||[ -ai,j ]|
|Logical||A as numerical (no precision lost)|
|Numerical||A as logical (floor(ai,j))|
|Logical||[ aj,i ]|
|Numerical||[ aj,i ]|
|Logical||[ sign(ai,j) ]|
|Numerical||[ sign(ai,j) ]|
These operators all push numerical results.
|Logical||[ ai,j << 1 ]|
|Numerical||[ sin(ai,j) ]|
|Logical||[ ai,j >> 1 ]|
|Numerical||[ cos(ai,j) ]|
All binary operators in Zahlen pop two arguments from the stack and push the result. The first argument on the stack is represented as A and its type is provided in the first column of each table. The second argument on the stack is represented as B and its type is provided in the first row of each table. The operation performed depends upon the types of those arguments. Each operation is named after its effect on two numericals. If A is 1 x 1 and B is m x n, then A is expanded to m x n and filled with a1,1.
Alternate pneumonic: xor
|Logical||[ ai,j xor bi,j ]||[ ai,j + bi,j ]|
|Numerical||[ ai,j + bi,j ]||[ ai,j + bi,j ]|
|Logical||[ ai,j xor bi,j ]||[ ai,j - bi,j ]|
|Numerical||[ ai,j - bi,j ]||[ ai,j - bi,j ]|
|Logical||[ (ai,1 and b1,j) xor (ai,2 and b2,j) ... ]||[ (ai,1 * b1,j) + (ai,2 * b2,j) ... ]|
|Numerical||[ (ai,1 * b1,j) + (ai,2 * b2,j) ... ]||[ (ai,1 * b1,j) + (ai,2 * b2,j) ... ]|
Alternate pneumonic: and
|Logical||[ ai,j and b1,j ]||[ ai,j * bi,j ]|
|Numerical||[ ai,j * bi,j ]||[ ai,j * bi,j ]|
Alternate pneumonic: or
|Logical||[ ai,j or bi,j ]||[ ai,j / bi,j ]|
|Numerical||[ ai,j / bi,j ]||[ ai,j / bi,j ]|
|Logical||[ ]||[ ai,j % bi,j ]|
|Numerical||[ ai,j % bi,j ]||[ ai,j % bi,j ]|
Alternate pneumonic: <<
The notation << denotes left shift.
|Logical||[ ai,j << bi,j ]||[ ai,jbi,j ]|
|Numerical||[ ai,jbi,j ]||[ ai,jbi,j ]|
Alternate pneumonic: >>
The notation >> denotes right shift.
|Logical||[ ai,j >> bi,j ]||[ logai,j(bi,j) ]|
|Numerical||[ logai,j(bi,j) ]||[ logai,j(bi,j) ]|
These operations are for decomposing and building matrices.
Notice this pushes two results: the first row of A and A with the first row removed.
|Logical||[ a1 ], [ a2, a3, ... ]|
|Numerical||[ a1 ], [ a2, a3, ... ]|
This is the opposite of car. It takes two matrices and concatenates them row-wise. A must have the same number of columns as B.
|Logical||[ A, B ]||[ A, B ]|
|Numerical||[ A, B ]||[ A, B ]|
TODO Think about passing a column vector of addresses to call for concurrent function calls.
Pop A from the stack. If A is 1 x 1, then call the subroutine at a1,1.
Return from subroutine.
Pops three arguments off the stack A, B, and C.
If A, B, and C are logical then the result is the logical [ (ai,j and bi,j) or (not ai,j and ci,j) ].
If A, B, or C are numerical, then the result is the numerical [ ai,j != 0 ? bi,j : ci,j ]
If A is 1 x 1, then write B sequentially starting at a1,1. Otherwise the dimensions of A and B must match.
|Logical||Write bi,j to address ai,j.|
|Numerical||Write bi,j to address ai,j.|
|Logical||Push matrix whose i,j-th cell is the memory contents of address ai,j.|
|Numerical||Push matrix whose i,j-th cell is the memory contents of address ai,j.|
The following operators manipulate the stack and control flow.
|0x99||swap||Swap top two arguments on the stack.|
|0x9A||drop||Pop the stack,|
|0x9B||dup||Duplicate the element on top of the stack.|
|0x9C||rot||Pop A. If A is n x 1, then rotate the first a1,1 elements of the stack, then rotate the first a2,1 elements of the stack, etc.|
|0x9D||jz||Pop A. Pop B. If a is 1 x 1 and B does not contain any non-zero values, then set IP to a1,1.|
|0x9F X||128, ..., 65535||Push 16-bit immediate value to stack as 1 x 1 logical.|
|b0xxxxxxx||0, 1, ..., 127||Push 7-bit immediate value xxxxxxx to the stack as 1 x 1 logical.|
Assuming display is 256 x 256 and mapped to the first 65,536 memory addresses in the normal way.
Reverse first n elements on stack
n count rot
256 ; [ 256 ] count dup ; [ 1, ..., 256 ], [ 1, ..., 256 ] logical column vectors trans mul ; [ x and y ] 0 st ; Write [ x and y ] to 0x00000000
Extract bits from logical
Suppose a 1 x 1 logical x is on the stack.
32 count sign swap cast pmul cast ; [ x, x, ..., x (32 times) ] 32 count dec cast 2 pow cast ; [ 1, 2, 4, 8, ..., 2^31 ], [ x, x, ..., x (32 times) ] and sign ; [ x, x, x, ..., x ]