#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_MACHINE_H_
#define COSMOPOLITAN_TOOL_BUILD_LIB_MACHINE_H_
#include "libc/runtime/runtime.h"
#include "third_party/xed/x86.h"
#include "tool/build/lib/fds.h"

#define kMachineHalt                 -1
#define kMachineDecodeError          -2
#define kMachineUndefinedInstruction -3
#define kMachineSegmentationFault    -4
#define kMachineExit                 -5
#define kMachineDivideError          -6
#define kMachineFpuException         -7
#define kMachineProtectionFault      -8
#define kMachineSimdException        -9

#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_

struct Machine {
  struct XedDecodedInst *xedd;
  uint64_t ip;
  uint8_t cs[8];
  uint8_t ss[8];
  uint64_t codevirt;
  uint8_t *codehost;
  uint32_t mode;
  uint32_t flags;
  uint32_t tlbindex;
  uint32_t stashsize;
  int64_t stashaddr;
  int64_t readaddr;
  int64_t writeaddr;
  uint32_t readsize;
  uint32_t writesize;
  union {
    uint8_t reg[16][8];
    struct {
      uint8_t ax[8];
      uint8_t cx[8];
      uint8_t dx[8];
      uint8_t bx[8];
      uint8_t sp[8];
      uint8_t bp[8];
      uint8_t si[8];
      uint8_t di[8];
      uint8_t r8[8];
      uint8_t r9[8];
      uint8_t r10[8];
      uint8_t r11[8];
      uint8_t r12[8];
      uint8_t r13[8];
      uint8_t r14[8];
      uint8_t r15[8];
    };
  };
  struct MachineTlb {
    int64_t virt;
    uint64_t entry;
  } tlb[16];
  struct MachineReal {
    size_t i, n;
    uint8_t *p;
  } real;
  uint64_t cr3;
  uint8_t xmm[16][16];
  uint8_t es[8];
  uint8_t ds[8];
  uint8_t fs[8];
  uint8_t gs[8];
  struct MachineFpu {
    long double st[8];
    union {
      uint32_t cw;
      struct {
        unsigned im : 1;   // invalid operation mask
        unsigned dm : 1;   // denormal operand mask
        unsigned zm : 1;   // zero divide mask
        unsigned om : 1;   // overflow mask
        unsigned um : 1;   // underflow mask
        unsigned pm : 1;   // precision mask
        unsigned _p1 : 2;  // reserved
        unsigned pc : 2;   // precision: 32,∅,64,80
        unsigned rc : 2;   // rounding: even,→-∞,→+∞,→0
      };
    };
    union {
      uint32_t sw;
      struct {
        unsigned ie : 1;  // invalid operation
        unsigned de : 1;  // denormalized operand
        unsigned ze : 1;  // zero divide
        unsigned oe : 1;  // overflow
        unsigned ue : 1;  // underflow
        unsigned pe : 1;  // precision
        unsigned sf : 1;  // stack fault
        unsigned es : 1;  // exception summary status
        unsigned c0 : 1;  // condition 0
        unsigned c1 : 1;  // condition 1
        unsigned c2 : 1;  // condition 2
        unsigned sp : 3;  // top stack
        unsigned c3 : 1;  // condition 3
        unsigned bf : 1;  // busy flag
      };
    };
    int tw;
    int op;
    int64_t ip;
    int64_t dp;
  } fpu;
  struct MachineSse {
    union {
      uint32_t mxcsr;
      struct {
        unsigned ie : 1;   // invalid operation flag
        unsigned de : 1;   // denormal flag
        unsigned ze : 1;   // divide by zero flag
        unsigned oe : 1;   // overflow flag
        unsigned ue : 1;   // underflow flag
        unsigned pe : 1;   // precision flag
        unsigned daz : 1;  // denormals are zeros
        unsigned im : 1;   // invalid operation mask
        unsigned dm : 1;   // denormal mask
        unsigned zm : 1;   // divide by zero mask
        unsigned om : 1;   // overflow mask
        unsigned um : 1;   // underflow mask
        unsigned pm : 1;   // precision mask
        unsigned rc : 2;   // rounding control
        unsigned ftz : 1;  // flush to zero
      };
    };
  } sse;
  uint64_t cr0;
  uint64_t cr2;
  uint64_t cr4;
  uint64_t gdt_base;
  uint64_t idt_base;
  uint16_t gdt_limit;
  uint16_t idt_limit;
  struct MachineRealFree {
    uint64_t i;
    uint64_t n;
    struct MachineRealFree *next;
  } * realfree;
  struct FreeList {
    uint32_t i;
    void *p[6];
  } freelist;
  struct MachineMemstat {
    int freed;
    int resizes;
    int reserved;
    int committed;
    int allocated;
    int reclaimed;
    int pagetables;
  } memstat;
  int64_t brk;
  int64_t bofram[2];
  jmp_buf onhalt;
  int64_t faultaddr;
  bool dlab;
  struct MachineFds fds;
  uint8_t stash[4096];
  uint8_t icache[1024][40];
  void (*onbinbase)(struct Machine *);
  void (*onlongbranch)(struct Machine *);
} forcealign(64);

struct Machine *NewMachine(void) nodiscard;
void FreeMachine(struct Machine *);
void ResetMem(struct Machine *);
void ResetCpu(struct Machine *);
void ResetTlb(struct Machine *);
void ResetInstructionCache(struct Machine *);
void LoadInstruction(struct Machine *);
void ExecuteInstruction(struct Machine *);
long AllocateLinearPage(struct Machine *);
long AllocateLinearPageRaw(struct Machine *);
int ReserveReal(struct Machine *, size_t);
int ReserveVirtual(struct Machine *, int64_t, size_t, uint64_t);
char *FormatPml4t(struct Machine *) nodiscard;
int64_t FindVirtual(struct Machine *, int64_t, size_t);
int FreeVirtual(struct Machine *, int64_t, size_t);

COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_MACHINE_H_ */
