/** This is a SAF interpreter of games written in programming language comun.
  Keep in mind that unless you make a precompiled version of the game, THIS IS
  A NON-STANDARD SAF PROGRAM because it reads from a file and writes errors to
  stderr (which is normally advised not to do with SAF). However once you make
  game, you can create a precompiled version that will again be a well-behaved
  SAF program.
  
  Usage: compile this like normal SAF program. Then when ran, the program
  looks for a file named "game.cmn" which is a comun source for the game to
  interpret. The file gets compiled and interpreted. In case of error in the
  program it is written to stderr.

  To make a precompiled version copy the precompiled data that's output by this
  program to some file (e.g. compiled_data.h) and include it as a library below.
  Then compiled this again.

  To learn the comun language, see its repository. SAF binding to comun is
  intuitive: the names of functions are the same as in saf.h, parameters are
  passed via stack so that they appear in the source code in the same order as
  in the function's prototype, e.g. what in C would be
  SAF_drawLine(1,2,3,4,SAF_COLOR_BLACK) in comun is
  1 2 3 4 SAF_COLOR_BLACK SAF_drawLine. Strings are expected to be zero
  terminated but pushed in reverse order (as is standard in comun), image data
  are also expected to be pushed in reverse order, so that image width is on
  stack top, image height is one cell below etc.). Top level code is
  initialization (what in C is inside SAF_init), loop code goes to SAF_loop
  function (just as in C). */

#define SAF_PROGRAM_NAME "comun game"

#include "../saf.h"

//#include "compiled_data.h"  // include precompiled data here

#if !PRECOMPILED
  #include <stdio.h>
#endif

#define CMN_STRING_PSEUDOHASH_SIZE 16
#include "comun.h"

#define GAME_FILE_NAME "game.cmn"
#define WORKINGMEM_SIZE 2048

#if !PRECOMPILED
  #define BYTECODEDMEM_SIZE 4096
  uint8_t bytecodeMem[BYTECODEDMEM_SIZE];
#endif

uint8_t workingMem[WORKINGMEM_SIZE];

#define HELPER_STRING_SIZE 32
char helperString[HELPER_STRING_SIZE];

#define HELPER_IMAGE_SIZE (16 * 16)
uint8_t helperImage[HELPER_IMAGE_SIZE];

#define F_BUTTON_PRESSED          0x00
#define F_BUTTON_JUST_PRESSED     0x01
#define F_PLAY_SOUND              0x02
#define F_SAVE                    0x03
#define F_LOAD                    0x04
#define F_FRAME                   0x05
#define F_TIME                    0x06
#define F_RANDOM                  0x07
#define F_RANDOM_SEED             0x08
#define F_SIN                     0x09
#define F_COS                     0x0a
#define F_SQRT                    0x0b
#define F_COLOR_FROM_RGB          0x0c
#define F_COLOR_TO_RGB            0x0d
#define F_COLOR_TO_GRAYSCALE      0x0e
#define F_COLOR_TO_1BIT           0x0f
#define F_COLOR_INVERT            0x10
#define F_DRAW_PIXEL              0x11
#define F_DRAW_RECT               0x12
#define F_DRAW_LINE               0x13
#define F_DRAW_CIRCLE             0x14
#define F_CLEAR_SCREEN            0x15
#define F_DRAW_TEXT               0x16
#define F_GET_FONT_CHARACTER      0x17
#define F_DRAW_IMAGE              0x18
#define F_DRAW_IMAGE_COMPRESSED   0x19
#define F_DRAW_IMAGE_1BIT         0x1a
#define F_INT_TO_STR              0x1b
#define F_BUTTON_UP               0x1c
#define F_BUTTON_DOWN             0x1d
#define F_BUTTON_LEFT             0x1e
#define F_BUTTON_RIGHT            0x1f
#define F_BUTTON_A                0x20
#define F_BUTTON_B                0x21
#define F_BUTTON_C                0x22
#define F_COLOR_BLACK             0x23
#define F_COLOR_WHITE             0x24
#define F_COLOR_GRAY              0x25
#define F_COLOR_GRAY_DARK         0x26
#define F_COLOR_RED               0x27
#define F_COLOR_RED_DARK          0x28
#define F_COLOR_GREEN             0x29
#define F_COLOR_GREEN_DARK        0x2a
#define F_COLOR_BLUE              0x2b
#define F_COLOR_BLUE_DARK         0x2c
#define F_COLOR_YELLOW            0x2d
#define F_COLOR_ORANGE            0x2e
#define F_COLOR_BROWN             0x2f
#define F_SOUND_BEEP              0x30
#define F_SOUND_CLICK             0x31
#define F_SOUND_BOOM              0x32
#define F_SOUND_BUMP              0x33
#define F_TRANSFORM_NONE          0x34
#define F_TRANSFORM_ROTATE_90     0x35
#define F_TRANSFORM_ROTATE_180    0x36
#define F_TRANSFORM_ROTATE_270    0x37
#define F_TRANSFORM_FLIP          0x38
#define F_TRANSFORM_SCALE_2       0x39
#define F_TRANSFORM_SCALE_3       0x3a
#define F_TRANSFORM_SCALE_4       0x3b
#define F_TRANSFORM_INVERT        0x3c
#define F_SCREEN_WIDTH            0x3d
#define F_SCREEN_HEIGHT           0x3e
#define F_FPS                     0x3f

#define FUNCTIONS_TOTAL           0x40

#if !PRECOMPILED
  int8_t functions[FUNCTIONS_TOTAL];
  CMN_Compiler compiler;
  int32_t loopFunctionID;
#endif

CMN_Interpreter interpreter;

uint8_t ok = 1;

int16_t ioFunction(int16_t x)
{
  if (x >= 0)
    putchar(x);

  return 0;
}

#define CV(n) CMN_interpreterGetValue(interp,n)
#define CPOP(n) CMN_interpreterPop(interp,n)
#define CPUSH(n) CMN_interpreterPush(interp,n)

void loadImage(CMN_Interpreter *interp, uint8_t *image, uint16_t maxSize,
  uint8_t mode)
{
  image[0] = CV(0);
  image[1] = CV(1);
  CPOP(2);

  uint16_t imageLen = ((uint16_t) image[0]) * ((uint16_t) image[1]);

  if (mode == 1) 
    imageLen = 16 + imageLen / 2 + ((imageLen % 2) != 0);
  else if (mode == 2)
    imageLen = imageLen / 8 + ((imageLen % 8) != 0);

  if (imageLen > maxSize - 2)
    imageLen = maxSize - 2;

  for (uint16_t i = 0; i < imageLen; ++i)
    image[2 + i] = CV(i);
      
  CPOP(imageLen);
}
 
void functionCall(uint16_t id, CMN_Interpreter *interp)
{
  uint8_t x, y, z, w;

  if (id >= FUNCTIONS_TOTAL || functions[id] < 0)
  {
    fputs("ERROR: call of unknown function\n",stderr);
    ok = 0;
    return;
  }

  switch (functions[id])
  {
    case F_BUTTON_PRESSED: 
      x = CV(0); CPOP(1); CPUSH(SAF_buttonPressed(x)); break;
    case F_BUTTON_JUST_PRESSED:
      x = CV(0); CPOP(1); CPUSH(SAF_buttonJustPressed(x)); break;
    case F_SAVE: x = CV(0); y = CV(1); CPOP(2); SAF_save(y,x); break;
    case F_LOAD: x = CV(0); CPOP(1); CPUSH(SAF_load(x)); break;
    case F_PLAY_SOUND: SAF_playSound(CV(0)); CPOP(1); break;
    case F_FRAME: CPUSH(SAF_frame()); break;
    case F_TIME: CPUSH(SAF_time()); break;
    case F_RANDOM: CPUSH(SAF_random()); break;
    case F_RANDOM_SEED: x = CV(0); CPOP(1); SAF_randomSeed(x); break;
    case F_SIN: x = CV(0); CPOP(1); CPUSH(SAF_sin(x)); break;
    case F_COS: x = CV(0); CPOP(1); CPUSH(SAF_cos(x)); break;
    case F_SQRT: { uint32_t x32 = CV(0); CPOP(1); CPUSH(SAF_sqrt(x32)); break; }
    case F_CLEAR_SCREEN: SAF_clearScreen(CV(0)); CPOP(1); break;
    case F_DRAW_PIXEL:   
      x = CV(0); y = CV(1); z = CV(2); CPOP(3); SAF_drawPixel(z,y,x); break;
    case F_DRAW_TEXT:
    {
      int8_t xs, ys;
      uint8_t l = 0;
      x = CV(0); y = CV(1);
      xs = CMN_unsignedToSignedNative(CV(2));
      ys = CMN_unsignedToSignedNative(CV(3));

      while (1)
      {
        if (l < HELPER_STRING_SIZE)
          helperString[l] = CV(l + 4);

        if (helperString[l] == 0)
          break;

        l++;
      }

      helperString[HELPER_STRING_SIZE - 1] = 0;
      CPOP(l + 5);
      SAF_drawText(helperString,ys,xs,y,x);
      break;
    }

    case F_COLOR_FROM_RGB: 
      x = CV(0); y = CV(1); z = CV(2); 
      CPOP(3); CPUSH(SAF_colorFromRGB(z,y,x));
      break;

    case F_COLOR_TO_RGB:
      x = CV(0); CPOP(1); 
      SAF_colorToRGB(x,&y,&z,&w);
      CPUSH(w); CPUSH(z); CPUSH(y);
      break;

    case F_DRAW_RECT:
    {
      int8_t xs, ys, zs, ws;

      x = CV(0); y = CV(1);
      xs = CMN_unsignedToSignedNative(CV(2));
      ys = CMN_unsignedToSignedNative(CV(3));
      zs = CMN_unsignedToSignedNative(CV(4));
      ws = CMN_unsignedToSignedNative(CV(5));
      CPOP(6);

      SAF_drawRect(ws,zs,ys,xs,y,x);
      break;
    }

    case F_DRAW_LINE:
    case F_DRAW_CIRCLE:
    {
      int8_t xs, ys, zs, ws;

      x = CV(0);
      xs = CMN_unsignedToSignedNative(CV(1));
      ys = CMN_unsignedToSignedNative(CV(2));
      zs = CMN_unsignedToSignedNative(CV(3));
      ws = CMN_unsignedToSignedNative(CV(4));
      CPOP(5);

      if (functions[id] == F_DRAW_LINE)
        SAF_drawLine(ws,zs,ys,xs,x);
      else
        SAF_drawCircle(ws,zs,ys,xs,x);

      break;
    }

    case F_GET_FONT_CHARACTER:
    {
      uint8_t r[2];
      x = CV(0); CPOP(1);

      SAF_getFontCharacter(x,r);
      CPUSH(r[1]);
      CPUSH(r[0]);
      break;
    }

    case F_INT_TO_STR:
    {
      int v = CV(0); CPOP(1);
      const char *s = helperString;

      SAF_intToStr(v,helperString);

      while (*s != 0)
        s++;

      s--;
      CPUSH(0);

      while (s >= helperString)
      {
        CPUSH(*s);
        s--;
      }

      break;
    }

    case F_DRAW_IMAGE:
    case F_DRAW_IMAGE_COMPRESSED:
    {
      int8_t xs, ys;
      x = CV(0); y = CV(1);
      xs = CMN_unsignedToSignedNative(CV(2));
      ys = CMN_unsignedToSignedNative(CV(3));
      CPOP(4);

      loadImage(interp,helperImage,HELPER_STRING_SIZE,
         functions[id] == F_DRAW_IMAGE ? 0 : 1);

      if (functions[id] == F_DRAW_IMAGE)
        SAF_drawImage(helperImage,ys,xs,y,x);
      else
        SAF_drawImageCompressed(helperImage,ys,xs,y,x);

      break;
    }

    case F_DRAW_IMAGE_1BIT:
    {
      int8_t xs, ys;

      x = CV(0); y = CV(1); z = CV(2);
      CPOP(3);

      loadImage(interp,helperImage + HELPER_IMAGE_SIZE / 2,
        HELPER_IMAGE_SIZE / 2,2);

      xs = CMN_unsignedToSignedNative(CV(0));
      ys = CMN_unsignedToSignedNative(CV(1));
      CPOP(2);

      loadImage(interp,helperImage,HELPER_IMAGE_SIZE / 2,2);

      SAF_drawImage1Bit(helperImage,ys,xs,helperImage + HELPER_IMAGE_SIZE / 2,
        z,y,x);

      break;
    }

    case F_COLOR_TO_GRAYSCALE: x = CV(0); CPUSH(SAF_colorToGrayscale(x)); break;
    case F_COLOR_TO_1BIT: x = CV(0); CPUSH(SAF_colorTo1Bit(x)); break;
    case F_COLOR_INVERT: x = CV(0); CPUSH(SAF_colorInvert(x)); break;
    case F_COLOR_YELLOW: CPUSH(SAF_COLOR_YELLOW); break;
    case F_COLOR_BLACK: CPUSH(SAF_COLOR_BLACK); break;
    case F_COLOR_WHITE: CPUSH(SAF_COLOR_WHITE); break;
    case F_COLOR_GRAY: CPUSH(SAF_COLOR_GRAY); break;
    case F_COLOR_GRAY_DARK: CPUSH(SAF_COLOR_GRAY_DARK); break;
    case F_COLOR_RED: CPUSH(SAF_COLOR_RED); break;
    case F_COLOR_RED_DARK: CPUSH(SAF_COLOR_RED_DARK); break;
    case F_COLOR_GREEN: CPUSH(SAF_COLOR_GREEN); break;
    case F_COLOR_GREEN_DARK: CPUSH(SAF_COLOR_GREEN_DARK); break;
    case F_COLOR_BLUE: CPUSH(SAF_COLOR_BLUE); break;
    case F_COLOR_BLUE_DARK: CPUSH(SAF_COLOR_BLUE_DARK); break;
    case F_COLOR_ORANGE: CPUSH(SAF_COLOR_ORANGE); break;
    case F_COLOR_BROWN: CPUSH(SAF_COLOR_BROWN); break;
    case F_BUTTON_UP: CPUSH(SAF_BUTTON_UP); break;
    case F_BUTTON_DOWN: CPUSH(SAF_BUTTON_DOWN); break;
    case F_BUTTON_LEFT: CPUSH(SAF_BUTTON_LEFT); break;
    case F_BUTTON_RIGHT: CPUSH(SAF_BUTTON_RIGHT); break;
    case F_BUTTON_A: CPUSH(SAF_BUTTON_A); break;
    case F_BUTTON_B: CPUSH(SAF_BUTTON_B); break;
    case F_BUTTON_C: CPUSH(SAF_BUTTON_C); break;
    case F_SOUND_BEEP: CPUSH(SAF_SOUND_BEEP); break;
    case F_SOUND_CLICK: CPUSH(SAF_SOUND_CLICK); break;
    case F_SOUND_BOOM: CPUSH(SAF_SOUND_BOOM); break;
    case F_SOUND_BUMP: CPUSH(SAF_SOUND_BUMP); break;
    case F_TRANSFORM_NONE: CPUSH(SAF_TRANSFORM_NONE); break;
    case F_TRANSFORM_ROTATE_90: CPUSH(SAF_TRANSFORM_ROTATE_90); break;
    case F_TRANSFORM_ROTATE_180: CPUSH(SAF_TRANSFORM_ROTATE_180); break;
    case F_TRANSFORM_ROTATE_270: CPUSH(SAF_TRANSFORM_ROTATE_270); break;
    case F_TRANSFORM_FLIP: CPUSH(SAF_TRANSFORM_FLIP); break;
    case F_TRANSFORM_SCALE_2: CPUSH(SAF_TRANSFORM_SCALE_2); break;
    case F_TRANSFORM_SCALE_3: CPUSH(SAF_TRANSFORM_SCALE_3); break;
    case F_TRANSFORM_SCALE_4: CPUSH(SAF_TRANSFORM_SCALE_4); break;
    case F_TRANSFORM_INVERT: CPUSH(SAF_TRANSFORM_INVERT); break;
    case F_SCREEN_WIDTH: CPUSH(SAF_SCREEN_WIDTH); break;
    case F_SCREEN_HEIGHT: CPUSH(SAF_SCREEN_HEIGHT); break;
    case F_FPS: CPUSH(SAF_FPS); break;
    default: break;
  }
}

#undef CV
#undef CPOP
#undef CPUSH

void SAF_init(void)
{
#if !PRECOMPILED
  CMN_compilerInit(&compiler,bytecodeMem,BYTECODEDMEM_SIZE,(char *) workingMem,
    WORKINGMEM_SIZE,0);

  FILE *f = fopen(GAME_FILE_NAME,"r");

  if (!f)
  {
    fputs("ERROR: couldn't open file \"" GAME_FILE_NAME "\"",stderr);
    ok = 0;
  }
  else
  {
    int line = 1;

    while (1)
    {
      int c = fgetc(f);

      if (c == '\n')
        line++;

      uint8_t cState = CMN_compilerFeedChar(&compiler,c == EOF ? 0 : c);

      if (cState != CMN_COMPILER_OK)
      {
        const char *errorStr = "unknown";

        switch (cState)
        {
          case CMN_COMPILER_ERROR_BAD_TOKEN: errorStr = "bad token"; break;
          case CMN_COMPILER_ERROR_UNEXPECTED_TOKEN: errorStr = "unexpected"; break;
          case CMN_COMPILER_ERROR_UNKNOWN_NAME: errorStr = "unknown name"; break;
          case CMN_COMPILER_ERROR_REDEFINED: errorStr = "symbol redefined"; break;
          case CMN_COMPILER_ERROR_UNEXPECTED_END: errorStr = "early end"; break;
          case CMN_COMPILER_ERROR_BYTECODE_TOO_BIG: errorStr = "program too big"; break;
          case CMN_COMPILER_ERROR_SYMBOL_TABLE: errorStr = "symbol table too small"; break;
          case CMN_COMPILER_ERROR_PARSE_STACK: errorStr = "parse stack overflow"; break;
          default: break;
        }

        fprintf(stderr,"ERROR: compilation failed, line %d: %s\n",line,errorStr);
        ok = 0;
        break;
      }

      if (c == EOF)
        break;
    }

    fclose(f);

    if (ok)
    {
      CMN_bytecodeOptimize(bytecodeMem,
        CMN_OPTIMIZE_REMOVE_NOPS | CMN_OPTIMIZE_INLINE |
        CMN_OPTIMIZE_REPLACE_OPS,&compiler);

      loopFunctionID = CMN_compilerFindFunction(&compiler,"SAF_loop",0);

      if (loopFunctionID < 0)
      {
        fputs("ERROR: no \"SAF_loop\" function\n",stderr);
        ok = 0;
      }
      else
      {
        for (uint8_t i = 0; i < FUNCTIONS_TOTAL; ++i)
          functions[i] = -1;

        int8_t id;

        #define FIND_FUNC(f,c)\
          id = CMN_compilerFindFunction(&compiler,f,1); \
          if (id >= 0 && id < FUNCTIONS_TOTAL) functions[id] = c;

        FIND_FUNC("SAF_buttonPressed",F_BUTTON_PRESSED)
        FIND_FUNC("SAF_buttonJustPressed",F_BUTTON_JUST_PRESSED)
        FIND_FUNC("SAF_playSound",F_PLAY_SOUND)
        FIND_FUNC("SAF_save",F_SAVE)
        FIND_FUNC("SAF_load",F_LOAD)
        FIND_FUNC("SAF_frame",F_FRAME)
        FIND_FUNC("SAF_time",F_TIME)
        FIND_FUNC("SAF_random",F_RANDOM)
        FIND_FUNC("SAF_randomSeed",F_RANDOM_SEED)
        FIND_FUNC("SAF_sin",F_SIN)
        FIND_FUNC("SAF_cos",F_COS)
        FIND_FUNC("SAF_sqrt",F_SQRT)
        FIND_FUNC("SAF_colorFromRGB",F_COLOR_FROM_RGB)
        FIND_FUNC("SAF_colorToRGB",F_COLOR_TO_RGB)
        FIND_FUNC("SAF_colorToGrayscale",F_COLOR_TO_GRAYSCALE)
        FIND_FUNC("SAF_colorTo1Bit",F_COLOR_TO_1BIT)
        FIND_FUNC("SAF_colorInvert",F_COLOR_INVERT)
        FIND_FUNC("SAF_drawPixel",F_DRAW_PIXEL)
        FIND_FUNC("SAF_drawRect",F_DRAW_RECT)
        FIND_FUNC("SAF_drawLine",F_DRAW_LINE)
        FIND_FUNC("SAF_drawCircle",F_DRAW_CIRCLE)
        FIND_FUNC("SAF_clearScreen",F_CLEAR_SCREEN)
        FIND_FUNC("SAF_drawText",F_DRAW_TEXT)
        FIND_FUNC("SAF_getFontCharacter",F_GET_FONT_CHARACTER)
        FIND_FUNC("SAF_drawImage",F_DRAW_IMAGE)
        FIND_FUNC("SAF_drawImageCompressed",F_DRAW_IMAGE_COMPRESSED)
        FIND_FUNC("SAF_drawImage1Bit",F_DRAW_IMAGE_1BIT)
        FIND_FUNC("SAF_intToStr",F_INT_TO_STR)
        FIND_FUNC("SAF_BUTTON_UP",F_BUTTON_UP)
        FIND_FUNC("SAF_BUTTON_RIGHT",F_BUTTON_RIGHT)
        FIND_FUNC("SAF_BUTTON_DOWN",F_BUTTON_DOWN)
        FIND_FUNC("SAF_BUTTON_LEFT",F_BUTTON_LEFT)
        FIND_FUNC("SAF_BUTTON_A",F_BUTTON_A)
        FIND_FUNC("SAF_BUTTON_B",F_BUTTON_B)
        FIND_FUNC("SAF_BUTTON_C",F_BUTTON_C)
        FIND_FUNC("SAF_COLOR_BLACK",F_COLOR_BLACK)
        FIND_FUNC("SAF_COLOR_WHITE",F_COLOR_WHITE)
        FIND_FUNC("SAF_COLOR_GRAY",F_COLOR_GRAY)
        FIND_FUNC("SAF_COLOR_GRAY_DARK",F_COLOR_GRAY_DARK)
        FIND_FUNC("SAF_COLOR_RED",F_COLOR_RED)
        FIND_FUNC("SAF_COLOR_RED_DARK",F_COLOR_RED_DARK)
        FIND_FUNC("SAF_COLOR_GREEN",F_COLOR_GREEN)
        FIND_FUNC("SAF_COLOR_GREEN_DARK",F_COLOR_GREEN_DARK)
        FIND_FUNC("SAF_COLOR_BLUE",F_COLOR_BLUE)
        FIND_FUNC("SAF_COLOR_BLUE_DARK",F_COLOR_BLUE_DARK)
        FIND_FUNC("SAF_COLOR_YELLOW",F_COLOR_YELLOW)
        FIND_FUNC("SAF_COLOR_ORANGE",F_COLOR_ORANGE)
        FIND_FUNC("SAF_COLOR_BROWN",F_COLOR_BROWN)
        FIND_FUNC("SAF_SOUND_BEEP",F_SOUND_BEEP)
        FIND_FUNC("SAF_SOUND_CLICK",F_SOUND_CLICK)
        FIND_FUNC("SAF_SOUND_BOOM",F_SOUND_BOOM)
        FIND_FUNC("SAF_SOUND_BUMP",F_SOUND_BUMP)
        FIND_FUNC("SAF_TRANSFORM_NONE",F_TRANSFORM_NONE)
        FIND_FUNC("SAF_TRANSFORM_ROTATE_90",F_TRANSFORM_ROTATE_90)
        FIND_FUNC("SAF_TRANSFORM_ROTATE_180",F_TRANSFORM_ROTATE_180)
        FIND_FUNC("SAF_TRANSFORM_ROTATE_270",F_TRANSFORM_ROTATE_270)
        FIND_FUNC("SAF_TRANSFORM_FLIP",F_TRANSFORM_FLIP)
        FIND_FUNC("SAF_TRANSFORM_SCALE_2",F_TRANSFORM_SCALE_2)
        FIND_FUNC("SAF_TRANSFORM_SCALE_3",F_TRANSFORM_SCALE_3)
        FIND_FUNC("SAF_TRANSFORM_SCALE_4",F_TRANSFORM_SCALE_4)
        FIND_FUNC("SAF_TRANSFORM_INVERT",F_TRANSFORM_INVERT)
        FIND_FUNC("SAF_SCREEN_WIDTH",F_SCREEN_WIDTH)
        FIND_FUNC("SAF_SCREEN_HEIGHT",F_SCREEN_HEIGHT)
        FIND_FUNC("SAF_FPS",F_FPS)
        #undef FIND_FUNC

        // now print the compiled bytecode (for precompiled version)

        puts("Compiled data follows (for making a precompiled version):");

        puts("#define PRECOMPILED 1");
        printf("int32_t loopFunctionID = %d;\n",loopFunctionID);

        unsigned int realSize = 0;
        
        for (uint32_t i = 0; i < BYTECODEDMEM_SIZE; i += 2)
        {
          realSize += 2;
          if (i > CMN_BYTECODE_HEADER_SIZE && bytecodeMem[i] == CMN_OPCODE_END)
            break;
        }

        printf("#define BYTECODEDMEM_SIZE %d\n",realSize);

        printf("uint8_t bytecodeMem[BYTECODEDMEM_SIZE] = {");

        for (uint32_t i = 0; i < realSize; ++i)
        {
          if (i != 0)
            putchar(',');

          if (i % 32 == 0)
            printf("\n  ");

          printf("%d",bytecodeMem[i]);
        }

        printf("};\nint8_t functions[] = {");

        for (uint32_t i = 0; i < FUNCTIONS_TOTAL; ++i)
        {
          if (i != 0)
            putchar(',');

          if (i % 32 == 0)
            printf("\n  ");

          printf("%d",functions[i]);
        }

        puts("};");

#endif
        if (!CMN_interpreterInit(&interpreter,bytecodeMem,workingMem,
          WORKINGMEM_SIZE,64,ioFunction,functionCall,0,0))
        {
          fputs("ERROR: couldn't initialize interpreter\n",stderr);
          ok = 0;
        }
        else if (CMN_interpreterStep(&interpreter,0) != CMN_INTERPRETER_END)
        {
          fputs("ERROR: runtime error during initialization\n",stderr);
          ok = 0;
        }
#if !PRECOMPILED
      }
    }
  }
#endif
}

uint8_t SAF_loop()
{
  if (ok)
  {
    CMN_interpreterCallFunction(&interpreter,loopFunctionID);

    uint8_t interpreterState = CMN_interpreterStep(&interpreter,0);

    if (interpreterState != CMN_INTERPRETER_END)
    {
#if !PRECOMPILED
      fputs("ERROR: error at runtime: ",stderr);

      switch(interpreterState)
      {
        case CMN_INTERPRETER_ERROR_CALLSTACK:
          fputs("call stack overflow",stderr); break;

        case CMN_INTERPRETER_ERROR_STACK_OF:
          fputs("stack overflow",stderr); break;

        case CMN_INTERPRETER_ERROR_ZERODIV:
          fputs("division by zero",stderr); break;

        default: fputs("unknown",stderr); break;
      }

      fputc('\n',stderr);
#endif
      ok = 0;
    }
  }
  else
  {
    SAF_clearScreen(SAF_COLOR_RED);
    SAF_drawText("ERROR",6,17,SAF_COLOR_BLACK,2);
  }

  return 1;
}
