Skip to content

BitP Guide

Hyreon edited this page Sep 25, 2020 · 22 revisions

This guide explains each of the 16 commands, explaining the language's architecture as necessary.


# VALUE: All commands in this language have a hexadecimal value, from 0-F. This language would completely break down if there were more or less commands than 16. There was an attempt to cheat back in v0.6.0, but that has been reverted. (Because it wasn't as fun!)

So, what VALUE does is say, "Ignore that next command. We want it's value, not its function."

The value goes on something called a "disc". This contains up to 8 64-bit values. At any given time, exactly one of these values is being pointed at.

So, if the entire program just looks like #:, it will terminate with a disc like [C], 0, 0, 0, 0, 0, 0, 0. The value of : in hex (everything here is in hex) is C, so the first disc value (the one being pointed to by default, indicated in brackets) is set to C. The rest are the other 7 disc values, which are by default at 0.

The value being pointed to is indicated in brackets, and called the 'disc value'.

You do not have to memorize the values of each command; the compiler recognizes C and : as the same thing. So that above program is identical to #C. A few things to note here:

  • For values A-F, they must be capitalized.
  • C is still technically the same function as :, so if it's not put in front of a # it will be treated as one!

If there is already a value on the disc, then VALUE doesn't get rid of it entirely. It instead shifts it left 4 times, and then puts the specified value there. This seems weird at first, but consider the following code:

#3#A#0

If it were to replace the value entirely, you would have 0 as the disc value. However, with the new process, it sets the disc value to 3A0.

Okay, cool. But we've only covered one command.


, NEXT: Looks at the next value on the disc. It can only go one way (normally), and that is forward, by using this operator. So, the following:

#3#A#0,#A#C

Puts the entire disc at something like 3A0, [AC], 0, 0, 0, 0, 0, 0.

Worth noting is that after the 8th value, the disc will loop back around at 1, and not throw an error. In fact, nothing in this language, after compilation, will throw an error.


~ NOT: Flip every bit in the disc value. So, binary 1 becomes a 0, and every 0 a 1. ez pz. It is the same thing as multiplying the disc value by -1 and then subtracting 1, if is language could do multiplication and subtraction.

#3#A#0,#A#C~ ends with the disc at 3A0, [FFFFFFFFFFFFFF53], 0, 0, 0, 0, 0, 0. Or, to simplify things, 3A0, [~AC].... This is not quite identical to a negative value; negative 0 is still 0, but ~0 is [...FFFF].... Most operating systems define this as -1, because a series of ones has essentially the same properties as -1.


& AND: If you do not know what a bitwise AND is, you're going to have a bad time. If you do, this command does just that.

Like a lot of other commands, this is an OPERATION. That means it takes the previous disc value as the operator, the current disc value as the operand, puts the result in the previous disc value, clears the current disc value, and points back one step. Which is a lot. Here's an example:

#3#A#0,#A#C&

  • Before command 0 is read, we're assuming a blank disc: [0]....
  • Before command 6 is read, we have [3A0]... After command 6, the comma, is read, we have 3A0, [0]....
  • Before command 11 (0xA in hex) is read, we have 3A0, [AC].... The end result is a fairly readable postfix operation, ending with the disc at [A0]... - exactly 3A0 & AC.

That covers ^ XOR, / OR, < LSHIFT, and > RSHIFT as well; they are all described here and behave identically to the & AND operation.

Seriously, you will need to know what these things do in order to use the language.

Knowing this: What is the disc at when #3#A,#A#C,#F&^ terminates?
3A ^ (AC & 0F) = 3A ^ C = 36. Since the operation clears everything, the final disc looks like [36]....

Get help if you got the wrong answer.


The disc defaults all values to 0, so ,#3#A#2&, ,#F#F#F& and ,~& all would have a disc value of 0. The initial comma isn't necessary either, since operations too will look around the disc, to the 8th value – which also defaults to 0.

That's why it's called a disc. It loops around, and there isn't necessarily a start or end point.


[ INPUT: Takes a single character of input. The user actually inputs a string, but this function will get that input character by character, only asking for more input when the string is exhausted. Every string is appended with a /0 NUL character, so the program can tell when user input has ended. Multiple lines of input will put the /n character in between each one.

] OUTPUT: Spits out the current disc value as output. Note: Even though input and output have matching brackets, they do not have to match.

So, a program that simply reads [] would take an entire string of input, and print the first character of it. A program reading [][][][][][][][] would take a string of input and print out 8 characters of it; if the input was less than 8 characters including the NUL at the end, it would ask for another string of input and print that out.

The encoding used is implementation defined. The default implementation uses ASCII (because that's what C++ works best with), but no UTF encoding will break programs that display normal ASCII characters.


% PORT: As of now, it's a NOP. Just don't use it. It'll get replaced or added sometime.


@ IF-GOTO: Jump to a different part in the program if a condition is met. Note that this is still technically an operation; the previous value is whether or not to jump (Any value other than 0 makes the program jump), and the current value is where to jump. Both of these are set to 0 regardless of whether it jumps or not.

#1,#0@ is an endless loop. #1 isn't 0 so it will jump, and #0 refers to the very first character in the program. (It's 0 indexed.)

#1,#6@ does nothing. The sixth character is right after the @. (It's 0 indexed.) It jumps according to that character in the program. Have fun working with that! It isn't actually that bad. If you format your code like the examples (.bp files) it shouldn't be too hard to keep track of where you're going.

Also, the compiler removes comments. So you can comment as much as you want without messing up this function.

Knowing this: What does #4#0,#1,#7@/] print?
@. It returns @.

First off, we put the value 40 in hex in the disc, then 1, then 7. Then we do the IF-GOTO function. If we look at the disc, we're at 40, 1, [7].... IF-GOTO sees the 1 there (which isn't 0) and so jumps to 7th character (0 indexed!). That's the comma. Now we're at 40, [0], 0..., and after the comma at 40, 0, [0].... Then we put 7 there and perform IF-GOTO again. This time, however, the previous value is zero, and no jump is performed! Everything is still cleared, so we're back at 40, [0], 0... but in front of the IF-GOTO. 0 | 40 gets you 40, and then you print that character out. It's @ in ASCII and UTF.

Now I hope you never see code like that again, but the longer the code, the longer those GOTO indexes get. Hopefully that didn't give you more of a headache than counting out 20 characters at a time would have.


= IF-THREAD: Very similar to IF-GOTO, but this one doesn't just go to the specified location; it creates a new thread at that location. That means it can run different parts of the program at the same time, continuing as before and doing stuff at the new thread as well.

Unlike IF-GOTO, this records whether it was successful or not on the calling thread. If a new thread was created (or should have been created), the index of the new thread is put on the disc value (instead of 0). If no thread should have been created, the disc value is 0. The new disc is not defaulted to any preset values; it uses whatever is already on the disc for that thread.

This implementation puts the disc at 1 every time, because there is no real multithreading here. (C++ thread support isn't quite working on my end.) However, if it can be multithreaded, do so! It'll speed things up for at least some compilers and some systems, and with a mere 16 commands working overtime, you'll need all the speed you can get.

If there aren't enough threads available to the device, no thread is created. The value on the disc is set to 0.

Threads created by IF-THREAD use a different disc value than the main program.


This language is self-modifying. Any code you have written can be changed by the code itself. These last three commands let you do that. But they're also great for mundane things, like storing variables, in reserved parts of the program. Just keep that in mind when you use them that they are actually writing in the code. If things go haywire fast, that's probably why. Also, the compiler (as mentioned before) removes comments. It also replaces the somewhat readable character commands with their raw hex value.


: READ: Read a portion of the program's code.

This is an operation. Actually, everything from hex 5 to C is an operation. The operator is the index (0-indexed!) in the code, and the operand is the number of bits to be read from that index minus one; if the operand is 0, 1 bit is red. Note that READ's index is not the same as IF-GOTO's and IF-THREAD's, because it looks at bits, not hex digits.

As an example: #4,#3: terminates with a disc of [4]..., because it reads the bit 4 after the first, and then another 3 bits after that one. That gets you the second hex value in the code, or 4.

: terminates with the very first bit of the program (1), because it uses the default value 0 as both the first and second operators.


{ REMEMBER: This takes inputs very similar to read, but does not modify the disc at all. Instead, it records the two values for use in:

} COMMIT: Overwrite the bits selected by REMEMBER with the ones currently on the disc value.

#1#8,#3{,#1} Modifies the program so that the bits between 0x18 and 0x1A are set to 0x1. (Remember, REMEMBER doesn't remove any values, so that comma is necessary this time.) The program reads #1#8,#1{,#1} when finished.

If it were to be run again (which there is no way to do currently, as the program resets on termination) it change again to #1#8,#5{,#1}. Which would change again to #1#8,#0&,#1}. Which changes to <,#8,#0&,#1}. Which changes to... itself.


There are also 3 meta-commands. These are read by the compiler to do certain functions.

_ ARBITRARY: Is replaced at compile-time with any of the 16 above functions. What it replaces with is compiler-defined; it could replace it with a pseudo-random sequence, 0 every time, or the entire bee movie script in hex. Random is recommended.

* TODO: Prevents compilation. That's literally it.

' COMMENT (` in v0.8.0): Every character after this one is ignored, until another one is seen. Note that anything other than the default commands, values 0-F and these meta commands are already treated as comments and do not affect the end program whatsoever.

$XXXX ADDRESS BLOCK: New in 0.8.0. Will pad the program with arbitrary values until the given value is reached. Useful for consistent labels and for ensuring the program is of a certain size. Encountering a non-hexadecimal literal, whether whitespace or a command, will end this address block.

; LINE END: New in 0.8.0. Prevents compilation if any command characters are found after this and before a new line.

A few special characters you still can use without comments: .?! () -+ | ; "`


Final notes:

  • Programs are not edited in between runs in v0.7.0.
  • The disc is not saved between runs in v0.7.0.
  • The $ and ; characters do nothing in v0.7.0.

Well, that's pretty much everything. If you have questions, ask! Hope this helped.


Clone this wiki locally