// em -- cpu emulator
//
// Usage:  em [-v] [-m memsize] [-f filesys] file
//
// Description:
//
// Written by Robert Swierczek

#include <u.h>
#include <libc.h>
#include <libm.h>
#include <net.h>

enum {
  MEM_SZ = 128*1024*1024, // default memory size of virtual machine (128M)
  TB_SZ  =     1024*1024, // page translation buffer array size (4G / pagesize)
  FS_SZ  =   4*1024*1024, // ram file system size (4M)
  TPAGES = 4096,          // maximum cached page translations
};

enum {           // page table entry flags
  PTE_P = 0x001, // Present
  PTE_W = 0x002, // Writeable
  PTE_U = 0x004, // User
  PTE_A = 0x020, // Accessed
  PTE_D = 0x040, // Dirty
};

enum {           // processor fault codes (some can be masked together)
  FMEM,          // bad physical address 
  FTIMER,        // timer interrupt
  FKEYBD,        // keyboard interrupt
  FPRIV,         // privileged instruction
  FINST,         // illegal instruction
  FSYS,          // software trap
  FARITH,        // arithmetic trap
  FIPAGE,        // page fault on opcode fetch
  FWPAGE,        // page fault on write
  FRPAGE,        // page fault on read
  USER = 16      // user mode exception 
};

uint verbose,    // chatty option -v
  memsz,         // physical memory size
  user,          // user mode
  iena,          // interrupt enable
  ipend,         // interrupt pending
  trap,          // fault code
  ivec,          // interrupt vector
  vadr,          // bad virtual address
  paging,        // virtual memory enabled
  tpage[TPAGES], // valid page translations
  tpages;        // number of cached page translations

ulong
  *trk, *twk,    // kernel read/write page transation tables
  *tru, *twu,    // user read/write page transation tables
  *tr,  *tw,     // current read/write page transation tables
  mem,           // physical memory
  pdir;          // page directory

char *cmd;       // command name

void *new(int size)
{
  void *p;
  if ((p = sbrk((size + 7) & -8)) == (void *)-1) { dprintf(2,"%s : fatal: unable to sbrk(%d)\n", cmd, size); exit(-1); }
  return (void *)(((long)p + 7) & -8);
}

void flush(void)
{
  uint v; 
//  static int xx; if (tpages >= xx) { xx = tpages; dprintf(2,"****** flush(%d)\n",tpages); }
//  if (verbose) printf("F(%d)",tpages);
  while (tpages) {
    v = tpage[--tpages];
    trk[v] = twk[v] = tru[v] = twu[v] = 0;    
  }
}

ulong setpage(uint v, uint p, uint writable, uint userable)
{
  ulong pp;
  if (p >= memsz) { trap = FMEM; vadr = v; return 0; }
  pp = ((v ^ (mem + p)) & -4096) + 1;
  if (!trk[v >>= 12]) {
    if (tpages >= TPAGES) flush();
    tpage[tpages++] = v;
  }
//  if (verbose) printf(".");
  trk[v] = pp;
  twk[v] = writable ? pp : 0;
  tru[v] = userable ? pp : 0;
  twu[v] = (userable && writable) ? pp : 0;
  return pp;
}
  
ulong rlook(uint v)
{
  uint pde, *ppde, pte, *ppte, q, userable;
//  dprintf(2,"rlook(%08x)\n",v);
  if (!paging) return setpage(v, v, 1, 1);
  pde = *(ppde = (uint *)(pdir + (v>>22<<2))); // page directory entry
  if (pde & PTE_P) {
    if (!(pde & PTE_A)) *ppde = pde | PTE_A;
    if (pde >= memsz) { trap = FMEM; vadr = v; return 0; }
    pte = *(ppte = (uint *)(mem + (pde & -4096) + ((v >> 10) & 0xffc))); // page table entry
    if ((pte & PTE_P) && ((userable = (q = pte & pde) & PTE_U) || !user)) {
      if (!(pte & PTE_A)) *ppte = pte | PTE_A;
      return setpage(v, pte, (pte & PTE_D) && (q & PTE_W), userable); // set writable after first write so dirty gets set
    }
  }
  trap = FRPAGE;
  vadr = v;
  return 0;
}

ulong wlook(uint v)
{
  uint pde, *ppde, pte, *ppte, q, userable;
//  dprintf(2,"wlook(%08x)\n",v);
  if (!paging) return setpage(v, v, 1, 1);
  pde = *(ppde = (uint *)(pdir + (v>>22<<2))); // page directory entry
  if (pde & PTE_P) {
    if (!(pde & PTE_A)) *ppde = pde | PTE_A;
    if (pde >= memsz) { trap = FMEM; vadr = v; return 0; }
    pte = *(ppte = (uint *)(mem + (pde & -4096) + ((v >> 10) & 0xffc)));  // page table entry
    if ((pte & PTE_P) && (((userable = (q = pte & pde) & PTE_U) || !user) && (q & PTE_W))) {
      if ((pte & (PTE_D | PTE_A)) != (PTE_D | PTE_A)) *ppte = pte | (PTE_D | PTE_A);
      return setpage(v, pte, q & PTE_W, userable);
    }
  }
  trap = FWPAGE;
  vadr = v;
  return 0;
}

void cpu(uint pc, uint sp)
{
  uint a, b, c, ssp, usp, t, v, u, cycle, timer, timeout, xpc, delta;
  ulong p, ppc, tt;

  double f, g;
  int ir, kbchar;
  char ch;
  struct pollfd pfd;
  struct sockaddr_in addr;
  static char rbuf[4096]; // XXX
  
  a = b = c = cycle = timer = timeout = 0;
  delta = 4096;
  kbchar = -1;
  xpc = -1;

  for (;;) {
//    if (sp & 7) { dprintf(2,"stack pointer not a multiple of 8!\n", sp); goto fatal; }
    if (!(++cycle % delta)) { // XXX maybe dont check if last char pending?
      if (iena || !(ipend & FKEYBD)) { // XXX dont do this, use a small queue instead
        pfd.fd = 0;
        pfd.events = POLLIN;
        if (poll(&pfd, 1, 0) == 1 && read(0, &ch, 1) == 1) {
          kbchar = ch;
          if (kbchar == '`') { dprintf(2,"ungraceful exit. cycle = %u\n", cycle); return; }
          if (iena) { trap = FKEYBD; iena = 0; goto interrupt; }
          ipend |= FKEYBD;
        }
      }
      if (timeout) {
        timer += delta;
        if (timer >= timeout) { // XXX  // any interrupt actually!
//          dprintf(2,"timeout! timer=%d, timeout=%d\n",timer,timeout);
          timer = 0;      
          if (iena) { trap = FTIMER; iena = 0; goto interrupt; }
          ipend |= FTIMER;
        }
      }
    }
    if ((pc & (-4096 + 3)) != xpc) { // XXX just a simple optimization for now
      if (pc & 3) { dprintf(2,"pc not word aligned!\n", pc); goto fatal; } // XXX just reallign it?
      if (!(ppc = tr[(xpc = pc & -4096) >> 12]) && !(ppc = rlook(pc))) { xpc = -1; trap = FIPAGE; goto exception; }  // XXX be sure to invalidate xpc
      ppc--;
    }
    ir = *(int *)(pc ^ ppc);
    pc += 4;
    switch ((uchar)ir) {    
    case HALT: if (user || verbose) dprintf(2,"halt(%d) cycle = %u\n", a, cycle); return; // XXX should be supervisor!
    case IDLE: if (user) { trap = FPRIV; break; }
      if (!iena) { trap = FINST; break; } // XXX this will be fatal !!!
      for (;;) {
        pfd.fd = 0;
        pfd.events = POLLIN;
        if (poll(&pfd, 1, 0) == 1 && read(0, &ch, 1) == 1) {
          kbchar = ch;
          if (kbchar == '`') { dprintf(2,"ungraceful exit. cycle = %u\n", cycle); return; }
          trap = FKEYBD; 
          iena = 0;
          goto interrupt;
        }
        cycle += delta;
        if (timeout) {
          timer += delta;
          if (timer >= timeout) { // XXX  // any interrupt actually!
//        dprintf(2,"IDLE timeout! timer=%d, timeout=%d\n",timer,timeout);
            timer = 0;      
            trap = FTIMER;
            iena = 0;
            goto interrupt;
          }
        }
      }

    // memory -- designed to be restartable/continuable after exception/interrupt
    case MCPY: // while (c) { *a = *b; a++; b++; c--; }
      while (c) {
        if (!(tt = tr[b >> 12]) && !(tt = rlook(b))) goto exception;
        if (!(p = tw[a >> 12]) && !(p = wlook(a))) goto exception;
        if ((v = 4096 - (a & 4095)) > c) v = c;
        if ((u = 4096 - (b & 4095)) > v) u = v;
        memcpy((char *)(a ^ (p & -2)), (char *)(b ^ (tt & -2)), u);
        a += u; b += u; c -= u;
//        if (!(++cycle % DELTA)) { pc -= 4; break; } XXX
      }
      continue;

    case MCMP: // for (;;) { if (!c) { a = 0; break; } if (*b != *a) { a = *b - *a; b += c; c = 0; break; } a++; b++; c--; }
      for (;;) {
        if (!c) { a = 0; break; }
        if (!(tt = tr[b >> 12]) && !(tt = rlook(b))) goto exception;
        if (!(p = tr[a >> 12]) && !(p = rlook(a))) goto exception;
        if ((v = 4096 - (a & 4095)) > c) v = c;
        if ((u = 4096 - (b & 4095)) > v) u = v;
        if (t = memcmp((char *)(a ^ (p & -2)), (char *)(b ^ (tt & -2)), u)) { a = t; b += c; c = 0; break; }
        a += u; b += u; c -= u;
//        if (!(++cycle % DELTA)) { pc -= 4; break; } XXX
      }
      continue;

    case MCHR: // for (;;) { if (!c) { a = 0; break; } if (*a == b) { c = 0; break; } a++; c--; }
      for (;;) {
        if (!c) { a = 0; break; }
        if (!(p = tr[a >> 12]) && !(p = rlook(a))) goto exception;
        if ((u = 4096 - (a & 4095)) > c) u = c;
        if (tt = (ulong)memchr((char *)(p = a ^ (p & -2)), b, u)) { a += tt - p; c = 0; break; } 
        a += u; c -= u;
//        if (!(++cycle % DELTA)) { pc -= 4; break; } XXX
      }
      continue;

    case MSET: // while (c) { *a = b; a++; c--; }
      while (c) {
        if (!(p = tw[a >> 12]) && !(p = wlook(a))) goto exception;
        if ((u = 4096 - (a & 4095)) > c) u = c;
        memset((char *)(a ^ (p & -2)), b, u);
        a += u; c -= u;
//        if (!(++cycle % DELTA)) { pc -= 4; break; } XXX
      }
      continue;

    // math
    case POW:  f = pow(f,g); continue;
    case ATN2: f = atan2(f,g); continue;
    case FABS: f = fabs(f); continue;
    case ATAN: f = atan(f); continue;
    case LOG:  if (f) f = log(f); continue; // XXX others?
    case LOGT: if (f) f = log10(f); continue; // XXX
    case EXP:  f = exp(f); continue;
    case FLOR: f = floor(f); continue;
    case CEIL: f = ceil(f); continue;
    case HYPO: f = hypot(f,g); continue;
    case SIN:  f = sin(f); continue;
    case COS:  f = cos(f); continue;
    case TAN:  f = tan(f); continue;
    case ASIN: f = asin(f); continue;
    case ACOS: f = acos(f); continue;
    case SINH: f = sinh(f); continue;
    case COSH: f = cosh(f); continue;
    case TANH: f = tanh(f); continue;
    case SQRT: f = sqrt(f); continue;
    case FMOD: f = fmod(f,g); continue;

    case ENT:  sp += ir>>8; continue;
    case LEV:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; pc = *(uint *)((v ^ p) & -8); sp += (ir>>8) + 8; continue;

    // jump
    case JMP:  pc += ir>>8; continue;
    case JMPI: if (!(p = tr[(v = pc + (ir>>8) + (a<<2)) >> 12]) && !(p = rlook(v))) break; pc += *(uint *)((v ^ p) & -4); continue;
    case JSR:  if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(uint *)((v ^ p) & -8) = pc; sp -= 8; pc += ir>>8; continue;
    case JSRA: if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(uint *)((v ^ p) & -8) = pc; sp -= 8; pc = a; continue;

    // stack
    case PSHA: if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(uint *)   ((v ^ p) & -8) = a;     sp -= 8; continue;
    case PSHB: if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(uint *)   ((v ^ p) & -8) = b;     sp -= 8; continue;
    case PSHC: if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(uint *)   ((v ^ p) & -8) = c;     sp -= 8; continue;
    case PSHF: if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(double *) ((v ^ p) & -8) = f;     sp -= 8; continue;
    case PSHG: if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(double *) ((v ^ p) & -8) = g;     sp -= 8; continue;
    case PSHI: if (!(p = tw[(v = sp - 8) >> 12]) && !(p = wlook(v))) break; *(int *)    ((v ^ p) & -8) = ir>>8; sp -= 8; continue;    

    case POPA: if (!(p = tr[sp >> 12]) && !(p = rlook(sp))) break; a = *(uint *)   ((sp ^ p) & -8); sp += 8; continue;
    case POPB: if (!(p = tr[sp >> 12]) && !(p = rlook(sp))) break; b = *(uint *)   ((sp ^ p) & -8); sp += 8; continue;
    case POPC: if (!(p = tr[sp >> 12]) && !(p = rlook(sp))) break; c = *(uint *)   ((sp ^ p) & -8); sp += 8; continue;
    case POPF: if (!(p = tr[sp >> 12]) && !(p = rlook(sp))) break; f = *(double *) ((sp ^ p) & -8); sp += 8; continue;
    case POPG: if (!(p = tr[sp >> 12]) && !(p = rlook(sp))) break; g = *(double *) ((sp ^ p) & -8); sp += 8; continue;

    // load effective address
    case LEA:  a = sp + (ir>>8); continue; 
    case LEAG: a = pc + (ir>>8); continue;

    // load a local
    case LL:   if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(uint *)   ((v ^ p) & -4); continue;
    case LLS:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(short *)  ((v ^ p) & -2); continue;
    case LLH:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(ushort *) ((v ^ p) & -2); continue;
    case LLC:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(char *)   (v ^ p & -2); continue;
    case LLB:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(uchar *)  (v ^ p & -2); continue;
    case LLD:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; f = *(double *) ((v ^ p) & -8); continue;
    case LLF:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; f = *(float *)  ((v ^ p) & -4); continue;

    // load a global
    case LG:   if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(uint *)   ((v ^ p) & -4); continue;
    case LGS:  if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(short *)  ((v ^ p) & -2); continue;
    case LGH:  if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(ushort *) ((v ^ p) & -2); continue;
    case LGC:  if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(char *)   (v ^ p & -2); continue;
    case LGB:  if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(uchar *)  (v ^ p & -2); continue;
    case LGD:  if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; f = *(double *) ((v ^ p) & -8); continue;
    case LGF:  if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; f = *(float *)  ((v ^ p) & -4); continue;

    // load a indexed
    case LX:   if (!(p = tr[(v = a + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(uint *)   ((v ^ p) & -4); continue;
    case LXS:  if (!(p = tr[(v = a + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(short *)  ((v ^ p) & -2); continue;
    case LXH:  if (!(p = tr[(v = a + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(ushort *) ((v ^ p) & -2); continue;
    case LXC:  if (!(p = tr[(v = a + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(char *)   (v ^ p & -2); continue;
    case LXB:  if (!(p = tr[(v = a + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = *(uchar *)  (v ^ p & -2); continue;
    case LXD:  if (!(p = tr[(v = a + (ir>>8)) >> 12]) && !(p = rlook(v))) break; f = *(double *) ((v ^ p) & -8); continue;
    case LXF:  if (!(p = tr[(v = a + (ir>>8)) >> 12]) && !(p = rlook(v))) break; f = *(float *)  ((v ^ p) & -4); continue;

    // load a immediate
    case LI:   a = ir>>8; continue;
    case LHI:  a = a<<24 | (uint)ir>>8; continue;
    case LIF:  f = (ir>>8)/256.0; continue;

    // load b local
    case LBL:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(uint *)   ((v ^ p) & -4); continue;
    case LBLS: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(short *)  ((v ^ p) & -2); continue;
    case LBLH: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(ushort *) ((v ^ p) & -2); continue;
    case LBLC: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(char *)   (v ^ p & -2); continue;
    case LBLB: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(uchar *)  (v ^ p & -2); continue;
    case LBLD: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; g = *(double *) ((v ^ p) & -8); continue;
    case LBLF: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; g = *(float *)  ((v ^ p) & -4); continue;

    // load b global
    case LBG:  if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(uint *)   ((v ^ p) & -4); continue;
    case LBGS: if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(short *)  ((v ^ p) & -2); continue;
    case LBGH: if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(ushort *) ((v ^ p) & -2); continue;
    case LBGC: if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(char *)   (v ^ p & -2); continue;
    case LBGB: if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(uchar *)  (v ^ p & -2); continue;
    case LBGD: if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; g = *(double *) ((v ^ p) & -8); continue;
    case LBGF: if (!(p = tr[(v = pc + (ir>>8)) >> 12]) && !(p = rlook(v))) break; g = *(float *)  ((v ^ p) & -4); continue;

    // load b indexed
    case LBX:  if (!(p = tr[(v = b + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(uint *)   ((v ^ p) & -4); continue;
    case LBXS: if (!(p = tr[(v = b + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(short *)  ((v ^ p) & -2); continue;
    case LBXH: if (!(p = tr[(v = b + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(ushort *) ((v ^ p) & -2); continue;
    case LBXC: if (!(p = tr[(v = b + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(char *)   (v ^ p & -2); continue;
    case LBXB: if (!(p = tr[(v = b + (ir>>8)) >> 12]) && !(p = rlook(v))) break; b = *(uchar *)  (v ^ p & -2); continue;
    case LBXD: if (!(p = tr[(v = b + (ir>>8)) >> 12]) && !(p = rlook(v))) break; g = *(double *) ((v ^ p) & -8); continue;
    case LBXF: if (!(p = tr[(v = b + (ir>>8)) >> 12]) && !(p = rlook(v))) break; g = *(float *)  ((v ^ p) & -4); continue;

    // load b immediate
    case LBI:  b = ir>>8; continue;
    case LBHI: b = b<<24 | (uint)ir>>8; continue;
    case LBIF: g = (ir>>8)/256.0; continue;

    // misc transfer
    case LCL:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; c = *(uint *) ((v ^ p) & -4); continue;

    case LBA:  b = a; continue;  // XXX need LAB, LAC to improve k.c  // or maybe a = a * imm + b ?  or b = b * imm + a ?
    case LCA:  c = a; continue;
    case LBAD: g = f; continue;

    // store a local
    case SL:   if (!(p = tw[(v = sp + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(uint *)   ((v ^ p) & -4) = a; continue;
    case SLH:  if (!(p = tw[(v = sp + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(ushort *) ((v ^ p) & -2) = a; continue;
    case SLB:  if (!(p = tw[(v = sp + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(uchar *)  (v ^ p & -2)   = a; continue;
    case SLD:  if (!(p = tw[(v = sp + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(double *) ((v ^ p) & -8) = f; continue;
    case SLF:  if (!(p = tw[(v = sp + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(float *)  ((v ^ p) & -4) = f; continue;

    // store a global
    case SG:   if (!(p = tw[(v = pc + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(uint *)   ((v ^ p) & -4) = a; continue;
    case SGH:  if (!(p = tw[(v = pc + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(ushort *) ((v ^ p) & -2) = a; continue;
    case SGB:  if (!(p = tw[(v = pc + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(uchar *)  (v ^ p & -2)   = a; continue;
    case SGD:  if (!(p = tw[(v = pc + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(double *) ((v ^ p) & -8) = f; continue;
    case SGF:  if (!(p = tw[(v = pc + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(float *)  ((v ^ p) & -4) = f; continue;

    // store a indexed
    case SX:   if (!(p = tw[(v = b + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(uint *)   ((v ^ p) & -4) = a; continue;
    case SXH:  if (!(p = tw[(v = b + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(ushort *) ((v ^ p) & -2) = a; continue;
    case SXB:  if (!(p = tw[(v = b + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(uchar *)  (v ^ p & -2)   = a; continue;
    case SXD:  if (!(p = tw[(v = b + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(double *) ((v ^ p) & -8) = f; continue;
    case SXF:  if (!(p = tw[(v = b + (ir>>8)) >> 12]) && !(p = wlook(v))) break; *(float *)  ((v ^ p) & -4) = f; continue;
      
    // arithmetic
    case ADDF: f += g; continue;
    case SUBF: f -= g; continue;
    case MULF: f *= g; continue;
    case DIVF: if (g == 0.0) { trap = FARITH; break; } f /= g; continue; // XXX

    case ADD:  a += b; continue;
    case ADDI: a += ir>>8; continue;
    case ADDL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a += *(uint *)((v ^ p) & -4); continue;

    case SUB:  a -= b; continue;
    case SUBI: a -= ir>>8; continue;
    case SUBL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a -= *(uint *)((v ^ p) & -4); continue;

    case MUL:  a = (int)a * (int)b; continue; // XXX MLU ???
    case MULI: a = (int)a * (ir>>8); continue;
    case MULL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = (int)a * *(int *)((v ^ p) & -4); continue;

    case DIV:  if (!b) { trap = FARITH; break; } a = (int)a / (int)b; continue;
    case DIVI: if (!(t = ir>>8)) { trap = FARITH; break; } a = (int)a / (int)t; continue;
    case DIVL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break;
               if (!(t = *(uint *)((v ^ p) & -4))) { trap = FARITH; break; } a = (int)a / (int)t; continue;

    case DVU:  if (!b) { trap = FARITH; break; } a /= b; continue;
    case DVUI: if (!(t = ir>>8)) { trap = FARITH; break; } a /= t; continue;
    case DVUL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break;
               if (!(t = *(uint *)((v ^ p) & -4))) { trap = FARITH; break; } a /= t; continue;

    case MOD:  a = (int)a % (int)b; continue;
    case MODI: a = (int)a % (ir>>8); continue;
    case MODL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = (int)a % *(int *)((v ^ p) & -4); continue;

    case MDU:  a %= b; continue;
    case MDUI: a %= (ir>>8); continue;
    case MDUL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a %= *(uint *)((v ^ p) & -4); continue;

    case AND:  a &= b; continue;
    case ANDI: a &= ir>>8; continue;
    case ANDL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a &= *(uint *)((v ^ p) & -4); continue;

    case OR:   a |= b; continue;
    case ORI:  a |= ir>>8; continue;
    case ORL:  if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a |= *(uint *)((v ^ p) & -4); continue;

    case XOR:  a ^= b; continue;
    case XORI: a ^= ir>>8; continue;
    case XORL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a ^= *(uint *)((v ^ p) & -4); continue;

    case SHL:  a <<= b; continue;
    case SHLI: a <<= ir>>8; continue;
    case SHLL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a <<= *(uint *)((v ^ p) & -4); continue;

    case SHR:  a = (int)a >> (int)b; continue;
    case SHRI: a = (int)a >> (ir>>8); continue;
    case SHRL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a = (int)a >> *(int *)((v ^ p) & -4); continue;

    case SRU:  a >>= b; continue;
    case SRUI: a >>= ir>>8; continue;
    case SRUL: if (!(p = tr[(v = sp + (ir>>8)) >> 12]) && !(p = rlook(v))) break; a >>= *(uint *)((v ^ p) & -4); continue;

    // logical
    case EQ:   a = a == b; continue;
    case EQF:  a = f == g; continue;
    case NE:   a = a != b; continue;
    case NEF:  a = f != g; continue;
    case LT:   a = (int)a < (int)b; continue;
    case LTU:  a = a < b; continue;
    case LTF:  a = f < g; continue;
    case GE:   a = (int)a >= (int)b; continue;
    case GEU:  a = a >= b; continue;
    case GEF:  a = f >= g; continue;

    // branch
    case BZ:   if (!a)               pc += ir>>8; continue;
    case BZF:  if (!f)               pc += ir>>8; continue;
    case BNZ:  if (a)                pc += ir>>8; continue;
    case BNZF: if (f)                pc += ir>>8; continue;
    case BE:   if (a == b)           pc += ir>>8; continue;
    case BEF:  if (f == g)           pc += ir>>8; continue;
    case BNE:  if (a != b)           pc += ir>>8; continue;
    case BNEF: if (f != g)           pc += ir>>8; continue;
    case BLT:  if ((int)a < (int)b)  pc += ir>>8; continue;
    case BLTU: if (a < b)            pc += ir>>8; continue;
    case BLTF: if (f <  g)           pc += ir>>8; continue;
    case BGE:  if ((int)a >= (int)b) pc += ir>>8; continue;
    case BGEU: if (a >= b)           pc += ir>>8; continue;
    case BGEF: if (f >= g)           pc += ir>>8; continue;
    
    // conversion
    case CID:  f = (int)a; continue;
    case CUD:  f = a; continue;
    case CDI:  a = (int)f; continue;
    case CDU:  a = f; continue;

    // misc
    case BIN:  if (user) { trap = FPRIV; break; } a = kbchar; kbchar = -1; continue;  // XXX
    case BOUT: if (user) { trap = FPRIV; break; } if (a != 1) { dprintf(2,"bad write a=%d\n",a); return; } ch = b; a = write(a, &ch, 1); continue;
    case SSP:  sp = a; continue;

    case NOP:  continue;
    case CYC:  a = cycle; continue; // XXX protected?  XXX also need wall clock time instruction
    case MSIZ: if (user) { trap = FPRIV; break; } a = memsz; continue;
    
    case CLI:  if (user) { trap = FPRIV; break; } a = iena; iena = 0; continue;
    case STI:  if (user) { trap = FPRIV; break; } if (ipend) { trap = ipend & -ipend; ipend ^= trap; iena = 0; goto interrupt; } iena = 1; continue;

    case RTI:
      if (user) { trap = FPRIV; break; }
      if (!(p = tr[sp >> 12]) && !(p = rlook(sp))) { dprintf(2,"RTI kstack fault\n"); goto fatal; }
      t  = *(uint *)((sp ^ p) & -8); sp += 8;
      if (!(p = tr[sp >> 12]) && !(p = rlook(sp))) { dprintf(2,"RTI kstack fault\n"); goto fatal; }
      pc = *(uint *)((sp ^ p) & -8); sp += 8;
      if (t & USER) { ssp = sp; sp = usp; user = 1; tr = tru; tw = twu; }
      if (!iena) { if (ipend) { trap = ipend & -ipend; ipend ^= trap; goto interrupt; } iena = 1; }
      continue;

    case IVEC: if (user) { trap = FPRIV; break; } ivec = a; continue;
    case PDIR: if (user) { trap = FPRIV; break; } if (a > memsz) { trap = FMEM; break; } pdir = (mem + a) & -4096; flush(); continue; // set page directory
    case SPAG: if (user) { trap = FPRIV; break; } if (a && !pdir) { trap = FMEM; break; } paging = a; flush(); continue; // enable paging
    
    case TIME: if (user) { trap = FPRIV; break; } 
       if (ir>>8) { dprintf(2,"timer%d=%u timeout=%u\n", ir>>8, timer, timeout); continue; }    // XXX undocumented feature!
       timeout = a; continue; // XXX cancel pending interrupts if disabled?

    // XXX need some sort of user mode thread locking functions to support user mode semaphores, etc.  atomic test/set?
    
    case LVAD: if (user) { trap = FPRIV; break; } a = vadr; continue;

    case TRAP: trap = FSYS; break;
    
    case LUSP: if (user) { trap = FPRIV; break; } a = usp; continue;
    case SUSP: if (user) { trap = FPRIV; break; } usp = a; continue;
    
    // networking -- XXX HACK CODE (and all wrong), but it gets some basic networking going...
    case NET1: if (user) { trap = FPRIV; break; } a = socket(a, b, c); continue; // XXX
    case NET2: if (user) { trap = FPRIV; break; } a = close(a); continue; // XXX does this block?
    case NET3: if (user) { trap = FPRIV; break; }
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = b & 0xFFFF;
      addr.sin_port = b >> 16;
      addr.sin_addr.s_addr = c;
      a = connect(a, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)); // XXX needs to be non-blocking
      continue;
    case NET4: if (user) { trap = FPRIV; break; }
      t = 0;
      // XXX if ((unknown || !ready) && !poll()) return -1;
      if ((int)c > 0) { if ((int)c > sizeof(rbuf)) c = sizeof(rbuf); a = c = read(a, rbuf, c); } else a = 0; // XXX uint or int??
      while ((int)c > 0) { 
        if (!(p = tw[b >> 12]) && !(p = wlook(b))) { dprintf(2,"unstable!!"); exit(9); } //goto exception; } XXX
        if ((u = 4096 - (b & 4095)) > c) u = c;
        memcpy((char *)(b ^ p & -2), &rbuf[t], u);
        t += u; b += u; c -= u;
      }
      continue;
    case NET5: if (user) { trap = FPRIV; break; }
      t = c;
      // XXX if ((unknown || !ready) && !poll()) return -1;
      while ((int)c > 0) {
        if (!(p = tr[b >> 12]) && !(p = rlook(b))) goto exception;
        if ((u = 4096 - (b & 4095)) > c) u = c;
        if ((int)(u = write(a, (char *)(b ^ p & -2), u)) > 0) { b += u; c -= u; } else { t = u; break; }
      }
      a = t;
      continue;
    case NET6: if (user) { trap = FPRIV; break; }
      pfd.fd = a;
      pfd.events = POLLIN;
      a = poll(&pfd, 1, 0); // XXX do something completely different
      continue;
    case NET7: if (user) { trap = FPRIV; break; }
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = b & 0xFFFF;
      addr.sin_port = b >> 16;
      addr.sin_addr.s_addr = c;
      a = bind(a, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
      continue;
    case NET8: if (user) { trap = FPRIV; break; }
      a = listen(a, b);
      continue;
    case NET9: if (user) { trap = FPRIV; break; }
      // XXX if ((unknown || !ready) && !poll()) return -1;
//      a = accept(a, (void *)b, (void *)c); // XXX cant do this with virtual addresses!!!
      continue;
    
    default: trap = FINST; break;
    }
exception:
    if (!iena) { dprintf(2,"exception in interrupt handler\n"); goto fatal; }
interrupt:
    if (user) { usp = sp; sp = ssp; user = 0; tr = trk; tw = twk; trap |= USER; }
    sp -= 8; if (!(p = tw[sp >> 12]) && !(p = wlook(sp))) { dprintf(2,"kstack fault!\n"); goto fatal; }
    *(uint *)((sp ^ p) & -8) = pc;
    sp -= 8; if (!(p = tw[sp >> 12]) && !(p = wlook(sp))) { dprintf(2,"kstack fault\n"); goto fatal; }
    *(uint *)((sp ^ p) & -8) = trap;
    pc = ivec;
  }
fatal:
  dprintf(2,"processor halted! cycle = %u pc = %08x ir = %08x sp = %08x a = %d b = %d c = %d trap = %u\n", cycle, pc, ir, sp, a, b, c, trap);
}

void usage(void)
{ 
  dprintf(2,"%s : usage: %s [-v] [-m memsize] [-f filesys] file\n", cmd, cmd);
  exit(-1);
}

int main(int argc, char *argv[])
{
  int i, f;
  struct { uint magic, bss, entry, flags; } hdr;
  char *file, *fs;
  struct stat st;
  
  cmd = *argv++;
  if (argc < 2) usage();
  file = *argv;
  memsz = MEM_SZ;
  fs = 0;
  while (--argc && *file == '-') {
    switch(file[1]) {
    case 'v': verbose = 1; break;
    case 'm': memsz = atoi(*++argv) * (1024 * 1024); argc--; break;
    case 'f': fs = *++argv; argc--; break;
    default: usage();
    }
    file = *++argv;
  }
  
  if (verbose) dprintf(2,"mem size = %u\n",memsz);
  mem = (((long) new(memsz + 4096)) + 4095) & -4096;
  
  if (fs) {
    if (verbose) dprintf(2,"%s : loading ram file system %s\n", cmd, fs);
    if ((f = open(fs, O_RDONLY)) < 0) { dprintf(2,"%s : couldn't open file system %s\n", cmd, fs); return -1; }
    if (fstat(f, &st)) { dprintf(2,"%s : couldn't stat file system %s\n", cmd, fs); return -1; }
    if ((i = read(f, (void*)(mem + memsz - FS_SZ), st.st_size)) != st.st_size) { dprintf(2,"%s : failed to read filesystem size %d returned %d\n", cmd, st.st_size, i); return -1; }
    close(f);
  }
  
  if ((f = open(file, O_RDONLY)) < 0) { dprintf(2,"%s : couldn't open %s\n", cmd, file); return -1; }
  if (fstat(f, &st)) { dprintf(2,"%s : couldn't stat file %s\n", cmd, file); return -1; }

  read(f, &hdr, sizeof(hdr));
  if (hdr.magic != 0xC0DEF00D) { dprintf(2,"%s : bad hdr.magic\n", cmd); return -1; }
  
  if (read(f, (void*)mem, st.st_size - sizeof(hdr)) != st.st_size - sizeof(hdr)) { dprintf(2,"%s : failed to read file %sn", cmd, file); return -1; }
  close(f);

//  if (verbose) dprintf(2,"entry = %u text = %u data = %u bss = %u\n", hdr.entry, hdr.text, hdr.data, hdr.bss);

  // setup virtual memory
  trk = (ulong *) new(TB_SZ * sizeof(ulong)); // kernel read table
  twk = (ulong *) new(TB_SZ * sizeof(ulong)); // kernel write table
  tru = (ulong *) new(TB_SZ * sizeof(ulong)); // user read table
  twu = (ulong *) new(TB_SZ * sizeof(ulong)); // user write table
  tr = trk;
  tw = twk;

  if (verbose) dprintf(2,"%s : emulating %s\n", cmd, file);
  cpu(hdr.entry, memsz - FS_SZ);
  return 0;
}

