From: Joshua Wise Date: Wed, 29 Aug 2007 20:51:57 +0000 (-0400) Subject: Initial commit X-Git-Url: http://git.joshuawise.com/jwcc.git/commitdiff_plain/933e60e38a9fe9d3770ab93b494d967361eef5b8 Initial commit --- 933e60e38a9fe9d3770ab93b494d967361eef5b8 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6e8c5b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +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 $< $@ + diff --git a/examples/blargcpu.ld b/examples/blargcpu.ld new file mode 100644 index 0000000..4d62c04 --- /dev/null +++ b/examples/blargcpu.ld @@ -0,0 +1,28 @@ +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 = .; +} diff --git a/examples/easytest.c b/examples/easytest.c new file mode 100644 index 0000000..fa78c17 --- /dev/null +++ b/examples/easytest.c @@ -0,0 +1,12 @@ +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 diff --git a/examples/fact.c b/examples/fact.c new file mode 100644 index 0000000..be31aea --- /dev/null +++ b/examples/fact.c @@ -0,0 +1,32 @@ +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; +} diff --git a/examples/fib.c b/examples/fib.c new file mode 100644 index 0000000..70479da --- /dev/null +++ b/examples/fib.c @@ -0,0 +1,18 @@ +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 diff --git a/examples/libblarg.s b/examples/libblarg.s new file mode 100644 index 0000000..9b6f6e9 --- /dev/null +++ b/examples/libblarg.s @@ -0,0 +1,60 @@ + .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 diff --git a/examples/test.c b/examples/test.c new file mode 100644 index 0000000..8762076 --- /dev/null +++ b/examples/test.c @@ -0,0 +1,45 @@ +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; +} diff --git a/grammar.txt b/grammar.txt new file mode 100644 index 0000000..f8e78d2 --- /dev/null +++ b/grammar.txt @@ -0,0 +1,18 @@ +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 diff --git a/jwcc.lua b/jwcc.lua new file mode 100755 index 0000000..c5949db --- /dev/null +++ b/jwcc.lua @@ -0,0 +1,131 @@ +#!/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 diff --git a/lib/backend/blargcpu.lua b/lib/backend/blargcpu.lua new file mode 100644 index 0000000..5bcd71c --- /dev/null +++ b/lib/backend/blargcpu.lua @@ -0,0 +1,620 @@ +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 diff --git a/lib/backend/default.lua b/lib/backend/default.lua new file mode 120000 index 0000000..9631719 --- /dev/null +++ b/lib/backend/default.lua @@ -0,0 +1 @@ +blargcpu.lua \ No newline at end of file diff --git a/lib/backend/interpreter.lua b/lib/backend/interpreter.lua new file mode 100644 index 0000000..a3988f6 --- /dev/null +++ b/lib/backend/interpreter.lua @@ -0,0 +1,142 @@ +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 diff --git a/lib/deparser.lua b/lib/deparser.lua new file mode 100644 index 0000000..49b32e6 --- /dev/null +++ b/lib/deparser.lua @@ -0,0 +1,65 @@ +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 diff --git a/lib/parser/default.lua b/lib/parser/default.lua new file mode 120000 index 0000000..6652ac0 --- /dev/null +++ b/lib/parser/default.lua @@ -0,0 +1 @@ +parser.lua \ No newline at end of file diff --git a/lib/parser/parser.lua b/lib/parser/parser.lua new file mode 100644 index 0000000..66a380a --- /dev/null +++ b/lib/parser/parser.lua @@ -0,0 +1,303 @@ +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 '") + 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 diff --git a/lib/tokenizer/default.lua b/lib/tokenizer/default.lua new file mode 120000 index 0000000..8905652 --- /dev/null +++ b/lib/tokenizer/default.lua @@ -0,0 +1 @@ +tokenizer.lua \ No newline at end of file diff --git a/lib/tokenizer/tokenizer.lua b/lib/tokenizer/tokenizer.lua new file mode 100644 index 0000000..97e6487 --- /dev/null +++ b/lib/tokenizer/tokenizer.lua @@ -0,0 +1,87 @@ +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 diff --git a/sim/Makefile b/sim/Makefile new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/sim/Makefile @@ -0,0 +1 @@ + diff --git a/sim/blargcpu2.c b/sim/blargcpu2.c new file mode 100644 index 0000000..75f1e1f --- /dev/null +++ b/sim/blargcpu2.c @@ -0,0 +1,776 @@ +/* + * BlargCPU2 + * + * A dummy CPU emulated on a microcode level, suitable for implementation in hardware. + * + * Copyright (c) 2005 Joshua Wise . + * Copyright (c) 2005 Matthew Maurer . + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 +#include + +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 +#include + +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 and Matthew Maurer .\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; +}