A while back I played this joke on Ruby people. It was an elaborate joke, where I wrote a fully dynamic assembler in a couple weeks, and then did an entire presentation at RubyEnRails (Amsterdam) basically showing how my “literal machine” beat the pants of all the Ruby virtual machines. I touted EaRing as the perfect accessory to every Ruby company, because now every Ruby company can claim to be making a VM for Ruby.
I loved that joke, but I didn’t really understand how cool EaRing actually was. I kind of wrote it, and then tossed it on my server since I’d done it already and the problem wasn’t interesting anymore.
What made EaRing so weird and fun was that I only had a couple weeks to crank out something that would mostly work, which meant I had to break all the rules for an assembler.
The end result was what I originally thought of as just a funny weird duck. It was a “dynamic assembler”, which even the thought of made people’s heads explode.
What the hell is a dynamic assembler? I determined that it was an assembler because you wrote fairly real machine opcodes, but since the source was JIT compiled on the fly into a C data structure it was all loose and you could introspect on it. You could do all the things you’d do in a more dynamic language like list out functions, call them on the fly, rewrite them, change variables, find out what variables were available. Of course in the context of an assembler that’s pretty simple, since beyond modules and functions there’s not that much more structure.
Yet, it’s a kind of weird language, so there wasn’t much I could do with it.
Recently though I had a thought that I wanted to relearn assembly language, but I didn’t want it to be a pain in the ass. It struck me that if I added Garbage Collection to EaRing it’d be a pretty awesome tool for learning assembly language. It works on most computers, has a modern syntax, hides much of the nasty details away, but still exposes the important ones, and you can dump the raw native assembler bytecodes on x86 platforms.
With super GC powers EaRing could be even easier to use. Imagine still mucking with raw ram and learning about structure packing in assembly langauge, but not having to worry about managing the ram? That’d be a massive help for people trying to learn assembler.
So that’s what I did tonight instead of working on my Lamson documentation. I grabed the Boehm GC and made it work with EaRing by inventing a set of new “opcodes” for handling memory:
There’s two opcodes that are missing for “atomic” and “uncollectable” RAM, but those acted really weird. In fact, the whole work I did tonight is a gigantic hack that needs to be reworked into a more correct version. I’m positive there’s evil things the Boehm GC does that won’t sit right with GNU lightning.
The end result is that I have the following EaRing source working:
# This is a little EaRing sample file showing the new GC setup I've added.
# It's pretty rough right now, but if I run this file using:
#
# earing $ build/earing -f forever samples/gc_test.asm > /dev/null
#
# Then it'll loop forever, allocating ram and printing the results, but after
# 2 minutes this is the usage:
#
# 35136 earing 99.3% 1:55.19 1 13 28 388K 184K 1152K 19M
#
# So it's allocating and the GC is working just fine. Boehm is the man.
#
%library("libc.dylib", "libc")
### these are constants, they stick around for the
### life of the module, but they're more
### like ASM data regions, meaning you can
### modify them and shove stuff in them.
# this is a 'constant', but we can modify it
NUMBERS = "01234567890"
# this is a constant for the length
NUMBER_LEN = 11 # includes the \0
# yep, another one
ONE_K_OF_RAM = 1024
# what's this? just a sample showing using the [size] syntax
# to make a constant blob of ram
A_CHUNK = [ONE_K_OF_RAM]
# This function tests out the new allocator ops by making
# some ram, reallocing it, forcing it freed, forcing the GC
# to collect, then making and realloc again. To make sure
# the ram was made, it then copies from NUMBERS into this
# new ram and printing it out.
function allocator_test() : void
movi.ui(V1, 0)
# do a bunch of stupid GC operations
alloc(R2, 1024) # makes 1k, currently have to use a number
realloc(R2, 1) # realloc it to 1 byte
free(R2) # do a force free (not necessary)
collect() # make the GC collect
alloc(R2, 1024) # allocate some more
realloc(R2, 30) # reallocate it to 30 bytes
# now we copy NUMBERS into our new chunk of ram
next:
ldxi.c(R0, V1, NUMBERS)
stxr.c(R2, V1, R0)
addi.ui(V1, V1, 1)
blti.ui(next:, V1, NUMBER_LEN)
# and finally, let's print that out
prepare.i(1)
pusharg.p(R2)
finish(libc.puts)
end
# This is a function that just loops forever and runs
# allocator_test. It forces a call to the GC in the loop to
# test that out.
function forever() : void
again:
calli(self.allocator_test)
collect()
jmpi(again:)
end
# The main funciton just runs allocator_test 1000 times and
# exits. Easy.
function main() : void
movi.ui(V0, 0)
again:
calli(self.allocator_test)
addi.ui(V0, V0, 1)
blti.ui(again:, V0, 1000)
end
Right now it’s just something fun for me to hack on that’s not another protocol. I do have this theory that if I make EaRing contain all the usual goodies a dynamic language needs, then it’ll be easy to build a langauge on it. For me though, I want to use it as a nice slick assembly language learning tool, and only late maybe turn it into a full platform.
Well, that’s what I hacked on tonight.