Bytemap

From Esolang
Jump to navigation Jump to search
This is still a work in progress. It may be changed in the future.

Bytemap is a self-modifying language that stores all source and data in the same place and format. All commands and data are written onto an indefinitely large 2D grid of bytes. Bytes may be read as instructions, read as data, or written over by other instructions.

Input is an ascii text file - where a single byte is specified in hex by two letters (range 00 - FF) Any bytes that are not specified are automatically assumed to be FF. Execution starts at the first position in the file, however, programs may read/write at places before that. Input files should contain only the 0-F symbols, and have an even number of rows Program terminates when it reads the end program command FF (if its read as a command)

Note on inputs

Since Bytemap's 'code' consists of a 2d byte grid, an interpreter can be made to read either straight up binary files, provided their row width is specified in the beginning. Alternativly (And more interestingly perhaps) a Bytemap interpreter can be made to read any 8bpp image files as program inputs

Commands

The following perform actions if they are executed as commands. Note that they can also be read as data

Byte Jump

5#  Byte Jump
   58  Jump Up
   54  Jump Left
   56  Jump Right
   52  Jump Down
   (correspond to Numpad Directions)
   All of these take the next 1 byte as an input:
   1   The number of bytes to jump in that direction

If executed by itself (i.e. not part of another command), control continues from the jumped to Byte. This allows for jumps to be daisy-chained together (as long as they're not executes as part of another command) Jumps are treated as offset from the start of the command. This means if a jump is part of another command, it will jump to a location relative to the start of its PARENT command (not relative to itself)

Output

0#  Console output - sends to the console
   00  Output as a decimal (reads all specified bytes, interprets it as two's compliment)
   0A  Output as an ASCII
   0F  Output as a hexadecimal (padded with 0 if necessary)
   All of these take 3 bytes as inputs - the next 3 bytes following (to the right)
   1-2 A Byte Jump, indicating from where to read data. If this isn't a Byte Jump, the whole instruction is ignored
   3   Number of Bytes to print (starting from the Byte jump, right). If this is 0, instruction is ignored

After execution, control returns to the byte immidiately following the last input

Input

1#  Console input - interprets input from the console and writes it to the byte map
   10  Read a single whole integer number from console, writes it as a 2's compliment integer
   1A  Read a single character from console, interpreting it as an ASCII
   1F  Read pairs of characters, interpreting each pair as a hex value (byte)
   Both these take 3 Bytes as input:
   1-2 A Byte Jump, indicating where to start writing
   3   Maximum number of Bytes to write
For whole integers, if the bytes necessary to represent the input integer are larger than the bytes allowed to write, 
the most significant bytes are trunkated. If the integer is smaller, the unecessary bytes are padded with 0s
For characters, this determines how many characters to read (1 char = 1 byte)
For hex values, this determines how many pairs to read. Any invalid hex numbers read are instead written as FF

After execution, control returns to the byte immidiately following the last input

Arithmetics

A#  Perform Integer Arithmetic operations. Treats inputs as 2s compliment integers
   A0  Addition
   A1  Subtraction (byte1 - byte2)
   A2  Multiplication (byte1 * byte2) - overflow is thrown away (not written)
   A3  Integer Division (byte1 / byte2)
   A4  Modulus operation (byte1 % byte2)
   All of these take 7 bytes as input
   1-2 Byte Jump to first value (byte1) (if this is an invalid jump, treats value as 0)
   3-4 Byte Jump to second value (byte2) (if this is an invalid jump, treats value as 1)
   5-6 Byte Jump to where to write the result
   7   Length - how many bytes to read to perform the operation on (i.e. 4bytes for c-like ints)
   

Conditionals

C#  Comparison operators
   C1 Less than (byte1 < byte2)
   C2 Less or equal than (byte1 <= byte2)
   C3 Equal (byte1 == byte2)
   C4    Greater or equal than (byte1 >= byte2)
   C5 Greater than (byte1 > byte2)
   C6 Different than (byte1 != byte2)
   All of these take 9 bytes as input
   1-2 Byte Jump for first value (if its an invalid byte jump, treats this value as 0 instead)
   3-4 Byte Jump for the 2nd value (if its an invalid byte jump, treats this value as 0 instead)
   5   Length of byte values to compare (if specified as 0, the TRUE path is always taken)
   6-7 Byte Jump to take if comparison is TRUE (if invalid, command is ignored)
   8-9 Byte Jump to take if comparison is FALSE (if invalid, command is ignored)

After this command, execution continues either at the TRUE location, the FALSE location, or if either of those are invalid, it continues immidiately after the command

Terminate

FF  Terminates program (if read as a command)


Examples

Note: Since no working interpreter exists yet, these were written and verified by hand to get an idea of how well this idea could work

Simple tests

Hello World!

0A56050CFF48656C6C6F20576F726C6421

Explanation:
0A   - Output as ASCII text
5605 - Starting at 5 bytes to the right (56 = right). Relative to the start of the output command, i.e. 0A
0C   - Number of bytes to read from code and output - in this case 12 (Hello World!)
FF   - Terminate Program. After reading the entire hex string and outputting it, control returns here.
The rest of the code is simply the ASCII representation of 'Hello World!' including the space and !

Output 1 continously

005603015404

Explanation:
00   - Output decimal (read as two's compliment)
5603 - Start 3 bytes right of the output command
01   - Read 1 byte. Note that this is also read as the data to output, illustrating how commands can read themselves
5404 - Byte Jump command - jumps 4 bytes to the left of it, essentially creating a loop

Quine (with explicit termination)

0F540005FF

Explanation:
0F   - output bytes in hex (basically same encoding as this file)
5400 - starting from 0 spaces to the left of 0F, meaning start at 0F. This is the equivalent of 5800 or 5600 or 5200
05   - Reading 5 bytes from the starting point
FF   - After the code outputs itself, this explicitly terminates the program

Quine (with implicit program termination)

0F540004

Explanation:
0F   - output bytes in hex (basically same encoding as this file)
5400 - starting without an offset from the start of 0F
04   - Reading 4 bytes from the starting point

After that the execution pointer tries to read past the specified info. This is completely legal, and since any unspecified bytes are assumed to be FF, it receives FF as its input, and terminates, implicitly.

Truth Machine (inputs an integer, stored as 4-bytes), if the input is 0 it prints 0 and exits. Otherwise, it continously prints 1

105201045202
FFFFFFFFFFFF
5201FFFF5404
C3580200000452015202
00540001FF
005603015404

Explanation: 
105201045202 - (10) input a single int, (5201) write at location 1 byte down, (04) write 4 bytes, (5202) new cmd, jump down 2
FFFFFFFFFFFF - Empty line for writing the inputted int to. 
5201FFFF5404 - (5201) jump down 1 byte, (FFFF) filler, (5404) jump left 4 bytes (the 5202 command from row 1 jumps here)
C3580200000452015202 - (C3) Equality comparison, (5802) - jump up 2 bytes to read the inputted int, (0000) invalid jump, value assumed 0
                      (04) Read 4 bytes, (5201) jump executed if comparison is TRUE, jump down 1 byte, 
                      (5202) jump executed if comparison is FASLE, jump down 2 bytes
00540001FF - (00) output, (5400) starting at 0 bytes left (i.e. starting at 00), (01) read 1 byte of data, (FF) terminate
005603015404 - Output 1 continuously - see the explained example above

Self-modificiation and Self-replication

A simple self-replicating pattern (copies itself as the next command to execute, and does so indefinitely along a single row)

A000005400560808

Explanation:
A0   - Perform integer Addition
0000 - First offset - this is an invalid byte jump, so the value is automatically assumed to be 0 (see spec above)
5400 - Second byte offset, basically starts at A0 (since it says 'move left by 0 bytes')
5608 - Offset of where to write the result - in this case 56=Right by 08 bytes. This places result immidiately after command.
08   - How many bytes to read/write - in this case 8, which is the length of the command

This illustrates how the Arithmetic commands can be used to copy data, by simply performing addition to 0. After the command is executed, a perfect copy of it is placed immediately after it, which is where the execution pointer goes to read, and repeats the whole thing again, and again...

A column-wise self-replicating pattern (copies itself downwards, and moves the command pointer there)

A00000540052010FA000005403540D025410

Explanation:
A0   - Perform integer Addition
0000 - First offset - invalid, so value assumed to be 0
5400 - Second offset - starting at the beginning of the command at A0
5201 - Where to write the output, in this case 52=Down, 01 byte - meaning directly below the A0 byte
0F   - How many bytes to read/write. 0F = 15 bytes, which is the entire length of the line (in bytes)
After this is executed, a perfect copy of the line is placed below it. However, the execution pointer continues RIGHT.
The next thing it reads is the A0 command after the 0B byte
A0   - Perform addition again
0000 - Assuming argument is 0 (standard way to copy something)
5403 - 2nd offset - 3 bytes to the left (54=left). This starts reading at the 52 byte (which is 3 left of the second A0)
540D - Where to write the result - in this case 13 (D=13) bytes left of the 2nd A0. This is right at the start of the line.
02 - How many bytes to read/write - only 2 are needed.
This has now placed the command 5201 at the front of the line. 5201 is coincidentally the command to jump down by 1 byte.
Execution pointer finishes the command, and still continues right, where it reads the last 2 bytes: 5410
5410 - Byte Jump - Jumps to the left (54) by 16 bytes. This places the execution pointer at the start of the line. 
Here the execution pointer now reads the newly written 5201 command, causing it to jump directly down to the clean-copied line.

Since the 2nd line is completely unmodified (identical to the original code), it repeats the whole process again, cloning itself below, on the 3rd line, and proceeding indefinitely. This example shows again how parts of a command can be used as data by another command, and overriding of the commands can be used to achieve some interesting effects.