#include <stdio.h>
#include <string.h>

#define NOP   0
#define LI    1
#define LXI   2
#define OUT   3
#define MOVAX 4
#define MOVXA 5
#define MOVSX 6
#define MOVXS 7
#define LD    8
#define ST    9
#define LDIX  10
#define STIX  11
#define JMP   12
#define PUSH  13
#define POP   14
#define PUSHX 15
#define POPX  16
#define CALL  17
#define RET   18
#define INC   19
#define DEC   20
#define INCX  21
#define DECX  22
#define BZ    23
#define BNZ   24
#define ADD   25
#define SUB   26
#define ADDI  27

int memory[0x10000];

int program[] =
{
  LXI, 0x10000,
  MOVSX,

  -2,
  LI, 123,
  OUT,
  -1
};

int sub1[] =
{
  LI, 789,
  OUT,
  RET
};

int illegal[] =
{
  LI, -2,
  OUT,
  RET
};

int main(void)
{
  int pc;
  int sp;
  int a;
  int x;
  int ireg;
  int eaddr;

  /* プログラムロード */
  memcpy(&memory[0], program, sizeof program);
  memcpy(&memory[0x100], sub1, sizeof sub1);
  memcpy(&memory[0xf000], illegal, sizeof illegal);

  /* リセット */
  pc = 0;

  for (;;)
  {
    ireg = memory[pc];
    pc = pc + 1;

    if (ireg == -1) break;

    switch (ireg)
    {
    case NOP:
      /* 何もしない */
      break;
    case LI:
      a = memory[pc];
      pc = pc + 1;
      break;
    case LXI:
      x = memory[pc];
      pc = pc + 1;
      break;
    case OUT:
      printf("0x%08x(%d)\n", a, a);
      break;
    case MOVAX:
      a = x;
      break;
    case MOVXA:
      x = a;
      break;
    case MOVSX:
      sp = x;
      break;
    case MOVXS:
      x = sp;
      break; 
    case LD:
      eaddr = memory[pc];
      pc = pc + 1;
      a = memory[eaddr];
      break; 
    case ST:
      eaddr = memory[pc];
      pc = pc + 1;
      memory[eaddr] = a;
      break; 
    case LDIX:
      eaddr = memory[pc] + x;
      pc = pc + 1;
      a = memory[eaddr];
      break; 
    case STIX:
      eaddr = memory[pc] + x;
      pc = pc + 1;
      memory[eaddr] = a;
      break; 
    case JMP:
      eaddr = memory[pc];
      pc = pc + 1;
      pc = eaddr;
      break;
    case PUSH:
      sp = sp - 1;
      memory[sp] = a;
      break;
    case POP:
      a = memory[sp];
      sp = sp + 1;
      break;
    case PUSHX:
      sp = sp - 1;
      memory[sp] = x;
      break;
    case POPX:
      x = memory[sp];
      sp = sp + 1;
      break;
    case CALL:
      eaddr = memory[pc];
      pc = pc + 1;
      sp = sp - 1;
      memory[sp] = pc;
      pc = eaddr;
      break;
    case RET:
      pc = memory[sp];
      sp = sp + 1;
      break;
    case INC:
      a = a + 1;
      break;
    case DEC:
      a = a - 1;
      break;
    case INCX:
      x = x + 1;
      break;
    case DECX:
      x = x - 1;
      break;
    case BZ:
      eaddr = memory[pc];
      pc = pc + 1;
      if (a == 0) pc = pc + eaddr;
      break;
    case BNZ:
      eaddr = memory[pc];
      pc = pc + 1;
      if (a != 0) pc = pc + eaddr;
      break;
    case ADD:
      a = a + x;
      break;
    case SUB:
      a = a - x;
      break;
    case ADDI:
      a = a + memory[pc];
      pc = pc + 1;
      break;
    default:
      eaddr = 0xf000;
      sp = sp - 1;
      memory[sp] = pc;
      pc = eaddr;
      break;
    }
  }
  return 0;
}


