A Pathological Way to Paint: Gammaplex

May 11 2007 Published by under pathological programming

Today's a mighty cool example of bizzare language design, called GammaPlex In terms of language
design, it's nothing particularly special: it's yet another stack language
with a befunge-like graphical syntax. What's unusual about GammaPlex is that it's strongly focused on graphics. It's got built in support for ascii graphics, OpenGL, and mouse input.

Gammaplex is by far the most complicated of the pathological languages that I've discussed. It's got a lot of instructions, and a lot of weird tricky little things in how the machine works - and frankly, most of them aren't particularly interesting. The basic computational machine underlying Gammaplex is a stack machine with random-access memory. Sort of a three-way cross between Befunge (for syntax and control flow), BrainFuck (for memory access), and False (for basic stack semantics).

The first 16 locations in memory are special. Locations 0 and 1 are named X and Y; they're the x and y indices of a pixel, called position A; 2,3,4 (called R, G, and B) form an RGB color value; and 5 is a radius - the size of a round pixel brush centered on position A which will be used to draw by various instructions. Locations 6 and 7 (named X2 and Y2) are the x and y indices of a second pixel, position B, with 8, 9, and 10 (R2, G2, and b2) as its RGB values. 11 is a transparency value for openGL drawing. 12 is a drawing style. 13, 14, and 15 are reserved - you can't use them, but they don't actually do anything.

There are also a bunch of special registers for doing things like accessing memory, and manging subroutine calls. The main ones that get manipulated by a program are:

  1. IP: the instruction pointer. An X,Y values of the current instruction pointer location on the playfield.
  2. ID: the instruction direction: the direction that the IP will move to find the next instruction.
  3. RP: the register pointer. Most memory-access instructions will use this to find memory locations. Like Brainfuck, the main way of selecting memory locations is just by incrementing and decrementing this.

Like I said, there are a ton of instructions. Most of them are very basic BrainFuck-like instructions, with the addition of a stack which is used for subroutine calls. The subroutine-stuff looks a lot like False. Instead of spending time walking through instructions, I'll just take you through a couple of examples, and explain the instructions as we encounter them.

X"Hello World!"XXSXrRE

Our old friend, the hello world program. Quite simple in Gammaplex:

  1. 'X"..."X' pushes the characters between the quotes onto the stack in order. So this puts the characters "Hello world!" onto the stack with "H" on the bottom, and "!" on the top.
  2. 'XS': mirror the stack - so what was on the bottom is now on top, and vice versa. So now "H" is on top, and "!" is on the bottom.
  3. 'Xr': print out the characters on the stack. This renders them as "character pixels" onto a graphic output field. They'll be drawn in white, since we didn't do anything to change the RGB registers, which default to white. The characgters aren't yet visible when this is done: they've been written into an output field, but the field has not been displayed.
  4. 'R': redraw the screen from the output field. Now the message is shown.
  5. 'E': end execution.
@v     >   r108wv  #        >         r100rv
2     1   #       #        8        #     >v
5     0   #       #        0       #       R
5     1   #       #        1       #       v
#######   v11rr     ^   >2r119  r111r    ^######   #####

A much cooler hello world. It's really trivial, but cute. Basically, it pushes the ASCII values of the characters of "Hello world!" onto the stack, and then renders them. But it's got a bunch of control flow modifiers mixed in so that it follows an execution path through the world. It starts at the "@" in the upper left. The "v" makes the intstruction pointer go down, so it executes the characters in the left leg of the H top to bottom; then at the bottom, it turns right, moves over to the right leg of the H, and then starts to go up, executing the right leg bottom to top. And so on.

X"Gammaplex"XXS0#0nv
v    )]21#4201}a4  t650:T124*124+t400v
^DcRrXwX}a1+29*29T:<

Bounce the word "GammaPlex" around the screen. This one is quite non-trivial despite its small size.

  1. 'X"Gammaplex"X': Push "Gammaplex" onto the stack.
  2. 'XS': mirror the stack, to get the characters in the right order.
  3. '0#0n4a}1024#': set the colors so that it renders with a gradient from white to red.
  4. "12])": basically sets things up for a loop that follows. The loop is done
    using nothing but layout: there are ">", "v", "<" and "^" at the corners, which will keep the instruction pointer looping around.
  5. 't650:T124*124+t400': 't' pushes the current time in milliseconds onto the stack; '650:' divides in by 650 (basically an arbitrary figure which is used to set the speed at which it will draw stuff); T takes the cosine of whatever is on top of the stack; then multiplies it by 124 and adds 124 (again, arbitrary constants - these set offets and ratios to keep the words within a specific rectangle on the screen). This results in setting up a "Y" position for where to print "Gammaplex".
  6. ':T92*92+': a similar trick for computing a y value for where to print "Gammaplex".
  7. '1}aXwXrR': prints the string at the locations on top of the stack.
  8. 'c': clear the view buffer in preparation for the next loop iteration.
  9. 'D': discard the value left on top of the stack.

So it will basically just keep using the time value to pick a new random location in which to put the word "gammaplex", and loop forever doing that.

I'm not going to explain it, but the following program is what actually caught my interest about gammaplex. It's a program to generate an OpenGL rendered colored image of the Mandelbrot set:

@   200)u150)l1a0#0}>1a{"}4#0XG  v
Mandelbrot by       ^   v?,y(]0  1a{s"sN}v
v0.1                ^    E?,h(]1R36](")34](32])35](33])32](w*33](
w*-30](+34])2#32](*33](*31](+35])
34](w*35](w*+4,?v36](16,?v7#0G   
G0#11<        >12#0G  
>255#255#36](16*H3a}PXg
>128#128#128#3a}PXg

mand.png

The numbers "200" and "150" are the dimensions of the image to generate - to generate a larger image, you just change those. Unfortunately, as a brain-dead pathological language, it's really slow. I tried to use the program on my linux box to render an example, and after 10 minutes, I gave up. It's churning away, but man it's slow. I don't think it needs to be so slow - but the interpreter is very much proof-of-concept code, not well-optimized stuff.

And even more incomprehensible: here's "pong" controlled by the mouse! Astonishingly, this actually goes fast enough to be playable!

#001#033GThisProgramWasWrittenByTron3k-tron3k_at_
gmail.com_BTW..._itWASreallyHARD8]103)9]103)18]15
8)t19])184]128)184](19](%19])19](64+19])2]255)3]2
55)4]255)99]0)20]1)21]1)20](_20])c8](50+30])0]5)8
](1])6]15)8](7])L30](1])30](7])L0]5)8](1])6]5)30]
(7])L0]15)6]15)L005#032Gomfglol9](50+30])0]250)6]
240)9](1])9](7])L30](1])30](7])L0]240)6]240)9](1]
)30](7])L0]250)6]250)L5]8)18](0])19](1])F0]10)1]1
)180](iD0]240)181](iDR18](20](+18])19](21](+19])
30]8)30](19](,31])31](!31])010#004#31](?GDD21](_2
1])30]247)19](30](,31])31](!31])011#009#31](?GDD2
1](_21])011#028GTRON3KRULESM22])23])8](25+31])31]
(23](,30])012#043#30](?GDD8](1+8])013#003G#8](1-8
])013#025#99](?GDD99]20)99](')40]3)40](99](,41])0
15#031#41](?GDD9](25+31])31](19](,30])015#022#30]
(?GDD9](1+9])015#031G#9](1-9])44]23)18](44](=29])
29](!29])018#014#29](?GDD8](19](,34])018#014#34](
?GDD8](50+31])19](31](,32])018#014#32](?GDD20](_2
])18](2+18])44]232)18](44](=29])29](!29])020#048
#29](?GDD9](19](,34])020#048#34](?GDD9](50+31])19
](31](,32])020#048#32](?GDD20](_20])#18](2-18])40
]13)40](')021#005#40](?GDD30]0)18](30](,31])025#0
46#31](?GDD0]0)6]255)1]255)7]255)1](')7](')L40]25
)40](')023#002#40](?GDDR022#034#1](?GDD2]0)3]0)4]
)0]0)6]255)1]255)7]255)1](')7](')L40]25)40](')02
4#042#40](?GDDR024#025#1](?GDD181](")001#033G30]2
55)30](18](,31])030#018#31](?GDD0]0)6]255)1]255)7
]255)1](')7](')L40]25)40](')027#023#40](?GDDR027#
06#1](?GDD2]0)3]0)4]0)0]0)6]255)1]255)7]255)1]('
)7](')L40]25)40](')029#014#40](?GDDR028#046#1](?G
DD180](")001#033G003#034G><######################

No responses yet

Leave a Reply