ARM codegen: Add disassembler, implement return

This program works:

    main() { return 42; }

The disassembler was borrowed from codeflinger, and just modified enough to compile
under C++ without warnings.

Implemented gsym
Implemented a hack verison of li, only works for -256..255
Implemented gjmp
diff --git a/libacc/acc.cpp b/libacc/acc.cpp
index 0cc1a0f..04994e2 100644
--- a/libacc/acc.cpp
+++ b/libacc/acc.cpp
@@ -31,6 +31,8 @@
 #include <unistd.h>
 #endif
 
+#include "disassem.h"
+
 namespace acc {
 
 class compiler {
@@ -178,6 +180,8 @@
 
         virtual void adjustStackAfterCall(int l) = 0;
 
+        virtual int disassemble(FILE* out) = 0;
+
         /* output a symbol and patch all calls to it */
         virtual void gsym(int t) {
             pCodeBuf->gsym(t);
@@ -185,15 +189,20 @@
 
         virtual int finishCompile() {
 #if defined(__arm__)
-        const long base = long(pCodeBuf->getBase());
-        const long curr = base + long(pCodeBuf->getSize());
-        int err = cacheflush(base, curr, 0);
-        return err;
+            const long base = long(pCodeBuf->getBase());
+            const long curr = base + long(pCodeBuf->getSize());
+            int err = cacheflush(base, curr, 0);
+            return err;
 #else
-        return 0;
+            return 0;
 #endif
         }
 
+        /**
+         * Adjust relative branches by this amount.
+         */
+        virtual int jumpOffset() = 0;
+
     protected:
         void o(int n) {
             pCodeBuf->o(n);
@@ -217,6 +226,10 @@
             return pCodeBuf->oad(n,t);
         }
 
+        int getBase() {
+            return (int) pCodeBuf->getBase();
+        }
+
         int getPC() {
             return pCodeBuf->getPC();
         }
@@ -271,12 +284,19 @@
         /* load immediate value */
         virtual void li(int t) {
             fprintf(stderr, "li(%d);\n", t);
-            oad(0xb8, t); /* mov $xx, %eax */
+            if (t >= 0 && t < 255) {
+                 o4(0xE3A00000 + t); // E3A00000 mov    r0, #0
+            } else if (t >= -256 && t < 0) {
+                // mvn means move constant ^ ~0
+                o4(0xE3E00001 - t); // E3E00000         mvn    r0, #0
+            } else {
+                error("immediate constant out of range -256..255: %d", t);
+            }
         }
 
         virtual int gjmp(int t) {
             fprintf(stderr, "gjmp(%d);\n", t);
-            return psym(0xe9, t);
+            return o4(0xEA000000 + encodeAddress(t));
         }
 
         /* l = 0: je, l == 1: jne */
@@ -397,7 +417,86 @@
             oad(0xc481, l); /* add $xxx, %esp */
         }
 
+        virtual int jumpOffset() {
+            return 4;
+        }
+
+        /* output a symbol and patch all calls to it */
+        virtual void gsym(int t) {
+            fprintf(stderr, "gsym(0x%x)\n", t);
+            int n;
+            int base = getBase();
+            int pc = getPC();
+            fprintf(stderr, "pc = 0x%x\n", pc);
+            while (t) {
+                int data = * (int*) t;
+                int decodedOffset = ((BRANCH_REL_ADDRESS_MASK & data) << 2);
+                if (decodedOffset == 0) {
+                    n = 0;
+                } else {
+                    n = base + decodedOffset; /* next value */
+                }
+                *(int *) t = (data & ~BRANCH_REL_ADDRESS_MASK)
+                    | encodeRelAddress(pc - t - 8);
+                t = n;
+            }
+        }
+
+        virtual int disassemble(FILE* out) {
+               disasmOut = out;
+            disasm_interface_t  di;
+            di.di_readword = disassemble_readword;
+            di.di_printaddr = disassemble_printaddr;
+            di.di_printf = disassemble_printf;
+
+            int base = getBase();
+            int pc = getPC();
+            for(int i = base; i < pc; i += 4) {
+                fprintf(out, "%08x: %08x  ", i, *(int*) i);
+                ::disasm(&di, i, 0);
+            }
+            return 0;
+        }
     private:
+        static FILE* disasmOut;
+
+        static u_int
+        disassemble_readword(u_int address)
+        {
+            return(*((u_int *)address));
+        }
+
+        static void
+        disassemble_printaddr(u_int address)
+        {
+            fprintf(disasmOut, "0x%08x", address);
+        }
+
+        static void
+        disassemble_printf(const char *fmt, ...) {
+            va_list ap;
+            va_start(ap, fmt);
+            vfprintf(disasmOut, fmt, ap);
+            va_end(ap);
+        }
+
+        static const int BRANCH_REL_ADDRESS_MASK = 0x00ffffff;
+
+        /** Encode a relative address that might also be
+         * a label.
+         */
+        int encodeAddress(int value) {
+            int base = getBase();
+            if (value >= base && value <= getPC() ) {
+                // This is a label, encode it relative to the base.
+                value = value - base;
+            }
+            return encodeRelAddress(value);
+        }
+
+        int encodeRelAddress(int value) {
+            return BRANCH_REL_ADDRESS_MASK & (value >> 2);
+        }
 
         void error(const char* fmt,...) {
             va_list ap;
@@ -522,6 +621,14 @@
             oad(0xc481, l); /* add $xxx, %esp */
         }
 
+        virtual int jumpOffset() {
+            return 5;
+        }
+
+        virtual int disassemble(FILE* out) {
+            return 1;
+        }
+
     private:
         static const int operatorHelper[];
 
@@ -896,7 +1003,7 @@
                 pGen->callIndirect(l);
                 l = l + 4;
             } else {
-                pGen->callRelative(n - codeBuf.getPC() - 5); /* call xxx */
+                pGen->callRelative(n - codeBuf.getPC() - pGen->jumpOffset()); /* call xxx */
             }
             if (l)
                 pGen->adjustStackAfterCall(l);
@@ -935,7 +1042,7 @@
             if (a && l > 8) {
                 a = pGen->gtst(t == OP_LOGICAL_OR, a);
                 pGen->li(t != OP_LOGICAL_OR);
-                pGen->gjmp(5); /* jmp $ + 5 */
+                pGen->gjmp(5); /* jmp $ + 5 (sizeof li, FIXME for ARM) */
                 pGen->gsym(a);
                 pGen->li(t == OP_LOGICAL_OR);
             }
@@ -974,7 +1081,7 @@
             next();
             skip('(');
             if (t == TOK_WHILE) {
-                n = codeBuf.getPC();
+                n = codeBuf.getPC(); // top of loop, target of "next" iteration
                 a = test_expr();
             } else {
                 if (tok != ';')
@@ -988,14 +1095,14 @@
                 if (tok != ')') {
                     t = pGen->gjmp(0);
                     expr();
-                    pGen->gjmp(n - codeBuf.getPC() - 5);
+                    pGen->gjmp(n - codeBuf.getPC() - pGen->jumpOffset());
                     pGen->gsym(t);
                     n = t + 4;
                 }
             }
             skip(')');
             block((int) &a);
-            pGen->gjmp(n - codeBuf.getPC() - 5); /* jmp */
+            pGen->gjmp(n - codeBuf.getPC() - pGen->jumpOffset()); /* jmp */
             pGen->gsym(a);
         } else if (tok == '{') {
             next();
@@ -1179,6 +1286,10 @@
         return 0;
     }
 
+    int disassemble(FILE* out) {
+        return pGen->disassemble(out);
+    }
+
 };
 
 const char* compiler::operatorChars =
@@ -1192,6 +1303,8 @@
             2, 2 /* ~ ! */
             };
 
+FILE* compiler::ARMCodeGenerator::disasmOut;
+
 const int compiler::X86CodeGenerator::operatorHelper[] = {
         0x1,     // ++
         0xff,    // --
@@ -1226,6 +1339,7 @@
 
 int main(int argc, char** argv) {
     bool doDump = false;
+    bool doDisassemble = false;
     const char* inFile = NULL;
     const char* outFile = NULL;
     const char* architecture = "arm";
@@ -1251,6 +1365,9 @@
                 outFile = argv[i + 1];
                 i += 1;
                 break;
+            case 'S':
+                doDisassemble = true;
+                break;
             default:
                 fprintf(stderr, "Unrecognized flag %s\n", arg);
                 return 3;
@@ -1281,6 +1398,9 @@
         fprintf(stderr, "Compile failed: %d\n", compileResult);
         return 6;
     }
+    if (doDisassemble) {
+        compiler.disassemble(stderr);
+    }
     if (doDump) {
         FILE* save = fopen(outFile, "w");
         if (!save) {