Byte-based Instruction Jumping
Byte-based Instruction Jumping (BIJ, pronounced Bee-Jay) is a self-modifying esoteric programming language created by me (User:Peter). It was created during the end of 2021 and the beginning of 2022, and is inspired by 2 Bits, 1 Byte. Pretty much any string of text is a valid BIJ program, as long as it doesn't include some really unusual symbols.
Language Overview
BIJ programs consist of an array of bytes. A pointer (called the program pointer) moves around in this array and performs different instructions depending on which bits in the byte it's pointing to are toggled and which aren't. These instructions includes things like moving the pointer around, storing a byte in the accumulator (an 8-bit register that the program pointer "remembers") and printing things onto the console.
The array of bytes is no longer than the length of the program that is written. If the program pointer moves to an index lower than 0 in the array, the program returns 0. If it moves to an index higher than the length of the array, the program returns 1.
The program pointer executes one byte at a time, and doesn't stop executing a byte before it has went through all the bits in it. This means that if the program pointer is told to move away from the byte it's executing, it will keep executing the byte anyways. When it's done, it'll start executing the byte it ended up pointing to after executing the last byte. The bytes can be represented in 3 different ways:
1. Characters: BIJ uses a special ascii table where values like NULL and DEL are replaced with actual writeable characters.
ø%{
2. Hexadecimal numbers: Uses 2-digit lowercase hexadecimal numbers separated by spaces.
07 25 7b
3. Lists of instructions: Each line is a list of 8 instructions. Which instructions you write depends on which bits are toggled.
mvr ... ... red ... spc neq mvl mvr ... jml red ... spc ... mvl mvr jmr jml wrt cns ... neq mvl
Instructions
Here's a list of instructions. The first element of the list (mvr
/mvl
) is the left-most bit in the byte that is currently being executed. The second element is the second bit from the left, the third element the third bit, et cetera. The instruction before the slash is the instruction you write if the bit is not toggled, and the instruction after the slash is what you write if the bit is toggled.
The commands are executed in chronological order.
Index of bit | Name of Instruction | Meaning of name | Description |
1 | mvr /mvl
|
MoVe Right / MoVe Left | If toggled, the program pointer moves 1 byte to the left. If not toggled, the program pointer moves 1 byte to the right. |
2 | ... /jmr
|
nothing / JuMp Right | If toggled, the program pointer will move to the next byte in the program array that is equal to the current byte. If the program pointer is pointing to, for example, an 'A', it will move to the next byte that stores the character 'A'. If it doesn't find a byte equal to the current one by moving to the right, the program exits with code 1. If not toggled, nothing happens. |
3 | ... /jml
|
nothing / JuMp Left | Just like the second instruction (jmr ) except the program pointer moves to the previous byte equal to the current one and returns 0 if it can't find one.
|
4 | red /wrt
|
REaD / WRiTe | If not toggled, the program pointer will store the byte it's pointing to in the accumulator (a special byte that the program pointer remembers without needing to point to it). If toggled, the program pointer copies the accumulator to the current byte. The accumulator is initialized to 0 at the beginning, so wrt will set the current byte to 0 if red hasn't been used yet.
|
5 | ... /cns
|
nothing / CoNSole | If toggled, the program pointer will read or write (see instruction 4) to the console instead of the accumulator. If used after wrt , the program pointer will print out the byte it's pointing to. If used after red , it will copy the user input to the byte it's pointing to. If this command is not toggled, the program pointer will read or write the way it's described for bit #4.
|
6 | ... /spc
|
nothing / SPeCial | If this command is toggled, red /wrt and ... /cns will do completely different things than they usually would. If toggled after red ... , red won't be executed, and the last mvr /mvl won't be executed either. If toggled after wrt ... , it will copy the value of ~(accumulator & the byte that's being pointed to) to the byte that is being pointed to. red cns spc will simply do nothing, and the other commands will be executed normally. If spc is toggled after wrt cns , the program pointer will bitshift the byte that is being pointed to once to the left or right, depending on the last move command (mvr makes the byte bitshift to the right, and mvl to the left).
|
7 | ... /neq
|
nothing / Not EQual | If this command is toggled and the accumulator is not equal to the byte that is being pointed to, the last move command will execute once more than usually. |
8 | mvr /mvl
|
MoVe Right / MoVe Left | Same as instruction #1 |
Example instruction
An example of an instruction would be
mvr ... ... wrt cns ... ... mvr
This command can also be represented by the character ↑ or the hexadecimal number 18.
Here's what the program pointer does when encountering this byte, step by step:
mvr
(bit is not not toggled): It moves one step to the right...
(bit is not toggled): It does nothing...
(bit is not toggled): It does nothingwrt
(bit is toggled): It writes ...cns
(bit is toggled): ... the current character to the console (prints it, in other words)...
(bit is not toggled): It does nothing...
(bit is not toggled): It does nothingmvr
(bit is not toggled): It moves one step to the right
Ascii Table
Knowing the ascii values of characters is never useful when writing programs due to the fact that you can use hexadecimal numbers or instructions instead. I will still leave a compact version of the ascii table here, just in case anyone would want to know what characters are and aren't used in BIJ.
‘☺☻♥♦♣♠øØ ♂♀∞♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙—√ⁿ²■’
Example programs
Hello World!
Here's the classic "Hello World!" program in BIJ, represented by characters:
↑H↑e↑l↑l↑o↑ ↑W↑o↑r↑l↑d↑!
Here's the same program, represented by hexadecimal numbers:
18 48 18 65 18 6c 18 6c 18 6f 18 20 18 57 18 6f 18 72 18 6c 18 64 18 21
And here it is again, represented by lists of instructions:
mvr ... ... wrt cns ... ... mvr mvr jmr ... red cns ... ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red ... spc ... mvl mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc neq mvl mvr ... ... wrt cns ... ... mvr mvr ... jml red ... ... ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr ... wrt ... spc neq mvl mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc neq mvl mvr ... ... wrt cns ... ... mvr mvr jmr jml wrt ... ... neq mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red ... spc ... mvr mvr ... ... wrt cns ... ... mvr mvr ... jml red ... ... ... mvl
Here's how it works:
- Move the program pointer to the right (
mvr
) - Print the character (
wrt cns
) - Move the program pointer to the right again (
mvr
) - Now the program pointer has either moved out of program bounds (in which case it exits), or it's pointing to the same instruction again, in which case it goes back to step 1 and prints a new character.
Cat
This is an infinite loop that constantly prints out whatever the user inputs.
Characters:
Ø‘Ö
Hexadecimal:
08 00 99
Instructions:
mvr ... ... red cns ... ... mvr mvr ... ... red ... ... ... mvr mvl ... ... wrt cns ... ... mvl
Here's what each byte does:
1. Ø
:
- Move the program pointer one step to the right (
mvr
) - Copy one character from user input to the byte the program pointer is pointing to (
red cns
) - Move the program pointer one step to the right again (
mvr
)
2. ‘
:
- This byte is simply a placeholder for user input. It can be initialized to anything, but having it be equal to 0 keeps things clean.
3 Ö
:
- Move the program pointer one step to the left (
mvl
). Now it's pointing to the byte where the input is stored. - Print the current byte (
wrt cns
). - Move the program pointer one step to the left again (
mvl
). Now it's back to the first byte (Ø
).
Infinite loop
This prints "Hello! " over and over again forever.
Characters:
♀♦↑H↑e↑l↑l↑o↑!↑ -♦
Hexadecimal:
0c 04 18 48 18 65 18 6c 18 6c 18 6f 18 21 18 20 2d 04
Instructions:
mvr ... ... red cns spc ... mvr mvr ... ... red ... spc ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr ... red cns ... ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red ... spc ... mvl mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc ... mvr mvr ... ... wrt cns ... ... mvr mvr jmr jml red cns spc neq mvl mvr ... ... wrt cns ... ... mvr mvr ... jml red ... ... ... mvl mvr ... ... wrt cns ... ... mvr mvr ... jml red ... ... ... mvr mvr ... jml red cns spc ... mvl mvr ... ... red ... spc ... mvr
Here's how it works:
1. ♀
:
- Move the program pointer one step to the right (
mvr
) - Do nothing (
red cns spc
) - Move the program pointer one step to the right again (
mvr
)
2. ↑H↑e↑l↑l↑o↑!↑
:
- Move the program pointer one step to the right (
mvr
) - Print the value (
wrt cns
) - Move the program pointer one step to the right again (
mvr
). If the program pointer has reached the end of the string, execute-♦
. If not, the program pointer is pointing to a byte with the same value as the current one, so go back to step 1.
3. -♦
:
- Move the program pointer one step to the right (
mvr
). - Jump back to the previous command that is equal to
♦
(jml
) - Move the program pointer one step to the left (
mvl
). Now it's pointing at♀
, and we're back to where we started from.
Truth machine
This program accepts one character as input. If that character equals 1, it prints it forever. Otherwise, it prints it once and exits.
Characters:
‘1Ø‘Ü
Hexadecimal:
00 31 08 00 9a
Instructions:
mvr ... ... red ... ... ... mvr mvr ... jml wrt ... ... ... mvl mvr ... ... red cns ... ... mvr mvr ... ... red ... ... ... mvr mvl ... ... wrt cns ... neq mvr
Here's how it works:
1. ‘
:
- Move the program pointer one step to the right (
mvr
) - Store the value the program pointer is pointing to in the accumulator (
red
) - Move the program pointer one step to the right again (
mvr
)
2. 1
:
- This value is stored in the accumulator by the first
‘
.
3. Ø
:
- Move the main program pointer one step to the right (
mvr
) - Copy the input to the byte that is being pointed to (
red cns
) - Move the main program pointer one step to the right again (
mvr
)
4. ‘
:
- This is a placeholder for user input. It doesn't matter what character is initially stored here.
5. Ü
:
- Move the main program pointer one step to the left (
mvl
). The character from user input is stored here. - Print the value in the byte that is being pointed to (
wrt cns
) neq
: If the byte that is being pointed to is not equal to the accumulator (in other words, if the user input is not equal to the character '1'), move the program pointer two steps to the right. This moves it out of program bounds, so the program exits, only printing the input once in theØ
command. If the input IS, however, equal to the character '1', the program pointer only moves to the right once. Now it's back to the same command (Ü
), so it executes it again. Since no bytes are changed in the process,Ü
will give the same result: Print the user input before executingÜ
, which prints the user input before executingÜ
et cetera.
Computational Class
Since BIJ can be looked at as a type of Turing machine with the accumulator storing the state and moving around on an infinite tape, it's most likely Turing-complete. Thanks to User:PixelatedStarfish for figuring that out :)
External resources
- My (User:Peter's) original C interpreter: https://github.com/Peter919/Byte-based-Instruction-Jumping