]> Joshua Wise's Git repositories - jwcc.git/commitdiff
Initial commit
authorJoshua Wise <joshua@rebirth.(none)>
Wed, 29 Aug 2007 20:51:57 +0000 (16:51 -0400)
committerJoshua Wise <joshua@rebirth.(none)>
Wed, 29 Aug 2007 20:51:57 +0000 (16:51 -0400)
19 files changed:
Makefile [new file with mode: 0644]
examples/blargcpu.ld [new file with mode: 0644]
examples/easytest.c [new file with mode: 0644]
examples/fact.c [new file with mode: 0644]
examples/fib.c [new file with mode: 0644]
examples/libblarg.s [new file with mode: 0644]
examples/test.c [new file with mode: 0644]
grammar.txt [new file with mode: 0644]
jwcc.lua [new file with mode: 0755]
lib/backend/blargcpu.lua [new file with mode: 0644]
lib/backend/default.lua [new symlink]
lib/backend/interpreter.lua [new file with mode: 0644]
lib/deparser.lua [new file with mode: 0644]
lib/parser/default.lua [new symlink]
lib/parser/parser.lua [new file with mode: 0644]
lib/tokenizer/default.lua [new symlink]
lib/tokenizer/tokenizer.lua [new file with mode: 0644]
sim/Makefile [new file with mode: 0644]
sim/blargcpu2.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..4d62c04
--- /dev/null
@@ -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 (file)
index 0000000..fa78c17
--- /dev/null
@@ -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 (file)
index 0000000..be31aea
--- /dev/null
@@ -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 (file)
index 0000000..70479da
--- /dev/null
@@ -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 (file)
index 0000000..9b6f6e9
--- /dev/null
@@ -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 (file)
index 0000000..8762076
--- /dev/null
@@ -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 (file)
index 0000000..f8e78d2
--- /dev/null
@@ -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 (executable)
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 (file)
index 0000000..5bcd71c
--- /dev/null
@@ -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 (symlink)
index 0000000..9631719
--- /dev/null
@@ -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 (file)
index 0000000..a3988f6
--- /dev/null
@@ -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 (file)
index 0000000..49b32e6
--- /dev/null
@@ -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 (symlink)
index 0000000..6652ac0
--- /dev/null
@@ -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 (file)
index 0000000..66a380a
--- /dev/null
@@ -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 <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
diff --git a/lib/tokenizer/default.lua b/lib/tokenizer/default.lua
new file mode 120000 (symlink)
index 0000000..8905652
--- /dev/null
@@ -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 (file)
index 0000000..97e6487
--- /dev/null
@@ -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 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/sim/blargcpu2.c b/sim/blargcpu2.c
new file mode 100644 (file)
index 0000000..75f1e1f
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * 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;
+}
This page took 0.194517 seconds and 4 git commands to generate.