We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
23
23 is an esoteric programming language designed by Philipp Winterberg. It has several different notations ("23.ixsy", "23.dezsy", and "23"); the default notation ("23") consists entirely of whitespace characters. The name is derived by reversing 32, which is the ASCII value of the space character.
Behavior
The program's state consists of its memory, one or more instruction pointers, and one additional pointer into the memory called index. The memory is an infinite (0-indexed) array of integers, storing both code and data. Instruction pointers can be created and destroyed; if there are no more instruction pointers remaining, the program halts.
Initialization
Each 23 notation is simply a way to initialize memory. 23.dezsy is the most direct, simply listing each value in memory separated by a comma. However, the other two notations, 23.ixsy and 23, initialize the memory by taking the length of each line, discarding the last one, then reversing it, and that becomes the initial state of memory; the only difference between 23.ixsy and 23 is that in 23.ixsy, the lines consist of lowercase x, whereas in 23, they consist of spaces. In all cases, values above 255 are clamped down to 255, and the rest of memory is initialized with all zeroes. (The clamping is only enforced during initialization, and only for values above 255, not below 0. Whether or not it was intended to have values outside of the 0-255 range during the program's runtime is unknown.)
The program always starts with one instruction pointer at address zero, and with index also initialized to address zero.
Opcodes
On each program step, each instruction pointer (oldest to newest) takes the value in memory at their current address (which here will be called ip), and executes a different operation depending on the value modulo 32:
| Opcode | Operation |
|---|---|
| 1 | Let n be the next value in memory (at address ip+1); the n values of memory starting from address ip+1 (not ip+2) are deleted (all succeeding values of memory are shifted back to fill the remaining space). ip increases by 2.
|
| 8 | Let n be the next value in memory (at address ip+1); index is increased by n. ip increases by 2.
|
| 10 | Let n and i be the next two values in memory (at address ip+1 and ip+2 respectively); the memory value at address i is decreased by n. ip increases by 3.
|
| 16 | The ASCII character corresponding to the next value in memory (at address ip+1) is outputted. ip increases by 2.
|
| 17 | Let n be the next value in memory (at address ip+1); the ASCII characters corresponding to the n succeeding values in memory (ip+2 to ip+n+1) are outputted. ip increases by n+2.
|
| 18 | Let i be the next value in memory (at address ip+1); the ASCII character corresponding to the memory value at address i is outputted. ip increases by 2.
|
| 19 | Let i and n be the next two values in memory (at address ip+1 and ip+2 respectively); the ASCII characters corresponding to the n memory values starting from address i are outputted. ip increases by 3.
|
| 20 | The ASCII character corresponding to the value in memory at address index is outputted. ip increases by 1.
|
| 21 | Let n be the next value in memory (at address ip+1); the ASCII characters corresponding to the n memory values starting from address index are outputted. ip increases by 2.
|
| 22 | The next value in memory (at address ip+1) is outputted in decimal. ip increases by 2.
|
| 23 | Let i be the next value in memory (at address ip+1); the memory value at address i is outputted in decimal. ip increases by 2.
|
| 27 | Let i and j be the next two values in memory (at address ip+1 and ip+2 respectively); if the memory value at address j is nonzero, ip is set to i, otherwise ip increases by 3.
|
| 30 | Let i be the next value in memory (at address ip+1); a new instruction pointer is created, initially pointing to address i. ip increases by 2.
|
| 31 | A new instruction pointer is created, with its address being given by the memory value at address index. ip increases by 1.
|
Undefined instructions are NOPs, although the original interpreter gives debug output showing the position and memory value encountered. Furthermore, if the instruction pointer encounters the exact memory value (not opcode) 0 10 times in a row, the instruction pointer is destroyed.
Examples
Hello, World!
The creator gives two different Hello, World! programs (given here in 23.dezsy for the sake of readability). One uses the 16 opcode:
16,72,16,101,16,108,16,108,16,111,16,32,16,87,16,111,16,114,16,108,16,100,16,33
While the other uses 17:
17,12,72,101,108,108,111,32,87,111,114,108,100,33
99 bottles of beer
The creator gives this 99 bottles program (which has been slightly modified here to turn carriage returns into newlines):
23,35,19,36,30,16,44,16,10,23,35,19,36,18,19,66,33, 10,1,35,23,35,19,36,30,17,3,46,10,10,27,0,35,1,255, 99,32,98,111,116,116,108,101,40,115,41,32,111,102, 32,98,101,101,114,32,111,110,32,116,104,101,32,119, 97,108,108,46,10,84,97,107,101,32,111,110,101,32, 100,111,119,110,44,32,112,97,115,115,32,105,116,32, 97,114,111,117,110,100,44,10
On each iteration, from 99 to 1, this program outputs:
{n} bottle(s) of beer on the wall,
{n} bottle(s) of beer.
Take one down, pass it around,
{n-1} bottle(s) of beer on the wall.
followed by two newlines.
Output 16 through 23
This program outputs the numbers 16 through 23 using the respective opcodes. This can be used to test that an implementation's output opcodes are working correctly.
16,49,16,54,16,10, 17,3,49,55,10, 18,1,18,49,18,5, 19,50,3, 8,18,20,8,17,20,8,1,20, 8,17,21,3, 22,22,48,10, 23,37, 0,0,0,0,0,0,0,0,0,0, 56,49,57,10,50,49,10
Other testing programs are given in the talk page.
See also
External resources
- Philipp Winterberg's 23 web page:
- English (from the Wayback Machine; retrieved on 17 July 2006)
- German (from the Wayback Machine; retrieved on 11 July 2006)