M-code

M-code is a Turing-complete esoteric programming language designed by User:Madk.

M-code takes a lot of inspiration from 6502 assembly, brainfuck, and then it tosses a handful of features of its own. Every character in a source file represents a single value, and numerical values can be specified inside [] brackets. There are three variations: 8 bit M-code, 16 bit M-code, and 32 bit M-code. The higher bit variations were introduced to allow a larger tape size than a mere 256 bytes. All values in each bit mode are that number of bits. 32 bit values are signed.

Key Features

 * Source code is stored in an accessible memory bank, making self-modification important to many functions.
 * 3 value register
 * File access
 * Relatively robust arithmetic and logic operands

Commands
These are the single-character commands recognized by the interpreter. All commands and symbols use 1 value, arguments must immediately follow commands. Program execution starts at memory address 0.

Register handling for the A, B, C registers. > puts a value into the register and < takes it out. > < } {  ) (  arguments: [memory address] Store a literal value in the A, B, C registers 4 5 6 arguments: [number] Handle the string in memory by printing it, getting it from input, or swapping it with the secondary (inactive) memory string ; \ ~ Clear the string in memory s Read characters from the memory string and put them in the A, B, C registers ^ ` ' arguments: [position in memory string] Treat the memory string as a number and put it in the A, B, C register. U V W Write characters to the end of the memory string from the A, B, C registers : ., Output the numerical value onto the end of the memory string 1 2 3 Store the length in characters of the string in memory to register A, B, C 7 8 9 Copy a byte, swap two bytes in memory from one place to another # S arguments: [origin memory address] [second memory address] Increment, decrement a byte in memory i d increment, decrement the value in the A, B, C registers o n u t f e Jump unconditionally j arguments: [jump to address] Jump if A > B ? arguments: [jump to address] Jump if A < B ! arguments: [jump to address] Jump if A = B = arguments: [jump to address] Jump if A != B N arguments: [jump to address] Swap A and B registers x Swap B and C registers y Swap C and A registers z Set A, B, C register to 0 a b c Perform a logical NOT operation on registers A, B, C A B C Copy the value in the A register into both B and C Y Shift a value in memory left, right by one bit L R arguments: [memory address] Perform an operation on A and B and place the result in C Add:			+ Subtract:		- Divide: 		/ Multiply:		* Modulo:		% Raise to power:	v Bit shift left:	l Bit shift right:	r Mean:			m Logical OR:		O Logical AND:		& Logical XOR:		X Creates a new external data file if one doesn't already exist. It opens it into memory if it does. F arguments: [file #] Write a value from a program memory address into a file w arguments: [write from memory address][write to file address] Read a byte from a file and store it in program memory p arguments: [write to memory address][read from file address] Read a file's entire contents and store it all in program memory starting at a given location P arguments: [write to memory address] Print memory string to console without a newline (with the exception of the inclusion of an actual newline character) H Print a newline to the console I Delay program execution for some number of milliseconds D arguments: [milliseconds] Output a number, character to the end of the memory string from a memory address 0 @ arguments: [memory address] Kill program _

Non-runtime parser instructions
These are interpreter instructions. They aren't processed in memory at runtime, but aid in the writing of code and pre-runtime memory management. Comment // (...) // Reference a label and store its position as a value in this memory address |(...)| Define a label mid-line |$(...)| Define a value numerically instead of an ASCII character. For example, [65] and "A" and also [35] and "#" would be interchangeable. [(...)] Set string memory contents before runtime - first instance sets first string, second instance sets second string. $"(...) Set the current location in memory that the following data will start being written at. It must be numerical. $(...) Define a label based on this line's position in the source file. It cannot start with a digit (0-9), "$", or a quote ("). It cannot contain "=". Labels are case-insensitive. $(...) Simultaneously define a label and the contents of the immediate memory address. The assignment should be formatted no differently than typical program data. Example: $pound = # OR $pound = [35] $(...)=(...) Count whitespace as valid characters in this line of code, with the exception of preceeding and following space. (...) $ Self-reference this address in memory - if [$$] were at the very beginning of a program, it would be equivalent to [0]. At $42, [$$] would be [42]. [$$] Put a $ character where the parser would normally see it as a non-runtime command (at the very beginning or very end of a line, excluding whitespace) [$] Put a [ character where the parser would normally see it as a non-runtime command [[] Put a | character where the parser would normally see it as a non-runtime command [|]

Hello, world!
// Print "Hello, world!" to the console. // 4|data end|	// Store the location of the last character, "!", in register A. // j|main|        // Jump to the main loop start // $ main		// This is a label signifying the beginning of the main loop. // }|$pos||data|	// Push "H" into register B from the start of the "Hello, world!" string. $pos is a label definition. // .		// Add "H" from B to the end of the memory string. // i|pos|		// Increment the number in }|data| by one, so next loop it'll push "e", then "l", (...) // }|pos|		// Push that same number into B to get ready for a comparison. // ?|main|        // Jump back to |main| to create a loop if the value in register A is > B to know if we've reached the end of "Hello, world!". // ;		// After the above loops has finished, ; prints the accumulated string to the console. // _		// Terminate program execution. // $data		// Label the location of the string data in memory // Hello,[32]world!// Whitespace is not recognized by the parser, [32] is the ASCII code for a space. // $data end	// This is a label to provide the ending point of the "Hello, world!" data. //

Much less elegant Hello, world!
$"Hello, world! I'm a big fat cheater. ;_

99 bottles of beer
// This program prints the lyrics to "99 bottles of beer". It exceeds the program memory space offered by 8-bit M-code and therefore is incompatible. // d[183]>[183]bu=G16[224]4[15]j[184]6[209]4[21]j[184]>[183]16[224] 4[30]j[184]4.:;s6[241]4)j[184]>[183]n1}[1]=76[224]4=j[184]6[273] 4=j[184]6[209]4Cj[184];sj[0]0[183]6[273]4Oj[184]6[209]4Uj[184]0[183]  6[273]4]j[184]4.:;s6[289]4hj[184]6[320]4nj[184]6[224]4tj[184]6[209]  4zj[184];s6[320]4[130]j[184]6[224]4[136]j[184]6[209]4[142]j[184]  6[320]4[148]j[184]6[224]4[154]j[184]4.:;s6[328]4[165]j[184]4c16[224]  4[174]j[184]6[209]4[180]j[184];s_d([201]<[202]#[201][192]>[0]b=[203] i[201]:j[188][0][0]#[202][207]j[0]_[32]on[32]the[32]wall.[32][0][32] bottles[32]of[32]beer[0]Take[32]one[32]down,[32]pass[32]it[32]around .[32][0][32]bottle[32]of[32]beer[0]Take[32]it[32]down,[32]pass[32]it [32]around.[32][0]No[32]more[0]Go[32]to[32]the[32]store[32]and[32]bu y[32]some[32]more.[32]

Fibonacci sequence
// Calculate the first several numbers of the Fibonacci sequence. // // A & B are added together each loop to calculate C, a member of the sequence. A then becomes B and B becomes C and the next number is calculated next cycle. // 5[1]			// The A and C registers start at 0. The B register needs to be set to 1. // $ loop start		// This label indicates the main loop initiation. // 3;s			// Output the value in C to the console. // < |memoryA| { |memoryB|	// Store the values in the A and B registers into memory so we can do an end-of-loop check. // d |sequence|		// |sequence| is the label dictating how many numbers of the sequence to show. Here it is decremented by 1 each loop. // > |sequence| b		// Store |sequence| in the A register and 0 in B for comparison. // = |program end|		// If A=B (sequence=0) jump to the program's end. // > |memoryB| } |memoryA|	// Restore the previous states of A and B, but also swap them in the process. // y			// Swap the contents of the B and C registers. // +			// Add A and B and put the result in C. // j |loop start|		// Go back to the loop start label and do it all again. // $ memoryA		// Used to temporarily store the A register during the loop. // [0] $ memoryB		// Used to store the B register. // [0] $ sequence		// Iterate the loop |sequence| number of times. The 15th member of the // [14]			// sequence exceeds 255, so only the first 14 are calculated. // $ program end		// The loop jumps here when it has completed |sequence| cycles. // _			// Program termination command //

Collatz sequence
// Calculate a Collatz sequence from a starting amount. // // This program is _not_ intended to be run in the 8-bit version of M-code. // $"Input a number to determine its Collatz sequence. $"Number of cycles: ;s;\Us; $loop <|var|1;s5[2]%5[0]z N|odd| $even >|var|5[2]/ j|thru| $odd >|var|5[3]*f $thru i|cycles|z5[1]=|end| j|loop| $end 1;~4[32]:>|cycles|1; _ $var=0 $cycles [0]

Output ASCII table
// Prints ASCII table to the console // 4[255]5[0]6[32].,,2;suN[6]_

Quine
In order to keep readable ASCII characters for memory address representations, the program memory storage starts at 35.

// This program outputs its source to the console. To maintain readable characters, the first symbol is located at $35 within program memory. // $35 >#y}$N/5#.j1:yi$>$5;!#;_

Formatted:

$35 >[35] }[36] N[45] 5[35] . j[47] : i[36] >[36] 5[59] ![35] ;_

Cat
\;_

Brainfuck Interpreter
While this interpreter is designed with 8-bit M-code in mind and has a maximum code size of 256 commands and a tape size of only 32, these values can be expanded into greater than 60,000 or over a million commands and tape by changing the wraparound code at the end and using the 16-bit or 32-bit M-code interpreters, respectively. It shows through simulation that M-code, if given infinite memory, is Turing-complete.

// Interpret a brainfuck script. // $">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.>>>++++++++[<++++>-]<.>>>++++++++++[<+++++++++>-]<---.<<<<.+++.--..>>+.# $"Please wait, this may take a while :P ~;s~ j |main loop| $ > 	 [62] 	$ < 	 [60] 	$ + 	 [43] 	$ - 	 [45] 	$ . 	 [46] 	$, 	 [44] 	$ [ 	 [91] 	$ ] 	 [93] 	$ # 	 [35] $ tape [223] $ main loop ^ $ code [1] $ check > } |>| N |check <| i |tape| j |end| $ check < } |<| N |check +| d |tape| j |end| $ check + } |+| N |check -| # |tape||+arg| i $ +arg [ 0 ] j |end| $ check - } |-| N |check .| # |tape||-arg| d $ -arg [ 0 ] j |end| $ check. } |.| N |check ,| # |tape||.arg| ) $ .arg [ 0 ] ~s,H~ j |end| $ check, } |,| N |check [| ~\'[1]~ # |tape||,arg| ( $ ,arg [ 0 ] j |end| $ check [ } |[| N |check ]| # |tape||[mod| } $ [mod [ 0 ] 4 [0] N |end| 4 [1] < |nest| $ [start 4 [0] } |nest| = |end| i |code| # |code||[char| ^ $ [char [ 0 ] } |[| N |[2| i |nest| $ [2 } |]| N |[start| d |nest| j |[start| $ check ] } |]| N |check #| 4 [1] < |nest| $ ]start 4 [0] } |nest| d |code| = |end| # |code||]char| ^ $ ]char [ 0 ] } |[| N |]2| d |nest| $ ]2 } |]| N |]start| i |nest| j |]start| $ check # } |#| N |end| I_ $end i |code| > |code| 5 [255] N |nc| # |const1||code| $ nc 5 [221] N |main loop| # |const2||code| j |main loop| $ const1 [222] $ const2 [254] $nest [0]

External resources

 * Download M-Code interpreter