Malbolge Tutorial – Learning Malbolge

In this beginner's tutorial, we will write a simple cat program in Malbolge. The result will be the Malbolge cat program from Esolangs. We start looking at some screenshots of a cat program in the HeLL IDE. Afterwards, we will start writing the cat program only using the Malbolge pages of Lou Scheffer and a tool to convert numbers into the ternary number system. I.e., you need not install any software in order to follow this tutorial.
The HeLL IDE allows to write and debug Malbolge programs using the assembly language HeLL. Before we start with plain Malbolge, we will learn some basic about HeLL. For that purpose, we take a look at a sample cat program in HeLL.

Remark. You are reading the very first version of this Malbolge tutorial. I want to improve this tutorial. So, please send me any comments. Especially, write me if there is anything unclear or hard to understand, so that I can improve the affected things. Contact me even for trivia please, e.g. typos or grammatical errors.
Email: matthias@lutter.cc
Thank you very much!

A cat program in HeLL

.CODE MOVD: Nop/MovD Jmp [empty line] IN_OUT: In/Nop/Out/Nop/Nop/Nop/Nop/Nop Jmp .DATA ENTRY: IN_OUT ?- R_MOVD MOVD ENTRY
When writing programs in Malbolge, we partition the memory into a the code section, in HeLL introduced by .CODE, and a data section, in HeLL introduced by .DATA.
Within these sections, we use labels to address code and data.
Malbolge consists of cyclic self-modifying code. We can specify such a cycle in the code section by separating its instructions with slashes. E.g., Nop/MovD is a Nop instruction (no operation) that turns into a MovD instruction after execution. Afterwards, it turns into a Nop instruction again, and so on.
If no cycle is specified (in the code above, this is the case for the Jmp instructions), then the corresponding instruction can change in any and every possible way.
Note that there are a lot of restrictions concerning the choice of cycles, of which more later.
The actual program flow happens in the data section.

The empty line is an essential syntax element of HeLL.
When LMAO translates the HeLL source to Malbolge, it must write the code and data into concrete memory cells. LMAO uses code blocks and data blocks for this purpose. Everything inside a block is guaranteed to stay consecutive in Malbolge's memory cells. However, the position of a block will be chosen freely by LMAO. In HeLL, blocks are divided by an empty line. Thus, if you try the example above by yourself, you have to take care of the empty line in the code above.

The debug mode of the HeLL IDE can be started via the menu.
Now let us start the debug mode to execute the program step-by-step in order to learn the functional principle of HeLL and Malbolge programs.

HeLL IDE: A yellow arrow point at 'IN_OUT' below the 'ENTRY' label. The instruction 'Nop' of the cycle 'Nop/MovD' is marked bold. The instruction 'In' of the cycle 'In/Nop/Out/Nop/Nop/Nop/Nop/Nop' is marked as well. Also the Jmp instructions are bold. At the bottom one can see a terminal. At the right the following information are provided: C: somewhere in the initialization section; [C]: Jmp/Nop/Nop/Nop/...; [D]: IN_OUT - 1; A: 0t2222211212 '9'. There is an empty table below his text, the two columns of which are labeled with address and value.
The HeLL IDE changed to the debug mode.
The yellow arrow points to the memory position the virtual Malbolge machine's D register is pointing to. In the code section, the current position of each instruction cycle is marked bold. At the beginning, the C register points to a Jmp instruction somewhere outside the HeLL code. Thus the position of the C register is not marked yet. Starting with the next step, the C register's position will be marked by a green arrow.
At the bottom of the window we can see a terminal for interactive input and output.
At the right side, we can see the state of the Malbolge registers and memory. While the C register points to some unknown memory address containing a Jmp/Nop/Nop/... instruction cycle, the D register points to a memory cell containing the value "IN_OUT - 1". Effectively, we can see in the left part of the window, that the memory cell should contain the value "IN_OUT". However, the statement on the right side is the correct one: Whenever a HeLL program is translated into Malbolge, every reference is decremented by one. The reason is as follows. Whenever the Malbolge interpreter has executed an instruction, it encrypts the memory cell the C register points to and increments the C and D register afterwards. Thus, if a Jmp is executed while the D register points to a memory cell containing the value IN_OUT - 1, the C register is set to IN_OUT - 1 and incremented, resulting in the C register containing the value IN_OUT. Note that in this case the memory cell at IN_OUT - 1, but not the memory cell containing the Jmp instruction, is encrypted. However, to save the user from decrementing every jump address, LMAO does this automatically. The same holds for modifications of the D register by MovD.
The value of the A register at the beginning of a HeLL program is not defined. Thus, we should not read from the A register until we wrote something into it (using the Rot or In instruction). In fact, the A register starts with the value ENTRY - 1 when the current version of LMAO is used. However, this may change in later versions of LMAO, so we should not rely on this.

HeLL IDE: In the right table, two rows were added. The first row contains address [MOVD] and value 0t0000002121 'F', while the second row contains address [IN_OUT] and value 0t0000002222 'P'. The remaining parts of the windows are unchanged.
Additionally to the registers and memory cells described above, it is possible to watch own expressions. This is not really necessary for a simple cat program. However, to demonstrate this feature, we watch the content of the memory cells at MOVD and IN_OUT. They contain the internal Malbolge representation of the Nop/MovD and In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop cycles. Later we will see, that the value stored in these memory cells will change whenever the current cycle position is shifted.

The debug menu of the HeLL IDE allows to step through the Malbolge program execution.
Let us execute one single step of the program now. Note that the C register points to a Jmp instruction while the D register points to a memory cell containing the value IN_OUT"-"1, as you can see at the right side of the window.

HeLL IDE: A green arrow appeared, pointing at the instruction cycle In/Nop/Out/Nop/Nop/Nop/Nop/Nop. The yellow arrow points at ?- now, directly behind IN_OUT. The text above the table at the right side is now: C: IN_OUT; [C]: In/Nop/Out/Nop/Nop/Nop/Nop/Nop; [D]: ?-; A: 0t2222211212 '9'. The remaining parts of the window are not changed.
After the first step, the code register points at the In instruction while the D register has been incremented and points to a memory cell with undefined content (indicated by "?-").

HeLL IDE: The word 'foo' has been entered at the terminal. Nothing else has been changed.
Before executing the next step, in which the program will read one byte of user input, we write something into the terminal that can be read by the program. Let us just write "foo". Now we can execute the next step that will load the first letter "f" of our input into the A register.

HeLL IDE: The first Nop of the In/Nop/Out/Nop/Nop/Nop/Nop/Nop instruction cycle is now marked. The green arrow points to the Jmp instruction right below In/Nop/Out/Nop/Nop/Nop/Nop/Nop. The yellow arrow points at R_MOVD below ?-. Information at the right side of the window: C: IN_OUT + 1; [C]: Jmp/Nop/Nop/Nop/MovD; [D]: R_MOVD - 1; A: 0t0000010210 'f'. The value of [IN_OUT] is now 0t0000002110 'B'. The other things are unchanged.
As you can see, the A register holds the value 'f' (ternary 10210) now. Further, the executed instruction In has been modified, so that the cycle's position is now its first Nop instruction. The C and D register have been incremented, with the result that the next action of the program is to jump to "R_MOVD - 1". The meaning of R_MOVD is explained below the following screenshot.

HeLL IDE: The MovD of the Nop/MovD instruction cycle is now marked. The green arrow points at the Jmp instruction below Nop/MovD. The yellow arrow points at MOVD below R_MOVD now. Information at the right side: C: R_MOVD; [C]: Jmp/Nop/Nop/Nop/Nop/Nop; [D]: MOVD - 1; A: 0t0000010210 'f'. The value of [MOVD] changed to 0t0000002202 'J'. The remaining parts of the window are unchanged.
The prefix "R_" means "restore". However, it is just an other way to write down the addition by one. Thus, "R_MOVD" is equivalent to "MOVD + 1". So, the memory cell will contain the position MOVD finally, because LMAO decrements each reference by one as stated above.
Let's see what this does. As you can see above, once the jump to MOVD has been executed, the code register points to a Jmp instruction again, but the jump instruction behind the MOVD label. This may seem to be useless, because the code register has already pointed to a jump instruction before. The idea is to control Malbolge's instruction modification. Whenever Malbolge has executed an instruction, the instruction the C register points to will be modified (before the C and D registers are incremented). For the jump instruction this means that the instruction at the C register is modified after the jump has been performed, i.e. the instruction at the destination. Thus, with HeLL's subtraction of one from references, it looks like the instruction right before the destination has been modified. Indeed, at the screenshot above we can see that the active instruction of the Nop/MovD instruction cycle has changed to MovD, while the Jmp instruction below the IN_OUT label is still functioning (it is still bold).
Restoring a 2-cycle instruction by a specific jump is an essential technique when writing HeLL or Malbolge programs. This is the reason why the prefix is called "restore".
Okay, let us execute the next step.

HeLL IDE: The green arrow points at the Nop/MovD cycle. The yellow arrow points at ENTRY right behind MOVD. Information at the right side: C: MOVD; [C]: MovD/Nop; [D]: ENTRY - 1; A: 0t0000010210 'f'. Nothing else has been changed.
The code register points at the MovD instruction and the data register points at the value "ENTRY - 1". Because both registers will be incremented after executing the instruction, the D register will contain the address of the ENTRY label after the next step.

HeLL IDE: The state of the cat program is almost equivalent to the beginning. However, the In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop instruction cycle is now in the first Nop, which is indicated by bold font and the value of [IN_OUT] at the right side of the window. The green arrow points to the Jmp instruction below the Nop/MovD cycle and the C register has the value MOVD + 1, while [C] has the value Jmp/Nop/Nop/Nop/Nop/Nop.
Indeed, the machine is nearly in its start state again. The code register points at a Jmp instruction and the Nop/MovD cycle is in the Nop state again. The data register points to the entry point ENTRY.
Nonetheless, there are two important differences. The A register still holds the ASCII value of "f" and the cycle In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop is now in the configuration Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop/In.

HeLL IDE: The debug menu can be used to toggle breakpoints at the cursor position. The current position of the cursor is equivalent to the D register. It is pointing at the IN_OUT reference at the ENTRY label in the data section.
Let us add a breakpoint for faster running through the program.

HeLL IDE: A breakpoint has been added at the cursor's position.
The breakpoint is visualized by a red circle.

HeLL IDE: The program can be run until it terminates or a breakpoint is reached via the debug menu.
With that breakpoint, we can run the program straight instead of step-wise. Whenever it reaches the breakpoint (again), execution will be paused. So, let us run the program.

HeLL IDE: The program ran through the loop one more time. Only the In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop cycle has been changed: The Out instruction is active now. Thus the value of [IN_OUT] is now 0t0000002022 '>'.
The program has reached the breakpoint again. The cycle behind the IN_OUT label points to the Out instruction now. Hence, the current state seems to become a bit more interesting than the last one, so let us step through the program again to watch the Out instruction.

HeLL IDE: After a single execution step, the Out instruction is now active while the data register points at ?-.
After the first step, the code register jumped to the Out instruction.

HeLL IDE: After a single execution step, an 'f' has been printed at the terminal.
Now we can see that the content of the A register has been printed to the terminal. Further, the Out instruction has been modified. Let us go through the program faster again.

HeLL IDE: The program ran until the breakpoint. In the In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop instruction cycle, the first Nop after the Out is now marked.
As expected, we reached the breakpoint again.

HeLL IDE: The cat program is in its initial state again. However, the code register is well defined, holding the value MOVD + 1, such that [C] = Jmp/Nop/Nop/Nop/Nop/Nop.
After some more iterations, the cycle behind the IN_OUT label is in its initial configuration again. Now everything is equivalent to the begin. Thus the program will read the next character and print it.

HeLL IDE: The entire string 'foo' has been printed to the terminal. The debug mode can be left via the debug menu.
After the whole input has been processed, we should abort the program execution. Otherwise the program would run for ever, because we have not implemented a break condition.

Translating the cat program into Malbolge

We will manually translate the HeLL cat program into Malbolge now. This makes sense for simple programs, because this way the code will become more succinct than the code generated by LMAO. Additionally, we can learn how to write programs in pure Malbolge.

Choosing memory positions

We continue with the HeLL syntax for now.

.CODE @0t20000101 MOVD: Nop/MovD Jmp [empty line] @0t20020111 IN_OUT: In/Nop/Out/Nop/Nop/Nop/Nop/Nop Jmp .DATA @0t20000000 ENTRY: IN_OUT ?- R_MOVD MOVD ENTRY
Using the @ operator, we can specify an offset for the subsequent code or data block. For code blocks, only specific offsets are allowed, depending on the instruction cycles. We will see the details of this later. The goal is to replace the references by the absolute memory addresses, so that the HeLL code approximates real Malbolge code a bit more.

.CODE @0t20000101 MOVD: Nop/MovD Jmp [empty line] @0t20020111 IN_OUT: In/Nop/Out/Nop/Nop/Nop/Nop/Nop Jmp .DATA @0t20000000 ENTRY: 0t20020110 ?- 0t20000101 0t20000100 0t12222222
We have replaced the references now. Note that we have to specify the address of the memory cell preceding the actual jump destination, as stated above.

HeLL IDE: The modified cat program can be built with LMAO and executed as well.
It is still possible to run the program in the HeLL IDE.
Before we translate the cat program to Malbolge, we should change a detail.

.CODE MOVD: MovD/Nop Jmp [empty line] IN_OUT: In/Nop/Out/Nop/Nop/Nop/Nop/Nop Jmp .DATA loop: R_MOVD ENTRY: IN_OUT ?- MOVD loop
In Malbolge, every "invalid" instruction is interpreted as a Nop. Thus, must of the Nops of the instruction cycles written down in HeLL are not "real" Nops. However, only cycles starting with a "real" instruction can be initialized directly in Malbolge. It is possible to initialize the other cycles during runtime, and this is what LMAO does. However, it is much easier to write down the instruction directly.
For that reason, we have replaced the Nop/MovD instruction cycle by a MovD/Nop instruction cycle above.
It is left to place the code and data of the new cat program at an absolute address. This time we choose addresses at the beginning of the memory.

.CODE @60 MOVD: MovD/Nop Jmp [empty line] @37 IN_OUT: In/Nop/Out/Nop/Nop/Nop/Nop/Nop Jmp .DATA @39 loop: 60 ENTRY: 36 ?- 59 38
Choosing addresses in the range of ASCII characters has two advantages. First and obviously, the Malbolge code need not be padded to fill these memory cells. Second the references in the data section are in the ASCII range. Thus there is a little chance that the reference matches a valid Malbolge command and can be initialized directly, which is much easier than initializing it during runtime (as we will see below).
The address of the data section is chosen a bit arbitrary. The data section must not intersect with the code section. Note that it must not intersect with a memory cell directly preceding a code block, because this memory cell will be modifier whenever the program jumps into the code block (this behavior has already been explained). If the data section intersects with such a memory cell, it will cause undefined behavior or even crash the Malbolge reference interpreter.
The choice of the addresses of the two code blocks are more interesting than the address of the data block. We have to ensure that the given instruction cycles exist at the corresponding addresses. We can look up this at Lou Scheffer's website Instruction Cycles in Malbolge.

offset 37:.INPUT .OUTPUT ..... offset 60:LOAD D . offset 64:.LOAD D
We can see that there exists a Nop/In/Nop/Out/Nop/Nop/Nop/Nop/Nop cycle at address 37. Note that the dots indicate invalid instructions that are interpreted as Nops while Nop indicates a valid instruction. Thus, only the non-dot instructions can be initialized directly. If we put an In or an Out instruction at address 37, it matches the desired instruction cycle. We want to start the cycle with an In instruction. A MovD/Nop cycle exists at address 60 and address 64. Let us choose address 60 randomly. This leads to the memory layout you have already seen above.

Direct initialization of memory cells

Now it's time to write real Malbolge code. Therefore, we use a further page of Lou Scheffer: Valid Instructions

37: 80(P)->/(47) 38: 60(<)->i(105) 60: 74(J)->j(106) 61: 37(%)->i(105)
At first, we place an In instruction at address 37 to get the In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop instruction cycle. To put the In instruction there – a slash in normalized Malbolge – we have to write a P at that address. For the MovD instruction, a j in normalized Malbolge, we write a J at address 60. Let us place the Jmp instructions (normalized Malbolge: i) right behind these two instructions. Now our Malbolge program is as following.

AddressMalbolge codeNormalizedComment
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
60JjMovD/Nop
61%iJmp

We have constructed the entire code section already. It remains to initialize the data section. We continue to use Lou Scheffer's valid instructions overview, to identify the memory cells we can directly initialize in the Malbolge code.

39: 60(<)-><(60) 43: 38(&)->v(118)
We are lucky: The 60 at address 39 and the 38 at address 43 can be initialized directly. The other two values of the data section – the 36 at address 40 and the 59 at address 42 – must not be written into the Malbolge code directly. We will solve this problem in the following. Before we do so, let us take a look at our current Malbolge program.

AddressMalbolge codeNormalizedComment
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
43&v38 (loop)
60JjMovD/Nop
61%iJmp

Runtime initialization of memory cells

We have to initialize the remaining memory cells of the data section now. Since this cannot be done directly, the memory cells must be initialized during runtime. Thus, we have to write Malbolge code that initializes the remaining memory cells. For this task, use-once code is sufficient, i.e. we need not care about instruction cycles.
We need the ternary number system and the Opr instruction now, because data manipulation in Malbolge is only possible in ternary and by the two instruction Opr and Rot. For the ternary number system, you may want to use an online calculator. You may also want to look up the Opr instruction in Esolang's Malbolge documentation if you don't know it by heart yet.

Before runtime initialization begins

Before we write Malbolge code for runtime initialization, we should do some very basic initialization. Every Malbolge program begins with its three registers set to zero. We have to separate the code and data pointer before any Rot or Opr instruction is performed. Otherwise it is very likely that the reference interpreter will crash. We start our Malbolge program with a MovD instruction to separate both pointers (alternatively, a Jmp instruction would also work). A MovD instruction at address 0 is coded by an opening bracket, which has the ASCII value 40. Thus, in the next step, the D register will have the value 41. Our Malbolge program looks as follows now:

AddressMalbolge codeNormalizedComment
0(jMovD
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
43&v38 (loop)
60JjMovD/Nop
61%iJmp

As stated above, the A register is initialized with zero. This value, resp. 0t1111111111, is very useful, so let us save the A register before we do anything that changes its value. We can safe the register by performing an Opr instruction on a memory cell that does not contain any ternary 2. We can directly use the current address of the D register, address 41, which is perfect for this purpose: It is not used by our HeLL code and it can be initialized with 0t0000001111, which does not contain any ternary 2. Now our Malbolge program looks as follows:

AddressMalbolge codeNormalizedComment
0(jMovD
1=pOpr
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
41(v0t0000001111; will be 0t1111111111 after the 2nd step
43&v38 (loop)
60JjMovD/Nop
61%iJmp

Initialization of address 40

The first memory cell we will initialize is cell 40. We have to generate the value 36 there – 1100 in ternary. From Lou Scheffer's valid instructions overview, we know that address 40 can be initialized with 58 – ternary 2011. This value is close to 0t0000001100, because it can be transformed into the desired value by a single Opr with A=0t1111112011.
Malbolge tools: Opr(0t1111112011, 0t0000002011) = 0t0000001100
So, let us write code that sets the A register to 0t1111112011. This can be done by reading 0t0000000200 into the A register and doing a Opr in 0t0000002000. Both are easy tasks since 0t0000002000 is a valid ASCII code.
Our Malbolge code to initialize address 40 is as follows:

Rot 0t0000002000 // afterwards A = 0t0000000200
Opr A into 0t0000002000 // afterwards A = 0t1111112011
Opr A into 0t0000002011 at address 40 // afterwards [40] = 0t0000001100

The D register points at address 42 right now. However, we should not use address 42, because we need to initialize it later and we may want to write a value there that helps us for its initialization (like the value 0t0000002011 that we put at address 40. However, we are lucky and can initialize address 44 and address 45 with 0t0000002000, which is 54 in the decimal number system). We can use the Nop instruction to set the D register to 44, because it is incremented after every instruction. After two Nops, the D register points to address 44, so that we can perform the Rot instruction followed by an Opr instruction on address 45.
Our current Malbolge program:

AddressMalbolge codeNormalizedComment
0(jMovD
1=pOpr
2BoNop
3AoNop
4#*Rot
59pOpr
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
40:i0t0000002011, should become 0t0000001100 later
41(v0t0000001111; will be 0t1111111111 after the 2nd step
43&v38 (loop)
446i0t000002000; will be 0t0000000200 after the 5th step
456<0t000002000; will be 0t1111112011 after the 6th step
60JjMovD/Nop
61%iJmp

The A register contains the value 0t1111112011 now and the D register points to address 46. To complete initialization of address 40, the D register must be moved there for an Opr instruction. Therefore, we write the value 35 at address 46 and move the D register to address 36 by MovD. Afterwards, we add four Nop instructions to reach address 40, where we perform the Opr instruction.

AddressMalbolge codeNormalizedComment
0(jMovD
1=pOpr
2BoNop
3AoNop
4#*Rot
59pOpr
6"jMovD
7=oNop
8<oNop
9;oNop
10:oNop
113pOpr
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
40:i0t0000002011, will be 0t0000001100 after the 12th step
41(v0t0000001111; will be 0t1111111111 after the 2nd step
43&v38 (loop)
446i0t000002000; will be 0t0000000200 after the 5th step
456<0t000002000; will be 0t1111112011 after the 6th step
46#v35 (destination for D register)
60JjMovD/Nop
61%iJmp

Initialization of address 42

It remains to initialize memory cell 42 with the value 59 – 2012 in ternary. It is possible to initialize cell 42 with 0t0000002002, which can be changed to the target value by an Opr with A = 0t1111111101. 0t1111111101 can be generated by performing an Opr instruction with A = 0t0000000020 and [D] = 0t0000000000. We have written the value 0t1111111111 at address 41 before, which can be changed to 0t0000000000 by an Opr with itself.

Rot 0t1111111111 at address 41 // afterwards A = 0t1111111111
Opr A into 0t1111111111 at address 41 // afterwards [41] = 0t0000000000
Rot 0t0000000200 at address 44 // afterwards A = 0t0000000020
Opr A into 0t0000000000 at address 41 // afterwards A = 0t1111111101
Opr A into 0t0000002002 at address 42 // afterwards [42] = 0t0000002012

We start with setting the value at address 41 to 0t0000000000. At the moment, the D register points to address 41, because the last thing we did was initializing address 40. We can use the value 38 stored at address 43 to move the D register back to address 41 again and write the following code.

// D = 41, [D] = A = 0t1111111111
Rot
Nop
// D = 43, [D] = 38
MovD
// D = 39
Nop
Nop
// D = 41, [D] = 0t1111111111
Opr

Our Malbolge code is now at follows.

AddressMalbolge codeNormalizedComment
0(jMovD
1=pOpr
2BoNop
3AoNop
4#*Rot
59pOpr
6"jMovD
7=oNop
8<oNop
9;oNop
10:oNop
113pOpr
12y*Rot
137oNop
14xjMovD
155oNop
164oNop
17-pOpr
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
40:i0t0000002011, will be 0t0000001100 after the 12th step
41(v0t0000001111; will be 0t0000000000 after the 18th step
428i0t0000002002, should become 0t0000002012 later
43&v38 (loop)
446i0t000002000; will be 0t0000000200 after the 5th step
456<0t000002000; will be 0t1111112011 after the 6th step
46#v35 (destination for D register)
60JjMovD/Nop
61%iJmp

The D register points to address 42 now. We can rotate 0t0000000200 at address 44 to load 0t0000000020 into the A register, Opr it into address 41 and Opr the result into address 42.

// D = 42
Nop
Nop
// D = 44, [D] = 0t0000000200
Rot
// A = 0t0000000020
Nop
// D = 46, [D] = 35
MovD
// D = 36
Nop
Nop
Nop
Nop
Nop
// D = 41, [D] = 0t0000000000
Opr
// D = 42, A = 0t1111111101, [D] = 0t0000002002
Opr
// [42] = 0t0000002010

Our current Malbolge program:

AddressMalbolge codeNormalizedComment
0(jMovD
1=pOpr
2BoNop
3AoNop
4#*Rot
59pOpr
6"jMovD
7=oNop
8<oNop
9;oNop
10:oNop
113pOpr
12y*Rot
137oNop
14xjMovD
155oNop
164oNop
17-pOpr
182oNop
191oNop
20q*Rot
21/oNop
22pjMovD
23-oNop
24,oNop
25+oNop
26*oNop
27)oNop
28"pOpr
29!pOpr
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
40:i0t0000002011, will be 0t0000001100 after the 12th step
41(v0t0000001111; will be 0t1111111101 after the 29th step
428i0t0000002002, will be 0t0000002012 after the 30th step
43&v38 (loop)
446i0t000002000; will be 0t0000000020 after the 21th step
456<0t000002000; will be 0t1111112011 after the 6th step
46#v35 (destination for D register)
60JjMovD/Nop
61%iJmp

Running the initialized code

It remains to start the completely initialized HeLL program. Therefore we only need to perform a Jmp instruction while the D register points to the entry point (labeled with ENTRY in HeLL), which is at address 40. At the moment, the D register points to 43. After a MovD instruction, it will point to address 39. Thus, we add the following code:

MovD
Nop
Jmp

The final Malbolge program looks as follows.

AddressMalbolge codeNormalizedComment
0(jMovD
1=pOpr
2BoNop
3AoNop
4#*Rot
59pOpr
6"jMovD
7=oNop
8<oNop
9;oNop
10:oNop
113pOpr
12y*Rot
137oNop
14xjMovD
155oNop
164oNop
17-pOpr
182oNop
191oNop
20q*Rot
21/oNop
22pjMovD
23-oNop
24,oNop
25+oNop
26*oNop
27)oNop
28"pOpr
29!pOpr
30hjMovD
31%oNop
32BiJmp
37P/In/Nop/Out/Nop/Nop/Nop/Nop/Nop/Nop
38<iJmp
39<<60 (R_MOVD)
40:i0t0000002011, will be 0t0000001100 after the 12th step
41(v0t0000001111; will be 0t1111111101 after the 29th step
428i0t0000002002, will be 0t0000002012 after the 30th step
43&v38 (loop)
446i0t000002000; will be 0t0000000020 after the 21th step
456<0t000002000; will be 0t1111112011 after the 6th step
46#v35 (destination for D register)
60JjMovD/Nop
61%iJmp

We are lucky that the entire initialization code fits into the memory before address 37.

Finishing our work

The only thing that is left to obtain our final Malbolge program is to fill the unused memory cells – addresses 33 to 36 and addresses 47  59 – with arbitrary valid instructions. We randomly choose the Hlt instruction for this purpose and obtain the final Malbolge cat program:

(=BA#9"=<;:3y7x54-21q/p-,+*)"!h%B0/.
~P<
<:(8&
66#"!~}|{zyxwvu
gJ%

Note that this program does not terminate.

Further readings

You have learned some very basic Malbolge programming techniques. Below you can find some resources that may help you to improve your Malbolge skills.

Please visit my other Malbolge and Malbolge Unshackled pages!
You may also write me an email: matthias@lutter.cc