Skip to content

Instantly share code, notes, and snippets.

@AMiller42
Last active January 27, 2024 03:52
Show Gist options
  • Save AMiller42/96daeb2ccf32f88cc0df212a5dba59b8 to your computer and use it in GitHub Desktop.
Save AMiller42/96daeb2ccf32f88cc0df212a5dba59b8 to your computer and use it in GitHub Desktop.

Vim Unshackled

💎

All explanations created with the help of Luminespire.

Helper Functions

These macros are needed for various common functions performed by other functions.

Macro: @f - Convert From Trinary
Changes a memory cell from, e.g. "m0 0 22" to "m0 0 8", and leaves the original value in @x

^WWD                                                                            # ‎⁡Delete the current memory value into @- and @"
    :let@x=@-                                                                   # ‎⁢Set @x equal to @-
             |let@"=                                                            # ‎⁣Set @" equal to:
                         map(split(@",'\zs'),                           )       # ‎  For each trit x in @":
                         map(               ,{a,b->           pow(3,a) })       ‎⁢⁡    Index of x raised to the power of 3...
                         map(               ,{a,b->  float2nr(        )})       ‎⁢⁢    cast to int...
                         map(               ,{a,b->b*                  })       ‎⁢⁣    multiplied by x
                    join(                                                ,'+')  ‎⁢⁤    Joined by '+'
# ‎⁣⁡Now @" is a string of the form x3^0 + x3^1 + x3^2 + x3^3...
"=<C-R>"                                                                        # ‎⁣⁢Execute @" as an expression
p                                                                               # ‎⁣⁣Paste the result
Macro: @t - Convert To Trinary
Changes a memory cell from, e.g. "m0 0 8" to "m 0 0 22"

^WWD                      # ‎⁡Delete the current memory value into @"
    :wh@">2               # ‎⁢While the number in @" is greater than 2:
norm"=<C-V><C-R>"%3<C-V>  # ‎⁣  Modulo @" by 3 to get the next trit,
p                         # ‎⁤  Paste the trit
endw                      # ‎⁢⁢End while
p                         # ‎⁢⁣Paste the final trit
Macro: @w - Get Value w/o Repeated Digits
Sets register @- to the currently selected memory value, deduplicating the highest trit

^WxP                       # ‎⁡Copy most significant trit (MST) into @-
    Wy$                    # ‎⁢Copy memory value into @0
       :s/<C-R>-*$         # ‎⁣Trim all of MST from the end of the value...
                  /<C-R>-  ‎⁤  and replace them with a single MST
^WWD                       # ‎⁢⁡Delete the modified value into @-
    "0p                    # ‎⁢⁢Replace the original value
Macro: @v - Increment
Increments the current memory value

@f@t@f                                  # ‎⁡Convert value from, to, from trinary (This is necessary so to remove leading 0s from the trinary value)
      <C-A>@t                           # ‎⁢Increment number and convert back to trinary
             ^WWy$                      # ‎⁣Copy memory value to @"
                  :if len(@0)!=len(@x)  # ‎⁤If the incremented number is longer than the original:
norm$x^Wx"=@-%2<C-V>                    # ‎⁢⁡  Increment the most significant trit,
P                
 $"=(@-+1)%3<C-V>                       # ‎⁢⁢  Carry the trit
p
en                                      # ‎⁢⁣End if
Macro: @g - Get Memory Value
Move cursor to the cell pointed to by the current value. Sets @" to the new value, and @- to the new value, from trinary

mg                                 # ‎⁡Place a marker `g at the current cell
  @w                               # ‎⁢Copy deduplicated memory value to @-
    gg/m<C-R>- \|m!                # ‎⁣Find cell pointed to by @- if it exists, or cell '!'
WWy$                               # ‎⁤Copy memory value to @"
    :let@s=@-                      # ‎⁢⁡Copy @- to @s for later use
             |if@"==-1             # ‎⁢⁢If at cell '!' (value is -1):
# ‎⁢⁣Initialize new memory cell:
norm`g                             # ‎⁢⁤  Jump back to `g
      ^WxP                         # ‎⁣⁡  Copy most significant trit (MST) to @-
          Wyll                     # ‎⁣⁢  Copy least significant trit to @"
let@x=4*@-+@"+6-@-                 # ‎⁣⁣  Set @x to (4 * @-) + @" + 6 - @-
norm y$                            # ‎⁣⁤  Copy remaining trits of value to @"
for_ in split(@",'\zs')            # ‎⁤⁡  For each trit _ in @":
let@x=(@x+3*(_+6-@-))%6            # ‎⁤⁢    Set @x to (@x + (3 * (_ + 6 - @-))) % 6
endfo                              # ‎⁤⁣  End for
let@x=@x+1                         # ‎⁤⁤  Increment @x
norm`n@xjy$                        # ‎⁢⁡⁡  Copy initial_values[@x - 1] to @"
           2Gom<C-R>s              # ‎⁢⁡⁢  Initialize cell @s...
                      <C-V><C-R>"  ‎⁢⁡⁣    with value @"
en                                 # ‎⁢⁡⁤End if
# ‎⁢⁢⁡End cell initialization. Whether or not initialization was needed, the cursor is now at the desired cell.
@f^WWD                             # ‎⁢⁢⁢Convert value from trinary, save to @x
      "xp                          # ‎⁢⁢⁣Restore original value
         :let@"=@x                 # ‎⁢⁢⁤Set @" equal to @x
Macro: @o - Crazy Operation
Uses @n and @m to do opr(@n, @m), saves result to @m (opr formula taken from https://codegolf.stackexchange.com/a/167684/101522)

:let@"=len(@n)-len(@m)                                                                   # ‎⁡Set @" equal to len(@n) - len(@m)
                      |if@">0                                                            # ‎⁢If @n is longer than @m:
let@m=@m.repeat(@m[len(@m)-1],@")                                                        # ‎⁣  Pad @m with @m[-1] to the length of @n
elsei@"<0                                                                                # ‎⁤Else if @m is longer than @n:
let@n=@n.repeat(@n[len(@n)-1],-@")                                                       # ‎⁢⁡  Pad @n with @n[-1] to the length of @m
en                                                                                       # ‎⁢⁢End if
  |let@m=                                                                                # ‎⁢⁣Set @m equal to:
         join(                                                                     ,'')  # ‎⁢⁤  Concatenation of:
              map(split(@n,'\zs'),                                                )      # ‎⁣⁡    Each trit x in @n:
              map(               ,{a,b->               b+(@m[a]*512)+8           })      ‎⁣⁢        x + (@m[index of x] * 512) + 8...
              map(               ,{a,b->           pow(               ,2)        })      ‎⁣⁣        squared...
              map(               ,{a,b->  float2nr(                      )       })      ‎⁣⁤        cast to int...
              map(               ,{a,b-> (                                %82)           ‎⁤⁡        modulo by 82...
              map(               ,{a,b->(                                     %3)})      ‎⁤⁢        modulo by 3
Macro: @e - Command Encipher
Translate (C) using the lookup table if 32 < (C) < 127

/rc                                # ‎⁡Jump to (C) copying the base10 to @-
@g           
  ^Wyl:if@"<1                      # ‎⁢If most significant trit (MST) of (C) is 0:
let@"=@-                           # ‎⁣  Set @" equal to @-
en                                 # ‎⁤End if
  |if@">32&&@"<127                 # ‎⁢⁡If 32 < @" < 127:
let@"=nr2char(@")                  # ‎⁢⁢  Cast @" to char
if@"=="/"                          # ‎⁢⁣  If @" is '/' (breaks regexes):
let@"='\/'                         # ‎⁢⁤    Escape @"
en                                 # ‎⁣⁡  End if
norm`x                             # ‎⁣⁢  Jump to lookup table
      /\V<C-V><C-R>"<C-V>          # ‎⁣⁣  Jump to @" in lookup table
j"zyl                              # ‎⁣⁤  Copy lookup value to @z
     /rc<C-V>                      # ‎⁤⁡  Jump to (C)
@g                               
  ^WWC                   @z        # ‎⁤⁢  Replace (C) with @z...
<C-V><ESC>  
      <C-V><C-R>=char2nr(@z)<C-V>  ‎⁤⁣    cast to int
          @t                       # ‎⁤⁤  Convert to trinary
en                                 # ‎⁢⁡⁡End if
Macro: @u - Update Rotwidth
Increases rotwidth by 5 if len(D) > rotwidth/2

/rd                    # ‎⁡Save deduplicated register D to @-
@w                   
  +Wy$                 # ‎⁢Copy current rotwidth to @"
      :if@0/2<len(@-)  # ‎⁣If len(@-) is more than half the current rotwidth:
let@0=@0+5             # ‎⁤  Increase rotwidth by 5
norm^WD"0p             # ‎⁢⁡  Save new rotwidth
Macro: @h - Get Command
Saves the number for the next command to @z, i.e. (C + (C)) % 94

/rc                  # ‎⁡Jump to register C
@f^WWD               # ‎⁢Convert from trinary and copy to @-
      "xp            # ‎⁣Restore original value
         :let@z=@-   # ‎⁤Save @- to @z for later use
@g                   # ‎⁢⁡Copy (C) to @-
  :let@z=(@-+@z)%94  # ‎⁢⁢Set @z to (@- + @z) % 94
Macro: @d - Read Command
Copy the next character of the program into memory and convert to trinary (assumes only valid programs)

gg"zx                                   # ‎⁡Delete next char of program into @z
     /ra                                # ‎⁢Jump to register A
^WWD"=_                                 # ‎⁣Replace register A with variable _ (variable set by outer for loop)
p                                     
 @t@w                                   # ‎⁤Copy deduplicated trinary form to @-
     ggjom<C-R>-                        # ‎⁢⁡Create new memory cell with address @-...
                 0               @z     # ‎⁢⁢and set initial value to @z...
                   <C-R>=char2nr(@z,1)  # ‎⁢⁣cast to int...
<ESC>@t                                 # ‎⁢⁤converted to trinary

Commands

These are the macros for the actual Malbolge Unshackled commands.

Note: Commands v (hlt) and o (NOP) don't need a macro.
Macro: @i
Command: i (jmp)

/rd        # ‎⁡Copy (D) to @0
@g^Wy$   
      /rc  # ‎⁢Replace register C with @0
WD"0p
Macro: @a
Command: < (out)

/ra                                        # ‎⁡Jump to register A
@f                                         # ‎⁢Convert from trinary
  ^Wyl                                     # ‎⁣Copy most significant trit (MST) to @0
      WD                                   # ‎⁤Move value to @-
        "xp                                # ‎⁢⁡Restore original value
           `o<DEL>                         # ‎⁢⁢Jump to end of output
                  :if@0.@-[len(@-)-1]==21  # ‎⁢⁣If highest trits are 21:
norm o                                     # ‎⁢⁤  Print newline to output
el                                         # ‎⁣⁡Else:
norm A              @-                     # ‎⁣⁢  Print @- to end of output...
      <C-R>=nr2char(@-,1)                  # ‎⁣⁣  cast to char
en                                         # ‎⁣⁤End if
Macro: @b
Command: / (in)

`i+x                  # ‎⁡Move next char of input to @-
    /ra               # ‎⁢Jump to register A
Wr0                   # ‎⁣Replace most significant trit (MST) with 0
   WD"=        @-     # ‎⁤Replace value with @-...
p  
     "=char2nr(@-,1)  # ‎⁢⁡cast to int
 @t                   # ‎⁢⁢Convert to trinary
Macro: @c
Command: * (rot)

/rw                                # ‎⁡Copy current rotwidth to @y
W"yy$                         
     -@g                           # ‎⁢Jump to (D)
        ^W"xyl                     # ‎⁣Copy most significant trit (MST) to @x
              Wy$                  # ‎⁤Copy value to @"
                 :if@y>len(@")     # ‎⁢⁡If @" is shorter than the rotwidth:
norm A<C-R>=repeat(@x,@y-len(@"))  # ‎⁢⁢  Pad @" to rotwidth using MST
en                                 # ‎⁢⁣End if
^WWx                               # ‎⁢⁤Delete least significant trit (LST) of value...
    $p                             ‎⁣⁡  and rotate it to the top
      ^Wy$                         # ‎⁣⁢Copy value with MST to @0
          /ra                      # ‎⁣⁣Replace register A with @0
WD"0p
Macro: @j
Command: j (movd)

/rd        # ‎⁡Jump to (D)
@g       
  ^Wy$     # ‎⁢Copy value to @0
      /rd  # ‎⁣Jump to register D
WD"0p      # ‎⁤Replace the value with @0
     @u    # ‎⁢⁡Update rotwidth
Macro: @p
Command: p (opr)

/ra                                                         # ‎⁡Jump to register A
Wyl$p                                                       # ‎⁢Copy most significant trit (MST) to the top of the value
     ^WW"ny$                                                # ‎⁣Copy value to @n
            ++@g                                            # ‎⁤Jump to (D)
                ^Wyl$p                                      # ‎⁢⁡Copy MST to the top of the value
                      ^WW"my$                               # ‎⁢⁢Copy value to @m
                             @o                             # ‎⁢⁣Run the crazy op
                               ^WWD"mp                      # ‎⁢⁤Replace value with result
                                      x^WR<C-R>"<ESC>       # ‎⁣⁡Move top trit back to MST
                                                     y$     # ‎⁣⁢Copy final value to @0
                                                       /ra  # ‎⁣⁣Jump to register A
WD"0p                                                       # ‎⁣⁤Replace value with @0

Memory Initialization

Most of the initialization is simply typing characters in different places, so here's the parts that are of note.

# Set marker `x, then print the printable ASCII codepage
     mx:for_ in range(32,126)
norm A<C-V><C-R>=nr2char(_)<C-V>
endfo


# Call macro @d for each character in the program
                   :for_ in range(len(@x))
norm @d
endfo


# Too tired to explain properly, but this extrapolates the 6 default cell values from the existing memory, and stores it after the `n marker
           /m<C-R>"
^W"zD"zpj^Wy$`no<ESC>"0p^2x$pxo<ESC>"zp^2x$px:norm<C-R>=repeat("-\"my$+\"ny$@oo<C-V><ESC>\"mp",18-((len(@x)%6)))
G5-d`nO%<ESC>mnj:g/^\d/norm$x^Pa 

Main Execution Loop

:wh1             # ‎⁡While true:
norm@h           # ‎⁢  Get next command, stored in @z
if@z             # ‎⁣  Command check:
norm@i           # ‎⁤    If command 4: Run @i (jmp)
norm@a           # ‎⁢⁡    If command 5: Run @a (out)
norm@b           # ‎⁢⁢    If command 23: Run @b (in)
norm@c           # ‎⁢⁣    If command 39: Run @c (rot)
norm@j           # ‎⁢⁤    If command 40: Run @j (movd)
norm@p           # ‎⁣⁡    If command 62: Run @p (opr)
brea             # ‎⁣⁢    If command 81: Break the loop (hlt)
en               # ‎⁣⁣  End if (All other command numbers are NOP)
norm @e          # ‎⁣⁤  Encipher (C)
       /rc<C-V>  # ‎⁤⁡  Increment register C
@v   
  j@v            # ‎⁤⁢  Increment register D
endw             # ‎⁤⁣End while
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment