--- /dev/null
+CC=./jwcc.lua
+AS=blarg-elf-as
+
+EXAMPLES=examples/easytest.bin examples/fact.bin examples/fib.bin
+
+all: sim/blargcpu2 $(EXAMPLES)
+
+STAMP_jwcc: jwcc.lua lib/backend/default.lua lib/parser/default.lua lib/tokenizer/default.lua
+ @echo "jwcc inputs changed; rebuilding all C programs"
+ @touch STAMP_jwcc
+
+sim/blargcpu2: sim/blargcpu2.c
+ gcc -O3 -o sim/blargcpu2 sim/blargcpu2.c -lreadline `sdl-config --cflags --libs`
+
+%.o: %.c STAMP_jwcc
+ $(CC) -c -o $@ $<
+
+%.elf: %.o examples/libblarg.o examples/blargcpu.ld
+ blarg-elf-ld -T examples/blargcpu.ld -o $@ $< examples/libblarg.o
+
+%.bin: %.elf
+ blarg-elf-objcopy -Obinary $< $@
+
--- /dev/null
+OUTPUT_ARCH(blarg)
+ENTRY(start)
+SECTIONS
+{
+ . = 0x0;
+ _text = .;
+ .text : {
+ _start = .;
+ *(.start)
+ . = ALIGN(2);
+ *(.text)
+ . = ALIGN(2);
+ *(.fixup)
+ . = ALIGN(2);
+ *(.gnu.warning)
+ . = ALIGN(2);
+ *(.rodata)
+ . = ALIGN(2);
+ *(.rodata.*)
+ . = ALIGN(2);
+ }
+ _etext = .;
+
+ . = 0x8000*2; /* bytes, not words */
+ _bss = .;
+ .bss : { *(.bss) }
+ _end = .;
+}
--- /dev/null
+int main()
+{
+ int iters = 0;
+ printint(1);
+ printint(2);
+ printint(3);
+ printint(iters);
+ printint(iters+1);
+ while (iters < 5)
+ iters = iters + 1;
+ return iters;
+}
\ No newline at end of file
--- /dev/null
+int dummy()
+{
+ return 0;
+}
+
+int mul(int in1, int in2) {
+ int result = 0;
+ while (in2 > 0) {
+ in2 = in2 - 1;
+ result = result + in1;
+ }
+ dummy();
+ return result;
+}
+
+int fact(int num) {
+ if (num == 0)
+ return 1;
+ return mul(num, fact(num-1));
+}
+
+int main() {
+ int i = 1;
+
+ while (i < 10)
+ {
+ printint(i);
+ printint(fact(i));
+ i = i + 1;
+ }
+ return i;
+}
--- /dev/null
+int main()
+{
+ int a = 1;
+ int b = 1;
+ int iters = 0;
+
+ printint(a);
+ while (iters < 15)
+ {
+ int tmp;
+ tmp = a + b;
+ a = b;
+ b = tmp;
+ printint(a);
+ iters = iters + 1;
+ }
+ return iters;
+}
\ No newline at end of file
--- /dev/null
+ .extern main
+ .global start
+ .section .start
+start:
+ mov r0, #_end
+ mov r1, #heapend
+ sto [r1], r0
+ mov sp, #stack
+ mov r0, #main
+ call sp, r0
+ push sp, r0 @ print the return value
+ mov r0, #printint
+ call sp, r0
+ mov r0, #0x1234 @ store to rom to terminate
+ sto [r0], r0
+
+ .section .text
+ .global malloc
+malloc:
+ mov r3, #heapend
+ ldr r1, [r3]
+ mov r2, r1
+ add r2, r0
+ mov r0, r1
+ sto [r3], r2
+ pop sp, pc
+
+ .global free
+free:
+ pop sp, pc
+
+ .global writestr
+writestr:
+1:
+ ldr r3, [r0] @ load in data
+ mov r1, #0x0 @ Is the loaded data equal to 0?
+ tst r3, r1 @ "" "" "" "" "" "" ""
+ moveq pc, #2f @ If so, bail out
+ mov r1, #0x4000 @ Write address
+ sto [r1], r3 @ Write the data out
+ mov r1, #0x1 @ Add one to the write address
+ add r0, r1 @ "" "" "" "" "" ""
+ mov pc, #1b @ Loop again
+2:
+ pop sp, pc
+
+ .global printint
+printint:
+ mov r0, #-2
+ add r0, sp
+ ldr r0, [r0]
+ mov r1, #0x4000
+ sto [r1], r0
+ pop sp, pc
+
+ .section .bss
+stack:
+ .space 32*2 @ bytes, not words
+heapend:
+ .word 0
--- /dev/null
+int j = 0;
+
+int fact(int num) {
+ if (num == 1)
+ return 1;
+ return num * fact(num-1);
+}
+
+int testreturn() {
+ return j;
+}
+
+int main() {
+ int i = 5;
+ int iters = 0;
+
+ printint(0);
+ i = 0;
+ while (i < 50000)
+ i = i + 1;
+ printint(1);
+
+ i = 0;
+ j = 50000;
+ while (i < j)
+ i = i + 1;
+ printint(2);
+
+ i = 0;
+ j = 50000;
+ while (i < testreturn())
+ i = i + 1;
+ printint(3);
+
+ printint(fact(10));
+ i = 5;
+ j = 0;
+ while (i > testreturn())
+ {
+ printint(i*5);
+ i = i - 1;
+ iters = iters + 1;
+ }
+ return iters;
+}
--- /dev/null
+GLOBAL := ( FUNCTION | VARIABLE )*
+FUNCTION := TYPE MODIFIERS _IDENTIFIER ARGSLIST ( ';' | STMTLIST )
+TYPE := 'int'
+MODIFIERS := '*'*
+ARGSLIST := '(' [ TYPE _IDENTIFIER ( ',' TYPE _IDENTIFIER)]* ] ')'
+STMTLIST := '{' ( VARIABLE | STATEMENT | STMTLIST )* '}'
+VARIABLE := TYPE MODIFIERS _IDENTIFIER [ = EXPRESSION ] ';'
+STATEMENT := ( ';' | EXPRESSION ';' | WHILE | IF | RETURN )
+WHILE := 'while' '(' EXPRESSION ')' CHUNK
+IF := 'if' '(' EXPRESSION ')' CHUNK
+RETURN := 'return' EXPRESSION ';'
+CHUNK := ( STMTLIST | STATEMENT )
+EXPRESSION := P13EXPR
+P13EXPR := P6EXPR ( '=' P6EXPR )*
+P6EXPR := P5EXPR ( '==' P5EXPR )*
+P5EXPR := P3EXPR ( ( '<' | '>' ) P3EXPR )*
+P3EXPR := P2EXPR ( ( '+' | '-' ) P2EXPR )*
+P2EXPR := _NUMBER ( ( '*' | '/' ) _NUMBER )*
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env lua
+require"lib/tokenizer/default"
+require"lib/parser/default"
+require"lib/deparser"
+require"lib/backend/default"
+
+local input,finaloutput,mode,cppopts
+mode = {"preprocess","compile","assemble"}
+cppopts = "-P -nostdinc -isystem include -traditional-cpp "
+local argn = 1
+while arg[argn] do
+ if arg[argn]:sub(1,1) == "-" then
+ -- parse as an option
+ local rem = arg[argn]:sub(2)
+ while true do
+ if rem:sub(1,1) == "E" then -- preprocess only
+ mode = {"preprocess"}
+ elseif rem:sub(1,1) == "S" then -- compile only
+ mode = {"preprocess","compile"}
+ elseif rem:sub(1,1) == "c" then -- compile and assemble
+ mode = {"preprocess","compile","assemble"}
+ elseif rem:sub(1,1) == "o" then -- output
+ if finaloutput then
+ print("jwcc: -o can only be specified once")
+ os.exit(254)
+ end
+ if rem:len() > 1 then
+ finaloutput = rem:sub(2)
+ break
+ else
+ if not arg[argn+1] then
+ print("jwcc: -o requires an option")
+ os.exit(254)
+ end
+ finaloutput = arg[argn+1]
+ argn = argn + 1
+ end
+ elseif rem:sub(1,1) == "D" then
+ if rem:lem() > 1 then
+ cppopts = cppopts .. "-D "..rem:sub(2).." "
+ break
+ else
+ if not arg[argn+1] then
+ print("jwcc: -D requires an option")
+ os.exit(254)
+ end
+ cppopts = cppopts .. "-D "..arg[argn+1].." "
+ argn = argn + 1
+ end
+ elseif rem:sub(1,1) == "I" then
+ if rem:lem() > 1 then
+ cppopts = cppopts .. "-I "..rem:sub(2).." "
+ break
+ else
+ if not arg[argn+1] then
+ print("jwcc: -I requires an option")
+ os.exit(254)
+ end
+ cppopts = cppopts .. "-I "..arg[argn+1].." "
+ argn = argn + 1
+ end
+ elseif rem:len() == 0 then
+ break
+ else
+ print("jwcc: unknown option \""..rem:sub(1,1).."\"")
+ os.exit(254)
+ end
+ rem = rem:sub(2)
+ end
+ else
+ if input then
+ print("jwcc: input can only be specified once")
+ os.exit(254)
+ end
+ input = arg[argn]
+ end
+ argn = argn + 1
+end
+
+if not input then
+ print("jwcc: no input specified")
+ os.exit(254)
+end
+
+local output
+for k,v in pairs(mode) do
+ if v == "preprocess" then
+ if input:match("%.c$") then
+ if not mode[k+1] and finaloutput then
+ output = finaloutput
+ else
+ output = input:gsub("%.c$", ".i")
+ end
+ -- do preprocessing
+ if os.execute("cpp "..cppopts.." "..input.." "..output) ~= 0 then
+ os.exit(1)
+ end
+ input = output
+ end
+ elseif v == "compile" then
+ if input:match("%.i$") then
+ if not mode[k+1] and finaloutput then
+ output = finaloutput
+ else
+ output = input:gsub("%.i$", ".s")
+ end
+ local f = assert(io.open(input, "r"))
+ local insrc = f:read("*all")
+ f:close()
+ tokenlist = tokenize(insrc)
+ parsetree = parse(tokenlist)
+ --deparse(parsetree)
+ if backend(parsetree, output) then
+ os.exit(1)
+ end
+ input = output
+ end
+ elseif v == "assemble" then
+ if input:match("%.s$") then
+ if not mode[k+1] and finaloutput then
+ output = finaloutput
+ else
+ output = input:gsub("%.o$", ".s")
+ end
+ if os.execute("blarg-elf-as --fatal-warnings -o "..output.." "..input) ~= 0 then
+ os.exit(1)
+ end
+ input = output
+ end
+ end
+end
--- /dev/null
+local outf = io.stderr
+local maxstack = 0
+local minstack = 0
+local labelnum = 0
+
+local regstate = {
+ r0 = {},
+ r1 = {},
+ r2 = {},
+ r3 = {}
+}
+
+local regvars = {}
+
+function regalloc_reset()
+ regvars = {}
+ for k,v in pairs(regstate) do
+ regstate[k] = {}
+ end
+end
+
+function regalloc_tmp()
+ for k,v in pairs(regstate) do -- first look for an empty one
+ if not v.contains then
+ regstate[k] = { contains = " tmp", name = k, locked = 0 }
+ table.insert(regvars, regstate[k])
+ return regstate[k]
+ end
+ end
+ for k,v in pairs(regstate) do -- now look for an unlocked variable to evict
+ if regalloc_unlocked(v) and v.contains ~= " tmp" then
+ regalloc_evict(v)
+ regstate[k] = { contains = " tmp", name = k, locked = 0 }
+ table.insert(regvars, regstate[k])
+ return regstate[k]
+ end
+ end
+ for k,v in pairs(regstate) do -- now look for an unlocked temporary to evict
+ if regalloc_unlocked(v) then
+ regalloc_evict(v)
+ regstate[k] = { contains = " tmp", name = k, locked = 0 }
+ table.insert(regvars, regstate[k])
+ return regstate[k]
+ end
+ end
+ error("register allocator: tried to allocate a temporary, but couldn't find anything to evict")
+end
+
+function regalloc_get(var)
+ for k,v in pairs(regvars) do
+ if v.contains == var then
+ return v
+ end
+ end
+ local reg = { contains = var, locked = 0, evicted = true, stackpos = var.stackpos }
+ table.insert(regvars, reg)
+ return reg
+end
+
+function regalloc_lock(reg)
+ if reg.evicted then
+ regalloc_unevict(reg)
+ end
+ reg.locked = reg.locked + 1
+end
+
+function regalloc_unlock(reg)
+ assert(reg.locked > 0)
+ reg.locked = reg.locked - 1
+end
+
+function regalloc_unlocked(reg)
+ return not reg.locked or reg.locked == 0
+end
+
+function regalloc_free(reg)
+ if reg.contains == " tmp" then
+ for k,v in pairs(regvars) do
+ if v == reg then
+ table.remove(regvars, k)
+ end
+ end
+ regstate[reg.name] = {}
+ reg.name = nil
+ reg.contains = nil
+ else
+ reg.locked = 0
+ end
+end
+
+function regalloc_savestate(save)
+ -- store all NON-evicted variables that MIGHT get evicted in the savestate
+ for k,v in pairs(regstate) do
+ if v.contains and regalloc_unlocked(v) then
+ save[v] = { var = v, reg = v.name, dirty = v.dirty }
+ end
+ end
+end
+
+function regalloc_restorestate(save)
+ -- restore any variables that got evicted
+ for k,v in pairs(regstate) do
+ if v.dirty and ((not save[v]) or (not save[v].dirty)) then
+ regalloc_clean(v)
+ end
+ end
+ for k,v in pairs(save) do
+ regalloc_unevict(v.var, v.reg)
+ end
+end
+
+function regalloc_printstate()
+ for k,reg in pairs(regstate) do
+ if not reg.contains then
+ print(k..": EMPTY")
+ for k1,v1 in pairs(reg) do
+ print(k.."."..tostring(k1)..": "..tostring(v1))
+ end
+ else
+ local name
+ if reg.contains == " tmp" then
+ name = "compiler internal variable"
+ elseif reg.contains.name then
+ name = reg.contains.name
+ else
+ name = "unknown variable"
+ end
+ print(k..": "..name.." ("..
+ (reg.evicted and "evicted" or "not evicted") .. ", "..
+ ((not regalloc_unlocked(reg)) and "locked" or "not locked") .. ", " ..
+ (reg.dirty and "dirty" or "clean") .. ")")
+ end
+ end
+end
+
+function regalloc_evict(reg)
+ assert(reg.contains, "can't evict nil")
+ assert(reg.contains ~= " tmp", "tmp eviction not implemented")
+
+ if reg.dirty then
+ regalloc_clean(reg)
+ end
+ reg.evicted = true
+ if reg.name then
+ regstate[reg.name] = {}
+ reg.name = nil
+ end
+end
+
+function regalloc_clean(reg)
+ assert(reg.contains, "can't clean nil")
+ assert(not reg.evicted, "register already evicted?")
+ assert(regalloc_unlocked(reg), "must be unlocked to clean")
+
+ local name = "unknown variable"
+ if type(reg.contains) == "string" then
+ name = reg.contains
+ elseif type(reg.contains) == "table" and reg.contains.name then
+ name = reg.contains.name
+ end
+ name = "`"..name.."'"
+
+ reg.dirty = false
+
+ -- first off, look for an easy eviction -- is there an empty register?
+ for k,v in pairs(regstate) do
+ if not v.contains then
+ local tmp = regalloc_tmp()
+ regalloc_lock(tmp)
+ emit_opcode("mov", tmp.name..", #"..(reg.stackpos-maxstack-1), "easy evict "..name.." to stack")
+ emit_opcode("add", tmp.name..", sp")
+ emit_opcode("sto", "["..tmp.name.."], "..reg.name)
+ regalloc_free(tmp)
+ return
+ end
+ end
+ -- damnit, we have to do an in-place evict
+
+ -- Step 1 -- push rN
+ emit_opcode("push", "sp, "..reg.name, "in-place evict "..name)
+ -- Step 2 -- Move offset from new sp to desired place into rN
+ emit_opcode("mov", reg.name..", #"..(reg.stackpos-maxstack-2))
+ -- Step 3 -- Add sp to result to get an absolute pointer
+ emit_opcode("add", reg.name..", sp")
+ -- Step 4 -- Load sp with original contents of rN
+ emit_opcode("pop", "sp, sp")
+ -- Step 5 -- Store original contents in address now stored in rN
+ emit_opcode("sto", "["..reg.name.."], sp")
+ -- Step 6 -- Move offset to get back to correct sp into sp
+ emit_opcode("mov", "sp, #"..(maxstack-reg.stackpos +1))
+ -- Step 7 -- Add rN to sp; sp is now restored
+ emit_opcode("add", "sp, "..reg.name)
+ -- In this case, the variable is toast. Sorry.
+ regstate[reg.name] = {}
+ reg.evicted = true
+ reg.name = nil
+end
+
+function regalloc_unevict(reg, wantreg)
+ assert(reg.contains, "can't unevict a nil pointer")
+ local name = "unknown variable"
+ if type(reg.contains) == "string" then
+ name = reg.contains
+ elseif type(reg.contains) == "table" and reg.contains.name then
+ name = reg.contains.name
+ end
+ name = "`"..name.."'"
+
+ if not reg.evicted and wantreg and wantreg ~= reg.name then
+ assert(regalloc_unlocked(regstate[wantreg]), "tried to unevict with a locked wantreg")
+ if regstate[wantreg].contains then
+ emit_opcode("push", "sp, "..wantreg, "unevict: swap "..name.." and "..wantreg)
+ emit_opcode("mov", wantreg..", "..reg.name)
+ emit_opcode("pop", "sp, "..reg.name)
+ regstate[reg.name] = regstate[wantreg]
+ regstate[reg.name].name = reg.name
+ regstate[wantreg] = reg
+ regstate[wantreg].name = wantreg
+ return -- wee!
+ else
+ emit_opcode("mov", wantreg..", "..reg.name, "unevict: move "..name.." to "..wantreg)
+ regstate[reg.name] = {}
+ regstate[wantreg] = reg
+ regstate[wantreg].name = wantreg
+ return -- double wee!
+ end
+ elseif not reg.evicted then
+ return -- well, if we don't actually have to do anything, ...!
+ else
+ assert(reg.contains ~= " tmp", "tmp uneviction not implemented")
+ assert(reg.evicted)
+ -- find a register
+ for k,v in pairs(regstate) do -- look for an empty
+ if not v.contains and not wantreg then
+ wantreg = k
+ end
+ end
+ for k,v in pairs(regstate) do -- look for a nontmp that's not dirty
+ if regalloc_unlocked(v) and v.contains ~= " tmp" and not v.dirty and not wantreg then
+ regalloc_evict(v)
+ wantreg = k
+ end
+ end
+ for k,v in pairs(regstate) do -- look for any nontmp
+ if regalloc_unlocked(v) and v.contains ~= " tmp" and not wantreg then
+ regalloc_evict(v)
+ wantreg = k
+ end
+ end
+ for k,v in pairs(regstate) do -- look for anything to evict
+ if regalloc_unlocked(v) and not wantreg then
+ regalloc_evict(v)
+ wantreg = k
+ end
+ end
+ assert(wantreg and regalloc_unlocked(regstate[wantreg]), "could not find an unlocked register to evict to")
+ emit_opcode("mov", wantreg..", #"..(reg.stackpos-maxstack-1), "unevict: pull "..name.." from stack to "..wantreg)
+ emit_opcode("add", wantreg..", sp")
+ emit_opcode("ldr", wantreg..", ["..wantreg.."]")
+ reg.name = wantreg
+ reg.evicted = false
+ regstate[wantreg] = reg
+ return
+ end
+ error("something got fucked up")
+end
+
+function regalloc_unevict_all_tmp()
+ for k,v in pairs(regvars) do
+ if v.evicted and v.contains == " tmp" then
+ regalloc_unevict(v)
+ end
+ end
+end
+
+function regalloc_destroy(variable)
+ for k,v in pairs(regvars) do
+ if v.contains == variable and not v.evicted then
+ regstate[v.name] = {}
+ v.name = nil
+ end
+ end
+end
+
+local cursection = nil
+function emit(text)
+ outf:write(text.."\n")
+end
+
+function emit_section(section)
+ if cursection == section then
+ return
+ end
+ emit("\t.section "..section)
+ cursection = section
+end
+
+function emit_label(label)
+ emit(label..":")
+end
+
+function emit_opcode(opcode, params, comment)
+ if comment then
+ emit("\t"..opcode.."\t"..params.."\t\t\t@ "..comment)
+ else
+ emit("\t"..opcode.."\t"..params)
+ end
+end
+
+local globalvariables = {}
+function do_globalvariable(var)
+ globalvariables[var.name] = { type = var.vtype }
+ if var.vtype == "int" then
+ local initializer = var.initializer or 0
+ emit_section(".bss")
+ emit_label(var.name)
+ emit_opcode(".word",tostring(initializer))
+ else
+ error("unknown variable emit in do_globalvariable")
+ end
+end
+
+function do_function(func)
+ local stack = {}
+
+
+ -- First, read in the arguments and register them as being on the stack.
+ for k,v in pairs(func.args) do
+ stack[-k] = { name = v.name, type = v.type, stackpos = -k }
+ assert(v.type == "int", "can only handle ints on the stack right now")
+ minstack = -k
+ end
+ stack[0] = {name = " return address", type = "int" } -- XXX not actually an int
+
+ regalloc_reset()
+ emit_section(".text")
+ emit("")
+ emit_opcode(".global", func.name)
+ emit_label(func.name)
+ local emitter = function(expr, emitter)
+ if expr.type == "stmtlist" then
+ local origstack = maxstack
+ local initialized = false
+ local savestate = {}
+ regalloc_savestate(savestate)
+ for k,v in pairs(expr.body) do
+ if v.type == "variable" then
+ assert(not initialized, "cannot have more variables after stack has been emitted")
+ maxstack = maxstack + 1
+ stack[maxstack] = { name = v.name, type = v.vtype, initializer = v.initializer, stackpos = maxstack }
+ else
+ if not initialized and origstack ~= maxstack then
+ local reg = regalloc_tmp()
+ regalloc_lock(reg)
+ emit_opcode("mov", reg.name..", #"..tostring(maxstack-origstack), "increase stack size for variables")
+ emit_opcode("add", "sp, "..reg.name)
+ regalloc_free(reg)
+ for k2,v2 in pairs(stack) do
+ if v2.initializer then
+ local data = regalloc_tmp() -- one for the data
+ local addr = regalloc_tmp() -- one for the stack address
+ regalloc_lock(data)
+ emit_opcode("mov", data.name..", #"..v2.initializer, "initialize variable "..v2.name)
+ regalloc_lock(addr)
+ emit_opcode("mov", addr.name..", #"..tostring(k2-maxstack-1))
+ emit_opcode("add", addr.name..", sp")
+ emit_opcode("sto", "["..addr.name.."], "..data.name)
+ regalloc_free(data)
+ regalloc_free(addr)
+ v2.initializer = nil
+ end
+ end
+ initialized = true
+ end
+ local reg = emitter(v, emitter)
+ if reg then
+ regalloc_free(reg)
+ end
+ end
+ end
+ if origstack ~= maxstack then
+ local reg = regalloc_tmp()
+ emit_opcode("mov", reg.name..", #"..tostring(origstack-maxstack), "decrease stack size for variables")
+ emit_opcode("add", "sp, "..reg.name)
+ regalloc_free(reg)
+ maxstack = origstack
+ for i=origstack+1,maxstack do
+ regalloc_destroy(stack[i])
+ stack[i] = nil
+ end
+ end
+ regalloc_restorestate(savestate)
+ elseif expr.type == "expression" then
+ return emitter(expr.value, emitter)
+ elseif expr.type == "funccall" then
+ -- push args on the stack from last to first
+ for arg=table.getn(expr.args),1,-1 do
+ local reg = emitter(expr.args[arg], emitter)
+ assert(reg, "argument "..arg.." did not return a register")
+ regalloc_lock(reg)
+ regalloc_unevict_all_tmp()
+ emit_opcode("push","sp, "..reg.name, "push argument "..arg.." onto the stack for function call "..expr.name)
+ regalloc_free(reg)
+ maxstack = maxstack + 1
+ end
+ -- meh, I hate fucking around with the allocator's internal state here, XXX
+ for k,v in pairs(regstate) do
+ assert(not(v.contains == " tmp" and not v.evicted), "it's too damn late now to evict tmp")
+ if not v.evicted and v.contains then
+ regalloc_evict(v)
+ end
+ end
+ local reg = regalloc_tmp()
+ regalloc_lock(reg)
+ emit_opcode("mov",reg.name..", #"..expr.name, "function call "..expr.name)
+ emit_opcode("call","sp, "..reg.name)
+ -- first thing: save the return value!
+ local retv = regalloc_tmp()
+ regalloc_lock(retv)
+ if reg.name == "r0" then -- try hard to get r0 for the return value
+ local tmp = reg
+ reg = retv
+ retv = tmp
+ end
+ if retv.name ~= "r0" then -- wee o_O
+ emit_opcode("mov", retv.name..", r0", "grab return value")
+ end
+ -- decrement the stack by the number of args
+ emit_opcode("mov",reg.name..", #-"..table.getn(expr.args), "clean up stack from function call")
+ emit_opcode("add","sp, "..reg.name)
+ maxstack = maxstack - table.getn(expr.args)
+ regalloc_free(reg)
+ return retv
+ elseif expr.type == "while" then
+ local headlabel, taillabel = labelnum, labelnum + 1
+ local savestate = {}
+ labelnum = labelnum + 2
+ emit_label(".L"..headlabel)
+ -- do expression
+ local resreg = emitter(expr.expression, emitter)
+ assert(resreg, "while expression did not return a register")
+ regalloc_lock(resreg)
+ local tmp = regalloc_tmp()
+ emit_opcode("mov",tmp.name..", #0")
+ emit_opcode("tst",tmp.name..", "..resreg.name)
+ emit_opcode("moveq","pc, #.L"..taillabel, "conditional break from while")
+ regalloc_free(resreg)
+ regalloc_free(tmp)
+ regalloc_savestate(savestate)
+ local tmp = emitter(expr.chunk, emitter)
+ if tmp then
+ regalloc_free(tmp)
+ end
+ regalloc_restorestate(savestate)
+ emit_opcode("mov","pc, #.L"..headlabel)
+ emit_label(".L"..taillabel)
+ elseif expr.type == "return" then
+ local reg = emitter(expr.expression, emitter)
+ assert(reg, "return expression did not return a register")
+ regalloc_unevict(reg, "r0")
+ regalloc_lock(reg)
+ assert(reg.name == "r0", "failed to unevict to r0?")
+ local tmp = regalloc_tmp()
+ regalloc_lock(tmp)
+ emit_opcode("mov", tmp.name..", #-"..maxstack, "clean up stack before return")
+ emit_opcode("add","sp, "..tmp.name)
+ emit_opcode("pop","sp, pc", "return")
+ regalloc_free(tmp)
+ regalloc_free(reg)
+ elseif expr.type == "if" then
+ local taillabel = labelnum
+ local savestate = {}
+ labelnum = labelnum + 1
+ local resreg = emitter(expr.expression, emitter)
+ assert(resreg, "if expression did not return a register")
+ regalloc_lock(resreg)
+ local tmp = regalloc_tmp()
+ emit_opcode("mov",tmp.name..", #0")
+ emit_opcode("tst",tmp.name..", "..resreg.name)
+ emit_opcode("moveq","pc, #.L"..taillabel, "conditional exit from if")
+ regalloc_free(resreg)
+ regalloc_free(tmp)
+ regalloc_savestate(savestate)
+ local tmp = emitter(expr.chunk, emitter)
+ if tmp then
+ regalloc_free(tmp)
+ end
+ regalloc_restorestate(savestate)
+ emit_label(".L"..taillabel)
+ elseif expr.type == "operator" and expr.value ~= "=" then
+ -- an arith oper
+ local lhs = emitter(expr.left, emitter)
+ local rhs = emitter(expr.right, emitter)
+ local tmp
+ assert(lhs, "lhs did not return a register")
+ assert(rhs, "rhs did not return a register")
+ regalloc_lock(lhs)
+ regalloc_lock(rhs)
+ if lhs.contains == " tmp" then
+ tmp = lhs
+ elseif rhs.contains == " tmp" then
+ tmp = rhs
+ end
+
+ if expr.value == "==" or expr.value == "<" or expr.value == ">" then
+ local predicates = { ["=="] = "eq", ["<"] = "lt", [">"] = "gt" }
+ if not tmp then
+ tmp = regalloc_tmp()
+ regalloc_lock(tmp)
+ end
+ -- tst is fucking backwards
+ emit_opcode("tst",rhs.name..", "..lhs.name, "comparison operator "..expr.value)
+ emit_opcode("mov",tmp.name..", #0")
+ emit_opcode("mov"..predicates[expr.value], tmp.name..", #1")
+ elseif expr.value == "+" then
+ if not tmp then
+ tmp = regalloc_tmp()
+ regalloc_lock(tmp)
+ end
+ if tmp == lhs then
+ emit_opcode("add",tmp.name..", "..rhs.name)
+ elseif tmp == rhs then
+ emit_opcode("add",tmp.name..", "..lhs.name)
+ else
+ emit_opcode("mov",tmp.name..", "..lhs.name)
+ emit_opcode("add",tmp.name..", "..rhs.name)
+ end
+ elseif expr.value == "-" then
+ if not tmp then
+ tmp = regalloc_tmp()
+ regalloc_lock(tmp)
+ end
+ emit_opcode("not", rhs.name)
+ local tmp2 = regalloc_tmp()
+ regalloc_lock(tmp2)
+ emit_opcode("mov",tmp2.name..", #1")
+ emit_opcode("add",rhs.name..", "..tmp2.name)
+ regalloc_free(tmp2)
+ if tmp == lhs then
+ emit_opcode("add",tmp.name..", "..rhs.name)
+ elseif tmp == rhs then
+ emit_opcode("add",tmp.name..", "..lhs.name)
+ else
+ emit_opcode("mov",tmp.name..", "..lhs.name)
+ emit_opcode("add",tmp.name..", "..rhs.name)
+ end
+ else
+ print("Dunno how to handle operator "..expr.value)
+ return nil
+ end
+
+ if lhs ~= tmp then
+ regalloc_free(lhs)
+ end
+ if rhs ~= tmp then
+ regalloc_free(rhs)
+ end
+ regalloc_unlock(tmp)
+ return tmp
+ elseif expr.type == "operator" and expr.value == "=" then
+ assert(expr.left.type == "identifier", "lhs has to be an identifier")
+ local lhs = emitter(expr.left, emitter)
+ local rhs = emitter(expr.right, emitter)
+ assert(rhs, "rhs did not return a register")
+ regalloc_lock(lhs)
+ regalloc_lock(rhs)
+ lhs.dirty = true
+ emit_opcode("mov",lhs.name..", "..rhs.name)
+ regalloc_free(rhs)
+ return lhs
+ elseif expr.type == "identifier" then
+ -- find the variable on the stack
+ local var
+ for k,v in pairs(stack) do
+ if v.name == expr.value and not var then
+ var = v
+ end
+ end
+ for k,v in pairs(globalvariables) do
+ if v.name == expr.value and not var then
+ var = v
+ end
+ end
+ assert(var, "I have no idea what identifier "..expr.value.." is")
+ return regalloc_get(var)
+ elseif expr.type == "number" then
+ local reg = regalloc_tmp()
+ regalloc_lock(reg)
+ emit_opcode("mov", reg.name..", #"..expr.value)
+ regalloc_unlock(reg)
+ return reg
+ else
+ error("unknown expression type "..expr.type)
+ end
+ end
+ emitter(func.body, emitter) -- call into it, and light it off
+ emit_opcode("pop", "sp, pc")
+end
+
+function backend(parsetree, outfn)
+ if outfn then
+ outf = io.open(outfn, "w")
+ else
+ outf = io.stdout
+ end
+ emit("@ blargCPU output generated by jwcc")
+ for k,v in pairs(parsetree) do
+ if v.type == "variable" then
+ do_globalvariable(v)
+ elseif v.type == "function" then
+ do_function(v)
+ else
+ error("invalid type in global deparse")
+ end
+ end
+ if outfn then
+ outf:close()
+ end
+ outf = io.stderr
+end
--- /dev/null
+blargcpu.lua
\ No newline at end of file
--- /dev/null
+variables = {}
+functions = {}
+
+
+
+function evaluate(expr, varlevel)
+ assert(varlevel)
+ if expr.type == "function" then
+ local retval,nvarlevel = evaluate(expr.body, varlevel)
+ assert((type(retval) == "nil" and expr.returntype == "void") or
+ (type(retval) == "number" and expr.returntype == "int"), "incorrect return type "..type(retval).." for function returning "..expr.returntype)
+ return retval,varlevel
+ elseif expr.type == "stmtlist" then
+ for k,v in pairs(expr.body) do
+ local retval
+ retval,varlevel = evaluate(v, varlevel)
+ if not varlevel then -- we've taken a 'return'
+ return retval,nil
+ end
+ end
+ return nil,varlevel
+ elseif expr.type == "variable" then
+ varlevel = varlevel + 1
+ variables[varlevel] = { name = expr.name, type = expr.vtype, value = expr.initializer }
+ return nil,varlevel
+ elseif expr.type == "expression" then
+ return evaluate(expr.value, varlevel), varlevel
+ elseif expr.type == "while" then
+ while evaluate(expr.expression, varlevel) ~= 0 do
+ local retval,donevarlevel = evaluate(expr.chunk, varlevel)
+ if donevarlevel == nil then
+ return retval, nil
+ end
+ end
+ return nil,varlevel
+ elseif expr.type == "if" then
+ if evaluate(expr.expression, varlevel) ~= 0 then
+ local retval,donevarlevel = evaluate(expr.chunk, varlevel)
+ if donevarlevel == nil then
+ return retval, nil
+ end
+ end
+ return nil,varlevel
+ elseif expr.type == "operator" then
+ local lhs = evaluate(expr.left, varlevel)
+ local rhs = evaluate(expr.right, varlevel)
+ if not (expr.value == "=" and lhs == nil) then -- This case is OK.
+ assert(type(lhs) == type(rhs), "operator "..expr.value.." on two different types: "..type(lhs).." and "..type(rhs))
+ end
+ if expr.value == "+" then
+ return lhs + rhs
+ elseif expr.value == "-" then
+ return lhs - rhs
+ elseif expr.value == "*" then
+ return lhs * rhs
+ elseif expr.value == "/" then
+ return lhs / rhs
+ elseif expr.value == ">" then
+ return lhs > rhs and 1 or 0
+ elseif expr.value == "<" then
+ return lhs < rhs and 1 or 0
+ elseif expr.value == "==" then
+ return lhs == rhs and 1 or 0
+ elseif expr.value == "=" then
+ -- blah
+ assert(expr.left.type == "identifier", "lvalue of operator '=' must be an identifier")
+ for k=varlevel,1,-1 do
+ if variables[k].name == expr.left.value then
+ assert(variables[k].type == "int")
+ variables[k].value = rhs
+ return rhs
+ end
+ end
+ error("unknown identifier "..expr.left.value.." in varlevel "..varlevel)
+ else
+ error("unknown operator "..expr.value.." in evaluate")
+ end
+ elseif expr.type == "number" then
+ return expr.value
+ elseif expr.type == "identifier" then
+ for k=varlevel,1,-1 do
+ if variables[k].name == expr.value then
+ return variables[k].value
+ end
+ end
+ error("unknown identifier "..expr.value)
+ elseif expr.type == "funccall" then
+ if not functions[expr.name] then
+ error("unknown function "..expr.name)
+ end
+ assert(table.getn(expr.args) == table.getn(functions[expr.name].args), "wrong number of args for function "..expr.name)
+
+ local orig_varlevel = varlevel
+ local vars_to_move = {}
+ for k,v in pairs(expr.args) do
+ assert(functions[expr.name].args[k].type == "int", "wrong type for argument #"..k.." to function "..expr.name)
+ varlevel = varlevel + 1
+ vars_to_move[varlevel] = { type = v.type, name = functions[expr.name].args[k].name }
+ vars_to_move[varlevel].value = evaluate(v.value, orig_varlevel)
+ end
+ for k,v in pairs(vars_to_move) do
+ variables[k] = vars_to_move[k]
+ end
+
+ if functions[expr.name].native then
+ return functions[expr.name].native(expr, varlevel), orig_varlevel
+ else
+ local retval, varlevel = evaluate(functions[expr.name].body, varlevel)
+ assert(not varlevel)
+ return retval, orig_varlevel
+ end
+ elseif expr.type == "return" then
+ local retval = nil
+ if expr.expression then
+ retval = evaluate(expr.expression, varlevel)
+ end
+ return retval,nil
+ else
+ error("unknown type "..expr.type.." in evaluate")
+ end
+ error("fell through? type was "..expr.type)
+end
+
+functions["printint"] = { returns = "void", args = { { type = "int", name = "toprint" } },
+ native = function(expr, varlevel)
+ print("NATIVE CALL: printint("..variables[varlevel].value..")")
+ end }
+
+function backend(parsetree, outfn)
+ for k,v in pairs(parsetree) do
+ if v.type == "variable" then
+ table.insert(variables, { name = v.name, type = v.vtype, value = v.initializer })
+ elseif v.type == "function" then
+ functions[v.name] = v
+ else
+ error("invalid type in global deparse")
+ end
+ end
+
+ local retval,varlevel = evaluate(functions["main"], table.getn(variables))
+ print("main returned "..retval)
+end
--- /dev/null
+function errprint(s)
+ io.stderr:write(s.."\n")
+end
+
+function deparse(parsetree)
+ function printtable(spaces, tbl)
+ if table.getn(tbl) == 1 and tbl[1] then
+ printtable(spaces, tbl[1])
+ return
+ end
+ if tbl.type == "function" then
+ errprint(spaces.."function: "..tbl.name)
+ errprint(spaces.." returns: "..tbl.returntype)
+ errprint(spaces.." args: ")
+ for k,v in pairs(tbl.args) do
+ printtable(spaces.." ",v)
+ end
+ errprint(spaces.." body: ")
+ printtable(spaces.." ", tbl.body)
+ elseif tbl.type == "stmtlist" then
+ errprint(spaces.."stmtlist:")
+ for k,v in pairs(tbl.body) do
+ errprint(spaces.." "..k..":")
+ printtable(spaces.." ", v)
+ end
+ elseif tbl.type == "variable" then
+ errprint(spaces.."variable: "..tbl.vtype.." "..tbl.name.." = "..tostring(tbl.initializer))
+ elseif tbl.type == "expression" then
+ errprint(spaces.."expression")
+ printtable(spaces.." ",tbl.value)
+ elseif tbl.type == "operator" then
+ errprint(spaces.."operator \""..tbl.value.."\"")
+ errprint(spaces.." left: ")
+ printtable(spaces.." ", tbl.left)
+ errprint(spaces.." right: ")
+ printtable(spaces.." ", tbl.right)
+ elseif tbl.type == "number" or tbl.type == "identifier" then
+ errprint(spaces..tbl.type.." "..tbl.value)
+ elseif tbl.type == "while" then
+ errprint(spaces.."while")
+ errprint(spaces.." expression:")
+ printtable(spaces.." ", tbl.expression)
+ errprint(spaces.." chunk:")
+ printtable(spaces.." ", tbl.chunk)
+ else
+ ignored = { ["parent"] = true }
+ for k,v in pairs(tbl) do
+ if type(v) ~= "table" then
+ errprint(spaces..k..": "..v)
+ end
+ end
+ for k,v in pairs(tbl) do
+ if type(v) == "table" and not ignored[k] then
+ if k == "v" then
+ printtable(spaces, v)
+ else
+ errprint(spaces..k..":")
+ printtable(spaces.." ",v)
+ end
+ end
+ end
+ end
+ end
+ printtable("", parsetree)
+end
--- /dev/null
+parser.lua
\ No newline at end of file
--- /dev/null
+function gettoken(tokenlist)
+ local token = table.remove(tokenlist, 1)
+ return token
+end
+
+function peektoken(tokenlist, n)
+ if not n then
+ n = 1
+ end
+ local token = tokenlist[n]
+ return token
+end
+
+function expecttoken(token, expected)
+ if not token then
+ error("unexpected EOF", 1)
+ end
+ for k,v in pairs(expected) do
+ if token.type == v then
+ return token
+ end
+ end
+ error("unexpected token "..token.type,1)
+end
+
+function parser_global(tokenlist, parsetree)
+ -- GLOBAL = (VARIABLE | FUNCTION)
+ local token = peektoken(tokenlist)
+ while token do
+ if token.type == "int" then
+ local subtoken
+ expecttoken(peektoken(tokenlist, 2), {"identifier"})
+ subtoken = peektoken(tokenlist, 3)
+ if not subtoken then
+ error("unexpected EOF")
+ end
+ if subtoken.type == "(" then
+ table.insert(parsetree, parser_function(tokenlist))
+ elseif subtoken.type == ";" or subtoken.type == "=" then
+ table.insert(parsetree, parser_variable(tokenlist))
+ else
+ error("unexpected token "..subtoken.type.." following 'int <identifier>'")
+ end
+ else
+ error("unexpected token "..token.type)
+ end
+ token = peektoken(tokenlist)
+ end
+end
+
+function parser_function(tokenlist)
+ -- FUNCTION = TYPE IDENTIFIER ARGSLIST STMTLIST
+ local myfunction = {}
+
+ myfunction.type = "function"
+ myfunction.returntype = parser_type(tokenlist)
+ local token = expecttoken(gettoken(tokenlist), {"identifier"})
+ myfunction.name = token.value
+ myfunction.args = parser_argslist(tokenlist)
+ myfunction.body = parser_stmtlist(tokenlist)
+ return myfunction
+end
+
+function parser_type(tokenlist)
+ -- TYPE = INT
+ return expecttoken(gettoken(tokenlist), { "int" }).type
+end
+
+function parser_istype(tokenlist)
+ if peektoken(tokenlist).type == "int" then
+ return true
+ end
+ return false
+end
+
+function parser_argslist(tokenlist)
+ -- ARGSLIST = ( [TYPE IDENTIFIER [, TYPE IDENTIFIER]*] )
+ local token
+ local argslist = {}
+ local firstarg = true
+ expecttoken(gettoken(tokenlist), { "(" })
+ while true do
+ local type, ident
+ token = peektoken(tokenlist)
+ if token.type == ")" then
+ gettoken(tokenlist)
+ return argslist
+ end
+ if not firstarg then
+ expecttoken(gettoken(tokenlist), { "," })
+ token = peektoken(tokenlist)
+ end
+ firstarg = false
+ type = parser_type(tokenlist)
+ token = expecttoken(gettoken(tokenlist), { "identifier" })
+ ident = token.value
+ table.insert(argslist, { type = type, name = ident })
+ end
+end
+
+function parser_stmtlist(tokenlist)
+ -- STMTLIST = { [( VARIABLE | STATEMENT | STMTLIST)]* }
+ local token
+ local stmtlist = {}
+
+ stmtlist.type = "stmtlist"
+ stmtlist.body = {}
+ expecttoken(gettoken(tokenlist), {"{"})
+ while true do
+ token = peektoken(tokenlist)
+ if token.type == "}" then
+ gettoken(tokenlist)
+ return stmtlist
+ end
+ if parser_istype(tokenlist) then
+ table.insert(stmtlist.body, parser_variable(tokenlist))
+ elseif token.type == "{" then
+ table.insert(stmtlist.body, parser_stmtlist(tokenlist))
+ else
+ table.insert(stmtlist.body, parser_statement(tokenlist))
+ end
+ end
+end
+
+function parser_variable(tokenlist)
+ -- VARIABLE = TYPE IDENTIFIER [ = TYPEINITIALIZER ] ;
+ local variable = {}
+ variable.type = "variable"
+ variable.vtype = parser_type(tokenlist)
+ variable.name = expecttoken(gettoken(tokenlist), { "identifier" }).value
+ expecttoken(peektoken(tokenlist), { ";", "=" })
+ local token = gettoken(tokenlist)
+ if token.type == ";" then
+ return variable
+ end
+ if token.type == '=' then
+ if variable.vtype == "int" then
+ variable.initializer = parser_getinitializer_int(tokenlist)
+ else
+ error("internal error")
+ end
+ expecttoken(gettoken(tokenlist), { ";" })
+ return variable
+ end
+ error("internal error")
+end
+
+function parser_getinitializer_int(tokenlist)
+ return expecttoken(gettoken(tokenlist), { "number" }).value
+end
+
+function parser_statement(tokenlist)
+ -- STATEMENT = ; | EXPRESSION ; | (WHILE | IF)
+ local token = peektoken(tokenlist)
+ if token.type == ";" then
+ return nil
+ elseif token.type == "while" then
+ return parser_while(tokenlist)
+ elseif token.type == "if" then
+ return parser_if(tokenlist)
+ elseif token.type == "return" then
+ return parser_return(tokenlist)
+ else
+ local result = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ";" })
+ return result
+ end
+end
+
+function parser_while(tokenlist)
+ -- WHILE = WHILE ( EXPRESSION ) CHUNK
+ local result = {}
+ result.type = "while"
+ expecttoken(gettoken(tokenlist), { "while" })
+ expecttoken(gettoken(tokenlist), { "(" })
+ result.expression = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ")" })
+ result.chunk = parser_chunk(tokenlist)
+ return result
+end
+
+function parser_if(tokenlist)
+ -- IF = IF ( EXPRESSION ) CHUNK
+ local result = {}
+ result.type = "if"
+ expecttoken(gettoken(tokenlist), { "if" })
+ expecttoken(gettoken(tokenlist), { "(" })
+ result.expression = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ")" })
+ result.chunk = parser_chunk(tokenlist)
+ return result
+end
+
+function parser_return(tokenlist)
+ -- RETURN [EXPRESSION] ;
+ local result = {}
+ result.type = "return"
+ expecttoken(gettoken(tokenlist), { "return" })
+ if peektoken(tokenlist).type == ";" then
+ gettoken(tokenlist)
+ return result
+ end
+ result.expression = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ";" })
+ return result
+end
+
+function parser_chunk(tokenlist)
+ local token = peektoken(tokenlist)
+ if token.type == "{" then
+ return parser_stmtlist(tokenlist)
+ else
+ return parser_statement(tokenlist)
+ end
+end
+
+function parser_expression(tokenlist)
+ -- we support:
+ -- ( ) + - < > == =
+ -- of those, ( ) has precedence 0
+ -- + - has precedence 3
+ -- < > has precedence 5
+ -- == has precedence 6
+ -- = has precedence 13
+
+ local operators = {
+ ["*"] = { precedence = 2, associative = "left" },
+ ["/"] = { precedence = 2, associative = "left" },
+ ["+"] = { precedence = 3, associative = "left" },
+ ["-"] = { precedence = 3, associative = "left" },
+ ["<"] = { precedence = 5, associative = "left" },
+ [">"] = { precedence = 5, associative = "left" },
+ ["=="] ={ precedence = 6, associative = "left" },
+ ["="] = { precedence = 13, associative = "left" }
+ }
+
+ -- Shunting yard algorithm
+ local outstack = {}
+ local opstack = {}
+
+ local addopertooutstack = function(oper)
+ assert(table.getn(outstack) >= 2, "attempted to add operator "..oper.value.." to stack containing "..table.getn(outstack).." elements")
+ oper.right = table.remove(outstack, table.getn(outstack))
+ oper.left = table.remove(outstack, table.getn(outstack))
+ table.insert(outstack, oper)
+ end
+
+ while true do
+ local token = peektoken(tokenlist)
+ if token.type == "identifier" or token.type == "number" then
+ gettoken(tokenlist)
+
+ if peektoken(tokenlist).type == "(" then -- function call guyz!
+ gettoken(tokenlist)
+ local outp = { type = "funccall", name = token.value, args = {} }
+ local token = peektoken(tokenlist)
+ while token.type ~= ")" do
+ table.insert(outp.args, parser_expression(tokenlist))
+ token = expecttoken(peektoken(tokenlist), { ")", "," })
+ if token.type == "," then
+ gettoken(tokenlist)
+ end
+ end
+ gettoken(tokenlist)
+
+ table.insert(outstack, outp)
+ else
+ table.insert(outstack, { type = token.type, value = token.value })
+ end
+ elseif token.type == "(" then
+ gettoken(tokenlist)
+
+ local outp = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ")" })
+ table.insert(outstack, outp)
+ elseif operators[token.type] then
+ gettoken(tokenlist)
+
+ while table.getn(opstack) > 0 and
+ ((operators[token.type].associative == "left" and
+ operators[token.type].precedence >= operators[opstack[table.getn(opstack)].value].precedence) or
+ (operators[token.type].associative == "right" and
+ operators[token.type].precedence > operators[opstack[table.getn(opstack)].value].precedence)) do
+ addopertooutstack(table.remove(opstack, table.getn(opstack)))
+ end
+ table.insert(opstack, { type = "operator", value = token.type })
+ elseif token.type == ")" or token.type == ";" or token.type == "," then
+ break
+ end
+ end
+
+ while table.getn(opstack) > 0 do
+ addopertooutstack(table.remove(opstack, table.getn(opstack)))
+ end
+ assert(table.getn(outstack) == 1, "wrong number of objects ("..table.getn(outstack)..") on output stack after expression parse")
+ return { type = "expression", value = outstack[1] }
+end
+
+function parse(tokenlist)
+ local parsetree = {}
+ parser_global(tokenlist, parsetree)
+ return parsetree
+end
--- /dev/null
+tokenizer.lua
\ No newline at end of file
--- /dev/null
+function chartonumber(char)
+ local chartable = { ["0"] = 0, ["1"] = 1, ["2"] = 2, ["3"] = 3, ["4"] = 4, ["5"] = 5,
+ ["6"] = 6, ["7"] = 7, ["8"] = 8, ["9"] = 9 }
+ return chartable[char]
+end
+
+function iswhitespace(char)
+ local whitespace = { [" "] = true, ["\r"] = true, ["\n"] = true, ["\t"] = true, [""] = true}
+ return whitespace[char]
+end
+
+function isidentifierchar(char)
+ if chartonumber(char) then
+ return true
+ end
+ if (char:byte(1) >= ("A"):byte(1) and char:byte(1) <= ("Z"):byte(1)) or
+ (char:byte(1) >= ("a"):byte(1) and char:byte(1) <= ("z"):byte(1)) or
+ char == "_" then
+ return true
+ end
+ return false
+end
+
+function readToken(input)
+ local token = {}
+ local keywords = {"if", "int", "(", ")", "{", "}", ";", ",", "+", "-", "*", "/", "while", "<", ">", "==", "=", "return"}
+
+ -- strip off whitespace from the input
+ while iswhitespace(input:sub(1,1)) and input:len() > 0 do
+ input = input:sub(2)
+ end
+
+ if input:len() == 0 then
+ return "", nil
+ end
+
+ for i,keyword in pairs(keywords) do
+ if input:sub(1,keyword:len()) == keyword then
+ input = input:sub(keyword:len() + 1)
+ token.type = keyword
+ return input,token
+ end
+ end
+
+ -- okay, let's try to tokenize a number
+ if chartonumber(input:sub(1,1)) then
+ token.type = "number"
+ token.value = 0
+ while chartonumber(input:sub(1,1)) do
+ token.value = token.value*10 + chartonumber(input:sub(1,1))
+ input = input:sub(2)
+ end
+ if not iswhitespace(input:sub(1,1))
+ and input:sub(1,1) ~= ")"
+ and input:sub(1,1) ~= "}"
+ and input:sub(1,1) ~= ";"
+ and input:sub(1,1) ~= "+"
+ and input:sub(1,1) ~= ","
+ and input:sub(1,1) ~= "-" then
+ error("expected one of whitespace, ), }, ;, +, - after number; got "..input:sub(1,1))
+ end
+ return input,token
+ end
+
+ -- ok, let's try to tokenize an identifier now.
+ if isidentifierchar(input:sub(1,1)) then
+ token.type = "identifier"
+ token.value = ""
+ while isidentifierchar(input:sub(1,1)) do
+ token.value = token.value .. input:sub(1,1)
+ input = input:sub(2)
+ end
+ return input,token
+ end
+
+ error("invalid character to start token: "..input:sub(1,1).." ("..input:byte(1)..")")
+end
+
+function tokenize(input)
+ local tokenlist = {}
+ while input:len() > 0 do
+ local token
+ input,token = readToken(input)
+ table.insert(tokenlist, token)
+ end
+ return tokenlist
+end
\ No newline at end of file
--- /dev/null
+/*
+ * BlargCPU2
+ *
+ * A dummy CPU emulated on a microcode level, suitable for implementation in hardware.
+ *
+ * Copyright (c) 2005 Joshua Wise <joshua@joshuawise.com>.
+ * Copyright (c) 2005 Matthew Maurer <Fallen.Azrael@gmail.com>.
+ *
+ * All rights reserved.
+ */
+
+/*
+four registers + flag + pc + stack pointer
+
+Insns:
+ 0000 mov reg, lit16
+ 0001 ldr reg, [reg]
+ 0010 sto [reg], reg
+ 0011 mov reg, reg
+ 0100 add reg, reg
+ 0101 tst reg, reg
+ 0110 and reg, reg
+ 0111 not reg
+ 1000 push reg, reg
+ 1001 pop reg, reg
+ 1010 call reg, reg
+ 1011 shr reg, reg
+ 1100 shl reg, reg
+
+Predicates:
+ 000 Never
+ 001 Not-Equal
+ 010 Equal
+ 011 Less than
+ 100 Greater than
+ 111 Always
+
+Opcode format:
+15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
+|--INSN---| |-PRED-| |TREGISTER| |SREGISTER|
+
+Vectors:
+ 0 - start
+ 4 - ISR
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define STACK_LOC 0x8000
+
+#define PRED_NV 0x0
+#define PRED_NE 0x1
+#define PRED_EQ 0x2
+#define PRED_LT 0x3
+#define PRED_GT 0x4
+#define PRED_AL 0x7
+
+#define FLAG_LT 0x1
+#define FLAG_GT 0x2
+#define FLAG_EQ 0x4
+
+#define REG_R0 0x0
+#define REG_R1 0x1
+#define REG_R2 0x2
+#define REG_R3 0x3
+#define REG_FR 0x4
+#define REG_PC 0x5
+#define REG_SP 0x6
+
+#define INSN_MOV_REG_LIT16 0x0
+#define INSN_LDR_REG_ADR_REG 0x1
+#define INSN_STO_ADR_REG_REG 0x2
+#define INSN_MOV_REG_REG 0x3
+#define INSN_ADD_REG_REG 0x4
+#define INSN_TST_REG_REG 0x5
+#define INSN_AND_REG_REG 0x6
+#define INSN_NOT_REG 0x7
+#define INSN_PUSH_REG_REG 0x8
+#define INSN_POP_REG_REG 0x9
+#define INSN_CALL_REG_REG 0xA
+#define INSN_SHR_REG_REG 0xB
+#define INSN_SHL_REG_REG 0xC
+
+#define INSN(insn, pred, treg, sreg) (((insn) << 12) | ((pred) << 9) | ((treg) << 4) | ((sreg)))
+
+/*******************************************************************/
+/*******************************************************************/
+
+struct peripheral {
+ struct peripheral *next;
+
+ char *name;
+ unsigned short start;
+ unsigned short length;
+ unsigned short (*read)(struct peripheral*, unsigned short);
+ void (*write)(struct peripheral*, unsigned short, unsigned short);
+ void *priv;
+};
+
+struct peripheral *plist = NULL;
+
+unsigned short null_read(struct peripheral *device, unsigned short address)
+{
+ printf("%s: read to address %04x disallowed\n", device->name, address);
+ abort();
+}
+
+void null_write(struct peripheral *device, unsigned short address, unsigned short data)
+{
+ printf("%s: write to address %04x disallowed\n", device->name, address);
+ abort();
+}
+
+unsigned short nodev_read(struct peripheral *device, unsigned short address)
+{
+ printf("peripherals: no device to read from at address %04x\n", address);
+ abort();
+}
+
+void nodev_write(struct peripheral *device, unsigned short address, unsigned short data)
+{
+ printf("peripherals: no device to write %04x to at address %04x\n", data, address);
+ abort();
+}
+
+struct peripheral noperiph = { NULL, "noperiph", 0x0, 0x0, nodev_read, nodev_write, NULL };
+
+struct peripheral* peripheral_cache[65536 /* ouch! */];
+
+/* this could be done in a more optimized manner by stepping through each periph and just setting the bits where it's at, but ... */
+void peripheral_rehash()
+{
+ int address;
+
+ for (address = 0; address < 65536; address++)
+ {
+ struct peripheral *p;
+
+ peripheral_cache[address] = &noperiph; /* No'bdy has harmed me!, quoth the Cyclops */
+
+ for (p=plist; p; p = p->next)
+ if ((p->start <= address) && ((p->start + p->length) > address))
+ {
+ peripheral_cache[address] = p;
+ break;
+ }
+ }
+}
+
+#define peripheral_get(address) (peripheral_cache[address])
+
+void peripheral_add(struct peripheral *device)
+{
+ device->next = plist;
+ plist = device;
+ if (!device->read)
+ device->read = null_read;
+ if (!device->write)
+ device->write = null_write;
+ peripheral_rehash();
+}
+
+void peripheral_remove(struct peripheral *device)
+{
+ struct peripheral *temp, *ptemp;
+
+ if (plist == device)
+ {
+ plist = device->next;
+ return;
+ }
+
+ ptemp = plist;
+ for (temp = plist; temp; temp->next)
+ if (temp == device)
+ ptemp->next = device->next;
+ peripheral_rehash();
+}
+
+/*******************************************************************/
+
+#define ROMSIZE 0x4000
+unsigned short rom_data[ROMSIZE]= {
+ INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R3,0 ), 9, /*mov r3, 9*/
+ INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R0,0 ), '9', /*mov r0, '9'*/
+ INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R1,0 ), 0x4000, /*mov r1, 0x4000*/
+ INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R2,0 ), 0xFFFF, /*mov r2, 0xFFFF*/
+ INSN(INSN_STO_ADR_REG_REG, PRED_AL, REG_R1,REG_R0 ), /* loop 0x0008 *//*mov [r1], r0*/
+ INSN(INSN_ADD_REG_REG, PRED_AL, REG_R3,REG_R2 ), /*add r3,r2*/
+ INSN(INSN_ADD_REG_REG, PRED_AL, REG_R0,REG_R2 ), /*add r0,r2*/
+ INSN(INSN_TST_REG_REG, PRED_AL, REG_R3,REG_R2 ), /* loop 0x0011 *//*test r3, r2*/
+ INSN(INSN_MOV_REG_LIT16, PRED_NE, REG_PC,0 ), 0x0008, /*jne 0x0008*/
+ INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R0,0 ), '\n', /*mov r0,0*/
+ INSN(INSN_STO_ADR_REG_REG, PRED_AL, REG_R1,REG_R0 ), /*mov [r1], r0*/
+ INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_PC,0 ), 0x0011, /*jmp 0x0011*/
+};
+
+/*ROM Read*/
+unsigned short rom_read(struct peripheral *device, unsigned short address)
+{
+ return ((unsigned short*)device->priv)[address - device->start];
+}
+
+unsigned short rom_optread(struct peripheral *device, unsigned short address)
+{
+ return ((unsigned short*)device->priv)[address];
+}
+
+void rom_add()
+{
+ struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
+ p->name = "rom";
+ p->start = 0x0;
+ p->length = ROMSIZE;
+ p->read = (p->start == 0x0) ? &rom_optread : &rom_read;
+ p->write = NULL;
+ p->priv = &rom_data;
+ peripheral_add(p);
+}
+
+/*******************************************************************/
+
+#define RAMSIZE 32768
+unsigned short ram_data[RAMSIZE];
+
+unsigned short ram_read(struct peripheral *device, unsigned short address)
+{
+ return ((unsigned short*)device->priv)[address - device->start];
+}
+
+void ram_write(struct peripheral *device, unsigned short address, unsigned short data)
+{
+ ((unsigned short*)device->priv)[address - device->start] = data;
+}
+
+void ram_add()
+{
+ struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
+ p->name = "ram";
+ p->start = 0x8000;
+ p->length = RAMSIZE;
+ p->read = &ram_read;
+ p->write = &ram_write;
+ p->priv = &ram_data;
+ peripheral_add(p);
+}
+
+/*******************************************************************/
+
+#include "SDL.h"
+
+struct blargfb {
+ SDL_Surface* screen;
+ SDL_Color palette[256];
+ unsigned short row, col;
+};
+
+Uint32 fb_events(Uint32 interval)
+{
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ if (event.key.keysym.sym == SDLK_q)
+ exit(0);
+ break;
+ case SDL_QUIT:
+ exit(0);
+ }
+ }
+ return interval;
+}
+
+void fb_initsurface(struct blargfb *fb)
+{
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) < 0)
+ {
+ printf("SDL init failed: %s, FB disabled\n", SDL_GetError());
+ fb->screen = NULL;
+ return;
+ }
+ atexit(SDL_Quit);
+
+ fb->screen = SDL_SetVideoMode(320,240,8,SDL_SWSURFACE);
+ if (!fb->screen)
+ {
+ printf("SDL video init failed: %s, FB disabled\n", SDL_GetError());
+ return;
+ }
+
+ SDL_WM_SetCaption("BlargCPU Framebuffer", "BlargFB");
+
+ fb->palette[0].r = 0; fb->palette[0].g = 0; fb->palette[0].b = 0;
+ fb->palette[1].r = 127; fb->palette[1].g = 0; fb->palette[1].b = 0;
+ fb->palette[2].r = 0; fb->palette[2].g = 127; fb->palette[2].b = 0;
+ fb->palette[3].r = 0; fb->palette[3].g = 0; fb->palette[3].b = 127;
+ fb->palette[4].r = 255; fb->palette[4].g = 0; fb->palette[4].b = 0;
+ fb->palette[5].r = 0; fb->palette[5].g = 255; fb->palette[5].b = 0;
+ fb->palette[6].r = 0; fb->palette[6].g = 0; fb->palette[6].b = 255;
+ fb->palette[7].r = 255; fb->palette[7].g = 255; fb->palette[7].b = 255;
+ SDL_SetColors(fb->screen, fb->palette, 0, 8);
+
+ SDL_SetTimer(50, fb_events);
+}
+
+void fb_init(struct peripheral *device)
+{
+ struct blargfb *priv = (struct blargfb*)device->priv;
+ priv->row = 0;
+ priv->col = 0;
+}
+
+unsigned short fb_read(struct peripheral *device, unsigned short address)
+{
+ struct blargfb *priv = (struct blargfb*)device->priv;
+ unsigned char res;
+
+ if (!priv->screen) fb_initsurface(priv);
+
+ switch (address - device->start)
+ {
+ case 0: return priv->row;
+ case 1: return priv->col;
+ case 2: SDL_LockSurface(priv->screen);
+ res = *((unsigned char*)(priv->screen->pixels + priv->row * priv->screen->pitch + priv->col));
+ SDL_UnlockSurface(priv->screen);
+ return res;
+ case 3: return 0;
+ default:printf("%s: read: incorrect offset %d\n", device->name, address - device->start);
+ };
+}
+
+void fb_write(struct peripheral *device, unsigned short address, unsigned short data)
+{
+ struct blargfb *priv = (struct blargfb*)device->priv;
+
+ if (!priv->screen) fb_initsurface(priv);
+
+ switch (address - device->start)
+ {
+ case 0: priv->row = data; break;
+ case 1: priv->col = data; break;
+ case 2: SDL_LockSurface(priv->screen);
+ *((unsigned char*)(priv->screen->pixels + priv->row * priv->screen->pitch + priv->col)) = data;
+ SDL_UnlockSurface(priv->screen);
+ break;
+ case 3: SDL_UpdateRect(priv->screen, 0, 0, 0, 0); break;
+ default:printf("%s: write: incorrect offset %d\n", device->name, address - device->start);
+ }
+}
+
+void fb_add()
+{
+ struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
+ struct blargfb *fb = (struct blargfb*)malloc(sizeof(struct blargfb));
+ p->name = "fb";
+ p->start = 0x4100;
+ p->length = 0x4;
+ p->read = &fb_read;
+ p->write = &fb_write;
+ p->priv = fb;
+ fb_init(p);
+ peripheral_add(p);
+}
+
+/*******************************************************************/
+
+void console_write(struct peripheral *device, unsigned short address, unsigned short data)
+{
+// write(1, &data, 1);
+ printf("Console write: data: %04x (%d)\n", data, data);
+}
+
+void console_add()
+{
+ struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
+ p->name = "console";
+ p->start = 0x4000;
+ p->length = 0x1;
+ p->read = NULL;
+ p->write = &console_write;
+ p->priv = NULL;
+ peripheral_add(p);
+}
+
+/*******************************************************************/
+
+unsigned short fetch(unsigned short address)
+{
+ struct peripheral* temp = peripheral_get(address);
+ return temp->read(temp, address);
+}
+
+void store(unsigned short address, unsigned short data)
+{
+ struct peripheral* temp = peripheral_get(address);
+ temp->write(temp, address, data);
+}
+
+/*******************************************************************/
+/*******************************************************************/
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+static int indebugger = 0;
+static int trace = 0;
+static int breakpoint = -1;
+
+unsigned short regs[7];
+
+char* insntostr(unsigned short opc)
+{
+ static char insn[] = "inspr rx,rx";
+ switch ((opc >> 12) & 0xF)
+ {
+ case INSN_MOV_REG_LIT16: memcpy(insn, "mvc", 3); break;
+ case INSN_STO_ADR_REG_REG: memcpy(insn, "sto", 3); break;
+ case INSN_LDR_REG_ADR_REG: memcpy(insn, "ldr", 3); break;
+ case INSN_MOV_REG_REG: memcpy(insn, "mov", 3); break;
+ case INSN_ADD_REG_REG: memcpy(insn, "add", 3); break;
+ case INSN_TST_REG_REG: memcpy(insn, "tst", 3); break;
+ case INSN_AND_REG_REG: memcpy(insn, "and", 3); break;
+ case INSN_NOT_REG: memcpy(insn, "not", 3); break;
+ case INSN_PUSH_REG_REG: memcpy(insn, "psh", 3); break;
+ case INSN_POP_REG_REG: memcpy(insn, "pop", 3); break;
+ case INSN_CALL_REG_REG: memcpy(insn, "cal", 3); break;
+ default: memcpy(insn, "bad", 3); break;
+ }
+ switch ((opc >> 9) & 0x7)
+ {
+ case PRED_NV: memcpy(insn+3, "nv", 2); break;
+ case PRED_NE: memcpy(insn+3, "ne", 2); break;
+ case PRED_EQ: memcpy(insn+3, "eq", 2); break;
+ case PRED_LT: memcpy(insn+3, "lt", 2); break;
+ case PRED_GT: memcpy(insn+3, "gt", 2); break;
+ case PRED_AL: memcpy(insn+3, " ", 2); break;
+ default: memcpy(insn+3, "!!", 2); break;
+ }
+
+ sprintf(insn+6, "r%x,r%x", (opc >> 4) & 0xF, opc & 0xF);
+ return insn;
+}
+
+void debugger()
+{
+ char *line;
+ static char *oldline;
+ int loopmore = 1;
+ unsigned short insn = fetch(regs[5]);
+
+ if (trace)
+ printf("r0:%04x r1:%04x r2:%04x r3:%04x fr:%04x pc:%04x sp:%04x -- %04x[%s]\n",
+ regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6],
+ insn, insntostr(insn));
+
+ if (regs[5] == breakpoint)
+ {
+ printf("Breakpoint reached (pc %04x)\n", breakpoint);
+ indebugger = 1;
+ }
+
+ if (!indebugger)
+ return;
+
+ while (loopmore && (line = readline("blargcpu> ")))
+ {
+ if (!*line)
+ {
+ free(line);
+ line = oldline;
+ } else
+ free(oldline);
+
+ if ((strcmp(line, "exit") == 0) || (strcmp(line, "quit") == 0) || (strcmp(line, "bye") == 0))
+ {
+ printf("Bye.\n");
+ exit(0);
+ } else if ((strcmp(line, "cont") == 0) || (strcmp(line, "continue") == 0)) {
+ loopmore = 0;
+ indebugger = 0;
+ } else if ((strcmp(line, "step") == 0) || (strcmp(line, "stepi") == 0))
+ loopmore = 0;
+ else if (strcmp(line, "trace") == 0) {
+ trace = !trace;
+ printf("Trace is now %s.\n", trace ? "ON" : "OFF");
+ } else if (strcmp(line, "status") == 0) {
+ int i;
+ printf("blargCPU status report\n");
+ printf("Register status:\n");
+ printf(" r0: %04x\n", regs[0]);
+ printf(" r1: %04x\n", regs[1]);
+ printf(" r2: %04x\n", regs[2]);
+ printf(" r3: %04x\n", regs[3]);
+ printf(" FR: %04x\n", regs[4]);
+ printf(" PC: %04x\n", regs[5]);
+ printf(" SP: %04x [%04x]\n", regs[6], fetch(regs[6]));
+ printf("Context:\n");
+ i = regs[5] - 3;
+ for (i=(((regs[5] - 3) < 0) ? 0 : (regs[5] - 3)); i < (regs[5] + 4); i++)
+ printf(" %s[%04x] %04x [%s]\n", (i == regs[5]) ? "==>" : " ", i, fetch(i), insntostr(fetch(i)));
+ } else if (strncmp(line, "break", 5) == 0) {
+ if (line[5] == ' ') {
+ unsigned short addr;
+ addr = strtol(line+6, NULL, 16);
+ breakpoint = addr;
+ printf("Breakpoint set to %04x.\n", addr);
+ } else {
+ breakpoint = -1;
+ printf("Breakpoint reset.\n");
+ }
+ } else if (strncmp(line, "xa ", 3) == 0) {
+ unsigned short addr, len = 0;
+ int i;
+ char *next;
+ addr = strtol(line+3, &next, 16);
+ if (*next)
+ len = strtol(next+1, NULL, 16);
+ if (len == 0)
+ len = 1;
+ printf("Memory examination of %04x words at %04x:\n", len, addr);
+ for (i=addr; i<(addr+len); i++)
+ printf(" [%04x] %04x\n", i, fetch(i));
+ } else
+ printf("Come again?\n");
+ oldline = line;
+ }
+}
+
+void reset()
+{
+ regs[REG_PC] = 0x0;
+ regs[REG_FR] = 0x0;
+ regs[REG_SP] = STACK_LOC;
+}
+
+void cpuloop()
+{
+ while(1)
+ {
+ unsigned short insn;
+ unsigned char pred_match;
+ unsigned short tmp;
+
+ debugger();
+
+ insn = fetch(regs[REG_PC]);
+ switch ((insn >> 9) & 0x7)
+ {
+ case PRED_NV: pred_match = 0; break;
+ case PRED_NE: pred_match = regs[REG_FR] & (FLAG_LT | FLAG_GT); break;
+ case PRED_EQ: pred_match = regs[REG_FR] & FLAG_EQ; break;
+ case PRED_LT: pred_match = regs[REG_FR] & FLAG_LT; break;
+ case PRED_GT: pred_match = regs[REG_FR] & FLAG_GT; break;
+ case PRED_AL: pred_match = 1; break;
+ default: printf("Invalid predicate %1x, aborting.\n", (insn >> 9) & 0x7);
+ abort();
+ }
+
+#define UCODE_NEXT_INSN break;
+#define UCODE_INC_PC regs[REG_PC]++;
+#define UCODE_INC_LATCH tmp++;
+#define UCODE_DEC_LATCH tmp--;
+#define UCODE_LATCH_ADR_PC tmp = fetch(regs[REG_PC]);
+#define UCODE_LATCH_PC tmp = regs[REG_PC];
+
+#define UCODE_STORE_TREG regs[(insn >> 4) & 0xF] = tmp;
+#define UCODE_LATCH_TREG tmp = regs[(insn >> 4) & 0xF];
+#define UCODE_LATCH_ADR_TREG tmp = fetch(regs[(insn >> 4) & 0xF]);
+
+#define UCODE_STORE_SREG regs[insn & 0xF] = tmp;
+#define UCODE_LATCH_SREG tmp = regs[insn & 0xF];
+#define UCODE_LATCH_ADR_SREG tmp = fetch(regs[insn & 0xF]);
+
+#define UCODE_STORE_ADR_TREG store(regs[(insn >> 4) & 0xF], tmp);
+#define UCODE_STORE_PC regs[REG_PC] = tmp;
+#define UCODE_ADD_TREG tmp += regs[(insn >> 4) & 0xF];
+#define UCODE_ZERO_FR regs[REG_FR] = 0;
+#define UCODE_SET_FR_EQ if (tmp == regs[(insn >> 4) & 0xF]) regs[REG_FR] |= FLAG_EQ;
+#define UCODE_SET_FR_LT if (tmp < regs[(insn >> 4) & 0xF]) regs[REG_FR] |= FLAG_LT;
+#define UCODE_SET_FR_GT if (tmp > regs[(insn >> 4) & 0xF]) regs[REG_FR] |= FLAG_GT;
+#define UCODE_AND_TREG tmp &= regs[(insn >> 4) & 0xF];
+#define UCODE_LATCH_NOT_TREG tmp = ~regs[(insn >> 4) & 0xF];
+
+#define UCODE_SHIFT_RIGHT tmp = tmp >> (regs[(insn >> 4) & 0xF]);
+#define UCODE_SHIFT_LEFT tmp = tmp << (regs[(insn << 4) & 0xF]);
+
+ switch ((insn >> 12) & 0xF)
+ {
+ case INSN_MOV_REG_LIT16: UCODE_INC_PC
+ if (pred_match) UCODE_LATCH_ADR_PC
+ UCODE_INC_PC
+ if (pred_match) UCODE_STORE_TREG
+ UCODE_NEXT_INSN
+
+ case INSN_STO_ADR_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_STORE_ADR_TREG
+ UCODE_NEXT_INSN
+
+ case INSN_LDR_REG_ADR_REG: if (pred_match) UCODE_LATCH_ADR_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_STORE_TREG
+ UCODE_NEXT_INSN
+
+ case INSN_MOV_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_STORE_TREG
+ UCODE_NEXT_INSN
+
+ case INSN_ADD_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_ADD_TREG
+ if (pred_match) UCODE_STORE_TREG
+ UCODE_NEXT_INSN
+
+ case INSN_TST_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_ZERO_FR
+ if (pred_match) UCODE_SET_FR_EQ
+ if (pred_match) UCODE_SET_FR_LT
+ if (pred_match) UCODE_SET_FR_GT
+ UCODE_NEXT_INSN
+
+ case INSN_AND_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_AND_TREG
+ if (pred_match) UCODE_STORE_TREG
+ UCODE_NEXT_INSN
+
+ case INSN_NOT_REG: if (pred_match) UCODE_LATCH_NOT_TREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_STORE_TREG
+ UCODE_NEXT_INSN
+
+ case INSN_PUSH_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ if (pred_match) UCODE_STORE_ADR_TREG
+ if (pred_match) UCODE_LATCH_TREG
+ if (pred_match) UCODE_INC_LATCH
+ if (pred_match) UCODE_STORE_TREG
+ UCODE_INC_PC
+ UCODE_NEXT_INSN
+
+ case INSN_POP_REG_REG: UCODE_INC_PC
+ if (pred_match) UCODE_LATCH_TREG
+ if (pred_match) UCODE_DEC_LATCH
+ if (pred_match) UCODE_STORE_TREG
+ if (pred_match) UCODE_LATCH_ADR_TREG
+ if (pred_match) UCODE_STORE_SREG
+ UCODE_NEXT_INSN
+
+ case INSN_CALL_REG_REG: UCODE_INC_PC
+ if (pred_match) UCODE_LATCH_PC
+ if (pred_match) UCODE_STORE_ADR_TREG
+ if (pred_match) UCODE_LATCH_TREG
+ if (pred_match) UCODE_INC_LATCH
+ if (pred_match) UCODE_STORE_TREG
+ if (pred_match) UCODE_LATCH_SREG
+ if (pred_match) UCODE_STORE_PC
+ UCODE_NEXT_INSN
+
+ case INSN_SHR_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_SHIFT_RIGHT
+ if (pred_match) UCODE_STORE_SREG
+ UCODE_NEXT_INSN
+
+ case INSN_SHL_REG_REG: if (pred_match) UCODE_LATCH_SREG
+ UCODE_INC_PC
+ if (pred_match) UCODE_SHIFT_LEFT
+ if (pred_match) UCODE_STORE_SREG
+ UCODE_NEXT_INSN
+/*
+* Two concerns as of right now about this microcode:
+* 1.) We latch to some things, but then access the registers directly in others. As far as I can tell, we need a second latch.
+* 2.) We increment the program counter before storing to tregs and the like sometimes, but don't others. For example, in
+* call, we increment, then proceed to store to the address in treg. However, at other times, such as in tst, we latch to a
+* register, first, almost giving the impression that we couldn't afterwards. I'm not sure which way it is, but it should
+* be one way throughout the entire system.
+* --Matthew Maurer
+*/
+ default: printf("Internal emulation error: Out of range opcode\n");
+ abort();
+ }
+ }
+}
+
+/*******************************************************************/
+/*******************************************************************/
+
+#include <signal.h>
+#include <getopt.h>
+
+void dbghandler(int sig)
+{
+ char *str;
+ if (indebugger)
+ {
+ str = "Caught signal in debugger; bailing out!\n";
+ write(2, str, strlen(str));
+ exit(1);
+ }
+ str = "^C hit; breaking into debugger\n";
+ write(2, str, strlen(str));
+ indebugger = 1;
+}
+
+struct option longopts[] = {
+ {"rom", required_argument, NULL, 'r'},
+ {"debugger", no_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {0,0,0,0} /* sentinel */
+};
+
+int main(int argc, char** argv)
+{
+ int arg;
+ while ((arg = getopt_long(argc, argv, "r:d", longopts, NULL)) != -1)
+ {
+ switch(arg)
+ {
+ case 'r':
+ {
+ int fd;
+ fd = open(optarg, O_RDONLY);
+ if (fd < 0)
+ {
+ perror("open");
+ exit(0);
+ }
+ read(fd, rom_data, ROMSIZE*sizeof(short));
+ close(fd);
+ }
+ case 'd':
+ indebugger = 1;
+ break;
+ case 'h':
+ printf(
+ "blargCPU2\n"
+ "\n"
+ "Usage: %s [OPTION] ...\n"
+ "Simulates a blargCPU.\n"
+ "\n"
+ "Mandatory arguments to long options are mandatory for short options too.\n"
+ " -r, --rom=ROMFILE preloads ROM with a boot image\n"
+ " -d, --debugger immediately drops into the debugger\n"
+ " --help shows this help\n"
+ "\n"
+ "Written by Joshua Wise <joshua@joshuawise.com> and Matthew Maurer <Fallen.Azrael@gmail.com>.\n"
+ , argv[0]
+ );
+ exit(2);
+ case '?': case ':':
+ printf("Try `%s --help' for more information.\n", argv[0]);
+ exit(2);
+ default:
+ printf("Oh God I am not good with computers how did this get here?\n");
+ exit(127);
+ }
+ }
+ signal(SIGINT, dbghandler);
+ rom_add();
+ ram_add();
+ console_add();
+ fb_add();
+ reset();
+ cpuloop();
+ return 0;
+}