Cufrab
Cufrab is an esoteric programming language designed by User:Zzo38 in 2010. Based on brainfuck, it uses 256 commands, where 1 byte is normally 8 bits (0-255), and 1 word is the address size. They have 16 tapes.
Commands
- 0x00 ]
- 0x01 [
- 0x02 <
- 0x03 >
- 0x04 -
- 0x05 +
- 0x06 ,
- 0x07 .
- 0x08 $ I/O control
- 0x09 * Double the value at pointer
- 0x0A { Move pointer backward by skip value
- 0x0B } Move pointer forward by skip value
- 0x0C ( Decrease value at pointer by skip value
- 0x0D ) Increase value at pointer by skip value
- 0x0E ? Swap this tape's pointer with active value at swap value tape (if this tape is bytes, swap only the low byte of the word)
- 0x0F Chunk mark: Hi nybble is stored in base 12, a chunk number. The code 0xDF begins define a chunk, and 0xEF calls a chunk with no return, and 0xFF calls a chunk with return. No chunk should contain unbalanced loops. Put the chunk number before the code to tell it what the chunk is defined/used.
Tapes
- 0x00 x First tape, first pointer (bytes)
- 0x10 y First tape, second pointer (bytes)
- 0x20 z Second tape, second pointer (bytes)
- 0x30 k Skip value tape (words)
- 0x40 p First pointer value tape (words)
- 0x50 q Second pointer value tape (words)
- 0x60 w Word value tape (words)
- 0x70 s Swap value tape (words)
- 0x80 b Bit manipulation tape (bytes)
- 0x90 c Call stack tape (words)
- 0xA0 u Wheel tape (bytes)
- 0xB0 v Wheel setting tape (words)
- 0xC0 e First external tape (bytes)
- 0xD0 f Second external tape (bytes)
- 0xE0 r Program reset tape (bytes)
- 0xF0 h Program loading tape (bytes)
The main tapes (first tape and second tape) can be any length according to the word size.
The skip value tape is 8 cells long, and the value at the active position is the skip value.
The first pointer value tape and second pointer value tape are each 8 cells long, and the values there is the first pointer and second pointer, used for the main tapes.
The word value tape is 16 cells long and just stores extra word values.
The swap value tape is 8 cells long, it is used for the 0x0E instruction.
The bit manipulation tape is however many cells long according to the number of bits in a word, and the low bit in each cell corresponds to a corresponding bit in the current value in the word value tape. The rest of the bits in each cell of this tape is just extra storage, and can be accessed the same storage in every cell of this tape.
The call stack tape is as long as the call stack. Moving this tape pointer backwards is another way to return from a subroutine call. Moving this tape pointer forwards will go back to the point in the subroutine call it returned from, if it was returned using this tape pointer, but results in undefined behaviour if a subroutine ended by reaching the end of chunk, instead. Manipulating the values on this tape directly results in undefined behaviour, except for swapping with the swap value tape.
The wheel tape has a varying number of cells. It starts with no cells. It is a circular queue.
The wheel setting tape has one cell, which stores the number of cells in the wheel. Increasing it will create cells with value zero on the wheel, and push the remaining cells of the wheel to the right. Decreasing will cut cells from the wheel, pulling the remaining cells to the left.
The first external tape and second external tape has however many cells according to word size. They read/write external media.
The program reset tape has one cell. Reading it is always zero. Setting it to one will reset all pointers of all tapes, and clear the first pointer value tape, second pointer value tape, and set the call stack to one value at the front which is the next instruction, so it will continue, and cannot be returned. Setting it to the highest byte value (255) will cause it to load the program according to the program loading tape.
The program loading tape has 3 cells. The first cell is a tape number. The second cell is a address on that tape. The third cell is the length. This will cause the program to be loaded when the program reset tape tells it to do so.
Loops
All loops must be nested according to loops of the same tape. (Loops such as 0x11 0x00 are disallowed, because the matching brackets are not the same tape.)
ASCII representation
The ASCII source file uses the following codes:
- Command codes insert that command into the binary, for the current tape.
- Tape letters (must be lowercase) set the tape for commands that will be inserted into the code.
- : begins a macro, follow it by a name and then the macro definition. Names can contain uppercase letters and underscores.
- ; ends a macro.
- = begins a chunk, follow it by a name (same rules for names as macro names), and another equal sign.
- ^ call a chunk without return. Put name between the ^ signs.
- / call a chunk with return. Put name between the / signs.
- % use macro parameter, inside of a macro.
- ` begin macro parameter.
- ' end macro parameter.
- A macro name will use that macro. The last macro parameter used is the one for this macro.
- Any decimal number will set a number register.
- @ causes it to repeat the next command or macro according to the number register number of times.
- | causes it to include the next command or macro only if number register is zero.
- \ causes it to include the next command or macro only if number register is not zero.
- ~ inserts * and + commands to make the number according to number register, assuming current value is zero.
- " you can make a C string. Follow by a macro name, use that macro for each character in string, including null character at end, with number register set to that character at each time.
- # is comment to end of line.
- & enter immediate mode.
Immediate mode
In immediate mode, you can deal with a tape which is used at compile time. The next & sign exits immediate mode, and parses any output from the immediate mode as a source code. The macros defined in immediate mode are a separate set of macros from normal mode, but they are retained when going to immediate mode multiple times. Immediate tape stores word values.
Chunk calling and chunk defining in immediate mode is just normal and not immediate (it compiles in those commands), except that any chunk names in immediate section are a separate name space for each section of immediate code defined with ampersands, and do not share with normal mode. However you can access chunks from previous immediate sections by having an underscore before the name. However many underscores is however many skip back it is.
The commands different from normal way, are:
- +-<>.[] are normal brainfuck codes, executed immediately.
- , inputs from a C string which has been inserted immediately before the ampersand which starts immediate mode.
- ~ sets the current cell to number register value.
- * doubles the current value on immediate tape.
- ? sets number register value to current cell value.
- ( followed by a macro name will copy a normal macro into a numbered macro according to number register value.
- ) followed by a macro name will copy a numbered macro according to number register value into a normal macro.
- { followed by a macro name will copy a immediate macro into a numbered macro according to number register value.
- } followed by a macro name will copy a numbered macro according to number register value into a immediate macro.
- $ Compiles one byte from current cell value.
- & exits immediate mode.
Compatibility mode
In compatibility mode, you can create normal brainfuck programs. Only tape 0x00 is allowed, and only normal brainfuck commands are allowed. Command 0x09 is also allowed, but only if the optimizer can determine the current value at the cell as a constant value.
Chunks are still allowed in compatibility mode, but you are not allowed to call a chunk with no return, except as the last command of a chunk, and chunks are not allowed to call themself directly or indirectly.
Examples
Hello world program:
:PRINT[-]~\.; "Hello, World!\n"PRINT
Beer program:
:_[-]; :PRINT _~\.; :LINE>_10~.<; :DUP[>+>+<<-]>>[<<+>>-]<; :DIVMOD[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]; :SWAP>[-<+>]<; :IF DUP[; :THEN _]<; x_99~[ /NUMBER//BOTTLES//WALL/ /NUMBER//BOTTLES/LINE >"Take one down and pass it around\n"PRINT< -IF/NUMBER//BOTTLES//WALL/LINE THEN ]"No more"PRINT/BOTTLES/^WALL^ =NUMBER=x >_>_10~<< DIVMOD SWAP>>>> IF48@+.THEN<48@+.<<< =BOTTLES=z " bottles of beer"PRINT =WALL=z " on the wall\n"PRINT