#include "libqb.h"
#include "common.h"

#ifdef QB64_WINDOWS
#    include <fcntl.h>
#    include <shellapi.h>
#endif

#ifdef QB64_MACOSX
#    include <ApplicationServices/ApplicationServices.h>

#    include <mach-o/dyld.h> //required for _NSGetExecutablePath
#endif

#include "audio.h"
#include "bitops.h"
#include "cmem.h"
#include "command.h"
#include "completion.h"
#include "compression.h"
#include "datetime.h"
#include "encoding.h"
#include "error_handle.h"
#include "event.h"
#include "extended_math.h"
#include "file-fields.h"
#include "filepath.h"
#include "filesystem.h"
#include "font.h"
#include "game_controller.h"
#include "gfs.h"
#include "glut-thread.h"
#include "graphics.h"
#include "gui.h"
#include "hashing.h"
#include "image.h"
#include "keyhandler.h"
#include "logging.h"
#include "mac-mouse-support.h"
#include "memblock.h"
#include "mutex.h"
#include "qb_http.h"
#include "qblist.h"
#include "qbs.h"
#include "rounding.h"
#include "shell.h"
#include "thread.h"

// These are here because they are used in func__loadfont()
#include <algorithm>
#include <atomic>
#include <string>
#include <vector>

int32 disableEvents = 0;

// Global console vvalues
static int32 consolekey;
static int32 consolemousex;
static int32 consolemousey;
static int32 consolebutton;
int32 func__getconsoleinput(); // declare here, so we can use with SLEEP and END commands

// This next block used to be in common.cpp; put here until I can find a better
// place for it (LC, 2018-01-05)

uint32 rotateLeft(uint32 word, uint32 shift) {
    return (word << shift) | (word >> (32 - shift));
}

#ifdef QB64_UNIX
#    include <libgen.h> //required for dirname()
#    include <pthread.h>
#endif

#ifdef QB64_LINUX
#    include <X11/Xatom.h>
#    include <X11/Xlib.h>
#    include <X11/Xutil.h>
Display *X11_display = NULL;
Window X11_window;
#endif

int32 x11_locked = 0;
int32 x11_lock_request = 0;

void x11_lock() {
    x11_lock_request = 1;
    while (x11_locked == 0)
        Sleep(1);
}

void x11_unlock() {
    x11_locked = 0;
}

// forward references
void sub__printimage(int32 i);

// GUI notification variables
int32 force_display_update = 0;

void *generic_window_handle = NULL;
int32 acceptFileDrop = 0;
#ifdef QB64_WINDOWS
HWND window_handle = NULL;
HDROP hdrop = NULL;
int32 totalDroppedFiles = 0;
#endif
//...

extern "C" void QB64_Window_Handle(void *handle) {
    generic_window_handle = handle;
#ifdef QB64_WINDOWS
    window_handle = (HWND)handle;
#endif
    //...
}

// forward references
void set_view(int32 new_mode);
void set_render_source(int32 new_handle);
void set_render_dest(int32 new_handle);

int32 framebufferobjects_supported = 0;

int32 environment_2d__screen_width = 0; // the size of the software SCREEN
int32 environment_2d__screen_height = 0;
int32 environment__window_width = 0; // window may be larger or smaller than the SCREEN
int32 environment__window_height = 0;
int32 environment_2d__screen_x1 = 0; // offsets of 'screen' within the window
int32 environment_2d__screen_y1 = 0;
int32 environment_2d__screen_x2 = 0;
int32 environment_2d__screen_y2 = 0;
int32 environment_2d__screen_scaled_width = 640; // initial values prevent _SCALEDWIDTH/_SCALEDHEIGHT returning 0
int32 environment_2d__screen_scaled_height = 400;
float environment_2d__screen_x_scale = 1.0f;
float environment_2d__screen_y_scale = 1.0f;
int32 environment_2d__screen_smooth = 0; // 1(LINEAR) or 0(NEAREST)
int32 environment_2d__letterbox = 0;     // 1=vertical black stripes required, 2=horizontal black stripes required

int32 window_focused = 0; // Not used on Windows
uint8 *window_title = NULL;

double max_fps = 60; // 60 is the default
int32 auto_fps = 0;  // set to 1 to make QB64 auto-adjust fps based on load

int32 os_resize_event = 0;

int32 resize_auto = 0; // 1=_STRETCH, 2=_SMOOTH
float resize_auto_ideal_aspect = 640.0 / 400.0;
float resize_auto_accept_aspect = 640.0 / 400.0;

int32 fullscreen_allowedmode = 0;
int32 fullscreen_allowedsmooth = 0;
int32 fullscreen_smooth = 0;
int32 fullscreen_width = 0;
int32 fullscreen_height = 0;
int32 screen_scale = 0;
int32 resize_pending = 1;
int32 resize_snapback = 1;
int32 resize_snapback_x = 640;
int32 resize_snapback_y = 400;
int32 resize_event = 0;
int32 resize_event_x = 0;
int32 resize_event_y = 0;

int32 ScreenResizeScale = 0;
int32 ScreenResize = 0;

extern "C" int QB64_Resizable() {
    return ScreenResize;
}

int32 sub_gl_called = 0;

static int32 image_qbicon16_handle;
static int32 image_qbicon32_handle;

static int32 image_qbicon16_w = 16;
static int32 image_qbicon16_h = 16;
static const uint8 image_qbicon16[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x05, 0x07, 0x07, 0x38, 0x55, 0x12, 0x12, 0x56, 0x96, 0x59, 0x51, 0x31, 0xBE, 0x4F, 0x47,
    0x25, 0x8B, 0x05, 0x04, 0x03, 0x0F, 0x19, 0x08, 0x06, 0x30, 0x6E, 0x1C, 0x11, 0x99, 0x6F, 0x24, 0x1B, 0xBD, 0x17, 0x12, 0x4F, 0x9A, 0x07, 0x07, 0x37, 0x54,
    0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x09, 0x09, 0x09, 0xA3, 0xC9, 0x06, 0x06,
    0xC3, 0xDB, 0x3A, 0x35, 0x22, 0x79, 0x52, 0x4B, 0x27, 0x93, 0x52, 0x4A, 0x26, 0x92, 0x38, 0x32, 0x1B, 0x67, 0x32, 0x0E, 0x0A, 0x52, 0xAA, 0x2B, 0x19, 0xE2,
    0xAD, 0x2B, 0x19, 0xE2, 0x2E, 0x10, 0x0E, 0x5D, 0x07, 0x07, 0xBE, 0xD7, 0x0A, 0x0A, 0x96, 0xBB, 0x02, 0x02, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05,
    0x1A, 0x2D, 0x0B, 0x0B, 0x6C, 0x97, 0x07, 0x07, 0xE0, 0xFC, 0x07, 0x07, 0x55, 0x71, 0x1E, 0x1B, 0x0F, 0x3F, 0x7A, 0x6F, 0x38, 0xCE, 0x85, 0x78, 0x3B, 0xD2,
    0x23, 0x20, 0x12, 0x4D, 0x30, 0x0E, 0x0A, 0x52, 0xA7, 0x2A, 0x18, 0xDF, 0x9C, 0x28, 0x19, 0xDA, 0x31, 0x0E, 0x0A, 0x52, 0x07, 0x07, 0x5E, 0x7A, 0x07, 0x07,
    0xE2, 0xFD, 0x0A, 0x0A, 0x71, 0x9C, 0x05, 0x05, 0x1B, 0x2F, 0x07, 0x07, 0x8A, 0xA7, 0x06, 0x06, 0xD5, 0xEA, 0x09, 0x09, 0xC8, 0xEE, 0x07, 0x07, 0x60, 0x7D,
    0x00, 0x00, 0x00, 0x00, 0x08, 0x1A, 0x22, 0x47, 0x23, 0x4E, 0x58, 0xB4, 0x0D, 0x0C, 0x07, 0x23, 0x01, 0x01, 0x01, 0x07, 0x0D, 0x23, 0x4D, 0x85, 0x0D, 0x26,
    0x55, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x72, 0x8F, 0x0A, 0x0A, 0xC4, 0xED, 0x05, 0x05, 0xDA, 0xEE, 0x07, 0x07, 0x89, 0xA6, 0x0C, 0x0C, 0x6B, 0x9C,
    0x00, 0x00, 0xFF, 0xFF, 0x02, 0x02, 0xF7, 0xFF, 0x08, 0x08, 0x9B, 0xBB, 0x03, 0x09, 0x0C, 0x1B, 0x0F, 0x85, 0xB5, 0xE0, 0x10, 0x70, 0x97, 0xCD, 0x04, 0x12,
    0x17, 0x2B, 0x09, 0x1D, 0x43, 0x65, 0x13, 0x44, 0x9C, 0xE6, 0x0D, 0x3D, 0x94, 0xBE, 0x01, 0x01, 0x02, 0x07, 0x07, 0x07, 0x9D, 0xBB, 0x02, 0x02, 0xF6, 0xFF,
    0x00, 0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x75, 0xA6, 0x0F, 0x0F, 0x96, 0xCF, 0x0E, 0x0E, 0x9C, 0xD3, 0x00, 0x00, 0xFE, 0xFF, 0x0F, 0x0F, 0x6D, 0xAA, 0x07, 0x14,
    0x1B, 0x3A, 0x10, 0x78, 0xA3, 0xD6, 0x10, 0x74, 0x9E, 0xD1, 0x06, 0x1E, 0x28, 0x42, 0x09, 0x1D, 0x41, 0x63, 0x12, 0x38, 0x7E, 0xC1, 0x0F, 0x45, 0xA9, 0xD8,
    0x05, 0x06, 0x0A, 0x21, 0x0F, 0x0F, 0x6B, 0xA8, 0x00, 0x00, 0xFD, 0xFF, 0x0E, 0x0E, 0xAC, 0xE5, 0x0F, 0x0F, 0xA4, 0xDE, 0x07, 0x07, 0xAA, 0xC8, 0x03, 0x03,
    0xF2, 0xFE, 0x05, 0x05, 0xEC, 0xFF, 0x0D, 0x0D, 0x9B, 0xCF, 0x0A, 0x0A, 0x8F, 0xB8, 0x04, 0x04, 0x09, 0x1D, 0x02, 0x02, 0x02, 0x0E, 0x08, 0x08, 0x58, 0x78,
    0x08, 0x08, 0x9D, 0xBF, 0x09, 0x09, 0x33, 0x59, 0x03, 0x03, 0x05, 0x10, 0x0B, 0x0B, 0x82, 0xAF, 0x0C, 0x0C, 0x8E, 0xBF, 0x06, 0x06, 0xE9, 0xFF, 0x02, 0x02,
    0xF6, 0xFF, 0x07, 0x07, 0xB0, 0xCE, 0x04, 0x04, 0x18, 0x29, 0x0E, 0x0E, 0x92, 0xCD, 0x09, 0x09, 0xCA, 0xEF, 0x04, 0x04, 0xF1, 0xFF, 0x0F, 0x0F, 0xB6, 0xF2,
    0x0B, 0x0B, 0xC3, 0xEE, 0x0E, 0x0E, 0x3B, 0x74, 0x0B, 0x0B, 0x9A, 0xC6, 0x05, 0x05, 0xE7, 0xFC, 0x07, 0x07, 0x1D, 0x38, 0x0B, 0x0B, 0xBE, 0xEA, 0x0F, 0x0F,
    0xAE, 0xEA, 0x04, 0x04, 0xEF, 0xFF, 0x09, 0x09, 0xC8, 0xEB, 0x0F, 0x0F, 0x8C, 0xC8, 0x04, 0x04, 0x15, 0x26, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x30, 0x49,
    0x0A, 0x0A, 0xC0, 0xE9, 0x08, 0x08, 0xDE, 0xFF, 0x06, 0x06, 0xE6, 0xFE, 0x10, 0x7D, 0xBB, 0xFF, 0x12, 0x95, 0x9B, 0xEC, 0x0A, 0x44, 0xB9, 0xF6, 0x04, 0x0A,
    0xE7, 0xFF, 0x12, 0xA7, 0xA8, 0xF0, 0x14, 0x92, 0xA4, 0xFF, 0x07, 0x09, 0xBD, 0xFF, 0x08, 0x08, 0xE1, 0xFF, 0x0B, 0x0B, 0xB0, 0xDE, 0x06, 0x06, 0x27, 0x40,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x0E, 0x07, 0x07, 0x53, 0x70, 0x11, 0x11, 0x5E, 0xA4, 0x12, 0xBD,
    0xB6, 0xFC, 0x0D, 0x82, 0xA9, 0xFF, 0x0B, 0x62, 0xA3, 0xFF, 0x08, 0x37, 0xD9, 0xFF, 0x0F, 0xBF, 0xBE, 0xFF, 0x10, 0x66, 0x7D, 0xF2, 0x0F, 0x0F, 0x5E, 0xAB,
    0x08, 0x08, 0x47, 0x65, 0x01, 0x01, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x14, 0x13, 0x32, 0x0E, 0x9D, 0x95, 0xE7, 0x0B, 0x28, 0x4B, 0xAF, 0x01, 0x01, 0xDA, 0xFF, 0x0A, 0x68, 0xD6, 0xFF,
    0x10, 0x91, 0x8A, 0xEA, 0x0C, 0x42, 0x3F, 0xB0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x1B, 0x07, 0x17, 0x1D, 0x6A,
    0x0C, 0x0C, 0xAC, 0xDF, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0x17, 0xD3, 0xFF, 0x0F, 0x33, 0x87, 0xF7, 0x07, 0x19, 0x1C, 0x6C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x0C, 0x20, 0x0D, 0x0D, 0x8B, 0xBD, 0x09, 0x09, 0xD9, 0xFE, 0x0B, 0x0B, 0xCC, 0xF7, 0x0D, 0x0D,
    0x92, 0xC7, 0x04, 0x04, 0x08, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09,
    0xAD, 0xD0, 0x0E, 0x0E, 0x9D, 0xD4, 0x0E, 0x0E, 0x8B, 0xC2, 0x08, 0x08, 0xB5, 0xD7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x12, 0x24, 0x10, 0x10, 0x4D, 0x8F, 0x10, 0x10, 0x51, 0x90, 0x05, 0x05, 0x19, 0x2D,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x04, 0x05, 0x15, 0x08, 0x08, 0x0E, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static int32 image_qbicon32_w = 32;
static int32 image_qbicon32_h = 32;
static const uint8 image_qbicon32[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x0B, 0x2F, 0x0F, 0x0F, 0x3C, 0x7B, 0x1C, 0x1C, 0x26, 0x9A, 0x15, 0x14, 0x14, 0x6F, 0x0A, 0x0A, 0x09, 0x3A,
    0x01, 0x01, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x1B, 0x08, 0x08, 0x08, 0x33, 0x08, 0x08,
    0x08, 0x33, 0x10, 0x10, 0x10, 0x5F, 0x1E, 0x1E, 0x22, 0x9D, 0x12, 0x12, 0x42, 0x89, 0x0B, 0x0B, 0x10, 0x3A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x12, 0x0F, 0x0F, 0x3E, 0x79,
    0x0F, 0x0F, 0xA1, 0xDA, 0x0D, 0x0D, 0xCA, 0xFB, 0x22, 0x20, 0x46, 0xB3, 0x85, 0x79, 0x3F, 0xEE, 0xAC, 0x9B, 0x4B, 0xFF, 0xAC, 0x9B, 0x4B, 0xFF, 0x86, 0x79,
    0x3F, 0xEB, 0x13, 0x12, 0x0C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x1D, 0x14, 0xA4, 0xDC, 0x31, 0x1B, 0xFF, 0xD0, 0x31, 0x1B, 0xFF,
    0xD3, 0x31, 0x1B, 0xFF, 0xBB, 0x2F, 0x1B, 0xFA, 0x33, 0x1C, 0x31, 0xB3, 0x0D, 0x0D, 0xBA, 0xF1, 0x0F, 0x0F, 0xA2, 0xDC, 0x0F, 0x0F, 0x37, 0x71, 0x02, 0x02,
    0x02, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x2C, 0x5C, 0x0B, 0x0B, 0xC1, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0x08, 0x08,
    0xDA, 0xF9, 0x0B, 0x0B, 0x1D, 0x4A, 0x66, 0x5D, 0x31, 0xC2, 0xA4, 0x94, 0x49, 0xFF, 0x14, 0x13, 0x0E, 0x4E, 0x13, 0x12, 0x0E, 0x4D, 0xA4, 0x93, 0x49, 0xFF,
    0x67, 0x5E, 0x32, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x1D, 0x14, 0xA4, 0xE0, 0x32, 0x1A, 0xFF, 0x15, 0x15, 0x15, 0x8C, 0x1B, 0x15,
    0x14, 0x8B, 0xDD, 0x31, 0x1B, 0xFF, 0x6E, 0x1F, 0x16, 0xB2, 0x08, 0x08, 0x10, 0x32, 0x09, 0x09, 0xCB, 0xF2, 0x00, 0x00, 0xFD, 0xFF, 0x0E, 0x0E, 0xB2, 0xE7,
    0x0A, 0x0A, 0x1C, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x04, 0x04, 0x04, 0x14, 0x03, 0x03, 0x03, 0x11, 0x0E, 0x0E, 0xA2, 0xD9, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0xFC, 0xFF, 0x0F, 0x0F, 0x37, 0x74,
    0x00, 0x00, 0x00, 0x00, 0x76, 0x6B, 0x38, 0xD7, 0x91, 0x83, 0x43, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x82, 0x43, 0xFC, 0x78, 0x6D,
    0x38, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x1D, 0x14, 0xA4, 0xE6, 0x32, 0x1A, 0xFF, 0xCF, 0x31, 0x1B, 0xFF, 0xD4, 0x31, 0x1B, 0xFF,
    0xE6, 0x32, 0x1A, 0xFF, 0x41, 0x18, 0x13, 0x91, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x30, 0x6B, 0x01, 0x01, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F,
    0x8A, 0xC3, 0x02, 0x02, 0x02, 0x0A, 0x05, 0x05, 0x05, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x0F,
    0x13, 0x13, 0x86, 0xD1, 0x0A, 0x0A, 0x0A, 0x34, 0x0E, 0x0E, 0xC2, 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0xC0, 0xF3, 0x02, 0x02, 0x02, 0x08, 0x00, 0x00,
    0x00, 0x00, 0x66, 0x5D, 0x31, 0xC2, 0xA4, 0x94, 0x49, 0xFF, 0x14, 0x13, 0x0E, 0x4E, 0x13, 0x12, 0x0E, 0x4D, 0xA4, 0x94, 0x49, 0xFF, 0x67, 0x5E, 0x32, 0xC4,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x1D, 0x14, 0xA4, 0xE0, 0x32, 0x1A, 0xFF, 0x13, 0x13, 0x13, 0x81, 0x11, 0x10, 0x10, 0x6D, 0xC9, 0x30,
    0x1C, 0xFF, 0x8D, 0x26, 0x19, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x0B, 0x0C, 0x0C, 0xCA, 0xFA, 0x00, 0x00, 0xFF, 0xFF, 0x0E, 0x0E, 0xC2, 0xF8,
    0x0A, 0x0A, 0x0A, 0x31, 0x12, 0x12, 0x93, 0xDC, 0x04, 0x04, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x63, 0xA4, 0x00, 0x00,
    0xFE, 0xFF, 0x0E, 0x0E, 0x20, 0x57, 0x0E, 0x0E, 0xC2, 0xF9, 0x00, 0x00, 0xFF, 0xFF, 0x0D, 0x0D, 0x93, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x13, 0x11, 0x0C, 0x3C, 0x85, 0x79, 0x3E, 0xEB, 0xAC, 0x9B, 0x4B, 0xFF, 0xAD, 0x9C, 0x4B, 0xFF, 0xB2, 0xA0, 0x4D, 0xFF, 0x26, 0x23, 0x17, 0x71, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x1D, 0x14, 0xA4, 0xDB, 0x31, 0x1B, 0xFF, 0xD0, 0x31, 0x1B, 0xFF, 0xD2, 0x31, 0x1B, 0xFF, 0xC5, 0x30, 0x1C, 0xFE,
    0x37, 0x14, 0x0F, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0D, 0xAD, 0xE4, 0x00, 0x00, 0xFF, 0xFF, 0x0D, 0x0D, 0xC8, 0xFD, 0x0F, 0x0F,
    0x2A, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x67, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0A, 0x2A, 0x07, 0x07, 0xE3, 0xFE, 0x00, 0x00, 0xFF, 0xFF,
    0x0F, 0x0F, 0x6F, 0xAB, 0x0E, 0x0E, 0xA3, 0xDB, 0x00, 0x00, 0xFF, 0xFF, 0x0E, 0x0E, 0x9C, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x01, 0x01, 0x01, 0x09, 0x0C, 0x0C, 0x0B, 0x49, 0x17, 0x1B, 0x1B, 0x8E, 0x59, 0x53, 0x33, 0xD9, 0x35, 0x31, 0x1D, 0x8B, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x1A, 0x08, 0x08, 0x08, 0x33, 0x11, 0x16, 0x21, 0x7C, 0x13, 0x1E, 0x32, 0x96, 0x08, 0x09, 0x0B, 0x39, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x04, 0x0D, 0x0D, 0xC3, 0xF7, 0x00, 0x00, 0xFF, 0xFF, 0x0E, 0x0E, 0xA0, 0xD8, 0x0F, 0x0F, 0x7E, 0xBA,
    0x00, 0x00, 0xFF, 0xFF, 0x07, 0x07, 0xE0, 0xFE, 0x07, 0x07, 0x08, 0x26, 0x0E, 0x0E, 0x3C, 0x74, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0x06,
    0xE6, 0xFE, 0x19, 0x19, 0x7E, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0x08, 0x08, 0xDD, 0xFD, 0x07, 0x07, 0x08, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x01, 0x01, 0x08, 0x12, 0x5C, 0x7A, 0xC3, 0x0F, 0xA6, 0xE4, 0xFF, 0x0C, 0x26, 0x31, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x17, 0x2D, 0x67, 0x0F, 0x59, 0xDE, 0xFE, 0x0D, 0x5E, 0xF2, 0xFF, 0x0B, 0x14, 0x25, 0x5E, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x14, 0x43, 0x03, 0x03, 0xF4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x1B, 0x1B, 0x71, 0xDD, 0x05, 0x05, 0xED, 0xFF, 0x00, 0x00,
    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0E, 0x0E, 0x3E, 0x76, 0x0D, 0x0D, 0x26, 0x5A, 0x02, 0x02, 0xF6, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
    0x08, 0x08, 0xE0, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x5E, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x48,
    0x5F, 0xAA, 0x0D, 0xAE, 0xF1, 0xFF, 0x13, 0x84, 0xB2, 0xF3, 0x0D, 0x2D, 0x3B, 0x7A, 0x01, 0x01, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x0B, 0x14, 0x25, 0x5A, 0x10, 0x56, 0xD8, 0xFD, 0x15, 0x47, 0xA5, 0xF6, 0x0D, 0x5E, 0xF2, 0xFF, 0x0B, 0x14, 0x25, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x0F, 0x0F, 0x62, 0x9F, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x02, 0xF9, 0xFF, 0x07, 0x07, 0xE3, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
    0x01, 0x01, 0xFB, 0xFF, 0x0E, 0x0E, 0x27, 0x5E, 0x11, 0x11, 0x15, 0x59, 0x11, 0x11, 0x7A, 0xBC, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x03, 0xF3, 0xFF, 0x0C, 0x0C, 0x1C, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x24, 0x2E, 0x69, 0x0E, 0xAA, 0xEB, 0xFF,
    0x11, 0x72, 0x9A, 0xD9, 0x11, 0x64, 0x85, 0xC8, 0x0E, 0xAA, 0xEB, 0xFF, 0x10, 0x45, 0x5C, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0E, 0x17, 0x41, 0x10, 0x54,
    0xD2, 0xFA, 0x15, 0x46, 0x9E, 0xF3, 0x14, 0x2C, 0x57, 0xB3, 0x0D, 0x5E, 0xF2, 0xFF, 0x12, 0x23, 0x44, 0x9A, 0x04, 0x05, 0x06, 0x1B, 0x00, 0x00, 0x00, 0x00,
    0x0D, 0x0D, 0x1D, 0x4F, 0x02, 0x02, 0xF5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F,
    0x98, 0xD5, 0x13, 0x13, 0x1A, 0x65, 0x15, 0x15, 0x9F, 0xF5, 0x10, 0x10, 0x24, 0x62, 0x0F, 0x0F, 0x99, 0xD5, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
    0x00, 0x00, 0xFF, 0xFF, 0x0E, 0x0E, 0xB2, 0xE9, 0x0C, 0x0C, 0x0C, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x41, 0x55, 0x93, 0x0D, 0xAF, 0xF2, 0xFF, 0x10, 0x2A,
    0x35, 0x87, 0x0C, 0x18, 0x1D, 0x5E, 0x10, 0x9F, 0xD9, 0xFF, 0x11, 0x65, 0x88, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x1C, 0x36, 0x77, 0x12, 0x53, 0xCA, 0xFF,
    0x12, 0x53, 0xCA, 0xFF, 0x11, 0x56, 0xD5, 0xFF, 0x0D, 0x5E, 0xF2, 0xFF, 0x11, 0x55, 0xD0, 0xFF, 0x0B, 0x11, 0x1D, 0x57, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C,
    0x0C, 0x3A, 0x0E, 0x0E, 0xB7, 0xED, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0D, 0x0D, 0xB6, 0xE7, 0x15, 0x15, 0x3C, 0x90,
    0x14, 0x14, 0xAB, 0xFB, 0x0E, 0x0E, 0xB2, 0xE9, 0x06, 0x06, 0xE4, 0xFD, 0x14, 0x14, 0x54, 0xA4, 0x15, 0x15, 0x83, 0xD5, 0x01, 0x01, 0xFA, 0xFF, 0x00, 0x00,
    0xFF, 0xFF, 0x0E, 0x0E, 0x96, 0xCD, 0x15, 0x15, 0x64, 0xB8, 0x09, 0x09, 0x11, 0x35, 0x05, 0x06, 0x07, 0x20, 0x12, 0x68, 0x8B, 0xD2, 0x10, 0x9E, 0xD9, 0xFF,
    0x0F, 0xA2, 0xDE, 0xFF, 0x12, 0x7A, 0xA6, 0xE7, 0x08, 0x13, 0x17, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x04, 0x02, 0x02, 0x02, 0x10, 0x0C, 0x0C,
    0x12, 0x45, 0x1B, 0x2A, 0x45, 0xBF, 0x13, 0x50, 0xC0, 0xFF, 0x0D, 0x13, 0x20, 0x62, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x0C, 0x2B, 0x16, 0x16, 0x52, 0xAC,
    0x0E, 0x0E, 0x96, 0xCD, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x02, 0xF9, 0xFF, 0x16, 0x16, 0x8F, 0xE7, 0x16, 0x16, 0x6F, 0xC7, 0x04, 0x04, 0xF0, 0xFF, 0x0E, 0x0E,
    0xB9, 0xEF, 0x0E, 0x0E, 0x85, 0xBD, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0xCD, 0xFB, 0x13, 0x13, 0xB1, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
    0x0C, 0x0C, 0xC2, 0xF3, 0x11, 0x11, 0x55, 0x99, 0x0C, 0x0C, 0xB1, 0xE3, 0x07, 0x07, 0x09, 0x24, 0x00, 0x00, 0x00, 0x01, 0x05, 0x05, 0x05, 0x23, 0x06, 0x06,
    0x06, 0x2A, 0x01, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x0E, 0x35, 0x0E, 0x0E, 0x3B, 0x72, 0x0F, 0x0F, 0x56, 0x8F, 0x14, 0x14, 0x9A, 0xEB,
    0x04, 0x04, 0x04, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x19, 0x0E, 0x0E, 0xA3, 0xDA, 0x10, 0x10, 0x40, 0x7F, 0x0D, 0x0D,
    0xBA, 0xEE, 0x01, 0x01, 0xFA, 0xFF, 0x15, 0x15, 0xAC, 0xFF, 0x0A, 0x0A, 0xD8, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0E, 0x0E, 0x90, 0xC8,
    0x0E, 0x0E, 0x2C, 0x63, 0x02, 0x02, 0xF9, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
    0xFE, 0xFF, 0x16, 0x16, 0x57, 0xB0, 0x0B, 0x0B, 0xC2, 0xEF, 0x0B, 0x0B, 0xBE, 0xEB, 0x0C, 0x0C, 0x20, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x02, 0x02, 0x02, 0x0C, 0x11, 0x11, 0x69, 0xAB, 0x05, 0x05, 0xEB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0x06, 0xE3, 0xFC, 0x0D, 0x0D, 0x2F, 0x62, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x15, 0x3D, 0x0D, 0x0D, 0xAD, 0xE1, 0x0D, 0x0D, 0xB5, 0xE8, 0x13, 0x13, 0x42, 0x90, 0x01, 0x01, 0xFB, 0xFF,
    0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0xFB, 0xFF, 0x0E, 0x0E, 0x38, 0x72, 0x01, 0x01,
    0x01, 0x03, 0x11, 0x11, 0x5E, 0xA1, 0x08, 0x08, 0xDE, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
    0x03, 0x03, 0xF1, 0xFF, 0x1A, 0x1A, 0x68, 0xD3, 0x0C, 0x0C, 0xCA, 0xF9, 0x04, 0x04, 0xEF, 0xFF, 0x0F, 0x0F, 0x7D, 0xBB, 0x08, 0x08, 0x10, 0x2E, 0x12, 0x12,
    0x4F, 0x97, 0x10, 0x10, 0x83, 0xC4, 0x0D, 0x0D, 0xC9, 0xFD, 0x00, 0x00, 0xFF, 0xFF, 0x0D, 0x0D, 0xBE, 0xF4, 0x00, 0x00, 0x00, 0x01, 0x06, 0x06, 0x0B, 0x25,
    0x0F, 0x0F, 0x71, 0xAD, 0x06, 0x06, 0xE6, 0xFE, 0x0B, 0x0B, 0xC9, 0xF5, 0x19, 0x19, 0x57, 0xBB, 0x05, 0x05, 0xE9, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0A, 0x0A, 0xD2, 0xFA, 0x10, 0x10, 0x55, 0x95, 0x01, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x23, 0x7F, 0x1B, 0x1B, 0x4A, 0xB8, 0x15, 0x15, 0x7C, 0xD0, 0x0F, 0x0F, 0xAF, 0xEC, 0x09, 0x09, 0xDA, 0xFF, 0x02, 0x02,
    0xF9, 0xFF, 0x01, 0x01, 0xFD, 0xFF, 0x15, 0x15, 0xAA, 0xFE, 0x16, 0x16, 0xA7, 0xFF, 0x02, 0x02, 0xF8, 0xFF, 0x0F, 0x0F, 0x77, 0xB2, 0x11, 0x11, 0x15, 0x59,
    0x0D, 0x0D, 0x22, 0x58, 0x02, 0x02, 0xF9, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x08, 0x08, 0xE0, 0xFF, 0x06, 0x06, 0x06, 0x1F, 0x0F, 0x0F, 0x61, 0x9C, 0x01, 0x01,
    0xFA, 0xFF, 0x15, 0x15, 0xA9, 0xFF, 0x16, 0x16, 0xA2, 0xFA, 0x01, 0x01, 0xFA, 0xFF, 0x01, 0x01, 0xFB, 0xFF, 0x09, 0x09, 0xDA, 0xFF, 0x0E, 0x0E, 0xAB, 0xE7,
    0x14, 0x14, 0x76, 0xC7, 0x1B, 0x1B, 0x40, 0xAC, 0x16, 0x16, 0x20, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x08, 0x08, 0x0D, 0x2C, 0x0D, 0x0D, 0xAD, 0xE4, 0x07, 0x07, 0xE4, 0xFF, 0x0C, 0x0C, 0xCD, 0xFE, 0x10, 0x10, 0xC0, 0xFF, 0x10, 0x10, 0xBD, 0xFF,
    0x02, 0x02, 0xF6, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0C, 0x26, 0xC3, 0xFF, 0x17, 0x82, 0x99, 0xFF, 0x14, 0x7E, 0xA5, 0xFD, 0x10, 0x71, 0x6C, 0xB6, 0x12, 0x2B,
    0x77, 0xDA, 0x00, 0x00, 0xF7, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0x05, 0xE0, 0xFF, 0x14, 0x78, 0x73, 0xCA, 0x13, 0x7D, 0x9F, 0xF8, 0x17, 0x81, 0x9B, 0xFF,
    0x12, 0x7D, 0xAC, 0xFF, 0x08, 0x0E, 0xB7, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x0E, 0x0E, 0xC5, 0xFF, 0x10, 0x10, 0xBF, 0xFE, 0x0D, 0x0D, 0xCB, 0xFE, 0x08, 0x08,
    0xDF, 0xFF, 0x11, 0x11, 0x92, 0xD3, 0x07, 0x07, 0x0A, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x16, 0x10, 0x10, 0x68, 0xA8, 0x06, 0x06, 0xE8, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0xFC, 0xFF, 0x12, 0x12,
    0xB5, 0xFB, 0x04, 0x04, 0xED, 0xFF, 0x0F, 0x6C, 0xB8, 0xFF, 0x0F, 0xE2, 0xD6, 0xFF, 0x12, 0x9F, 0x9F, 0xFF, 0x11, 0xC8, 0xC0, 0xFF, 0x10, 0xDB, 0xD0, 0xFF,
    0x07, 0x09, 0xA5, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0x0B, 0x24, 0xC0, 0xFF, 0x0D, 0xF2, 0xE5, 0xFF, 0x13, 0xB1, 0xAB, 0xFF, 0x12, 0xA9, 0xA7, 0xFF, 0x13, 0xA1,
    0xA0, 0xFF, 0x09, 0x09, 0x76, 0xFF, 0x0C, 0x0C, 0xCC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x0A, 0x0A, 0xCF, 0xF7, 0x0F, 0x0F, 0x45, 0x83,
    0x02, 0x02, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x11, 0x37, 0x0F, 0x0F, 0x71, 0xAD, 0x0A, 0x0A, 0xD5, 0xFB, 0x0C, 0x0C, 0xBB, 0xEA,
    0x14, 0x14, 0x22, 0x73, 0x14, 0x9F, 0x9F, 0xF3, 0x13, 0xA9, 0xA4, 0xFF, 0x01, 0x01, 0x7E, 0xFF, 0x10, 0x5A, 0x92, 0xFF, 0x0E, 0xEB, 0xDE, 0xFF, 0x09, 0x0C,
    0x7A, 0xFF, 0x00, 0x00, 0xF9, 0xFF, 0x0E, 0x55, 0xBB, 0xFF, 0x0E, 0xEC, 0xDF, 0xFF, 0x11, 0x37, 0x59, 0xFF, 0x0C, 0x30, 0x79, 0xFF, 0x10, 0x20, 0x3F, 0xDA,
    0x11, 0x11, 0x20, 0x8C, 0x0A, 0x0A, 0xCD, 0xF5, 0x0B, 0x0B, 0xC3, 0xF3, 0x0F, 0x0F, 0x56, 0x92, 0x05, 0x05, 0x06, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x19, 0x0E, 0x0E, 0x36, 0x6F, 0x15, 0x15,
    0x63, 0xC2, 0x12, 0xCC, 0xC1, 0xFF, 0x10, 0xDD, 0xD1, 0xFF, 0x13, 0xC4, 0xBA, 0xFF, 0x0E, 0xE8, 0xDC, 0xFF, 0x13, 0x8F, 0x93, 0xFF, 0x01, 0x01, 0xA1, 0xFF,
    0x00, 0x00, 0xFC, 0xFF, 0x11, 0x87, 0xB6, 0xFF, 0x0D, 0xF0, 0xE3, 0xFF, 0x0E, 0xEB, 0xDE, 0xFF, 0x0E, 0xEB, 0xDE, 0xFF, 0x14, 0x5C, 0x5D, 0xF0, 0x12, 0x12,
    0x5D, 0xC4, 0x0E, 0x0E, 0x2F, 0x68, 0x03, 0x03, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x12, 0x11, 0x4B,
    0x0D, 0xEE, 0xE2, 0xFF, 0x12, 0x7D, 0x77, 0xF0, 0x0E, 0x56, 0x52, 0xD2, 0x11, 0x3D, 0x47, 0xE3, 0x03, 0x03, 0x87, 0xFF, 0x00, 0x00, 0xE9, 0xFF, 0x01, 0x01,
    0xF8, 0xFF, 0x13, 0xB7, 0xB7, 0xFF, 0x12, 0x90, 0x89, 0xF7, 0x0B, 0x0B, 0x0B, 0xB3, 0x0E, 0x0E, 0x0E, 0xAF, 0x04, 0x04, 0x04, 0x74, 0x00, 0x00, 0x00, 0x0C,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x3F, 0x3D, 0x7F, 0x0D, 0xF0,
    0xE3, 0xFF, 0x0A, 0x17, 0x16, 0xAE, 0x01, 0x01, 0x01, 0x29, 0x0D, 0x0D, 0x94, 0xDE, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x07, 0x07, 0xD3, 0xFF,
    0x0F, 0xE2, 0xD6, 0xFF, 0x10, 0xDE, 0xD2, 0xFF, 0x12, 0xCC, 0xC2, 0xFF, 0x12, 0xCC, 0xC2, 0xFF, 0x0D, 0x28, 0x26, 0x9E, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x22, 0x21, 0x67, 0x0E, 0x4D, 0x4A, 0xC9,
    0x02, 0x02, 0x02, 0x68, 0x10, 0x10, 0x45, 0x89, 0x02, 0x02, 0xF7, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0x09, 0xD6, 0xFF, 0x0E, 0x54,
    0x8D, 0xFF, 0x0E, 0x55, 0x7E, 0xFF, 0x11, 0x58, 0x65, 0xF0, 0x0E, 0x53, 0x4F, 0xCE, 0x05, 0x07, 0x07, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x0C, 0x0C,
    0x26, 0x62, 0x07, 0x07, 0xE1, 0xFE, 0x19, 0x19, 0x91, 0xF5, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0xEB, 0xFF,
    0x19, 0x19, 0x72, 0xEF, 0x07, 0x07, 0xC6, 0xFC, 0x0A, 0x0A, 0x19, 0x59, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0E, 0x29, 0x62,
    0x12, 0x12, 0x6F, 0xB5, 0x0E, 0x0E, 0xA6, 0xE0, 0x09, 0x09, 0xDD, 0xFF, 0x03, 0x03, 0xF3, 0xFF, 0x0A, 0x0A, 0xD8, 0xFF, 0x06, 0x06, 0xE9, 0xFF, 0x0F, 0x0F,
    0xA6, 0xE0, 0x12, 0x12, 0x77, 0xBD, 0x0D, 0x0D, 0x1D, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x1D, 0x10, 0x10,
    0x21, 0x61, 0x03, 0x03, 0xF5, 0xFF, 0x0F, 0x0F, 0xC0, 0xFA, 0x0B, 0x0B, 0xD1, 0xFE, 0x0E, 0x0E, 0xB8, 0xEF, 0x0D, 0x0D, 0xBA, 0xF1, 0x02, 0x02, 0xF9, 0xFF,
    0x13, 0x13, 0x31, 0x80, 0x03, 0x03, 0x03, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x10, 0x95, 0xD4,
    0x00, 0x00, 0xFF, 0xFF, 0x0D, 0x0D, 0xAD, 0xE3, 0x0E, 0x0E, 0xAB, 0xE2, 0x0E, 0x0E, 0x9C, 0xD3, 0x0E, 0x0E, 0x95, 0xCC, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F,
    0xAA, 0xE7, 0x01, 0x01, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x34, 0x6E, 0x05, 0x05,
    0xEA, 0xFE, 0x0E, 0x0E, 0x9A, 0xD1, 0x0E, 0x0E, 0x82, 0xB9, 0x0E, 0x0E, 0x81, 0xB8, 0x0D, 0x0D, 0x7C, 0xB3, 0x03, 0x03, 0xF2, 0xFF, 0x0F, 0x0F, 0x3B, 0x78,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x48, 0x89,
    0x0E, 0x0E, 0x85, 0xBE, 0x0E, 0x0E, 0x58, 0x8F, 0x0D, 0x0D, 0x66, 0x9C, 0x0D, 0x0D, 0x6C, 0xA3, 0x11, 0x11, 0x60, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x06, 0x17, 0x17,
    0x29, 0x88, 0x0E, 0x0E, 0x2F, 0x66, 0x0E, 0x0E, 0x4A, 0x81, 0x16, 0x16, 0x28, 0x81, 0x03, 0x03, 0x03, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x04,
    0x0C, 0x0C, 0x0E, 0x3D, 0x10, 0x10, 0x28, 0x66, 0x01, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
    0x04, 0x13, 0x0F, 0x0F, 0x0F, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static int32 display_x = 640;
static int32 display_y = 400;
int32 display_x_prev = 640, display_y_prev = 400;

static int32 display_required_x = 640;
static int32 display_required_y = 400;

int32 dont_call_sub_gl = 0;

void GLUT_DISPLAY_REQUEST();

struct display_frame_struct {
    int32 state;
    int64 order;
    uint32 *bgra;
    int32 w;
    int32 h;
    int32 bytes; // w*h*4
};

display_frame_struct display_frame[3];
int64 display_frame_order_next = 1;
#define DISPLAY_FRAME_STATE__EMPTY 1
#define DISPLAY_FRAME_STATE__BUILDING 2
#define DISPLAY_FRAME_STATE__READY 3
#define DISPLAY_FRAME_STATE__DISPLAYING 4

// when a new software frame is not required by display(), hardware content may exist,
// and if it does then this variable is used to determine to highest order index to render
int64 last_rendered_hardware_display_frame_order = 0;
int64 last_hardware_display_frame_order = 0;

enum class special_handle_type {
    Invalid,
    Stream,
    Host,
    Http,
};

// Special Handle system
//---------------------
// Purpose: Manage handles to custom QB64 interfaces alongside the standard QB file handle indexing
// Method:  Uses negative values for custom interface handles
struct special_handle_struct {
    special_handle_type type;
    // an index or pointer to the type's object
    //
    // Http streams use this as an EOF flag
    ptrszint index;
};

list *special_handles = NULL;

enum class stream_type {
    Tcp,
};

// Stream system
//-------------
// Purpose: Unify access to the input and/or output of streamed data
struct stream_struct {
    uint8 *in;
    ptrszint in_size;  // current size in bytes
    ptrszint in_limit; // size before reallocation of buffer is required
    int8 eof;          // user attempted to read past end of stream
    // Note: 'out' is unrequired because data can be sent directly to the interface
    //-----------------------------------------
    stream_type type;

    ptrszint index; // an index or pointer to the type's object
};

list *stream_handles = NULL;

void stream_free(stream_struct *st) {
    if (st->in_limit)
        free(st->in);
    list_remove(stream_handles, list_get_index(stream_handles, st));
}

void stream_update(stream_struct *stream);
void stream_out(stream_struct *st, void *offset, ptrszint bytes);

void connection_close(ptrszint i);

RENDER_STATE_GLOBAL render_state;
RENDER_STATE_DEST dest_render_state0;

list *hardware_img_handles = NULL;

int32 first_hardware_command = 0;          // only set once
int32 last_hardware_command_added = 0;     // 0 if none exist
int32 last_hardware_command_rendered = 0;  // 0 if all have been processed
int32 next_hardware_command_to_remove = 0; // 0 if all have been processed

list *hardware_graphics_command_handles = NULL;

int32 SOFTWARE_IMG_HANDLE_MIN = -8388608;
int32 HARDWARE_IMG_HANDLE_OFFSET = -16777216; // added to all hardware image handles to avoid collision
// the lowest integer value a single precision number can exactly represent,
// because users put handles in SINGLEs
#define NEW_HARDWARE_IMG__BUFFER_CONTENT 1
#define NEW_HARDWARE_IMG__DUPLICATE_PROVIDED_BUFFER 2

// Common
int32 requestedKeyboardOverlayImage = 0;

#ifndef QB64_GUI // begin stubs

// STUB: simulate generating a hardware surface
int32 new_hardware_img(int32 x, int32 y, uint32 *pixels, int32 flags) {
    // create hardware img
    int32 handle;
    hardware_img_struct *hardware_img;
    handle = list_add(hardware_img_handles);
    hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle);
    hardware_img->w = x;
    hardware_img->h = y;
    hardware_img->dest_context_handle = 0;
    hardware_img->depthbuffer_handle = 0;
    hardware_img->pending_commands = 0;
    hardware_img->remove = 0;
    hardware_img->alpha_disabled = 0;
    hardware_img->depthbuffer_mode = DEPTHBUFFER_MODE__ON;
    hardware_img->valid = 1;
    hardware_img->source_state.PO2_fix = PO2_FIX__OFF;
    hardware_img->source_state.texture_wrap = TEXTURE_WRAP_MODE__UNKNOWN;
    hardware_img->source_state.smooth_stretched = SMOOTH_MODE__UNKNOWN;
    hardware_img->source_state.smooth_shrunk = SMOOTH_MODE__UNKNOWN;
    if (flags & NEW_HARDWARE_IMG__BUFFER_CONTENT) {
        hardware_img->texture_handle = 0;
        if (flags & NEW_HARDWARE_IMG__DUPLICATE_PROVIDED_BUFFER) {
            hardware_img->software_pixel_buffer = NULL;
        } else {
            free(pixels); // the buffer was meant to be consumed, so we just
                          // free it immediately
            hardware_img->software_pixel_buffer = NULL;
        }
    }
    return handle;
}

#else // end stubs

int32 force_NPO2_fix = 0; // This should only be set to 1 for debugging QB64

uint32 *NPO2_buffer = (uint32 *)malloc(4);
int32 NPO2_buffer_size_in_pixels = 1;

uint32 *NPO2_texture_generate(int32 *px, int32 *py, uint32 *pixels) {
    auto ox = *px;
    auto oy = *py;

    // assume not negative & not 0
    int32_t nx = Math_RoundUpToPowerOf2(uint32_t(ox));
    int32_t ny = Math_RoundUpToPowerOf2(uint32_t(oy));

    if (nx == ox && ny == oy) {
        // no action required
        return pixels;
    }

    int32 size_in_pixels = nx * ny;
    if (size_in_pixels > NPO2_buffer_size_in_pixels) {
        NPO2_buffer = (uint32 *)realloc(NPO2_buffer, size_in_pixels * 4);
        NPO2_buffer_size_in_pixels = size_in_pixels;
    }

    // copy source NPO2 rectangle into destination PO2 rectangle
    if (nx == ox) { // can copy as a single block
        memcpy(NPO2_buffer, pixels, ox * oy * 4);
    } else {
        uint32 *dst_pixel_offset = NPO2_buffer;
        uint32 *src_pixel_offset = pixels;
        while (oy--) {
            memcpy(dst_pixel_offset, src_pixel_offset, ox * 4);
            dst_pixel_offset += nx;
            src_pixel_offset += ox;
        }
        oy = *py;
    }

    // tidy edges - extend the right-most column and bottom-most row to avoid
    // pixel/color bleeding rhs column
    if (ox != nx) {
        for (int y = 0; y < oy; y++) {
            NPO2_buffer[ox + nx * y] = NPO2_buffer[ox + nx * y - 1];
        }
    }
    // bottom row + 1 pixel for corner
    if (oy != ny) {
        for (int x = 0; x < (ox + 1); x++) {
            NPO2_buffer[nx * oy + x] = NPO2_buffer[nx * oy + x - nx];
        }
    }

    // int maxtexsize;
    // glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize);
    // alert(maxtexsize);

    // alert(nx);
    // alert(ny);

    *px = nx;
    *py = ny;

    return NPO2_buffer;
}

int32 new_texture_handle() {
    GLuint texture = 0;
    glGenTextures(1, &texture);
    return (int32)texture;
}

int32 new_hardware_img(int32 x, int32 y, uint32 *pixels, int32 flags) {
    // note: non power-of-2 dimensioned textures are supported on modern 3D
    // cards and
    //      even on some older cards, as long as mip-mapping is not being used
    //      therefore, no attempt is made to convert the non-power-of-2 SCREEN
    //      sizes via software to avoid the performance hit this would incur
    // create hardware img
    int32 handle;
    hardware_img_struct *hardware_img;
    handle = list_add(hardware_img_handles);
    hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle);
    hardware_img->w = x;
    hardware_img->h = y;
    hardware_img->dest_context_handle = 0;
    hardware_img->depthbuffer_handle = 0;
    hardware_img->pending_commands = 0;
    hardware_img->remove = 0;
    hardware_img->alpha_disabled = 0;
    hardware_img->depthbuffer_mode = DEPTHBUFFER_MODE__ON;
    hardware_img->valid = 1;
    hardware_img->source_state.PO2_fix = PO2_FIX__OFF;
    hardware_img->source_state.texture_wrap = TEXTURE_WRAP_MODE__UNKNOWN;
    hardware_img->source_state.smooth_stretched = SMOOTH_MODE__UNKNOWN;
    hardware_img->source_state.smooth_shrunk = SMOOTH_MODE__UNKNOWN;

    if (flags & NEW_HARDWARE_IMG__BUFFER_CONTENT) {
        hardware_img->texture_handle = 0;
        if (flags & NEW_HARDWARE_IMG__DUPLICATE_PROVIDED_BUFFER) {
            hardware_img->software_pixel_buffer = (uint32 *)malloc(x * y * 4);
            memcpy(hardware_img->software_pixel_buffer, pixels, x * y * 4);
        } else {
            hardware_img->software_pixel_buffer = pixels;
        }
    } else {
        hardware_img->software_pixel_buffer = NULL;
        hardware_img->texture_handle = new_texture_handle();
        glBindTexture(GL_TEXTURE_2D, hardware_img->texture_handle);
        // non-power of 2 dimensions fallback support
        static int glerrorcode;
        glerrorcode = glGetError(); // clear any previous errors
        if (force_NPO2_fix == 0)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
        glerrorcode = glGetError();
        if (glerrorcode != 0 || force_NPO2_fix == 1) {
            int32 nx = x, ny = y;
            uint32 *npixels = NPO2_texture_generate(&nx, &ny, pixels);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nx, ny, 0, GL_BGRA, GL_UNSIGNED_BYTE, npixels);
            hardware_img->source_state.PO2_fix = PO2_FIX__EXPANDED;
            hardware_img->PO2_w = nx;
            hardware_img->PO2_h = ny;
            glerrorcode = glGetError();
            if (glerrorcode) {
                gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, x, y, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
                glerrorcode = glGetError();
                if (glerrorcode) {
                    gui_alert("gluBuild2DMipmaps failed: %i", glerrorcode);
                }
                hardware_img->source_state.PO2_fix = PO2_FIX__MIPMAPPED;
                hardware_img->PO2_w = x;
                hardware_img->PO2_h = y;
            }
        }
        set_render_source(INVALID_HARDWARE_HANDLE);
    }
    return handle;
}

void hardware_img_buffer_to_texture(int32 handle) {
    static hardware_img_struct *hardware_img;
    hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle);
    if (hardware_img->texture_handle == 0) {
        hardware_img->texture_handle = new_texture_handle();
        glBindTexture(GL_TEXTURE_2D, hardware_img->texture_handle);
        // non-power of 2 dimensions fallback support
        static int glerrorcode;
        glerrorcode = glGetError(); // clear any previous errors
        if (force_NPO2_fix == 0)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, hardware_img->w, hardware_img->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, hardware_img->software_pixel_buffer);
        glerrorcode = glGetError();
        if (glerrorcode != 0 || force_NPO2_fix == 1) {
            hardware_img->source_state.PO2_fix = PO2_FIX__EXPANDED;
            int32 x = hardware_img->w;
            int32 y = hardware_img->h;
            uint32 *pixels = NPO2_texture_generate(&x, &y, hardware_img->software_pixel_buffer);
            hardware_img->PO2_w = x;
            hardware_img->PO2_h = y;
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
            glerrorcode = glGetError();
            if (glerrorcode) {
                gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, hardware_img->w, hardware_img->h, GL_BGRA, GL_UNSIGNED_BYTE, hardware_img->software_pixel_buffer);
                glerrorcode = glGetError();
                if (glerrorcode) {
                    gui_alert("gluBuild2DMipmaps failed: %i", glerrorcode);
                }
                hardware_img->source_state.PO2_fix = PO2_FIX__MIPMAPPED;
                hardware_img->PO2_w = hardware_img->w;
                hardware_img->PO2_h = hardware_img->h;
            }
        }
        free(hardware_img->software_pixel_buffer);
        hardware_img->software_pixel_buffer = NULL; // 2015 critical bug fix
        set_render_source(INVALID_HARDWARE_HANDLE);
    }
}

void hardware_img_requires_depthbuffer(hardware_img_struct *hardware_img) {
    if (hardware_img->depthbuffer_handle == 0) {
        // inspiration...
        // http://www.opengl.org/wiki/Framebuffer_Object_Examples#Color_texture.2C_Depth_texture
        static GLuint depth_tex;
        glGenTextures(1, &depth_tex);
        glBindTexture(GL_TEXTURE_2D, depth_tex);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, hardware_img->w, hardware_img->h, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0 /*mipmap level*/);

        // NULL means reserve texture memory, but texels are undefined
        glClear(GL_DEPTH_BUFFER_BIT);
        hardware_img->depthbuffer_handle = depth_tex;
        set_render_source(INVALID_HARDWARE_HANDLE);
    }
}

#endif

// these lock values increment
int64 display_lock_request = 0;
int64 display_lock_confirmed = 0;
int64 display_lock_released = 0;

extern uint8 close_program;
extern uint8 suspend_program;
extern uint8 stop_program;

#ifdef DEPENDENCY_GL
static std::atomic<int32> display_lock_modal_count{0};
#endif

void gui_modal_lock_begin() {
#ifdef DEPENDENCY_GL
       display_lock_modal_count.fetch_add(1, std::memory_order_acq_rel);

    // If the render thread is already waiting for the QB thread to acknowledge
    // a display lock, acknowledge it before entering a modal OS dialog. The
    // dialog blocks the QB thread and would otherwise prevent evnt() from doing
    // this acknowledgement.
    if (display_lock_request > display_lock_confirmed)
        display_lock_confirmed = display_lock_request;
#endif
}

void gui_modal_lock_end() {
#ifdef DEPENDENCY_GL
    if (display_lock_modal_count.load(std::memory_order_acquire) > 0)
        display_lock_modal_count.fetch_sub(1, std::memory_order_acq_rel);

    // Close any last race where the render thread requested the display lock
    // just as the modal dialog returned. A dialog opened from SUB _GL already
    // runs on the GLUT thread, so it must not wait for the lock that the same
    // thread can release only after SUB _GL returns.
    if (display_lock_request > display_lock_confirmed)
        display_lock_confirmed = display_lock_request;

    if (!libqb_is_glut_thread()) {
        while ((display_lock_released < display_lock_confirmed) && (!close_program) && (!suspend_program) && (!stop_program))
            Sleep(1);
    }
#endif
}

// note: only to be used by user functions, not internal functions
hardware_img_struct *get_hardware_img(int32 handle) {
    hardware_img_struct *img;
    if (handle < HARDWARE_IMG_HANDLE_OFFSET || handle >= SOFTWARE_IMG_HANDLE_MIN)
        return NULL;
    img = (hardware_img_struct *)list_get(hardware_img_handles, handle - HARDWARE_IMG_HANDLE_OFFSET);
    if (img == NULL)
        return NULL;
    if (!img->valid)
        return NULL;
    return img;
}

int32 get_hardware_img_index(int32 handle) {
    return handle - HARDWARE_IMG_HANDLE_OFFSET;
}

// clang-format off
/// @brief Code page 437 to UTF16 LUT. This is not static because it is shared by other translation units
uint16_t codepage437_to_unicode16[] = {
    0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
    0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x2302,
    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
};
// clang-format on

/* Restricted Functionality: (Security focused approach, does not include restricting sound etc)

    Block while compiling: (ONLY things that cannot be caught at runtime)
    - $CHECKING:OFF [X]
    - _MEM(x,y) [X]
    - DECLARE LIBRARY [X]

    Block at runtime:
    - paths [fixdir]
    - MKDIR [sub_mkdir]
    - SHELL(subs/functions)
    [func__shellhide,
    func__shell,
    sub_shell,
    sub_shell2,
    sub_shell3,
    sub_shell4]
    - RUN "filename" [sub_run]
    - CHAIN [sub_chain]
    - SCREENPRINT [sub__screenprint]
    - SCREENCLICK [sub__screenclick]
    - SCREENIMAGE (returns a blank 1024x768 image)[func__screenimage]
    - ENVIRON [func_environ(num&str), sub_environ]

    Reference notes:
    - KILL calls fixdir()

    Ports:
    - Client connections are unrestricted
    - Host ports values are either 1 or 2, but the default is port 1 for out of range values

*/

/*
    int32 allocated_bytes=0;
    void *malloc2(int x){
    allocated_bytes+=x;
    return malloc(x);
    }
    void *realloc2(void *x, int y){
    allocated_bytes+=y;
    return realloc(x,y);
    }
    void *calloc2(int x, int y){
    allocated_bytes+=(x*y);
    return calloc(x,y);
    }
    #define malloc(x) malloc2(x)
    #define calloc(x,y) calloc2(x,y)
    #define realloc(x,y) realloc2(x,y)
*/

int64 device_event_index = 0;
int32 device_mouse_relative = 0;

int32 lock_mainloop = 0; // 0=unlocked, 1=lock requested, 2=locked

int32 lpos = 1;
int32 width_lprint = 80;

// forward refs
int32 autodisplay = 1;
void key_update();
int32 key_display_state = 0;
int32 key_display = 0;
int32 key_display_redraw = 0;

extern ontimer_struct *ontimer;
extern onkey_struct *onkey;
extern int32 onkey_inprogress;

extern int32 console;
extern int32 screen_hide_startup;
extern int32 asserts;
extern int32 vwatch;
//...

int64 exit_code = 0;

int32 console_active = 1;
int32 console_child = 0; // set if console is only being used by this program
int32 console_image = -1;
int32 screen_hide = 0;

// format:[deadkey's symbol in UTF16],[ASCII code of alphabet letter],[resulting UTF16 character]...0
static uint16 deadchar_lookup[] = {
    96,  97,   224,  96,  65,   192, 180, 97,   225,  180,  65,   193, 94,  97,   226, 94,   65,   194,  126, 97,   227,  126, 65,   195,  168, 97,
    228, 168,  65,   196, 730,  97,  229, 730,  65,   197,  180,  99,  263, 94,   99,  265,  96,   101,  232, 96,   69,   200, 180,  101,  233, 180,
    69,  201,  94,   101, 234,  94,  69,  202,  126,  101,  7869, 168, 101, 235,  168, 69,   203,  180,  103, 501,  94,   103, 285,  94,   104, 293,
    168, 104,  7719, 96,  105,  236, 96,  73,   204,  180,  105,  237, 180, 73,   205, 94,   105,  238,  94,  73,   206,  126, 105,  297,  168, 105,
    239, 168,  73,   207, 94,   106, 309, 180,  107,  7729, 180,  108, 314, 180,  109, 7743, 96,   110,  505, 180,  110,  324, 126,  110,  241, 126,
    78,  209,  96,   111, 242,  96,  79,  210,  180,  111,  243,  180, 79,  211,  94,  111,  244,  94,   79,  212,  126,  111, 245,  126,  79,  213,
    168, 111,  246,  168, 79,   214, 180, 112,  7765, 180,  114,  341, 180, 115,  347, 94,   115,  349,  168, 116,  7831, 96,  117,  249,  96,  85,
    217, 180,  117,  250, 180,  85,  218, 94,   117,  251,  94,   85,  219, 126,  117, 361,  168,  117,  252, 168,  85,   220, 730,  117,  367, 126,
    118, 7805, 96,   119, 7809, 180, 119, 7811, 94,   119,  373,  168, 119, 7813, 730, 119,  7832, 168,  120, 7821, 96,   121, 7923, 180,  121, 253,
    180, 89,   221,  94,  121,  375, 126, 121,  7929, 168,  121,  255, 168, 89,   376, 730,  121,  7833, 180, 122,  378,  94,  122,  7825, 0};

int32 keydown_glyph = 0;

int32 convert_unicode(int32 src_fmt, void *src_buf, int32 src_size, int32 dest_fmt, void *dest_buf) {
    /*
        important: to ensure enough space is available for the conversion, dest_buf must be at least src_size*4+4 in length
        returns: the number of bytes written to dest_buf
        fmt values:
        1=ASCII(CP437)
        8=UTF8
        16=UTF16
        32=UTF32
    */

    static int32 dest_size;
    dest_size = 0;

    // setup source
    uint8 *src_uint8p = NULL;
    if (src_fmt == 1) {
        src_uint8p = (uint8 *)src_buf;
    }
    uint16 *src_uint16p = NULL;
    if (src_fmt == 16) {
        src_uint16p = (uint16 *)src_buf;
        src_size = src_size - (src_size & 1); // cull trailing bytes
    }
    uint32 *src_uint32p = NULL;
    if (src_fmt == 32) {
        src_uint32p = (uint32 *)src_buf;
        src_size = src_size - (src_size & 3); // cull trailing bytes
    }

    // setup dest
    uint16 *dest_uint16p = NULL;
    if (dest_fmt == 16) {
        dest_uint16p = (uint16 *)dest_buf;
    }
    uint32 *dest_uint32p = NULL;
    if (dest_fmt == 32) {
        dest_uint32p = (uint32 *)dest_buf;
    }

    uint32 x; // scalar

    while (src_size) {

        // convert src to scalar UNICODE value 'x'

        if (src_fmt == 1) { // CP437
            x = *src_uint8p++;
            src_size--;
            x = codepage437_to_unicode16[x];
        }
        if (src_fmt == 16) { // UTF16
            src_size -= 2;
            x = *src_uint16p++;
            // note: does not handle surrogate pairs yet
        }
        if (src_fmt == 32) { // UTF32
            src_size -= 4;
            x = *src_uint32p++;
        }

        // convert scalar UNICODE value 'x' to dest

        if (dest_fmt == 16) { // UTF16
            *dest_uint16p++ = x;
            dest_size += 2;
            // note: does not handle surrogate pairs yet
        }
        if (dest_fmt == 32) { // UTF32
            *dest_uint32p++ = x;
            dest_size += 4;
        }

    } // loop

    // add NULL terminator (does not change the size in bytes returned)
    if (dest_fmt == 16)
        *dest_uint16p = 0;
    if (dest_fmt == 32)
        *dest_uint32p = 0;

    return dest_size;
}

int32 lastfont = 48;
int32 *font = (int32 *)calloc(4 * (48 + 1), 1); // NULL=unused index
int32 *fontheight = (int32 *)calloc(4 * (48 + 1), 1);
int32 *fontwidth = (int32 *)calloc(4 * (48 + 1), 1);
int32 *fontflags = (int32 *)calloc(4 * (48 + 1), 1);

// keyhit cyclic buffer
int64 keyhit[8192];
//    keyhit specific internal flags: (stored in high 32-bits)
//    &4294967296->numpad was used
int32 keyhit_nextfree = 0;
int32 keyhit_next = 0;
// note: if full, the oldest message is discarded to make way for the new message

void update_shift_state();

uint32 bindkey = 0;

void scancodedown(uint8 scancode);
void scancodeup(uint8 scancode);

/*
    QB64 Mapping of audio control keyboard keys:
    MEDIA_PLAY_PAUSE 0x2200
    MEDIA_STOP 0x2400
    MEDIA_NEXT_TRACK 0x1900
    MEDIA_PREV_TRACK 0x1000
*/

/*
    compatibility upgrade:
    0-255
    num & caps versions are totally ignored
    shift is USUALLY the same as ascii but not always
    0 anywhere else means a key cannot be entered
*/

// clang-format off
static const int32 scancode_lookup[]={
    //DESCRIPTION OFFSET  SCANCODE      ASCII      SHIFT       CTRL        ALT        NUM       CAPS SHIFT+CAPS  SHIFT+NUM
    /* ?       */    0 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    1 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    2 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    3 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    4 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    5 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    6 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    7 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* Bksp    */    8 ,      0x0E,      0x08,      0x08,      0x7F,    0x0E00,         0,         0,         0,         0,
    /* Tab     */    9 ,      0x0F,      0x09,    0x0F00,    0x9400,         0,         0,         0,         0,         0,
    /* ?       */   10 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   11 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   12 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* enter   */   13 ,      0x1C,      0x0D,      0x0D,      0x0A,         0,         0,         0,         0,         0,
    /* ?       */   14 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   15 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   16 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   17 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   18 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   19 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   20 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   21 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   22 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   23 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   24 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   25 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   26 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* Esc     */   27 ,      0x01,      0x1B,      0x1B,      0x1B,         0,         0,         0,         0,         0,
    /* ?       */   28 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   29 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   30 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   31 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* space   */   32 ,      0x39,      0x20,      0x20,      0x20,         0,         0,         0,         0,         0,
    /* 1 !     */   33 ,      0x02,      0x21,      0x21,         0,    0x7800,         0,         0,         0,         0,
    /* \91 \93     */   34 ,      0x28,      0x22,      0x22,         0,    0x2800,         0,         0,         0,         0,
    /* 3 #     */   35 ,      0x04,      0x23,      0x23,         0,    0x7A00,         0,         0,         0,         0,
    /* 4 $     */   36 ,      0x05,      0x24,      0x24,         0,    0x7B00,         0,         0,         0,         0,
    /* 5 %     */   37 ,      0x06,      0x25,      0x25,         0,    0x7C00,         0,         0,         0,         0,
    /* 7 &     */   38 ,      0x08,      0x26,      0x26,         0,    0x7E00,         0,         0,         0,         0,
    /* \91 \93     */   39 ,      0x28,      0x27,      0x27,         0,    0x2800,         0,         0,         0,         0,
    /* 9 (     */   40 ,      0x0A,      0x28,      0x28,         0,    0x8000,         0,         0,         0,         0,
    /* 0 )     */   41 ,      0x0B,      0x29,      0x29,         0,    0x8100,         0,         0,         0,         0,
    /* 8 *     */   42 ,      0x09,      0x2A,      0x2A,         0,    0x7F00,         0,         0,         0,         0,
    /* = +     */   43 ,      0x0D,      0x2B,      0x2B,         0,    0x8300,         0,         0,         0,         0,
    /* , <     */   44 ,      0x33,      0x2C,      0x2C,         0,    0x3300,         0,         0,         0,         0,
    /* - _     */   45 ,      0x0C,      0x2D,      0x2D,      0x1F,    0x8200,         0,         0,         0,         0,
    /* . >     */   46 ,      0x34,      0x2E,      0x2E,         0,    0x3400,         0,         0,         0,         0,
    /* / ?     */   47 ,      0x35,      0x2F,      0x2F,         0,    0x3500,         0,         0,         0,         0,
    /* 0 )     */   48 ,      0x0B,      0x30,      0x30,         0,    0x8100,         0,         0,         0,         0,
    /* 1 !     */   49 ,      0x02,      0x31,      0x31,         0,    0x7800,         0,         0,         0,         0,
    /* 2 @     */   50 ,      0x03,      0x32,      0x32,    0x0300,    0x7900,         0,         0,         0,         0,
    /* 3 #     */   51 ,      0x04,      0x33,      0x33,         0,    0x7A00,         0,         0,         0,         0,
    /* 4 $     */   52 ,      0x05,      0x34,      0x34,         0,    0x7B00,         0,         0,         0,         0,
    /* 5 %     */   53 ,      0x06,      0x35,      0x35,         0,    0x7C00,         0,         0,         0,         0,
    /* 6 ^     */   54 ,      0x07,      0x36,      0x36,      0x1E,    0x7D00,         0,         0,         0,         0,
    /* 7 &     */   55 ,      0x08,      0x37,      0x37,         0,    0x7E00,         0,         0,         0,         0,
    /* 8 *     */   56 ,      0x09,      0x38,      0x38,         0,    0x7F00,         0,         0,         0,         0,
    /* 9 (     */   57 ,      0x0A,      0x39,      0x39,         0,    0x8000,         0,         0,         0,         0,
    /* ; :     */   58 ,      0x27,      0x3A,      0x3A,         0,    0x2700,         0,         0,         0,         0,
    /* ; :     */   59 ,      0x27,      0x3B,      0x3B,         0,    0x2700,         0,         0,         0,         0,
    /* , <     */   60 ,      0x33,      0x3C,      0x3C,         0,    0x3300,         0,         0,         0,         0,
    /* = +     */   61 ,      0x0D,      0x3D,      0x3D,         0,    0x8300,         0,         0,         0,         0,
    /* . >     */   62 ,      0x34,      0x3E,      0x3E,         0,    0x3400,         0,         0,         0,         0,
    /* / ?     */   63 ,      0x35,      0x3F,      0x3F,         0,    0x3500,         0,         0,         0,         0,
    /* 2 @     */   64 ,      0x03,      0x40,      0x40,    0x0300,    0x7900,         0,         0,         0,         0,
    /* A       */   97 ,      0x1E,      0x41,      0x41,      0x01,    0x1E00,         0,         0,         0,         0,
    /* B       */   98 ,      0x30,      0x42,      0x42,      0x02,    0x3000,         0,         0,         0,         0,
    /* C       */   99 ,      0x2E,      0x43,      0x43,      0x03,    0x2E00,         0,         0,         0,         0,
    /* D       */  100 ,      0x20,      0x44,      0x44,      0x04,    0x2000,         0,         0,         0,         0,
    /* E       */  101 ,      0x12,      0x45,      0x45,      0x05,    0x1200,         0,         0,         0,         0,
    /* F       */  102 ,      0x21,      0x46,      0x46,      0x06,    0x2100,         0,         0,         0,         0,
    /* G       */  103 ,      0x22,      0x47,      0x47,      0x07,    0x2200,         0,         0,         0,         0,
    /* H       */  104 ,      0x23,      0x48,      0x48,      0x08,    0x2300,         0,         0,         0,         0,
    /* I       */  105 ,      0x17,      0x49,      0x49,      0x09,    0x1700,         0,         0,         0,         0,
    /* J       */  106 ,      0x24,      0x4A,      0x4A,      0x0A,    0x2400,         0,         0,         0,         0,
    /* K       */  107 ,      0x25,      0x4B,      0x4B,      0x0B,    0x2500,         0,         0,         0,         0,
    /* L       */  108 ,      0x26,      0x4C,      0x4C,      0x0C,    0x2600,         0,         0,         0,         0,
    /* M       */  109 ,      0x32,      0x4D,      0x4D,      0x0D,    0x3200,         0,         0,         0,         0,
    /* N       */  110 ,      0x31,      0x4E,      0x4E,      0x0E,    0x3100,         0,         0,         0,         0,
    /* O       */  111 ,      0x18,      0x4F,      0x4F,      0x0F,    0x1800,         0,         0,         0,         0,
    /* P       */  112 ,      0x19,      0x50,      0x50,      0x10,    0x1900,         0,         0,         0,         0,
    /* Q       */  113 ,      0x10,      0x51,      0x51,      0x11,    0x1000,         0,         0,         0,         0,
    /* R       */  114 ,      0x13,      0x52,      0x52,      0x12,    0x1300,         0,         0,         0,         0,
    /* S       */  115 ,      0x1F,      0x53,      0x53,      0x13,    0x1F00,         0,         0,         0,         0,
    /* T       */  116 ,      0x14,      0x54,      0x54,      0x14,    0x1400,         0,         0,         0,         0,
    /* U       */  117 ,      0x16,      0x55,      0x55,      0x15,    0x1600,         0,         0,         0,         0,
    /* V       */  118 ,      0x2F,      0x56,      0x56,      0x16,    0x2F00,         0,         0,         0,         0,
    /* W       */  119 ,      0x11,      0x57,      0x57,      0x17,    0x1100,         0,         0,         0,         0,
    /* X       */  120 ,      0x2D,      0x58,      0x58,      0x18,    0x2D00,         0,         0,         0,         0,
    /* Y       */  121 ,      0x15,      0x59,      0x59,      0x19,    0x1500,         0,         0,         0,         0,
    /* Z       */  122 ,      0x2C,      0x5A,      0x5A,      0x1A,    0x2C00,         0,         0,         0,         0,
    /* [ {     */   91 ,      0x1A,      0x5B,      0x5B,      0x1B,    0x1A00,         0,         0,         0,         0,
    /* \ |     */   92 ,      0x2B,      0x5C,      0x5C,      0x1C,    0x2B00,         0,         0,         0,         0,
    /* ] }     */   93 ,      0x1B,      0x5D,      0x5D,      0x1D,    0x1B00,         0,         0,         0,         0,
    /* 6 ^     */   94 ,      0x07,      0x5E,      0x5E,      0x1E,    0x7D00,         0,         0,         0,         0,
    /* - _     */   95 ,      0x0C,      0x5F,      0x5F,      0x1F,    0x8200,         0,         0,         0,         0,
    /* ` ~     */   96 ,      0x29,      0x60,      0x60,         0,    0x2900,         0,         0,         0,         0,
    /* A       */   97 ,      0x1E,      0x61,      0x61,      0x01,    0x1E00,         0,         0,         0,         0,
    /* B       */   98 ,      0x30,      0x62,      0x62,      0x02,    0x3000,         0,         0,         0,         0,
    /* C       */   99 ,      0x2E,      0x63,      0x63,      0x03,    0x2E00,         0,         0,         0,         0,
    /* D       */  100 ,      0x20,      0x64,      0x64,      0x04,    0x2000,         0,         0,         0,         0,
    /* E       */  101 ,      0x12,      0x65,      0x65,      0x05,    0x1200,         0,         0,         0,         0,
    /* F       */  102 ,      0x21,      0x66,      0x66,      0x06,    0x2100,         0,         0,         0,         0,
    /* G       */  103 ,      0x22,      0x67,      0x67,      0x07,    0x2200,         0,         0,         0,         0,
    /* H       */  104 ,      0x23,      0x68,      0x68,      0x08,    0x2300,         0,         0,         0,         0,
    /* I       */  105 ,      0x17,      0x69,      0x69,      0x09,    0x1700,         0,         0,         0,         0,
    /* J       */  106 ,      0x24,      0x6A,      0x6A,      0x0A,    0x2400,         0,         0,         0,         0,
    /* K       */  107 ,      0x25,      0x6B,      0x6B,      0x0B,    0x2500,         0,         0,         0,         0,
    /* L       */  108 ,      0x26,      0x6C,      0x6C,      0x0C,    0x2600,         0,         0,         0,         0,
    /* M       */  109 ,      0x32,      0x6D,      0x6D,      0x0D,    0x3200,         0,         0,         0,         0,
    /* N       */  110 ,      0x31,      0x6E,      0x6E,      0x0E,    0x3100,         0,         0,         0,         0,
    /* O       */  111 ,      0x18,      0x6F,      0x6F,      0x0F,    0x1800,         0,         0,         0,         0,
    /* P       */  112 ,      0x19,      0x70,      0x70,      0x10,    0x1900,         0,         0,         0,         0,
    /* Q       */  113 ,      0x10,      0x71,      0x71,      0x11,    0x1000,         0,         0,         0,         0,
    /* R       */  114 ,      0x13,      0x72,      0x72,      0x12,    0x1300,         0,         0,         0,         0,
    /* S       */  115 ,      0x1F,      0x73,      0x73,      0x13,    0x1F00,         0,         0,         0,         0,
    /* T       */  116 ,      0x14,      0x74,      0x74,      0x14,    0x1400,         0,         0,         0,         0,
    /* U       */  117 ,      0x16,      0x75,      0x75,      0x15,    0x1600,         0,         0,         0,         0,
    /* V       */  118 ,      0x2F,      0x76,      0x76,      0x16,    0x2F00,         0,         0,         0,         0,
    /* W       */  119 ,      0x11,      0x77,      0x77,      0x17,    0x1100,         0,         0,         0,         0,
    /* X       */  120 ,      0x2D,      0x78,      0x78,      0x18,    0x2D00,         0,         0,         0,         0,
    /* Y       */  121 ,      0x15,      0x79,      0x79,      0x19,    0x1500,         0,         0,         0,         0,
    /* Z       */  122 ,      0x2C,      0x7A,      0x7A,      0x1A,    0x2C00,         0,         0,         0,         0,
    /* [ {     */  123 ,      0x1A,      0x7B,      0x7B,      0x1B,    0x1A00,         0,         0,         0,         0,
    /* \ |     */  124 ,      0x2B,      0x7C,      0x7C,      0x1C,    0x2B00,         0,         0,         0,         0,
    /* ] }     */  125 ,      0x1B,      0x7D,      0x7D,      0x1D,    0x1B00,         0,         0,         0,         0,
    /* ` ~     */  126 ,      0x29,      0x7E,      0x7E,         0,    0x2900,         0,         0,         0,         0,
    /* ?       */  127 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  128 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  129 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  130 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  131 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  132 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  133 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  134 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  135 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  136 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  137 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  138 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  139 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  140 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  141 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  142 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  143 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  144 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  145 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  146 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  147 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  148 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  149 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  150 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  151 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  152 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  153 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  154 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  155 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  156 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  157 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  158 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  159 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  160 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  161 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  162 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  163 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  164 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  165 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  166 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  167 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  168 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  169 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  170 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  171 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  172 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  173 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  174 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  175 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  176 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  177 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  178 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  179 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  180 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  181 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  182 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  183 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  184 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  185 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  186 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  187 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  188 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  189 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  190 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  191 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  192 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  193 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    
    /* ?       */  194 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  195 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  196 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  197 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  198 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  199 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  200 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  201 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  202 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  203 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  204 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  205 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  206 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  207 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  208 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  209 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  210 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  211 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  212 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  213 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  214 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  215 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  216 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  217 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  218 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  219 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  220 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  221 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  222 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  223 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  224 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  225 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  226 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  227 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  228 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  229 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  230 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  231 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  232 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  233 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  234 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  235 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  236 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  237 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  238 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  239 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  240 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  241 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  242 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  243 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  244 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  245 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  246 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  247 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  248 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  249 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  250 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  251 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  252 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  253 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  254 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  255 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    0 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    1 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    2 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    3 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    4 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    5 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    6 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    7 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    8 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */    9 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   10 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   11 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   12 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   13 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   14 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   15 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   16 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   17 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   18 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   19 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   20 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   21 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   22 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   23 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   24 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   25 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   26 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   27 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   28 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   29 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   30 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   31 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   32 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   33 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   34 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   35 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   36 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   37 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   38 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   39 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   40 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   41 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   42 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   43 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   44 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   45 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   46 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   47 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   48 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   49 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   50 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   51 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   52 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   53 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   54 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   55 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   56 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   57 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   58 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* F1      */   59 ,      0x3B,    0x3B00,    0x5400,    0x5E00,    0x6800,         0,         0,         0,         0,
    /* F2      */   60 ,      0x3C,    0x3C00,    0x5500,    0x5F00,    0x6900,         0,         0,         0,         0,
    /* F3      */   61 ,      0x3D,    0x3D00,    0x5600,    0x6000,    0x6A00,         0,         0,         0,         0,
    /* F4      */   62 ,      0x3E,    0x3E00,    0x5700,    0x6100,    0x6B00,         0,         0,         0,         0,
    /* F5      */   63 ,      0x3F,    0x3F00,    0x5800,    0x6200,    0x6C00,         0,         0,         0,         0,
    /* F6      */   64 ,      0x40,    0x4000,    0x5900,    0x6300,    0x6D00,         0,         0,         0,         0,
    /* F7      */   65 ,      0x41,    0x4100,    0x5A00,    0x6400,    0x6E00,         0,         0,         0,         0,
    /* F8      */   66 ,      0x42,    0x4200,    0x5B00,    0x6500,    0x6F00,         0,         0,         0,         0,
    /* F9      */   67 ,      0x43,    0x4300,    0x5C00,    0x6600,    0x7000,         0,         0,         0,         0,
    /* F10     */   68 ,      0x44,    0x4400,    0x5D00,    0x6700,    0x7100,         0,         0,         0,         0,
    /* ?       */   69 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   70 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* home *X */   71 ,      0x47,    0x4700,    0x4700,    0x7700,    0x9700,         0,         0,         0,         0, //note: X=not on NUMPAD
    /* up *X   */   72 ,      0x48,    0x4800,    0x4800,    0x8D00,    0x9800,         0,         0,         0,         0,
    /* pgup *X */   73 ,      0x49,    0x4900,    0x4900,    0x8400,    0x9900,         0,         0,         0,         0,
    /* ?       */   74 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* left *X */   75 ,      0x4B,    0x4B00,    0x4B00,    0x7300,    0x9B00,         0,         0,         0,         0,
    /* (center)*/   76 ,         0,         0,         0,         0,         0,         0,         0,         0,         0, //note: not used
    /* right *X*/   77 ,      0x4D,    0x4D00,    0x4D00,    0x7400,    0x9D00,         0,         0,         0,         0,
    /* ?       */   78 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* end  *X */   79 ,      0x4F,    0x4F00,    0x4F00,    0x7500,    0x9F00,         0,         0,         0,         0,
    /* down *X */   80 ,      0x50,    0x5000,    0x5000,    0x9100,    0xA000,         0,         0,         0,         0,
    /* pgdn *X */   81 ,      0x51,    0x5100,    0x5100,    0x7600,    0xA100,         0,         0,         0,         0,
    /* ins *X  */   82 ,      0x52,    0x5200,    0x5200,    0x9200,    0xA200,         0,         0,         0,         0,
    /* del *X  */   83 ,      0x53,    0x5300,    0x5300,    0x9300,    0xA300,         0,         0,         0,         0,
    /* ?       */   84 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   85 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   86 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   87 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   88 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   89 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   90 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   91 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   92 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   93 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   94 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   95 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   96 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   97 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   98 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */   99 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  100 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  101 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  102 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  103 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  104 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  105 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  106 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  107 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  108 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  109 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  110 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  111 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  112 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  113 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  114 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  115 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  116 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  117 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  118 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  119 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  120 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  121 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  122 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  123 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  124 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  125 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  126 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  127 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  128 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  129 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  130 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  131 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  132 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* F11     */  133 ,      0x57,    0x8500,    0x8500,    0x8900,    0x8B00,         0,         0,         0,         0,
    /* F12     */  134 ,      0x58,    0x8600,    0x8600,    0x8A00,    0x8C00,         0,         0,         0,         0,
    /* ?       */  135 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  136 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  137 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  138 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  139 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  140 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  141 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  142 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  143 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  144 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  145 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  146 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  147 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  148 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  149 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  150 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  151 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  152 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  153 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  154 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  155 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  156 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  157 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  158 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  159 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  160 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  161 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  162 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  163 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  164 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  165 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  166 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  167 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  168 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  169 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  170 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  171 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  172 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  173 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  174 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  175 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  176 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  177 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  178 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  179 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  180 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  181 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  182 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  183 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  184 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  185 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  186 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  187 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  188 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  189 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  190 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  191 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  192 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  193 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  194 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  195 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  196 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  197 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  198 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  199 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  200 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  201 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  202 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  203 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  204 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  205 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  206 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    
    
    /* ?       */  207 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  208 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  209 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  210 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  211 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  212 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  213 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  214 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  215 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  216 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  217 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  218 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  219 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  220 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  221 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  222 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  223 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  224 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  225 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  226 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  227 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  228 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  229 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  230 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  231 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  232 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  233 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  234 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  235 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  236 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  237 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  238 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  239 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  240 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  241 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  242 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  243 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  244 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  245 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  246 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  247 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  248 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  249 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  250 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  251 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  252 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  253 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  254 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    /* ?       */  255 ,         0,         0,         0,         0,         0,         0,         0,         0,         0,
    //NUMPAD keys in VK order
    /* ins     */  512 ,      0x52,    0x5200,      0x30,    0x9200,         0,      0x30,         0,         0,    0x5200,
    /* end     */  513 ,      0x4F,    0x4F00,      0x31,    0x7500,         0,      0x31,         0,         0,    0x4F00,
    /* down    */  514 ,      0x50,    0x5000,      0x32,    0x9100,         0,      0x32,         0,         0,    0x5000,
    /* pgdn    */  515 ,      0x51,    0x5100,      0x33,    0x7600,         0,      0x33,         0,         0,    0x5100,
    /* left    */  516 ,      0x4B,    0x4B00,      0x34,    0x7300,         0,      0x34,         0,         0,    0x4B00,
    /* center  */  517 ,      0x4C,    0x4C00,      0x35,    0x8F00,         0,      0x35,         0,         0,    0x4C00,
    /* right   */  518 ,      0x4D,    0x4D00,      0x36,    0x7400,         0,      0x36,         0,         0,    0x4D00,
    /* home    */  519 ,      0x47,    0x4700,      0x37,    0x7700,         0,      0x37,         0,         0,    0x4700,
    /* up      */  520 ,      0x48,    0x4800,      0x38,    0x8D00,         0,      0x38,         0,         0,    0x4800,
    /* pgup    */  521 ,      0x49,    0x4900,      0x39,    0x8400,         0,      0x39,         0,         0,    0x4900,
    /* del     */  522 ,      0x53,    0x5300,      0x2E,    0x9300,         0,      0x2E,         0,         0,    0x5300,
    /* divide  */  523 ,      0x35,      0x2F,      0x2F,    0x9500,    0xA400,      0x2F,         0,         0,      0x2F,
    /* mult    */  524 ,      0x37,      0x2A,      0x2A,    0x9600,    0x3700,      0x2A,         0,         0,      0x2A,
    /* -d      */  525 ,      0x4A,      0x2D,      0x2D,    0x8E00,    0x4A00,      0x2D,         0,         0,      0x2D,
    /* +e      */  526 ,      0x4E,      0x2B,      0x2B,    0x9000,    0x4E00,      0x2B,         0,         0,      0x2B,
    /* enter   */  527 ,      0x1C,      0x0D,      0x0D,      0x0A,    0xA600,      0x0D,         0,         0,      0x0D,
    
    /* ctrl    */  000 ,         0,         0,         0,         0,         0,         0,         0,         0,      0x1D,
    /* Lshift  */  000 ,      0x2A,         0,         0,         0,         0,         0,         0,         0,         0,
    /* Rshift  */  000 ,      0x36,         0,         0,         0,         0,         0,         0,         0,         0,
    /* alt     */  000 ,      0x38,         0,         0,         0,         0,         0,         0,         0,         0,
    /* caps    */  000 ,      0x3A,         0,         0,         0,         0,         0,         0,         0,         0,
    /* num     */  000 ,      0x45,         0,         0,         0,         0,         0,         0,         0,         0,
    /* scrl    */  000 ,      0x46,         0,         0,         0,         0,         0,         0,         0,         0,
    /* * PrtSc */  000 ,      0x37,      0x2A,         0,      0x10,         0,      0x2A,      0x2A,         0,         0,
};
// clang-format on

void keydown(uint32 x);
void keyup(uint32 x);

uint32 unicode_to_cp437(uint32 x) {
    static int32 i;
    for (i = 0; i <= 255; i++) {
        if (x == codepage437_to_unicode16[i])
            return i;
    }
    return 0;
}

uint32 *keyheld_buffer = (uint32 *)malloc(1);
uint32 *keyheld_bind_buffer = (uint32 *)malloc(1);
int32 keyheld_n = 0;
int32 keyheld_size = 0;

int32 keyheld(uint32 x) {
    static int32 i;
    for (i = 0; i < keyheld_n; i++) {
        if (keyheld_buffer[i] == x)
            return 1;
    }
    // check multimapped NUMPAD keys
    if ((x >= 42) && (x <= 57)) {
        if ((x >= 48) && (x <= 57))
            return keyheld(VK + QBVK_KP0 + (x - 48)); // 0-9
        if (x == 46)
            return keyheld(VK + QBVK_KP_PERIOD);
        if (x == 47)
            return keyheld(VK + QBVK_KP_DIVIDE);
        if (x == 42)
            return keyheld(VK + QBVK_KP_MULTIPLY);
        if (x == 45)
            return keyheld(VK + QBVK_KP_MINUS);
        if (x == 43)
            return keyheld(VK + QBVK_KP_PLUS);
    }
    if (x == 13)
        return keyheld(VK + QBVK_KP_ENTER);
    if (x & 0xFF00) {
        static uint32 x2;
        x2 = (x >> 8) & 255;
        if ((x2 >= 71) && (x2 <= 83)) {
            if (x2 == 82)
                return keyheld(QBK + QBVK_KP0 - QBVK_KP0);
            if (x2 == 79)
                return keyheld(QBK + QBVK_KP1 - QBVK_KP0);
            if (x2 == 80)
                return keyheld(QBK + QBVK_KP2 - QBVK_KP0);
            if (x2 == 81)
                return keyheld(QBK + QBVK_KP3 - QBVK_KP0);
            if (x2 == 75)
                return keyheld(QBK + QBVK_KP4 - QBVK_KP0);
            if (x2 == 76)
                return keyheld(QBK + QBVK_KP5 - QBVK_KP0);
            if (x2 == 77)
                return keyheld(QBK + QBVK_KP6 - QBVK_KP0);
            if (x2 == 71)
                return keyheld(QBK + QBVK_KP7 - QBVK_KP0);
            if (x2 == 72)
                return keyheld(QBK + QBVK_KP8 - QBVK_KP0);
            if (x2 == 73)
                return keyheld(QBK + QBVK_KP9 - QBVK_KP0);
            if (x2 == 83)
                return keyheld(QBK + QBVK_KP_PERIOD - QBVK_KP0);
        }
    }
    return 0;
}

void keyheld_add(uint32 x) {
    static int32 i;
    for (i = 0; i < keyheld_n; i++) {
        if (keyheld_buffer[i] == x)
            return;
    } // already in buffer
    if (keyheld_n == keyheld_size) {
        keyheld_size++;
        keyheld_buffer = (uint32 *)realloc(keyheld_buffer, keyheld_size * 4);
        keyheld_bind_buffer = (uint32 *)realloc(keyheld_bind_buffer, keyheld_size * 4);
    } // expand buffer
    keyheld_buffer[keyheld_n] = x; // add entry
    keyheld_bind_buffer[keyheld_n] = bindkey;
    bindkey = 0; // add binded key (0=none)
    keyheld_n++; // note: inc. must occur after setting entry (threading reasons)
}

void keyheld_remove(uint32 x) {
    static int32 i;
    for (i = 0; i < keyheld_n; i++) {
        if (keyheld_buffer[i] == x) { // exists
            memmove(&keyheld_buffer[i], &keyheld_buffer[i + 1], (keyheld_n - i - 1) * 4);
            memmove(&keyheld_bind_buffer[i], &keyheld_bind_buffer[i + 1], (keyheld_n - i - 1) * 4);
            keyheld_n--; // note: dec. must occur after memmove (threading reasons)
            return;
        }
    }
}

void keyheld_unbind(uint32 x) {
    static int32 i;
    for (i = 0; i < keyheld_n; i++) {
        if (keyheld_bind_buffer[i] == x) { // exists
            keyup(keyheld_buffer[i]);
            return;
        }
    }
}

void keydown_ascii(uint32 x) {
    keydown(x);
}

void keydown_unicode(uint32 x) {
    keydown_glyph = 1;
    // note: UNICODE 0-127 map directly to ASCII 0-127
    if (x <= 127) {
        keydown_ascii(x);
        return;
    }
    // note: some UNICODE values map directly to CP437 values found in the extended ASCII set
    auto x2 = unicode_to_cp437(x);
    if (x2) {
        keydown_ascii(x2);
        return;
    }
    // note: full width latin characters will be mapped to their normal width equivalents
    // Wikipedia note: Range U+FF01\96FF5E reproduces the characters of ASCII 21 to 7E as fullwidth forms, that is, a fixed width form used in CJK computing.
    // This is useful for typesetting Latin characters in a CJK  environment. U+FF00 does not correspond to a fullwidth ASCII 20 (space character), since that
    // role is already fulfilled by U+3000 "ideographic space."
    if ((x >= 0x0000FF01) && (x <= 0x0000FF5E)) {
        keydown_ascii(x - 0x0000FF01 + 0x21);
        return;
    }
    if (x == 0x3000) {
        keydown_ascii(32);
        return;
    }
    x |= UC;
    keydown(x);
}

void keydown_vk(uint32 x) {
    keydown(x);
}

void keyup_ascii(uint32 x) {
    keyup(x);
}

void keyup_unicode(uint32 x) {
    // note: UNICODE 0-127 map directly to ASCII 0-127
    if (x <= 127) {
        keyup_ascii(x);
        return;
    }
    // note: some UNICODE values map directly to CP437 values found in the extended ASCII set
    auto x2 = unicode_to_cp437(x);
    if (x2) {
        keyup_ascii(x2);
        return;
    }
    // note: full width latin characters will be mapped to their normal width equivalents
    // Wikipedia note: Range U+FF01\96FF5E reproduces the characters of ASCII 21 to 7E as fullwidth forms, that is, a fixed width form used in CJK computing.
    // This is useful for typesetting Latin characters in a CJK  environment. U+FF00 does not correspond to a fullwidth ASCII 20 (space character), since that
    // role is already fulfilled by U+3000 "ideographic space."
    if ((x >= 0x0000FF01) && (x <= 0x0000FF5E)) {
        keyup_ascii(x - 0x0000FF01 + 0x21);
        return;
    }
    if (x == 0x3000) {
        keyup_ascii(32);
        return;
    }
    x |= UC;
    keyup(x);
}

void keyup_vk(uint32 x) {
    keyup(x);
}

int32 exit_ok = 0;

// substitute Windows functionality
#ifndef QB64_WINDOWS
void AllocConsole() {
    return;
}

void FreeConsole() {
    return;
}
#endif

// vc->project->properties->configuration properties->general->configuration type->application(.exe)
// vc->project->properties->configuration properties->general->configuration type->static library(.lib)

extern void QBMAIN(void *);
extern void TIMERTHREAD(void *);
void MAIN_LOOP(void *);

void GLUT_MAINLOOP_THREAD(void *);
void GLUT_DISPLAY_REQUEST();

extern qbs *FUNC__DECODEURL(qbs *_FUNC__DECODEURL_STRING__URL);
extern qbs *FUNC__WHATISMYIP();

int32 lprint = 0; // set to 1 during LPRINT operations
int32 lprint_image = 0;
double lprint_last = 0;    // TIMER(0.001) value at last time LPRINT was used
int32 lprint_buffered = 0; // set to 1 if content is pending to print
int32 lprint_locked = 0;   // set to 1 to deny access by QB64 program

static int32 file_charset8_raw_len = 16384;
static const uint8 file_charset8_raw[] = {
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 0,   0,   255, 0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   255, 255, 255,
    255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   255,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255,
    255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,
    0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255,
    255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255,
    255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 0,   0,   0,   0,   255, 0,   0,   255, 0,   0,   0,   0,   255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
    0,   0,   0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,
    0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    0,   0,   255, 255, 0,   0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   255, 255, 255, 0,   0,   255, 255, 255,
    255, 255, 255, 0,   0,   255, 255, 255, 0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255,
    255, 0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 255,
    255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,
    255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255,
    255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,
    255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 0,   0,   255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   255, 255,
    0,   0,   255, 255, 0,   0,   0,   255, 0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 0,   255,
    255, 255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,
    255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 0,   0,
    255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 0,
    255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255,
    0,   0,   255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,
    255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255,
    255, 0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 0,   255, 255, 0,   255, 0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255,
    0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255,
    255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255,
    255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255,
    255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255,
    255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255,
    0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255,
    255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 0,   0,   255, 255, 255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 255,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 255, 255, 255, 0,   255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255,
    0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255,
    255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255,
    255, 0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 0,
    0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255,
    0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,   255,
    0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,
    255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   255, 255, 255,
    255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   255, 255,
    255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255,
    255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255,
    255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   255,
    255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,
    255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,
    0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255,
    255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,
    255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255,
    255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0};

static int32 file_chrset16_raw_len = 32768;
static const uint8 file_chrset16_raw[] = {
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   255, 0,   0,   255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 0,
    255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255,
    0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255,
    255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,
    0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,
    0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,
    255, 255, 255, 255, 0,   0,   255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255,
    255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 0,   0,   0,   0,   255, 0,   0,   255, 0,   0,   0,   0,   255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 0,   0,
    0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 0,   0,   0,   255, 255, 0,   0,   255, 0,   0,
    255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255,
    255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 0,   0,   255, 255, 255, 0,   0,   255, 255, 255,
    0,   0,   255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255,
    255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,
    0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255,
    255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 0,   255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   0,   255, 0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255,
    255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255,
    0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255,
    255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255,
    255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255,
    0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,
    255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255,
    255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   0,   255, 0,   0,   255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   0,   255, 0,   0,   255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255,
    255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   0,   255, 255, 255, 0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   255, 255, 255, 0,   255,
    255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,
    255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,
    255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255,
    255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255,
    0,   255, 255, 0,   255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 0,   255,
    255, 0,   255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,
    0,   255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255,
    0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 255,
    255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   255,
    255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255,
    255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255,
    0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   255, 255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255,
    255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255, 0,   255, 0,   255, 255, 0,   255, 255,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255,
    0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,
    0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255,
    255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   0,   255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   255, 255, 0,   255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   255, 0,   0,   255, 255,
    0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255,
    0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255,
    255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,

    255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255,
    255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255,
    255, 0,   255, 255, 255, 0,   0,   255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,
    255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 0,   0,
    0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 0,
    0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255,
    0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,   255, 0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   0,   0,
    255, 0,   0,   0,   255, 0,   255, 0,   0,   0,   255, 0,   0,   0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,   255,
    0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,
    255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,
    0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255,
    0,   0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 0,   255, 0,   255, 0,   255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255, 255, 0,   255,
    255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255, 255, 0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255, 255, 0,
    255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255, 255, 0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255, 255,
    0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255, 255, 0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255,
    255, 0,   255, 255, 255, 255, 255, 0,   255, 255, 255, 0,   255, 0,   255, 255, 255, 0,   255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255,
    255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255,
    255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255,
    255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 255, 0,
    0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255,
    0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255,
    255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255,
    255, 255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255,
    0,   255, 255, 0,   255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,
    0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,
    255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255,
    255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 255, 255,
    0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255,
    0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255,
    255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,
    255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 0,   255, 255,
    0,   255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255,
    255, 0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   255, 255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 255, 255,
    255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255,
    255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255,
    0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255,
    0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255,
    255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,
    0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,
    0,   0,   0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,
    0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,
    255, 255, 0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   255, 255, 0,   255, 255, 0,   255, 255, 255, 0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,
    0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 255, 255, 0,   255, 255, 0,
    0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 255, 255, 0,   0,   0,   0,   0,   255, 255, 255,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,
    255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 0,   0,   0,   0,   255, 255, 0,   255, 255, 0,   0,   0,
    0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   0,   0,   0,   255, 255, 0,   0,   255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255,
    255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255, 255, 255, 255, 255, 0,   0,   0,   255,
    255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0};

static int32 file_qb64_pal_len = 1024;
static const uint8 file_qb64_pal[] = {
    0,   0,   0,   0, 170, 0,   0,   0, 0,   170, 0,   0, 170, 170, 0,   0, 0,   0,   170, 0, 170, 0,   170, 0, 0,   85,  170, 0, 170, 170, 170, 0,
    85,  85,  85,  0, 255, 85,  85,  0, 85,  255, 85,  0, 255, 255, 85,  0, 85,  85,  255, 0, 255, 85,  255, 0, 85,  255, 255, 0, 255, 255, 255, 0,
    0,   0,   0,   0, 20,  20,  20,  0, 32,  32,  32,  0, 45,  45,  45,  0, 57,  57,  57,  0, 69,  69,  69,  0, 81,  81,  81,  0, 97,  97,  97,  0,
    113, 113, 113, 0, 130, 130, 130, 0, 146, 146, 146, 0, 162, 162, 162, 0, 182, 182, 182, 0, 202, 202, 202, 0, 227, 227, 227, 0, 255, 255, 255, 0,
    255, 0,   0,   0, 255, 0,   65,  0, 255, 0,   125, 0, 255, 0,   190, 0, 255, 0,   255, 0, 190, 0,   255, 0, 125, 0,   255, 0, 65,  0,   255, 0,
    0,   0,   255, 0, 0,   65,  255, 0, 0,   125, 255, 0, 0,   190, 255, 0, 0,   255, 255, 0, 0,   255, 190, 0, 0,   255, 125, 0, 0,   255, 65,  0,
    0,   255, 0,   0, 65,  255, 0,   0, 125, 255, 0,   0, 190, 255, 0,   0, 255, 255, 0,   0, 255, 190, 0,   0, 255, 125, 0,   0, 255, 65,  0,   0,
    255, 125, 125, 0, 255, 125, 158, 0, 255, 125, 190, 0, 255, 125, 223, 0, 255, 125, 255, 0, 223, 125, 255, 0, 190, 125, 255, 0, 158, 125, 255, 0,
    125, 125, 255, 0, 125, 158, 255, 0, 125, 190, 255, 0, 125, 223, 255, 0, 125, 255, 255, 0, 125, 255, 223, 0, 125, 255, 190, 0, 125, 255, 158, 0,
    125, 255, 125, 0, 158, 255, 125, 0, 190, 255, 125, 0, 223, 255, 125, 0, 255, 255, 125, 0, 255, 223, 125, 0, 255, 190, 125, 0, 255, 158, 125, 0,
    255, 182, 182, 0, 255, 182, 198, 0, 255, 182, 219, 0, 255, 182, 235, 0, 255, 182, 255, 0, 235, 182, 255, 0, 219, 182, 255, 0, 198, 182, 255, 0,
    182, 182, 255, 0, 182, 198, 255, 0, 182, 219, 255, 0, 182, 235, 255, 0, 182, 255, 255, 0, 182, 255, 235, 0, 182, 255, 219, 0, 182, 255, 198, 0,
    182, 255, 182, 0, 198, 255, 182, 0, 219, 255, 182, 0, 235, 255, 182, 0, 255, 255, 182, 0, 255, 235, 182, 0, 255, 219, 182, 0, 255, 198, 182, 0,
    113, 0,   0,   0, 113, 0,   28,  0, 113, 0,   57,  0, 113, 0,   85,  0, 113, 0,   113, 0, 85,  0,   113, 0, 57,  0,   113, 0, 28,  0,   113, 0,
    0,   0,   113, 0, 0,   28,  113, 0, 0,   57,  113, 0, 0,   85,  113, 0, 0,   113, 113, 0, 0,   113, 85,  0, 0,   113, 57,  0, 0,   113, 28,  0,
    0,   113, 0,   0, 28,  113, 0,   0, 57,  113, 0,   0, 85,  113, 0,   0, 113, 113, 0,   0, 113, 85,  0,   0, 113, 57,  0,   0, 113, 28,  0,   0,
    113, 57,  57,  0, 113, 57,  69,  0, 113, 57,  85,  0, 113, 57,  97,  0, 113, 57,  113, 0, 97,  57,  113, 0, 85,  57,  113, 0, 69,  57,  113, 0,
    57,  57,  113, 0, 57,  69,  113, 0, 57,  85,  113, 0, 57,  97,  113, 0, 57,  113, 113, 0, 57,  113, 97,  0, 57,  113, 85,  0, 57,  113, 69,  0,
    57,  113, 57,  0, 69,  113, 57,  0, 85,  113, 57,  0, 97,  113, 57,  0, 113, 113, 57,  0, 113, 97,  57,  0, 113, 85,  57,  0, 113, 69,  57,  0,
    113, 81,  81,  0, 113, 81,  89,  0, 113, 81,  97,  0, 113, 81,  105, 0, 113, 81,  113, 0, 105, 81,  113, 0, 97,  81,  113, 0, 89,  81,  113, 0,
    81,  81,  113, 0, 81,  89,  113, 0, 81,  97,  113, 0, 81,  105, 113, 0, 81,  113, 113, 0, 81,  113, 105, 0, 81,  113, 97,  0, 81,  113, 89,  0,
    81,  113, 81,  0, 89,  113, 81,  0, 97,  113, 81,  0, 105, 113, 81,  0, 113, 113, 81,  0, 113, 105, 81,  0, 113, 97,  81,  0, 113, 89,  81,  0,
    65,  0,   0,   0, 65,  0,   16,  0, 65,  0,   32,  0, 65,  0,   49,  0, 65,  0,   65,  0, 49,  0,   65,  0, 32,  0,   65,  0, 16,  0,   65,  0,
    0,   0,   65,  0, 0,   16,  65,  0, 0,   32,  65,  0, 0,   49,  65,  0, 0,   65,  65,  0, 0,   65,  49,  0, 0,   65,  32,  0, 0,   65,  16,  0,
    0,   65,  0,   0, 16,  65,  0,   0, 32,  65,  0,   0, 49,  65,  0,   0, 65,  65,  0,   0, 65,  49,  0,   0, 65,  32,  0,   0, 65,  16,  0,   0,
    65,  32,  32,  0, 65,  32,  40,  0, 65,  32,  49,  0, 65,  32,  57,  0, 65,  32,  65,  0, 57,  32,  65,  0, 49,  32,  65,  0, 40,  32,  65,  0,
    32,  32,  65,  0, 32,  40,  65,  0, 32,  49,  65,  0, 32,  57,  65,  0, 32,  65,  65,  0, 32,  65,  57,  0, 32,  65,  49,  0, 32,  65,  40,  0,
    32,  65,  32,  0, 40,  65,  32,  0, 49,  65,  32,  0, 57,  65,  32,  0, 65,  65,  32,  0, 65,  57,  32,  0, 65,  49,  32,  0, 65,  40,  32,  0,
    65,  45,  45,  0, 65,  45,  49,  0, 65,  45,  53,  0, 65,  45,  61,  0, 65,  45,  65,  0, 61,  45,  65,  0, 53,  45,  65,  0, 49,  45,  65,  0,
    45,  45,  65,  0, 45,  49,  65,  0, 45,  53,  65,  0, 45,  61,  65,  0, 45,  65,  65,  0, 45,  65,  61,  0, 45,  65,  53,  0, 45,  65,  49,  0,
    45,  65,  45,  0, 49,  65,  45,  0, 53,  65,  45,  0, 61,  65,  45,  0, 65,  65,  45,  0, 65,  61,  45,  0, 65,  53,  45,  0, 65,  49,  45,  0,
    0,   0,   0,   0, 0,   0,   0,   0, 0,   0,   0,   0, 0,   0,   0,   0, 0,   0,   0,   0, 0,   0,   0,   0, 0,   0,   0,   0, 0,   0,   0,   0};

static int32 file_qb64ega_pal_len = 256;
static const uint8 file_qb64ega_pal[] = {
    0,  0,  0,  0, 170, 0,  0,  0, 0,  170, 0,  0, 170, 170, 0,  0, 0,  0,  170, 0, 170, 0,  170, 0, 0,  170, 170, 0, 170, 170, 170, 0,
    85, 0,  0,  0, 255, 0,  0,  0, 85, 170, 0,  0, 255, 170, 0,  0, 85, 0,  170, 0, 255, 0,  170, 0, 85, 170, 170, 0, 255, 170, 170, 0,
    0,  85, 0,  0, 170, 85, 0,  0, 0,  255, 0,  0, 170, 255, 0,  0, 0,  85, 170, 0, 170, 85, 170, 0, 0,  255, 170, 0, 170, 255, 170, 0,
    85, 85, 0,  0, 255, 85, 0,  0, 85, 255, 0,  0, 255, 255, 0,  0, 85, 85, 170, 0, 255, 85, 170, 0, 85, 255, 170, 0, 255, 255, 170, 0,
    0,  0,  85, 0, 170, 0,  85, 0, 0,  170, 85, 0, 170, 170, 85, 0, 0,  0,  255, 0, 170, 0,  255, 0, 0,  170, 255, 0, 170, 170, 255, 0,
    85, 0,  85, 0, 255, 0,  85, 0, 85, 170, 85, 0, 255, 170, 85, 0, 85, 0,  255, 0, 255, 0,  255, 0, 85, 170, 255, 0, 255, 170, 255, 0,
    0,  85, 85, 0, 170, 85, 85, 0, 0,  255, 85, 0, 170, 255, 85, 0, 0,  85, 255, 0, 170, 85, 255, 0, 0,  255, 255, 0, 170, 255, 255, 0,
    85, 85, 85, 0, 255, 85, 85, 0, 85, 255, 85, 0, 255, 255, 85, 0, 85, 85, 255, 0, 255, 85, 255, 0, 85, 255, 255, 0, 255, 255, 255, 0};

uint16 *unicode16_buf = (uint16 *)malloc(1);
int32 unicode16_buf_size = 1;

void convert_text_to_utf16(int32 fonthandle, void *buf, int32 size) {
    // expand buffer if necessary
    if (unicode16_buf_size < (size * 4 + 4)) {
        unicode16_buf_size = size * 4 + 4;
        free(unicode16_buf);
        unicode16_buf = (uint16 *)malloc(unicode16_buf_size);
    }
    // convert text
    if ((fontflags[fonthandle] & FONT_LOAD_UNICODE) && fonthandle) { // unicode font
        if (size == 1)
            size = 4;
        convert_unicode(32, buf, size, 16, unicode16_buf);
    } else {
        convert_unicode(1, buf, size, 16, unicode16_buf);
    }
}

qbs *unknown_opcode_mess;

int32 exit_blocked = 0;
int32 exit_value = 0;
// 1=X-button
// 2=CTRL-BREAK
// 3=X-button and CTRL-BREAK

// MLP
// int32 qbshlp1=0;

int32 width8050switch = 1; // if set, can automatically switch to WIDTH 80,50 if LOCATE'ing beyond row 26

uint32 pal[256];

extern qbs *nothingstring;

int32 sub_screen_height_in_characters = -1; //-1=undefined
int32 sub_screen_width_in_characters = -1;  //-1=undefined
int32 sub_screen_font = -1;                 //-1=undefined
int32 sub_screen_keep_page0 = 0;

uint32 palette_256[256];
uint32 palette_64[64];

// QB64 2D PROTOTYPE 1.0

int32 pages = 1;
int32 *page = (int32 *)calloc(1, 4);

#define IMG_BUFFERSIZE 4096
img_struct *img = (img_struct *)malloc(IMG_BUFFERSIZE * sizeof(img_struct));
int32 nimg = IMG_BUFFERSIZE;
int32 nextimg = 0;

uint32 *fimg = (uint32 *)malloc(IMG_BUFFERSIZE * 4); // a list to recover freed indexes
int32 nfimg = IMG_BUFFERSIZE;
int32 lastfimg = -1; //-1=no freed indexes exist

uint8 *cblend = NULL;
uint8 *ablend = NULL;
uint8 *ablend127;
uint8 *ablend128;

// to save 16MB of RAM, software blend tables are only allocated if a 32-bit image is created
void init_blend() {
    uint8 *cp;
    int32 i, x2, x3, i2, z;
    float f, f2, f3;
    cblend = (uint8 *)malloc(16777216);
    cp = cblend;
    for (i = 0; i < 256; i++) {            // source alpha
        for (x2 = 0; x2 < 256; x2++) {     // source
            for (x3 = 0; x3 < 256; x3++) { // dest
                f = i;
                f2 = x2;
                f3 = x3;
                f /= 255.0;                                             // 0.0-1.0
                *cp++ = qbr_float_to_long((f * f2) + ((1.0 - f) * f3)); // CINT(0.0-255.0)
            }
        }
    }
    /*
        "60%+60%=84%" formula
        imagine a 60% opaque lens, you can see 40% of what's behind
        now put another 60% opaque lens on top of it
        you can now see 40% of the previous lens of which 40% is of the original scene
        40% of 40% is 16%
        100%-16%=84%
        V1=60, V2=60
        v1=V1/100, v2=V2/100
        iv1=1-v1, iv2=1-v2
        iv3=iv1*iv2
        v3=1-iv3
        V3=v3*100
    */
    ablend = (uint8 *)malloc(65536);
    cp = ablend;
    for (i = 0; i < 256; i++) {        // first alpha value
        for (i2 = 0; i2 < 256; i2++) { // second alpha value
            f = i;
            f2 = i2;
            f /= 255.0;
            f2 /= 255.0;
            f = 1.0 - f;
            f2 = 1.0 - f2;
            f3 = f * f2;
            z = qbr_float_to_long((1.0 - f3) * 255.0);
            *cp++ = z;
        }
    }
    ablend127 = ablend + (127 << 8);
    ablend128 = ablend + (128 << 8);
}

uint32 display_page_index = 0;
uint32 write_page_index = 0;
uint32 read_page_index = 0;
// use of non-indexed forms assumes valid indexes (may not be suitable for all commands)
img_struct *write_page = NULL;
img_struct *read_page = NULL;
img_struct *display_page = NULL;
uint32 *display_surface_offset = 0;

void restorepalette(img_struct *im) {
    static uint32 *pal;
    if (im->bytes_per_pixel == 4)
        return;
    pal = im->pal;

    switch (im->compatible_mode) {

    case 1:
        /*
            SCREEN Mode 1 Syntax:  COLOR [background][,palette]
            \A6 background is the screen color (range = 0-15)
            \A6 palette is a three-color palette (range = 0-1)
            0 = green, red, and brown         1 = cyan, magenta, and bright white
            Note: option 1 is the default, palette can override these though
            OPTION 1:*DEFAULT*
            0=black(color 0)
            1=cyan(color 3)
            2=purple(color 5)
            3=light grey(color 7)
            OPTION 0:
            0=black(color 0)
            1=green(color 2)
            2=red(color 4)
            3=brown(color 6)
        */
        pal[0] = palette_256[0];
        pal[1] = palette_256[3];
        pal[2] = palette_256[5];
        pal[3] = palette_256[7];
        return;
        break;

    case 2: // black/white 2 color palette
        pal[0] = 0;
        pal[1] = 0xFFFFFF;
        return;
        break;

    case 9: // 16 colors selected from 64 possibilities
        pal[0] = palette_64[0];
        pal[1] = palette_64[1];
        pal[2] = palette_64[2];
        pal[3] = palette_64[3];
        pal[4] = palette_64[4];
        pal[5] = palette_64[5];
        pal[6] = palette_64[20];
        pal[7] = palette_64[7];
        pal[8] = palette_64[56];
        pal[9] = palette_64[57];
        pal[10] = palette_64[58];
        pal[11] = palette_64[59];
        pal[12] = palette_64[60];
        pal[13] = palette_64[61];
        pal[14] = palette_64[62];
        pal[15] = palette_64[63];
        return;
        break;

    case 10: // 4 colors selected from 9 possibilities (indexes held in array pal[4-7])
        pal[4] = 0;
        pal[5] = 4;
        pal[6] = 6;
        pal[7] = 8;
        return;
        break;

    case 11: // black/white 2 color palette
        pal[0] = 0;
        pal[1] = 0xFFFFFF;
        return;
        break;

    case 13:
        memcpy(pal, palette_256, 1024);
        return;
        break;

    case 256:
        memcpy(pal, palette_256, 1024);
        return;
        break;

    default:
        // default 16 color palette
        memcpy(pal, palette_256, 64);

    }; // switch

} // restorepalette

void pset(int32 x, int32 y, uint32 col) {
    static uint8 *cp;
    static uint32 *o32;
    static uint32 destcol;
    if (write_page->bytes_per_pixel == 1) {
        write_page->offset[y * write_page->width + x] = col & write_page->mask;
        return;
    } else {
        if (write_page->alpha_disabled) {
            write_page->offset32[y * write_page->width + x] = col;
            return;
        }
        switch (col & 0xFF000000) {
        case 0xFF000000: // 100% alpha, so regular pset (fast)
            write_page->offset32[y * write_page->width + x] = col;
            return;
            break;
        case 0x0: // 0%(0) alpha, so no pset (very fast)
            return;
            break;
        case 0x80000000: //~50% alpha (optimized)

            o32 = write_page->offset32 + (y * write_page->width + x);
            *o32 = (((*o32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend128[*o32 >> 24] << 24);
            return;
            break;
        case 0x7F000000: //~50% alpha (optimized)
            o32 = write_page->offset32 + (y * write_page->width + x);
            *o32 = (((*o32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend127[*o32 >> 24] << 24);
            return;
            break;
        default: // other alpha values (uses a lookup table)
            o32 = write_page->offset32 + (y * write_page->width + x);
            destcol = *o32;
            cp = cblend + (col >> 24 << 16);
            *o32 = cp[(col << 8 & 0xFF00) + (destcol & 255)] + (cp[(col & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                   (cp[(col >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(col >> 24) + (destcol >> 16 & 0xFF00)] << 24);
        };
    }
}

/*
    img_struct *img=(img_struct*)malloc(1024*sizeof(img_struct));
    uint32 nimg=1024;
    uint32 nextimg=0;//-1=none have been assigned

    uint32 *freeimg=(uint32*)malloc(1024*4);//a list to recover freed indexes
    uint32 nfreeimg=1024;
    uint32 lastfreeimg=-1;//-1=no freed indexes exist
*/

// returns an index to free img structure
uint32 newimg() {
    static int32 i;
    if (lastfimg != -1) {
        i = fimg[lastfimg--];
        goto gotindex;
    }
    if (nextimg < nimg) {
        i = nextimg++;
        goto gotindex;
    }
    img = (img_struct *)realloc(img, (nimg + IMG_BUFFERSIZE) * sizeof(img_struct));
    if (!img)
        error(502);
    // update existing img pointers to new locations
    display_page = &img[display_page_index];
    write_page = &img[write_page_index];
    read_page = &img[read_page_index];
    memset(&img[nimg], 0, IMG_BUFFERSIZE * sizeof(img_struct));
    nimg += IMG_BUFFERSIZE;
    i = nextimg++;
gotindex:
    img[i].valid = 1;
    return i;
}

int32 freeimg(uint32 i) {
    // returns: 0=failed, 1=success
    if (i >= nimg)
        return 0;
    if (!img[i].valid)
        return 0;
    if (lastfimg >= (nfimg - 1)) { // extend
        fimg = (uint32 *)realloc(fimg, (nfimg + IMG_BUFFERSIZE) * 4);
        if (!fimg)
            error(503);
        nfimg += IMG_BUFFERSIZE;
    }
    if (img[i].lock_id) {
        free_mem_lock((mem_lock *)img[i].lock_offset); // untag
    }
    memset(&img[i], 0, sizeof(img_struct));
    lastfimg++;
    fimg[lastfimg] = i;
    return 1;
}

void imgrevert(int32 i) {
    static int32 bpp;
    static img_struct *im;

    im = &img[i];
    bpp = im->compatible_mode;

    // revert to assumed default values
    im->bytes_per_pixel = 1;
    im->font = 16;
    im->color = 15;
    im->print_mode = 3;
    im->background_color = 0;
    im->draw_ta = 0.0;
    im->draw_scale = 1.0;

    // revert to mode's set values
    switch (bpp) {
    case 0:
        im->bits_per_pixel = 16;
        im->bytes_per_pixel = 2;
        im->color = 7;
        im->text = 1;
        im->cursor_show = 0;
        im->cursor_firstvalue = 4;
        im->cursor_lastvalue = 4;
        break;
    case 1:
        im->bits_per_pixel = 2;
        im->font = 8;
        im->color = 3;
        break;
    case 2:
        im->bits_per_pixel = 1;
        im->font = 8; // it gets stretched from 8 to 16 later
        im->color = 1;
        break;
    case 7:
        im->bits_per_pixel = 4;
        im->font = 8;
        break;
    case 8:
        im->bits_per_pixel = 4;
        im->font = 8;
        break;
    case 9:
        im->bits_per_pixel = 4;
        im->font = 14;
        break;
    case 10:
        im->bits_per_pixel = 2;
        im->font = 14;
        im->color = 3;
        break;
    case 11:
        im->bits_per_pixel = 1;
        im->color = 1;
        break;
    case 12:
        im->bits_per_pixel = 4;
        break;
    case 13:
        im->bits_per_pixel = 8;
        im->font = 8;
        break;
    case 256:
        im->bits_per_pixel = 8;
        break;
    case 32:
        im->bits_per_pixel = 32;
        im->bytes_per_pixel = 4;
        im->color = 0xFFFFFFFF;
        im->background_color = 0xFF000000;
        break;
    };
    im->draw_color = im->color;

    // revert palette
    if (bpp != 32) {
        restorepalette(im);
        im->transparent_color = -1;
    }

    // revert calculatable values
    if (im->bits_per_pixel < 32)
        im->mask = (1 << im->bits_per_pixel) - 1;
    else
        im->mask = 0xFFFFFFFF;
    // text
    im->cursor_x = 1;
    im->cursor_y = 1;
    im->top_row = 1;
    if (bpp)
        im->bottom_row = (im->height / im->font);
    else
        im->bottom_row = im->height;
    im->bottom_row--;
    if (im->bottom_row <= 0)
        im->bottom_row = 1;
    if (!bpp)
        return;
    // graphics
    // clipping/scaling
    im->x = ((double)im->width) / 2.0;
    im->y = ((double)im->height) / 2.0;
    im->view_x2 = im->width - 1;
    im->view_y2 = im->height - 1;
    im->scaling_x = 1;
    im->scaling_y = 1;
    im->window_x2 = im->view_x2;
    im->window_y2 = im->view_y2;

    // clear
    if (bpp) { // graphics
        memset(im->offset, 0, im->width * im->height * im->bytes_per_pixel);
    } else { // text
        static int32 i2, i3;
        static uint16 *sp;
        i3 = im->width * im->height;
        sp = (uint16 *)im->offset;
        for (i2 = 0; i2 < i3; i2++) {
            *sp++ = 0x0720;
        }
    }

} // imgrevert

int32 imgframe(uint8 *o, int32 x, int32 y, int32 bpp) {
    static int32 i;
    static img_struct *im;
    if (x <= 0 || y <= 0)
        return 0;
    i = newimg();
    im = &img[i];
    im->offset = o;
    im->width = x;
    im->height = y;

    // assume default values
    im->bytes_per_pixel = 1;
    im->font = 16;
    im->color = 15;
    im->compatible_mode = bpp;
    im->print_mode = 3;
    im->draw_ta = 0.0;
    im->draw_scale = 1.0;

    // set values
    switch (bpp) {
    case 0:
        im->bits_per_pixel = 16;
        im->bytes_per_pixel = 2;
        im->color = 7;
        im->text = 1;
        im->cursor_show = 0;
        im->cursor_firstvalue = 4;
        im->cursor_lastvalue = 4;
        break;
    case 1:
        im->bits_per_pixel = 2;
        im->font = 8;
        im->color = 3;
        break;
    case 2:
        im->bits_per_pixel = 1;
        im->font = 8; // it gets stretched from 8 to 16 later
        im->color = 1;
        break;
    case 7:
        im->bits_per_pixel = 4;
        im->font = 8;
        break;
    case 8:
        im->bits_per_pixel = 4;
        im->font = 8;
        break;
    case 9:
        im->bits_per_pixel = 4;
        im->font = 14;
        break;
    case 10:
        im->bits_per_pixel = 2;
        im->font = 14;
        im->color = 3;
        break;
    case 11:
        im->bits_per_pixel = 1;
        im->color = 1;
        break;
    case 12:
        im->bits_per_pixel = 4;
        break;
    case 13:
        im->bits_per_pixel = 8;
        im->font = 8;
        break;
    case 256:
        im->bits_per_pixel = 8;
        break;
    case 32:
        im->bits_per_pixel = 32;
        im->bytes_per_pixel = 4;
        im->color = 0xFFFFFFFF;
        im->background_color = 0xFF000000;
        break;
    default:
        return 0;
    };
    im->draw_color = im->color;

    // attach palette
    if (bpp != 32) {
        im->pal = (uint32 *)calloc(256, 4);
        if (!im->pal) {
            freeimg(i);
            return 0;
        }
        im->flags |= IMG_FREEPAL;
        restorepalette(im);
        im->transparent_color = -1;
    }

    // set calculatable values
    if (im->bits_per_pixel < 32)
        im->mask = (1 << im->bits_per_pixel) - 1;
    else
        im->mask = 0xFFFFFFFF;
    // text
    im->cursor_x = 1;
    im->cursor_y = 1;
    im->top_row = 1;
    if (bpp)
        im->bottom_row = (im->height / im->font);
    else
        im->bottom_row = im->height;
    im->bottom_row--;
    if (im->bottom_row <= 0)
        im->bottom_row = 1;
    if (!bpp)
        return i;
    // graphics
    // clipping/scaling
    im->x = ((double)im->width) / 2.0;
    im->y = ((double)im->height) / 2.0;
    im->view_x2 = im->width - 1;
    im->view_y2 = im->height - 1;
    im->scaling_x = 1;
    im->scaling_y = 1;
    im->window_x2 = im->view_x2;
    im->window_y2 = im->view_y2;

    return i;
}

int32 imgnew(int32 x, int32 y, int32 bpp) {
    static int32 i, i2, i3;
    static img_struct *im;
    static uint16 *sp;
    static uint32 *lp;
    i = imgframe(NULL, x, y, bpp);
    if (!i)
        return 0;
    im = &img[i];
    if (bpp) { // graphics
        if (bpp == 32) {
            if (!cblend)
                init_blend();
            im->offset = (uint8 *)calloc(x * y, 4);
            if (!im->offset) {
                sub__freeimage(-i, 1);
                return 0;
            }
            // i3=x*y; lp=im->offset32; for (i2=0;i2<i3;i2++){*lp++=0xFF000000;}
        } else {
            im->offset = (uint8 *)calloc(x * y * im->bytes_per_pixel, 1);
            if (!im->offset) {
                sub__freeimage(-i, 1);
                return 0;
            }
        }
    } else { // text
        im->offset = (uint8 *)malloc(x * y * im->bytes_per_pixel);
        if (!im->offset) {
            sub__freeimage(-i, 1);
            return 0;
        }
        i3 = x * y;
        sp = (uint16 *)im->offset;
        for (i2 = 0; i2 < i3; i2++) {
            *sp++ = 0x0720;
        }
    }
    im->flags |= IMG_FREEMEM;
    return i;
}

void sub__font(int32 f, int32 i, int32 passed); // forward def

void flush_old_hardware_commands() {
    static int32 old_command;
    static int32 command_to_remove;
    static hardware_graphics_command_struct *last_rendered_hgc;
    static hardware_graphics_command_struct *old_hgc;
    static hardware_graphics_command_struct *next_hgc;

    if (next_hardware_command_to_remove && last_hardware_command_rendered) {

        last_rendered_hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, last_hardware_command_rendered);

        old_command = next_hardware_command_to_remove;
        old_hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, old_command);

    remove_next_hgc:

        if (old_hgc->next_command == 0)
            goto cant_remove;
        next_hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, old_hgc->next_command);
        if (next_hgc->order >= last_rendered_hgc->order)
            goto cant_remove;

        command_to_remove = old_command;

        if (old_hgc->command == HARDWARE_GRAPHICS_COMMAND__FREEIMAGE_REQUEST) {
            static hardware_img_struct *himg;
            himg = (hardware_img_struct *)list_get(hardware_img_handles, old_hgc->src_img);
            // alert("HARDWARE_GRAPHICS_COMMAND__FREEIMAGE_REQUEST");
            // alert(old_hgc->src_img);
            // add command to free image
            // create new command handle & structure
            int32 hgch = list_add(hardware_graphics_command_handles);
            hardware_graphics_command_struct *hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, hgch);
            hgc->remove = 0;
            // set command values
            hgc->command = HARDWARE_GRAPHICS_COMMAND__FREEIMAGE;
            hgc->src_img = old_hgc->src_img;
            // queue the command
            hgc->next_command = 0;
            hgc->order = display_frame_order_next;
            if (last_hardware_command_added) {
                hardware_graphics_command_struct *hgc2 =
                    (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, last_hardware_command_added);
                hgc2->next_command = hgch;
            }
            last_hardware_command_added = hgch;
            if (first_hardware_command == 0)
                first_hardware_command = hgch;
        }

        old_command = old_hgc->next_command;
        next_hardware_command_to_remove = old_command;
        old_hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, old_command);
        list_remove(hardware_graphics_command_handles, command_to_remove);

        goto remove_next_hgc;

    cant_remove:;

    } // next_hardware_command_to_remove&&last_hardware_command_rendered
} // flush_old_hardware_commands

uint8 charset8x8[256][8][8];
uint8 charset8x16[256][16][8];

void sub__putimage(double f_dx1, double f_dy1, double f_dx2, double f_dy2, int32 src, int32 dst, double f_sx1, double f_sy1, double f_sx2, double f_sy2,
                   int32 passed) {

    /*
        Format & passed bits: (needs updating)
        [[{STEP}](?,?)[-[{STEP}](?,?)]][,[?][,[?][,[[{STEP}](?,?)[-[{STEP}](?,?)]][,{_SMOOTH}]]]]
        2?     1              4?       8                                 512      128
    */

    static int32 w, h, sskip, dskip, x, y, xx, yy, z, x2, y2, dbpp, sbpp;
    static img_struct *s, *d;
    static uint32 *soff32, *doff32, col, clearcol, destcol;
    static uint8 *soff, *doff;
    static uint8 *cp;
    static int32 xdir, ydir, no_stretch, no_clip, no_reverse, flip, mirror;
    static double mx, my, fx, fy, fsx1, fsy1, fsx2, fsy2, dv, dv2;
    static int32 sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2;
    static int32 sw, sh, dw, dh;
    static uint32 *pal;
    static uint32 *ulp;

    no_stretch = 0;
    no_clip = 0;
    no_reverse = 1;
    flip = 0;
    mirror = 0;

    static int32 use_hardware;
    static img_struct s_emu, d_emu; // used to emulate a source/dest image for calculation purposes
    use_hardware = 0;

    // is source a hardware handle?
    hardware_img_struct *dst_himg = NULL;
    hardware_img_struct *src_himg = NULL;
    if (src) {
        src_himg = (hardware_img_struct *)list_get(hardware_img_handles, src - HARDWARE_IMG_HANDLE_OFFSET);
        if (src_himg != NULL) { // source is hardware image
            src -= HARDWARE_IMG_HANDLE_OFFSET;

            flush_old_hardware_commands();

            s_emu.width = src_himg->w;
            s_emu.height = src_himg->h;
            s_emu.clipping_or_scaling = 0;
            s_emu.alpha_disabled = src_himg->alpha_disabled;
            s = &s_emu;

            // check dst
            if (dst < 0) {
                dst_himg = (hardware_img_struct *)list_get(hardware_img_handles, dst - HARDWARE_IMG_HANDLE_OFFSET);
                if (dst_himg == NULL) {
                    error(258);
                    return;
                }
                dst -= HARDWARE_IMG_HANDLE_OFFSET;

                d_emu.width = dst_himg->w;
                d_emu.height = dst_himg->h;
                d_emu.clipping_or_scaling = 0;
                d_emu.alpha_disabled = dst_himg->alpha_disabled;
                d = &d_emu;

            } else {
                if (dst > 1) {
                    error(5);
                    return;
                }
                dst = -dst;

                d = display_page; // use parameters from display page
            }

            sbpp = 4;
            dbpp = 4;
            use_hardware = 1;

            goto resolve_coordinates;

        } // source is hardware image
    } // src passed

    if (passed & 8) { // src
        // validate
        if (src >= 0) {
            validatepage(src);
            s = &img[page[src]];
        } else {
            src = -src;
            if (src >= nextimg) {
                error(258);
                return;
            }
            s = &img[src];
            if (!s->valid) {
                error(258);
                return;
            }
        }
    } else {
        s = read_page;
    } // src
    sbpp = s->bytes_per_pixel;

    if (passed & 32) { // dst
        // validate
        if (dst >= 0) {
            validatepage(dst);
            d = &img[page[dst]];
        } else {
            dst = -dst;
            if (dst >= nextimg) {
                error(258);
                return;
            }
            d = &img[dst];
            if (!d->valid) {
                error(258);
                return;
            }
        }
    } else {
        d = write_page;
    } // dst
    if (!s->text && d->text) {
        error(5);
        return;
    }
    dbpp = d->bytes_per_pixel;
    if (s->text && !d->text) {
        // Text glyphs use up to 16 palette indices; destination must be able to represent all of them.
        if ((d->bits_per_pixel != 32) && (d->bits_per_pixel < 4)) {
            error(5);
            return;
        }
    }
    if ((sbpp == 4) && (dbpp == 1)) {
        error(5);
        return;
    }
    if (s == d) {
        //{error(5); return;}//cannot put source onto itself!
        int32 temphandle = func__copyimage(dst, NULL, 0);
        passed = passed | 8; // make certain we set the flag TO LET QB64 know we're passing a handle to the temp image
        sub__putimage(f_dx1, f_dy1, f_dx2, f_dy2, temphandle, dst, f_sx1, f_sy1, f_sx2, f_sy2, passed);
        sub__freeimage(temphandle, 1);
        return;
    }

resolve_coordinates:

    // quick references
    sw = s->width;
    sh = s->height;
    dw = d->width;
    dh = d->height;

    // change coordinates according to step
    if (passed & 2) {
        f_dx1 = d->x + f_dx1;
        f_dy1 = d->y + f_dy1;
    }
    if (passed & 16) {
        f_dx2 = f_dx1 + f_dx2;
        f_dy2 = f_dy1 + f_dy2;
    }
    if (passed & 256) {
        f_sx1 = s->x + f_sx1;
        f_sy1 = s->y + f_sy1;
    }
    if (passed & 1024) {
        f_sx2 = f_sx1 + f_sx2;
        f_sy2 = f_sy1 + f_sy2;
    }

    // Here we calculate what our final point is going to be and put that value into the _DEST x/y so we can get STEP back correctly on the next call.
    // or something like that...  I have no idea how to explain what the heck I'm gdoing here!
    // Basically I'm just trying to update the x/y point that we last plot to on our screen so we can pick it back up and use it again...
    if (passed & 4) {
        // we entered both dest numbers.  Our last point plotted should be f_dx2/f_dy2
        d->x = f_dx2;
        d->y = f_dy2;
    } else {
        if (passed & 1) {
            // we only sent it the first dest value.  We want to put our rectangle on a portion of the screen starting at this point
            if (passed & 512) {
                // we have all the source values.  We want to put that rectangle over to dest starting at that point
                d->x = f_dx1 + abs(f_sx2 - f_sx1);
                d->y = f_dy1 + abs(f_sy2 - f_sy1);
            } else {
                // we want to go from f_sx1,F_sx2 to the edge of the screen and put it over to dest starting at that point
                d->x = f_dx1 + abs(sw - f_sx1);
                d->y = f_dy1 + abs(sh - f_sy1);
            }
        } else {
            // we never sent the first source value.  We want to put the image over the whole screen.
            d->x = dw;
            d->y = dh;
        }
    }

    // And here we update our source page information so the STEP will work properly there as well.
    // This seems a little simpler logic

    if (passed & 512) {
        // we sent it the stop coordinate of where we're reading from
        s->x = f_sx2;
        s->y = f_sy2;
    } else {
        // we didn't and we need to have it copy from wherever the starting point is to the bottom right of the screen.
        // so our final point read will be the source width/height
        s->x = sw;
        s->y = sh;
    }

    // resolve coordinates
    if (passed & 1) { // dx1,dy1
        if (d->clipping_or_scaling) {
            if (d->clipping_or_scaling == 2) {
                dx1 = qbr_float_to_long(f_dx1 * d->scaling_x + d->scaling_offset_x) + d->view_offset_x;
                dy1 = qbr_float_to_long(f_dy1 * d->scaling_y + d->scaling_offset_y) + d->view_offset_y;
            } else {
                dx1 = qbr_float_to_long(f_dx1) + d->view_offset_x;
                dy1 = qbr_float_to_long(f_dy1) + d->view_offset_y;
            }
        } else {
            dx1 = qbr_float_to_long(f_dx1);
            dy1 = qbr_float_to_long(f_dy1);
        }
        // note: dx2 & dy2 cannot be passed if dx1 & dy1 weren't passed
        if (passed & 4) { // dx2,dy2
            if (d->clipping_or_scaling) {
                if (d->clipping_or_scaling == 2) {
                    dx2 = qbr_float_to_long(f_dx2 * d->scaling_x + d->scaling_offset_x) + d->view_offset_x;
                    dy2 = qbr_float_to_long(f_dy2 * d->scaling_y + d->scaling_offset_y) + d->view_offset_y;
                } else {
                    dx2 = qbr_float_to_long(f_dx2) + d->view_offset_x;
                    dy2 = qbr_float_to_long(f_dy2) + d->view_offset_y;
                }
            } else {
                dx2 = qbr_float_to_long(f_dx2);
                dy2 = qbr_float_to_long(f_dy2);
            }
        } else { // dx2,dy2
            dx2 = 0;
            dy2 = 0;
        } // dx2,dy2
    } else { // dx1,dy1
        dx1 = 0;
        dy1 = 0;
        dx2 = 0;
        dy2 = 0;
    } // dx1,dy1

    if (passed & 64) { // sx1,sy1
        if (s->clipping_or_scaling) {

            if (s->clipping_or_scaling == 2) {
                sx1 = qbr_float_to_long(f_sx1 * s->scaling_x + s->scaling_offset_x) + s->view_offset_x;
                sy1 = qbr_float_to_long(f_sy1 * s->scaling_y + s->scaling_offset_y) + s->view_offset_y;
            } else {
                sx1 = qbr_float_to_long(f_sx1) + s->view_offset_x;
                sy1 = qbr_float_to_long(f_sy1) + s->view_offset_y;
            }
        } else {
            sx1 = qbr_float_to_long(f_sx1);
            sy1 = qbr_float_to_long(f_sy1);
        }
        // note: sx2 & sy2 cannot be passed if sx1 & sy1 weren't passed
        if (passed & 512) { // sx2,sy2
            if (s->clipping_or_scaling) {
                if (s->clipping_or_scaling == 2) {
                    sx2 = qbr_float_to_long(f_sx2 * s->scaling_x + s->scaling_offset_x) + s->view_offset_x;
                    sy2 = qbr_float_to_long(f_sy2 * s->scaling_y + s->scaling_offset_y) + s->view_offset_y;
                } else {
                    sx2 = qbr_float_to_long(f_sx2) + s->view_offset_x;
                    sy2 = qbr_float_to_long(f_sy2) + s->view_offset_y;
                }
            } else {
                sx2 = qbr_float_to_long(f_sx2);
                sy2 = qbr_float_to_long(f_sy2);
            }
        } else { // sx2,sy2
            sx2 = 0;
            sy2 = 0;
        } // sx2,sy2
    } else { // sx1,sy1
        sx1 = 0;
        sy1 = 0;
        sx2 = 0;
        sy2 = 0;
    } // sx1,sy1

    // all co-ordinates resolved (but omitted co-ordinates are set to 0!)

    if (s->text) {
        int32 srcCellStepX = 1;
        int32 srcCellStepY = 1;
        int32 dstStepX = 1;
        int32 dstStepY = 1;

        if (passed & 64) {
            if (passed & 512) {
                srcCellStepX = (sx2 >= sx1) ? 1 : -1;
                srcCellStepY = (sy2 >= sy1) ? 1 : -1;
            } else {
                sx2 = sw - 1;
                sy2 = sh - 1;
            }
        } else {
            sx1 = 0;
            sy1 = 0;
            sx2 = sw - 1;
            sy2 = sh - 1;
        }

        const int32 srcCellsW = abs(sx2 - sx1) + 1;
        const int32 srcCellsH = abs(sy2 - sy1) + 1;
        const int32 fontWidth = fontwidth[s->font];
        const int32 fontHeight = fontheight[s->font];
        const int32 expectedDstW = d->text ? srcCellsW : (srcCellsW * fontWidth);
        const int32 expectedDstH = d->text ? srcCellsH : (srcCellsH * fontHeight);

        if (passed & 1) {
            if (passed & 4) {
                dstStepX = (dx2 >= dx1) ? 1 : -1;
                dstStepY = (dy2 >= dy1) ? 1 : -1;

                // Text source blit is copy-only (no scaling).
                if (((abs(dx2 - dx1) + 1) != expectedDstW) || ((abs(dy2 - dy1) + 1) != expectedDstH)) {
                    error(5);
                    return;
                }
            } else {
                dx2 = dx1 + expectedDstW - 1;
                dy2 = dy1 + expectedDstH - 1;
            }
        } else {
            dx1 = 0;
            dy1 = 0;
            dx2 = expectedDstW - 1;
            dy2 = expectedDstH - 1;
        }

        int32 clipLeadX = 0;
        int32 clipTrailX = 0;
        int32 clipLeadY = 0;
        int32 clipTrailY = 0;

        const int32 srcClipX1 = s->clipping_or_scaling ? s->view_x1 : 0;
        const int32 srcClipY1 = s->clipping_or_scaling ? s->view_y1 : 0;
        const int32 srcClipX2 = s->clipping_or_scaling ? s->view_x2 : (sw - 1);
        const int32 srcClipY2 = s->clipping_or_scaling ? s->view_y2 : (sh - 1);

        if (srcCellStepX > 0) {
            if (sx1 < srcClipX1)
                clipLeadX = srcClipX1 - sx1;
            if (sx2 > srcClipX2)
                clipTrailX = sx2 - srcClipX2;
        } else {
            if (sx1 > srcClipX2)
                clipLeadX = sx1 - srcClipX2;
            if (sx2 < srcClipX1)
                clipTrailX = srcClipX1 - sx2;
        }

        if (srcCellStepY > 0) {
            if (sy1 < srcClipY1)
                clipLeadY = srcClipY1 - sy1;
            if (sy2 > srcClipY2)
                clipTrailY = sy2 - srcClipY2;
        } else {
            if (sy1 > srcClipY2)
                clipLeadY = sy1 - srcClipY2;
            if (sy2 < srcClipY1)
                clipTrailY = srcClipY1 - sy2;
        }

        if ((clipLeadX + clipTrailX >= srcCellsW) || (clipLeadY + clipTrailY >= srcCellsH))
            return;

        sx1 += srcCellStepX * clipLeadX;
        sy1 += srcCellStepY * clipLeadY;
        sx2 -= srcCellStepX * clipTrailX;
        sy2 -= srcCellStepY * clipTrailY;

        const int32 dstUnitX = d->text ? 1 : fontWidth;
        const int32 dstUnitY = d->text ? 1 : fontHeight;
        dx1 += dstStepX * clipLeadX * dstUnitX;
        dy1 += dstStepY * clipLeadY * dstUnitY;
        dx2 -= dstStepX * clipTrailX * dstUnitX;
        dy2 -= dstStepY * clipTrailY * dstUnitY;

        const int32 clippedCellsW = abs(sx2 - sx1) + 1;
        const int32 clippedCellsH = abs(sy2 - sy1) + 1;

        auto srcData = reinterpret_cast<uint16 *>(s->offset);
        const uint32 textTransparentColor = uint32(s->transparent_color);

        if (d->text) {
            auto dstData = reinterpret_cast<uint16 *>(d->offset);
            const int32 viewPrintTop = d->top_row - 1;
            const int32 viewPrintBottom = d->bottom_row - 1;

            for (int32 cellY = 0; cellY < clippedCellsH; ++cellY) {
                const int32 srcY = sy1 + cellY * srcCellStepY;
                const int32 dstY = dy1 + cellY * dstStepY;
                if ((dstY < 0) || (dstY >= d->height))
                    continue;
                if ((dstY < viewPrintTop) || (dstY > viewPrintBottom))
                    continue;

                for (int32 cellX = 0; cellX < clippedCellsW; ++cellX) {
                    const int32 srcX = sx1 + cellX * srcCellStepX;
                    const int32 dstX = dx1 + cellX * dstStepX;
                    if ((dstX < 0) || (dstX >= d->width))
                        continue;

                    const uint16 cell = srcData[srcY * s->width + srcX];
                    if (uint32(cell) != textTransparentColor)
                        dstData[dstY * d->width + dstX] = cell;
                }
            }
        } else {
            const int32 dstClipX1 = d->clipping_or_scaling ? d->view_x1 : 0;
            const int32 dstClipY1 = d->clipping_or_scaling ? d->view_y1 : 0;
            const int32 dstClipX2 = d->clipping_or_scaling ? d->view_x2 : (d->width - 1);
            const int32 dstClipY2 = d->clipping_or_scaling ? d->view_y2 : (d->height - 1);

            for (int32 cellY = 0; cellY < clippedCellsH; ++cellY) {
                const int32 srcY = sy1 + cellY * srcCellStepY;

                for (int32 cellX = 0; cellX < clippedCellsW; ++cellX) {
                    const int32 srcX = sx1 + cellX * srcCellStepX;
                    const uint16 cell = srcData[srcY * s->width + srcX];
                    if (uint32(cell) == textTransparentColor)
                        continue;

                    const uint8 c = uint8(cell & 0xFF);
                    const uint8 attr = uint8(cell >> 8);
                    const uint8 fc = uint8(attr & 0x0F);
                    const uint8 bc = uint8(((attr >> 4) & 7) + ((attr >> 7) << 3));

                    const uint8 *glyphBits;
                    uint8 *ttfGlyphData = nullptr;

                    if (s->font >= 32) {
                        int32 rt_w, rt_h;
                        if (!FontRenderTextASCII(font[s->font], &c, 1, FONT_RENDER_MONOCHROME, &ttfGlyphData, &rt_w, &rt_h))
                            continue;
                        glyphBits = ttfGlyphData;
                    } else {
                        switch (fontHeight) {
                        case 8:
                            glyphBits = &charset8x8[c][0][0];
                            break;
                        case 14:
                            glyphBits = &charset8x16[c][1][0];
                            break;
                        default:
                            glyphBits = &charset8x16[c][0][0];
                            break;
                        }
                    }

                    if (d->bits_per_pixel == 32) {
                        for (int32 py = 0; py < fontHeight; ++py) {
                            const int32 dstY = dy1 + dstStepY * (cellY * fontHeight + py);
                            if ((dstY < dstClipY1) || (dstY > dstClipY2))
                                continue;

                            for (int32 px = 0; px < fontWidth; ++px) {
                                const int32 dstX = dx1 + dstStepX * (cellX * fontWidth + px);
                                if ((dstX < dstClipX1) || (dstX > dstClipX2))
                                    continue;

                                const uint8 bit = glyphBits[py * fontWidth + px];
                                d->offset32[dstY * d->width + dstX] = bit ? s->pal[fc] : s->pal[bc];
                            }
                        }
                    } else {
                        for (int32 py = 0; py < fontHeight; ++py) {
                            const int32 dstY = dy1 + dstStepY * (cellY * fontHeight + py);
                            if ((dstY < dstClipY1) || (dstY > dstClipY2))
                                continue;

                            for (int32 px = 0; px < fontWidth; ++px) {
                                const int32 dstX = dx1 + dstStepX * (cellX * fontWidth + px);
                                if ((dstX < dstClipX1) || (dstX > dstClipX2))
                                    continue;

                                const uint8 bit = glyphBits[py * fontWidth + px];
                                d->offset[dstY * d->width + dstX] = bit ? fc : bc;
                            }
                        }
                    }

                    if (ttfGlyphData) {
                        free(ttfGlyphData);
                        ttfGlyphData = nullptr;
                    }
                }
            }
        }

        return;
    }

    if (use_hardware) {
        // calculate omitted co-ordinates
        if ((passed & 4) && (passed & 512))
            goto got_hw_coord; // all passed
        if (passed & 4) {      //(dx1,dy1)-(dx2,dy2)...
            if (passed & 64) { //(dx1,dy1)-(dx2,dy2),...,(sx1,sy1)
                sx2 = sx1 + abs(dx2 - dx1);
                sy2 = sy1 + abs(dy2 - dy1);
                goto got_hw_coord;
            } else { //(dx1,dy1)-(dx2,dy2)
                sx2 = sw - 1;
                sy2 = sh - 1;
                goto got_hw_coord;
            }
        }
        if (passed & 512) {   //...(sx1,sy1)-(sx2,sy2)
            if (passed & 1) { //(dx1,dy1),,(sx1,sy1)-(sx2,sy2)
                dx2 = dx1 + abs(sx2 - sx1);
                dy2 = dy1 + abs(sy2 - sy1);
                goto got_hw_coord;
            } else { //(sx1,sy1)-(sx2,sy2)
                dx2 = dw - 1;
                dy2 = dh - 1;
                goto got_hw_coord;
            }
        }
        if (passed & 64) {
            error(5);
            return;
        } // Invalid: NULL-NULL,?,?,(sx1,sy1)-NULL
        if (passed & 1) { //(dx1,dy1)
            sx2 = s->width - 1;
            sy2 = s->height - 1;
            dx2 = dx1 + sx2;
            dy2 = dy1 + sy2;
            goto got_hw_coord;
        }
        // no coords provided
        sx2 = s->width - 1;
        sy2 = s->height - 1;
        dx2 = d->width - 1;
        dy2 = d->height - 1;
    got_hw_coord:

        // create new command handle & structure
        int32 hgch = list_add(hardware_graphics_command_handles);
        hardware_graphics_command_struct *hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, hgch);

        hgc->remove = 0;

        // set command values
        hgc->command = HARDWARE_GRAPHICS_COMMAND__PUTIMAGE;

        hgc->src_img = src;
        hgc->src_x1 = sx1;
        hgc->src_y1 = sy1;
        hgc->src_x2 = sx2;
        hgc->src_y2 = sy2;

        hgc->dst_img = dst;
        hgc->dst_x1 = dx1;
        hgc->dst_y1 = dy1;
        hgc->dst_x2 = dx2;
        hgc->dst_y2 = dy2;

        hgc->smooth = 0; // unless specified, no filtering will be applied
        if (passed & 128)
            hgc->smooth = 1;

        hgc->use_alpha = 1;
        if (s->alpha_disabled)
            hgc->use_alpha = 0;
        // only consider dest alpha setting if it is a hardware image
        if (dst_himg) {
            if (d->alpha_disabled)
                hgc->use_alpha = 0;
        }

        // queue the command
        hgc->next_command = 0;
        hgc->order = display_frame_order_next;

        if (last_hardware_command_added) {
            hardware_graphics_command_struct *hgc2 =
                (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, last_hardware_command_added);
            hgc2->next_command = hgch;
        }
        last_hardware_command_added = hgch;
        if (first_hardware_command == 0)
            first_hardware_command = hgch;

        return;

    } // use hardware

    //(decided not to throw error, QB64 will use linear filtering if/when available)
    // if (passed&128){error(5); return;}//software surfaces do not support pixel _SMOOTHing yet

    const int32 srcClipX1 = s->clipping_or_scaling ? s->view_x1 : 0;
    const int32 srcClipY1 = s->clipping_or_scaling ? s->view_y1 : 0;
    const int32 srcClipX2 = s->clipping_or_scaling ? s->view_x2 : (sw - 1);
    const int32 srcClipY2 = s->clipping_or_scaling ? s->view_y2 : (sh - 1);
    const int32 dstClipX1 = d->clipping_or_scaling ? d->view_x1 : 0;
    const int32 dstClipY1 = d->clipping_or_scaling ? d->view_y1 : 0;
    const int32 dstClipX2 = d->clipping_or_scaling ? d->view_x2 : (dw - 1);
    const int32 dstClipY2 = d->clipping_or_scaling ? d->view_y2 : (dh - 1);

    if ((passed & 4) && (passed & 512)) { // all co-ords given
        // could be stretched
        if ((abs(dx2 - dx1) == abs(sx2 - sx1)) && (abs(dy2 - dy1) == abs(sy2 - sy1))) { // non-stretched
            // could be flipped/reversed
            // could need clipping
            goto reverse;
        }
        goto stretch;
    }

    if (passed & 4) {      //(dx1,dy1)-(dx2,dy2)...
        if (passed & 64) { //(dx1,dy1)-(dx2,dy2),...,(sx1,sy1)
            sx2 = sx1 + abs(dx2 - dx1);
            sy2 = sy1 + abs(dy2 - dy1);
            // can't be stretched
            // could be flipped/reversed
            // could need clipping
            goto reverse;
        } else { //(dx1,dy1)-(dx2,dy2)
            sx2 = sw - 1;
            sy2 = sh - 1;
            // could be stretched
            if (((abs(dx2 - dx1) + 1) == sw) && ((abs(dy2 - dy1) + 1) == sh)) { // non-stretched
                // could be flipped/reversed
                // could need clipping
                goto reverse;
            }
            goto stretch;
        } // 16
    } // 2

    if (passed & 512) {   //...(sx1,sy1)-(sx2,sy2)
        if (passed & 1) { //(dx1,dy1),,(sx1,sy1)-(sx2,sy2)
            dx2 = dx1 + abs(sx2 - sx1);
            dy2 = dy1 + abs(sy2 - sy1);
            // can't be stretched
            // could be flipped/reversed
            // could need clipping
            goto reverse;
        } else { //(sx1,sy1)-(sx2,sy2)
            dx2 = dw - 1;
            dy2 = dh - 1;
            // could be stretched
            if (((abs(sx2 - sx1) + 1) == dw) && ((abs(sy2 - sy1) + 1) == dh)) { // non-stretched
                // could be flipped/reversed
                // could need clipping
                goto reverse;
            }
            goto stretch;
        } // 1
    } // 32

    if (passed & 64) {
        error(5);
        return;
    } // Invalid: NULL-NULL,?,?,(sx1,sy1)-NULL

    if (passed & 1) { //(dx1,dy1)
        sx2 = s->width - 1;
        sy2 = s->height - 1;
        dx2 = dx1 + sx2;
        dy2 = dy1 + sy2;
        goto clip;
    }

    // no co-ords given
    sx2 = s->width - 1;
    sy2 = s->height - 1;
    dx2 = d->width - 1;
    dy2 = d->height - 1;
    if ((sx2 == dx2) && (sy2 == dy2)) { // non-stretched
        // note: because 0-size image is illegal, no null size check is necessary
        goto noflip; // cannot be reversed
    }
    // precalculate required values
    w = dx2 - dx1;
    h = dy2 - dy1;
    fsx1 = sx1;
    fsy1 = sy1;
    fsx2 = sx2;
    fsy2 = sy2;
    //"pull" corners so all source pixels are evenly represented in dest rect
    if (fsx1 <= fsx2) {
        fsx1 -= 0.499999;
        fsx2 += 0.499999;
    } else {
        fsx1 += 0.499999;
        fsx2 -= 0.499999;
    }
    if (fsy1 <= fsy2) {
        fsy1 -= 0.499999;
        fsy2 += 0.499999;
    } else {
        fsy1 += 0.499999;
        fsy2 -= 0.499999;
    }
    // calc source gradients
    if (w)
        mx = (fsx2 - fsx1) / ((double)w);
    else
        mx = 0.0;
    if (h)
        my = (fsy2 - fsy1) / ((double)h);
    else
        my = 0.0;
    // note: mx & my represent the amount of change per dest pixel
    goto stretch_noreverse_noclip;

stretch:
    // stretch is required

    // mirror?
    if (dx2 < dx1) {
        if (sx2 > sx1)
            mirror = 1;
    }
    if (sx2 < sx1) {
        if (dx2 > dx1)
            mirror = 1;
    }
    if (dx2 < dx1) {
        x = dx1;
        dx1 = dx2;
        dx2 = x;
    }
    if (sx2 < sx1) {
        x = sx1;
        sx1 = sx2;
        sx2 = x;
    }
    // flip?
    if (dy2 < dy1) {
        if (sy2 > sy1)
            flip = 1;
    }
    if (sy2 < sy1) {
        if (dy2 > dy1)
            flip = 1;
    }
    if (dy2 < dy1) {
        y = dy1;
        dy1 = dy2;
        dy2 = y;
    }
    if (sy2 < sy1) {
        y = sy1;
        sy1 = sy2;
        sy2 = y;
    }

    w = dx2 - dx1;
    h = dy2 - dy1;
    fsx1 = sx1;
    fsy1 = sy1;
    fsx2 = sx2;
    fsy2 = sy2;
    //"pull" corners so all source pixels are evenly represented in dest rect
    if (fsx1 <= fsx2) {
        fsx1 -= 0.499999;
        fsx2 += 0.499999;
    } else {
        fsx1 += 0.499999;
        fsx2 -= 0.499999;
    }
    if (fsy1 <= fsy2) {
        fsy1 -= 0.499999;
        fsy2 += 0.499999;
    } else {
        fsy1 += 0.499999;
        fsy2 -= 0.499999;
    }
    // calc source gradients
    if (w)
        mx = (fsx2 - fsx1) / ((double)w);
    else
        mx = 0.0;
    if (h)
        my = (fsy2 - fsy1) / ((double)h);
    else
        my = 0.0;
    // note: mx & my represent the amount of change per dest pixel

    // crop dest pixels against destination clipping bounds
    if (dx1 < dstClipX1) {
        if (mirror)
            fsx2 += ((double)(dx1 - dstClipX1)) * mx;
        else
            fsx1 -= ((double)(dx1 - dstClipX1)) * mx;
        dx1 = dstClipX1;
    }
    if (dy1 < dstClipY1) {
        if (flip)
            fsy2 += ((double)(dy1 - dstClipY1)) * my;
        else
            fsy1 -= ((double)(dy1 - dstClipY1)) * my;
        dy1 = dstClipY1;
    }
    if (dx2 > dstClipX2) {
        if (mirror)
            fsx1 += ((double)(dx2 - dstClipX2)) * mx;
        else
            fsx2 -= ((double)(dx2 - dstClipX2)) * mx;
        dx2 = dstClipX2;
    }
    if (dy2 > dstClipY2) {
        if (flip)
            fsy1 += ((double)(dy2 - dstClipY2)) * my;
        else
            fsy2 -= ((double)(dy2 - dstClipY2)) * my;
        dy2 = dstClipY2;
    }
    // crop source pixels against source clipping bounds
    if (w) { // gradient cannot be 0
        if (fsx1 < (((double)srcClipX1) - 0.4999999)) {
            x = ((((double)srcClipX1) - 0.499999) - fsx1) / mx + 1.0;
            if (mirror)
                dx2 -= x;
            else
                dx1 += x;
            fsx1 += ((double)x) * mx;
        }
        if (fsx2 > (((double)srcClipX2) + 0.4999999)) {
            x = (fsx2 - (((double)srcClipX2) + 0.499999)) / mx + 1.0;
            if (mirror)
                dx1 += x;
            else
                dx2 -= x;
            fsx2 -= (((double)x) * mx);
        }
    } // w
    if (h) { // gradient cannot be 0
        if (fsy1 < (((double)srcClipY1) - 0.4999999)) {
            y = ((((double)srcClipY1) - 0.499999) - fsy1) / my + 1.0;
            if (flip)
                dy2 -= y;
            else
                dy1 += y;
            fsy1 += ((double)y) * my;
        }
        if (fsy2 > (((double)srcClipY2) + 0.4999999)) {
            y = (fsy2 - (((double)srcClipY2) + 0.499999)) / my + 1.0;
            if (flip)
                dy1 += y;
            else
                dy2 -= y;
            fsy2 -= (((double)y) * my);
        }
    } // h
    //<0-size/offscreen?
    // note: <0-size will cause reversal of dest
    //      offscreen values will result in reversal of dest
    if (dx1 > dx2)
        return;
    if (dy1 > dy2)
        return;
    // all values are now within the boundaries of the source & dest

stretch_noreverse_noclip:
    w = dx2 - dx1 + 1;
    h = dy2 - dy1 + 1; // recalculate based on actual number of pixels

    if (sbpp == 4) {
        if (s->alpha_disabled || d->alpha_disabled)
            goto put_32_noalpha_stretch;
        goto put_32_stretch;
    }
    if (dbpp == 1) {
        if (s->transparent_color == -1)
            goto put_8_stretch;
        goto put_8_clear_stretch;
    }
    if (s->transparent_color == -1)
        goto put_8_32_stretch;
    goto put_8_32_clear_stretch;

put_32_stretch:
    // calc. starting points & change values
    if (flip) {
        if (mirror) {
            doff32 = d->offset32 + (dy2 * dw + dx2);
            dskip = -dw + w;
        } else {
            doff32 = d->offset32 + (dy2 * dw + dx1);
            dskip = -dw - w;
        }
    } else {
        if (mirror) {
            doff32 = d->offset32 + (dy1 * dw + dx2);
            dskip = dw + w;
        } else {
            doff32 = d->offset32 + (dy1 * dw + dx1);
            dskip = dw - w;
        }
    }
    if (mirror)
        xdir = -1;
    else
        xdir = 1;
    // plot rect
    yy = h;
    fy = fsy1;
    fsx1 -= mx; // prev value is moved on from
    do {
        xx = w;
        ulp = s->offset32 + sw * qbr_double_to_long(fy);
        fx = fsx1;
        do {
            //--------plot pixel--------
            switch ((col = *(ulp + qbr_double_to_long(fx += mx))) & 0xFF000000) {
            case 0xFF000000:
                *doff32 = col;
                break;
            case 0x0:
                break;
            case 0x80000000:
                *doff32 = (((*doff32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
                break;
            case 0x7F000000:
                *doff32 = (((*doff32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
                break;
            default:
                destcol = *doff32;
                cp = cblend + (col >> 24 << 16);
                *doff32 = cp[(col << 8 & 0xFF00) + (destcol & 255)] + (cp[(col & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                          (cp[(col >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(col >> 24) + (destcol >> 16 & 0xFF00)] << 24);
            }; // switch
            //--------done plot pixel--------
            doff32 += xdir;
        } while (--xx);
        doff32 += dskip;
        fy += my;
    } while (--yy);
    return;

put_32_noalpha_stretch:
    // calc. starting points & change values
    if (flip) {
        if (mirror) {
            doff32 = d->offset32 + (dy2 * dw + dx2);
            dskip = -dw + w;
        } else {
            doff32 = d->offset32 + (dy2 * dw + dx1);
            dskip = -dw - w;
        }
    } else {
        if (mirror) {
            doff32 = d->offset32 + (dy1 * dw + dx2);
            dskip = dw + w;
        } else {
            doff32 = d->offset32 + (dy1 * dw + dx1);
            dskip = dw - w;
        }
    }
    if (mirror)
        xdir = -1;
    else
        xdir = 1;
    // plot rect
    yy = h;
    fy = fsy1;
    fsx1 -= mx; // prev value is moved on from
    doff32 -= xdir;
    do {
        xx = w;
        ulp = s->offset32 + sw * qbr_double_to_long(fy);
        fx = fsx1;
        do {
            *(doff32 += xdir) = *(ulp + qbr_double_to_long(fx += mx));
        } while (--xx);
        doff32 += dskip;
        fy += my;
    } while (--yy);
    return;

put_8_stretch:
    // calc. starting points & change values
    if (flip) {
        if (mirror) {
            doff = d->offset + (dy2 * dw + dx2);
            dskip = -dw + w;
        } else {
            doff = d->offset + (dy2 * dw + dx1);
            dskip = -dw - w;
        }
    } else {
        if (mirror) {
            doff = d->offset + (dy1 * dw + dx2);
            dskip = dw + w;
        } else {
            doff = d->offset + (dy1 * dw + dx1);
            dskip = dw - w;
        }
    }
    if (mirror)
        xdir = -1;
    else
        xdir = 1;
    // plot rect
    yy = h;
    fy = fsy1;
    fsx1 -= mx; // prev value is moved on from
    doff -= xdir;
    do {
        xx = w;
        cp = s->offset + sw * qbr_double_to_long(fy);
        fx = fsx1;
        do {
            *(doff += xdir) = *(cp + qbr_double_to_long(fx += mx));
        } while (--xx);
        doff += dskip;
        fy += my;
    } while (--yy);
    return;

put_8_clear_stretch:
    clearcol = s->transparent_color;
    // calc. starting points & change values
    if (flip) {
        if (mirror) {
            doff = d->offset + (dy2 * dw + dx2);
            dskip = -dw + w;
        } else {
            doff = d->offset + (dy2 * dw + dx1);
            dskip = -dw - w;
        }
    } else {
        if (mirror) {
            doff = d->offset + (dy1 * dw + dx2);
            dskip = dw + w;
        } else {
            doff = d->offset + (dy1 * dw + dx1);
            dskip = dw - w;
        }
    }
    if (mirror)
        xdir = -1;
    else
        xdir = 1;
    // plot rect
    yy = h;
    fy = fsy1;
    fsx1 -= mx; // prev value is moved on from
    do {
        xx = w;
        cp = s->offset + sw * qbr_double_to_long(fy);
        fx = fsx1;
        do {
            if ((col = *(cp + qbr_double_to_long(fx += mx))) != clearcol) {
                *doff = col;
            }
            doff += xdir;
        } while (--xx);
        doff += dskip;
        fy += my;
    } while (--yy);
    return;

put_8_32_stretch:
    pal = s->pal;
    // calc. starting points & change values
    if (flip) {
        if (mirror) {
            doff32 = d->offset32 + (dy2 * dw + dx2);
            dskip = -dw + w;
        } else {
            doff32 = d->offset32 + (dy2 * dw + dx1);
            dskip = -dw - w;
        }
    } else {
        if (mirror) {
            doff32 = d->offset32 + (dy1 * dw + dx2);
            dskip = dw + w;
        } else {
            doff32 = d->offset32 + (dy1 * dw + dx1);
            dskip = dw - w;
        }
    }
    if (mirror)
        xdir = -1;
    else
        xdir = 1;
    // plot rect
    yy = h;
    fy = fsy1;
    fsx1 -= mx; // prev value is moved on from
    doff32 -= xdir;
    do {
        xx = w;
        cp = s->offset + sw * qbr_double_to_long(fy);
        fx = fsx1;
        do {
            *(doff32 += xdir) = pal[*(cp + qbr_double_to_long(fx += mx))];
        } while (--xx);
        doff32 += dskip;
        fy += my;
    } while (--yy);
    return;

put_8_32_clear_stretch:
    clearcol = s->transparent_color;
    pal = s->pal;
    // calc. starting points & change values
    if (flip) {
        if (mirror) {
            doff32 = d->offset32 + (dy2 * dw + dx2);
            dskip = -dw + w;
        } else {
            doff32 = d->offset32 + (dy2 * dw + dx1);
            dskip = -dw - w;
        }
    } else {
        if (mirror) {
            doff32 = d->offset32 + (dy1 * dw + dx2);
            dskip = dw + w;
        } else {
            doff32 = d->offset32 + (dy1 * dw + dx1);
            dskip = dw - w;
        }
    }
    if (mirror)
        xdir = -1;
    else
        xdir = 1;
    // plot rect
    yy = h;
    fy = fsy1;
    fsx1 -= mx; // prev value is moved on from
    do {
        xx = w;
        cp = s->offset + sw * qbr_double_to_long(fy);
        fx = fsx1;
        do {
            if ((col = *(cp + qbr_double_to_long(fx += mx))) != clearcol) {
                *doff32 = pal[col];
            }
            doff32 += xdir;
        } while (--xx);
        doff32 += dskip;
        fy += my;
    } while (--yy);
    return;

reverse:
    // mirror?
    if (dx2 < dx1) {
        if (sx2 > sx1)
            mirror = 1;
    }
    if (sx2 < sx1) {
        if (dx2 > dx1)
            mirror = 1;
    }
    if (dx2 < dx1) {
        x = dx1;
        dx1 = dx2;
        dx2 = x;
    }
    if (sx2 < sx1) {
        x = sx1;
        sx1 = sx2;
        sx2 = x;
    }
    // flip?
    if (dy2 < dy1) {
        if (sy2 > sy1)
            flip = 1;
    }
    if (sy2 < sy1) {
        if (dy2 > dy1)
            flip = 1;
    }
    if (dy2 < dy1) {
        y = dy1;
        dy1 = dy2;
        dy2 = y;
    }
    if (sy2 < sy1) {
        y = sy1;
        sy1 = sy2;
        sy2 = y;
    }

clip:
    // crop destination against destination clipping bounds
    if (dx1 < dstClipX1) {
        if (mirror)
            sx2 += (dx1 - dstClipX1);
        else
            sx1 -= (dx1 - dstClipX1);
        dx1 = dstClipX1;
    }
    if (dy1 < dstClipY1) {
        if (flip)
            sy2 += (dy1 - dstClipY1);
        else
            sy1 -= (dy1 - dstClipY1);
        dy1 = dstClipY1;
    }
    if (dx2 > dstClipX2) {
        if (mirror)
            sx1 += (dx2 - dstClipX2);
        else
            sx2 -= (dx2 - dstClipX2);
        dx2 = dstClipX2;
    }
    if (dy2 > dstClipY2) {
        if (flip)
            sy1 += (dy2 - dstClipY2);
        else
            sy2 -= (dy2 - dstClipY2);
        dy2 = dstClipY2;
    }
    // crop source against source clipping bounds
    if (sx1 < srcClipX1) {
        if (mirror)
            dx2 += (sx1 - srcClipX1);
        else
            dx1 -= (sx1 - srcClipX1);
        sx1 = srcClipX1;
    }
    if (sy1 < srcClipY1) {
        if (flip)
            dy2 += (sy1 - srcClipY1);
        else
            dy1 -= (sy1 - srcClipY1);
        sy1 = srcClipY1;
    }
    if (sx2 > srcClipX2) {
        if (mirror)
            dx1 += (sx2 - srcClipX2);
        else
            dx2 -= (sx2 - srcClipX2);
        sx2 = srcClipX2;
    }
    if (sy2 > srcClipY2) {
        if (flip)
            dy1 += (sy2 - srcClipY2);
        else
            dy2 -= (sy2 - srcClipY2);
        sy2 = srcClipY2;
    }
    //<0-size/offscreen?
    // note: <0-size will cause reversal of dest
    //      offscreen values will result in reversal of dest
    if (dx1 > dx2)
        return;
    if (dy1 > dy2)
        return;
    // all values are now within the boundaries of the source & dest

    // mirror put
    if (mirror) {
        if (sbpp == 4) {
            if (s->alpha_disabled || d->alpha_disabled)
                goto put_32_noalpha_mirror;
            goto put_32_mirror;
        }
        if (dbpp == 1) {
            if (s->transparent_color == -1)
                goto put_8_mirror;
            goto put_8_clear_mirror;
        }
        if (s->transparent_color == -1)
            goto put_8_32_mirror;
        goto put_8_32_clear_mirror;
    } // mirror put

noflip:
    if (sbpp == 4) {
        if (s->alpha_disabled || d->alpha_disabled)
            goto put_32_noalpha;
        goto put_32;
    }
    if (dbpp == 1) {
        if (s->transparent_color == -1)
            goto put_8;
        goto put_8_clear;
    }
    if (s->transparent_color == -1)
        goto put_8_32;
    goto put_8_32_clear;

put_32:
    w = dx2 - dx1 + 1;
    doff32 = d->offset32 + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff32 = s->offset32 + (sy2 * sw + sx1);
        sskip = -w - sw;
    } else {
        soff32 = s->offset32 + (sy1 * sw + sx1);
        sskip = sw - w;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            //--------plot pixel--------
            switch ((col = *soff32++) & 0xFF000000) {
            case 0xFF000000:
                *doff32++ = col;
                break;
            case 0x0:
                doff32++;
                break;
            case 0x80000000:
                *doff32++ = (((*doff32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
                break;
            case 0x7F000000:
                *doff32++ = (((*doff32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
                break;
            default:
                destcol = *doff32;
                cp = cblend + (col >> 24 << 16);
                *doff32++ = cp[(col << 8 & 0xFF00) + (destcol & 255)] + (cp[(col & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                            (cp[(col >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(col >> 24) + (destcol >> 16 & 0xFF00)] << 24);
            }; // switch
            //--------done plot pixel--------
        } while (--xx);
        soff32 += sskip;
        doff32 += dskip;
    } while (--h);
    return;

put_32_noalpha:
    doff32 = d->offset32 + (dy1 * dw + dx1);
    if (flip) {
        soff32 = s->offset32 + (sy2 * sw + sx1);
        sskip = -sw;
    } else {
        soff32 = s->offset32 + (sy1 * sw + sx1);
        sskip = sw;
    }
    h = dy2 - dy1 + 1;
    w = (dx2 - dx1 + 1) * 4;
    while (h--) {
        memcpy(doff32, soff32, w);
        soff32 += sskip;
        doff32 += dw;
    }
    return;

put_8:
    doff = d->offset + (dy1 * dw + dx1);
    if (flip) {
        soff = s->offset + (sy2 * sw + sx1);
        sskip = -sw;
    } else {
        soff = s->offset + (sy1 * sw + sx1);
        sskip = sw;
    }
    h = dy2 - dy1 + 1;
    w = dx2 - dx1 + 1;
    while (h--) {
        memcpy(doff, soff, w);
        soff += sskip;
        doff += dw;
    }
    return;

put_8_clear:
    clearcol = s->transparent_color;
    w = dx2 - dx1 + 1;
    doff = d->offset + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff = s->offset + (sy2 * sw + sx1);
        sskip = -w - sw;
    } else {
        soff = s->offset + (sy1 * sw + sx1);
        sskip = sw - w;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            if ((col = *soff++) != clearcol) {
                *doff = col;
            }
            doff++;
        } while (--xx);
        soff += sskip;
        doff += dskip;
    } while (--h);
    return;

put_8_32:
    pal = s->pal;
    w = dx2 - dx1 + 1;
    doff32 = d->offset32 + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff = s->offset + (sy2 * sw + sx1);
        sskip = -w - sw;
    } else {
        soff = s->offset + (sy1 * sw + sx1);
        sskip = sw - w;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            *doff32++ = pal[*soff++];
        } while (--xx);
        soff += sskip;
        doff32 += dskip;
    } while (--h);
    return;

put_8_32_clear:
    pal = s->pal;
    clearcol = s->transparent_color;
    w = dx2 - dx1 + 1;
    doff32 = d->offset32 + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff = s->offset + (sy2 * sw + sx1);
        sskip = -w - sw;
    } else {
        soff = s->offset + (sy1 * sw + sx1);
        sskip = sw - w;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            if ((col = *soff++) != clearcol) {
                *doff32 = pal[col];
            }
            doff32++;
        } while (--xx);
        soff += sskip;
        doff32 += dskip;
    } while (--h);
    return;

put_32_mirror:
    w = dx2 - dx1 + 1;
    doff32 = d->offset32 + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff32 = s->offset32 + (sy2 * sw + sx2);
        sskip = -sw + w;
    } else {
        soff32 = s->offset32 + (sy1 * sw + sx2);
        sskip = w + sw;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            //--------plot pixel--------
            switch ((col = *soff32--) & 0xFF000000) {
            case 0xFF000000:
                *doff32++ = col;
                break;
            case 0x0:
                doff32++;
                break;
            case 0x80000000:
                *doff32++ = (((*doff32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
                break;
            case 0x7F000000:
                *doff32++ = (((*doff32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
                break;
            default:
                destcol = *doff32;
                cp = cblend + (col >> 24 << 16);
                *doff32++ = cp[(col << 8 & 0xFF00) + (destcol & 255)] + (cp[(col & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                            (cp[(col >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(col >> 24) + (destcol >> 16 & 0xFF00)] << 24);
            }; // switch
            //--------done plot pixel--------
        } while (--xx);
        soff32 += sskip;
        doff32 += dskip;
    } while (--h);
    return;

put_32_noalpha_mirror:
    w = dx2 - dx1 + 1;
    doff32 = d->offset32 + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff32 = s->offset32 + (sy2 * sw + sx2);
        sskip = -sw + w;
    } else {
        soff32 = s->offset32 + (sy1 * sw + sx2);
        sskip = w + sw;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            *doff32++ = *soff32--;
        } while (--xx);
        soff32 += sskip;
        doff32 += dskip;
    } while (--h);
    return;

put_8_mirror:
    w = dx2 - dx1 + 1;
    doff = d->offset + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff = s->offset + (sy2 * sw + sx2);
        sskip = -sw + w;
    } else {
        soff = s->offset + (sy1 * sw + sx2);
        sskip = w + sw;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            *doff++ = *soff--;
        } while (--xx);
        soff += sskip;
        doff += dskip;
    } while (--h);
    return;

put_8_clear_mirror:
    clearcol = s->transparent_color;
    w = dx2 - dx1 + 1;
    doff = d->offset + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff = s->offset + (sy2 * sw + sx2);
        sskip = -sw + w;
    } else {
        soff = s->offset + (sy1 * sw + sx2);
        sskip = w + sw;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            if ((col = *soff--) != clearcol) {
                *doff = col;
            }
            doff++;
        } while (--xx);
        soff += sskip;
        doff += dskip;
    } while (--h);
    return;

put_8_32_mirror:
    pal = s->pal;
    w = dx2 - dx1 + 1;
    doff32 = d->offset32 + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff = s->offset + (sy2 * sw + sx2);
        sskip = -sw + w;
    } else {
        soff = s->offset + (sy1 * sw + sx2);
        sskip = w + sw;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            *doff32++ = pal[*soff--];
        } while (--xx);
        soff += sskip;
        doff32 += dskip;
    } while (--h);
    return;

put_8_32_clear_mirror:
    pal = s->pal;
    clearcol = s->transparent_color;
    w = dx2 - dx1 + 1;
    doff32 = d->offset32 + (dy1 * dw + dx1);
    dskip = dw - w;
    if (flip) {
        soff = s->offset + (sy2 * sw + sx2);
        sskip = -sw + w;
    } else {
        soff = s->offset + (sy1 * sw + sx2);
        sskip = w + sw;
    }
    // plot rect
    h = dy2 - dy1 + 1;
    do {
        xx = w;
        do {
            if ((col = *soff--) != clearcol) {
                *doff32 = pal[col];
            }
            doff32++;
        } while (--xx);
        soff += sskip;
        doff32 += dskip;
    } while (--h);
    return;

} //_putimage

int32 selectfont(int32 f, img_struct *im) {
    im->font = f;
    im->cursor_x = 1;
    im->cursor_y = 1;
    im->top_row = 1;
    if (im->compatible_mode)
        im->bottom_row = im->height / fontheight[f];
    else
        im->bottom_row = im->height;
    im->bottom_row--;
    if (im->bottom_row <= 0)
        im->bottom_row = 1;
    return 1; // success
}

int32 nmodes = 0;
int32 anymode = 0;

int32 x_offset = 0, y_offset = 0;
int32 x_limit = 0, y_limit = 0;

int32 x_monitor = 0, y_monitor = 0;

int32 conversion_required = 0;
uint32 *conversion_layer = (uint32 *)malloc(8);

#define AUDIO_CHANNELS 256

#define sndqueue_lastindex 9999
uint32 sndqueue[sndqueue_lastindex + 1];
int32 sndqueue_next = 0;
int32 sndqueue_first = 0;
int32 sndqueue_wait = -1;
int32 sndqueue_played = 0;

void call_int(int32 i);

uint32 frame = 0;

extern uint8 cmem[1114099]; // 16*65535+65535+3 (enough for highest referencable dword in conv memory)

static int32 mouse_hiddden = 0;

struct mouse_message {
    int16 x;
    int16 y;
    uint32 buttons;
    int16 movementx;
    int16 movementy;
};

// Mouse message queue
//--------------------
struct mouse_message_queue_struct {
    mouse_message *queue;
    int32 lastIndex;
    int32 current;
    int32 last;
};

static mouse_message_queue_struct mouse_message_queue = {NULL, 0, 0, 0};

// x86 Virtual CMEM emulation
// Note: x86 CPU emulation is still experimental and is not available in QB64 yet.
struct cpu_struct {
    // al,ah,ax,eax (unsigned & signed)
    union {
        struct {
            union {
                uint8 al;
                int8 al_signed;
            };

            union {
                uint8 ah;
                int8 ah_signed;
            };
        };

        uint16 ax;
        int16 ax_signed;
        uint32 eax;
        int32 eax_signed;
    };

    // bl,bh,bx,ebx (unsigned & signed)
    union {
        struct {
            union {
                uint8 bl;
                int8 bl_signed;
            };

            union {
                uint8 bh;
                int8 bh_signed;
            };
        };

        uint16 bx;
        int16 bx_signed;
        uint32 ebx;
        int32 ebx_signed;
    };

    // cl,ch,cx,ecx (unsigned & signed)
    union {
        struct {
            union {
                uint8 cl;
                int8 cl_signed;
            };

            union {
                uint8 ch;
                int8 ch_signed;
            };
        };

        uint16 cx;
        int16 cx_signed;
        uint32 ecx;
        int32 ecx_signed;
    };

    // dl,dh,dx,edx (unsigned & signed)
    union {
        struct {
            union {
                uint8 dl;
                int8 dl_signed;
            };

            union {
                uint8 dh;
                int8 dh_signed;
            };
        };

        uint16 dx;
        int16 dx_signed;
        uint32 edx;
        int32 edx_signed;
    };

    // si,esi (unsigned & signed)
    union {
        uint16 si;
        int16 si_signed;
        uint32 esi;
        int32 esi_signed;
    };

    // di,edi (unsigned & signed)
    union {
        uint16 di;
        int16 di_signed;
        uint32 edi;
        int32 edi_signed;
    };

    // bp,ebp (unsigned & signed)
    union {
        uint16 bp;
        int16 bp_signed;
        uint32 ebp;
        int32 ebp_signed;
    };

    // sp,esp (unsigned & signed)
    union {
        uint16 sp;
        int16 sp_signed;
        uint32 esp;
        int32 esp_signed;
    };

    // cs,ss,ds,es,fs,gs (unsigned & signed)
    union {
        uint16 cs;
        uint16 cs_signed;
    };

    union {
        uint16 ss;
        uint16 ss_signed;
    };

    union {
        uint16 ds;
        uint16 ds_signed;
    };

    union {
        uint16 es;
        uint16 es_signed;
    };

    union {
        uint16 fs;
        uint16 fs_signed;
    };

    union {
        uint16 gs;
        uint16 gs_signed;
    };

    // ip,eip (unsigned & signed)
    union {
        uint16 ip;
        uint16 ip_signed;
        uint32 eip;
        uint32 eip_signed;
    };

    // flags
    uint8 overflow_flag;
    uint8 direction_flag;
    uint8 interrupt_flag;
    uint8 trap_flag;
    uint8 sign_flag;
    uint8 zero_flag;
    uint8 auxiliary_flag;
    uint8 parity_flag;
    uint8 carry_flag;
};

cpu_struct cpu;

uint8 *ip;
uint8 *seg;    // default segment (DS unless overridden)
uint8 *seg_bp; // the segment bp will be accessed from (SS unless overridden)

uint8 *reg8[8];
uint16 *reg16[8];
uint32 *reg32[8];
uint16 *segreg[8];

int32_t emu_a32;
int32_t emu_b32; // size of data to read/write in bits is 32

uint32 sib() {
    static uint32 i; // sib byte
    i = *ip++;
    switch (i >> 6) {
    case 0:
        return *reg32[i & 7] + *reg32[i >> 3 & 7];
        break;
    case 1:
        return *reg32[i & 7] + (*reg32[i >> 3 & 7] << 1);
        break;
    case 2:
        return *reg32[i & 7] + (*reg32[i >> 3 & 7] << 2);
        break;
    case 3:
        return *reg32[i & 7] + (*reg32[i >> 3 & 7] << 3);
        break;
    }

    return 0;
}

uint32 sib_mod0() {
    // Note: Called when top 2 bits of rm byte before sib byte were 0, base register is ignored
    //      and replaced with an int32 following the sib byte
    static uint32 i; // sib byte
    i = *ip++;
    if ((i & 7) == 5) {
        switch (i >> 6) {
        case 0:
            return (*(uint32 *)((ip += 4) - 4)) + *reg32[i >> 3 & 7];
            break;
        case 1:
            return (*(uint32 *)((ip += 4) - 4)) + (*reg32[i >> 3 & 7] << 1);
            break;
        case 2:
            return (*(uint32 *)((ip += 4) - 4)) + (*reg32[i >> 3 & 7] << 2);
            break;
        case 3:
            return (*(uint32 *)((ip += 4) - 4)) + (*reg32[i >> 3 & 7] << 3);
            break;
        }
    }
    switch (i >> 6) {
    case 0:
        return *reg32[i & 7] + *reg32[i >> 3 & 7];
        break;
    case 1:
        return *reg32[i & 7] + (*reg32[i >> 3 & 7] << 1);
        break;
    case 2:
        return *reg32[i & 7] + (*reg32[i >> 3 & 7] << 2);
        break;
    case 3:
        return *reg32[i & 7] + (*reg32[i >> 3 & 7] << 3);
        break;
    }

    return 0;
}

uint8 *rm8() {
    static uint32 i; // r/m byte
    i = *ip++;
    switch (i >> 6) {
    case 3:
        return reg8[i & 7];
        break;
    case 0:
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return seg + cpu.ax;
                break;
            case 1:
                return seg + cpu.cx;
                break;
            case 2:
                return seg + cpu.dx;
                break;
            case 3:
                return seg + cpu.bx;
                break;
            case 4:
                return seg + (uint16)sib_mod0();
                break;
            case 5:
                return seg + (*(uint16 *)((ip += 4) - 4));
                break;
            case 6:
                return seg + cpu.si;
                break;
            case 7:
                return seg + cpu.di;
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return seg + ((uint16)(cpu.bx + cpu.si));
                break;
            case 1:
                return seg + ((uint16)(cpu.bx + cpu.di));
                break;
            case 2:
                return seg_bp + ((uint16)(cpu.bp + cpu.si));
                break;
            case 3:
                return seg_bp + ((uint16)(cpu.bp + cpu.di));
                break;
            case 4:
                return seg + cpu.si;
                break;
            case 5:
                return seg + cpu.di;
                break;
            case 6:
                return seg + (*(uint16 *)((ip += 2) - 2));
                break;
            case 7:
                return seg + cpu.bx;
                break;
            }
        }
        break;
    case 1:
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return seg + ((uint16)(cpu.eax + *(int8 *)ip++));
                break;
            case 1:
                return seg + ((uint16)(cpu.ecx + *(int8 *)ip++));
                break;
            case 2:
                return seg + ((uint16)(cpu.edx + *(int8 *)ip++));
                break;
            case 3:
                return seg + ((uint16)(cpu.ebx + *(int8 *)ip++));
                break;
            case 4:
                i = sib();
                return seg + ((uint16)(i + *(int8 *)ip++));
                break;
            case 5:
                return seg_bp + ((uint16)(cpu.ebp + *(int8 *)ip++));
                break;
            case 6:
                return seg + ((uint16)(cpu.esi + *(int8 *)ip++));
                break;
            case 7:
                return seg + ((uint16)(cpu.edi + *(int8 *)ip++));
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return seg + ((uint16)(cpu.bx + cpu.si + *(int8 *)ip++));
                break;
            case 1:
                return seg + ((uint16)(cpu.bx + cpu.di + *(int8 *)ip++));
                break;
            case 2:
                return seg_bp + ((uint16)(cpu.bp + cpu.si + *(int8 *)ip++));
                break;
            case 3:
                return seg_bp + ((uint16)(cpu.bp + cpu.di + *(int8 *)ip++));
                break;
            case 4:
                return seg + ((uint16)(cpu.si + *(int8 *)ip++));
                break;
            case 5:
                return seg + ((uint16)(cpu.di + *(int8 *)ip++));
                break;
            case 6:
                return seg_bp + ((uint16)(cpu.bp + *(int8 *)ip++));
                break;
            case 7:
                return seg + ((uint16)(cpu.bx + *(int8 *)ip++));
                break;
            }
        }
        break;
    case 2:
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return seg + ((uint16)(cpu.eax + *(uint32 *)((ip += 4) - 4)));
                break;
            case 1:
                return seg + ((uint16)(cpu.ecx + *(uint32 *)((ip += 4) - 4)));
                break;
            case 2:
                return seg + ((uint16)(cpu.edx + *(uint32 *)((ip += 4) - 4)));
                break;
            case 3:
                return seg + ((uint16)(cpu.ebx + *(uint32 *)((ip += 4) - 4)));
                break;
            case 4:
                i = sib();
                return seg + ((uint16)(i + *(uint32 *)((ip += 4) - 4)));
                break;
            case 5:
                return seg_bp + ((uint16)(cpu.ebp + *(uint32 *)((ip += 4) - 4)));
                break;
            case 6:
                return seg + ((uint16)(cpu.esi + *(uint32 *)((ip += 4) - 4)));
                break;
            case 7:
                return seg + ((uint16)(cpu.edi + *(uint32 *)((ip += 4) - 4)));
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return seg + ((uint16)(cpu.bx + cpu.si + *(uint16 *)((ip += 2) - 2)));
                break;
            case 1:
                return seg + ((uint16)(cpu.bx + cpu.di + *(uint16 *)((ip += 2) - 2)));
                break;
            case 2:
                return seg_bp + ((uint16)(cpu.bp + cpu.si + *(uint16 *)((ip += 2) - 2)));
                break;
            case 3:
                return seg_bp + ((uint16)(cpu.bp + cpu.di + *(uint16 *)((ip += 2) - 2)));
                break;
            case 4:
                return seg + ((uint16)(cpu.si + *(uint16 *)((ip += 2) - 2)));
                break;
            case 5:
                return seg + ((uint16)(cpu.di + *(uint16 *)((ip += 2) - 2)));
                break;
            case 6:
                return seg_bp + ((uint16)(cpu.bp + *(uint16 *)((ip += 2) - 2)));
                break;
            case 7:
                return seg + ((uint16)(cpu.bx + *(uint16 *)((ip += 2) - 2)));
                break;
            }
        }
        break;
    }

    return nullptr;
}

uint16 *rm16() {
    static int32 i; // r/m byte
    i = *ip;
    switch (i >> 6) {
    case 3:
        ip++;
        return reg16[i & 7];
        break;
    case 0:
        ip++;
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return (uint16 *)(seg + cpu.ax);
                break;
            case 1:
                return (uint16 *)(seg + cpu.cx);
                break;
            case 2:
                return (uint16 *)(seg + cpu.dx);
                break;
            case 3:
                return (uint16 *)(seg + cpu.bx);
                break;
            case 4:
                return (uint16 *)(seg + (uint16)sib_mod0());
                break;
            case 5:
                return (uint16 *)(seg + (*(uint16 *)((ip += 4) - 4)));
                break;
            case 6:
                return (uint16 *)(seg + cpu.si);
                break;
            case 7:
                return (uint16 *)(seg + cpu.di);
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return (uint16 *)(seg + ((uint16)(cpu.bx + cpu.si)));
                break;
            case 1:
                return (uint16 *)(seg + ((uint16)(cpu.bx + cpu.di)));
                break;
            case 2:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + cpu.si)));
                break;
            case 3:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + cpu.di)));
                break;
            case 4:
                return (uint16 *)(seg + cpu.si);
                break;
            case 5:
                return (uint16 *)(seg + cpu.di);
                break;
            case 6:
                return (uint16 *)(seg + (*(uint16 *)((ip += 2) - 2)));
                break;
            case 7:
                return (uint16 *)(seg + cpu.bx);
                break;
            }
        }
        break;
    case 1:
        ip++;
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return (uint16 *)(seg + ((uint16)(cpu.eax + *(int8 *)ip++)));
                break;
            case 1:
                return (uint16 *)(seg + ((uint16)(cpu.ecx + *(int8 *)ip++)));
                break;
            case 2:
                return (uint16 *)(seg + ((uint16)(cpu.edx + *(int8 *)ip++)));
                break;
            case 3:
                return (uint16 *)(seg + ((uint16)(cpu.ebx + *(int8 *)ip++)));
                break;
            case 4:
                i = sib();
                return (uint16 *)(seg + ((uint16)(i + *(int8 *)ip++)));
                break;
            case 5:
                return (uint16 *)(seg_bp + ((uint16)(cpu.ebp + *(int8 *)ip++)));
                break;
            case 6:
                return (uint16 *)(seg + ((uint16)(cpu.esi + *(int8 *)ip++)));
                break;
            case 7:
                return (uint16 *)(seg + ((uint16)(cpu.edi + *(int8 *)ip++)));
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return (uint16 *)(seg + ((uint16)(cpu.bx + cpu.si + *(int8 *)ip++)));
                break;
            case 1:
                return (uint16 *)(seg + ((uint16)(cpu.bx + cpu.di + *(int8 *)ip++)));
                break;
            case 2:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + cpu.si + *(int8 *)ip++)));
                break;
            case 3:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + cpu.di + *(int8 *)ip++)));
                break;
            case 4:
                return (uint16 *)(seg + ((uint16)(cpu.si + *(int8 *)ip++)));
                break;
            case 5:
                return (uint16 *)(seg + ((uint16)(cpu.di + *(int8 *)ip++)));
                break;
            case 6:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + *(int8 *)ip++)));
                break;
            case 7:
                return (uint16 *)(seg + ((uint16)(cpu.bx + *(int8 *)ip++)));
                break;
            }
        }
        break;
    case 2:
        ip++;
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return (uint16 *)(seg + ((uint16)(cpu.eax + *(uint32 *)((ip += 4) - 4))));
                break;
            case 1:
                return (uint16 *)(seg + ((uint16)(cpu.ecx + *(uint32 *)((ip += 4) - 4))));
                break;
            case 2:
                return (uint16 *)(seg + ((uint16)(cpu.edx + *(uint32 *)((ip += 4) - 4))));
                break;
            case 3:
                return (uint16 *)(seg + ((uint16)(cpu.ebx + *(uint32 *)((ip += 4) - 4))));
                break;
            case 4:
                i = sib();
                return (uint16 *)(seg + ((uint16)(i + *(uint32 *)((ip += 4) - 4))));
                break;
            case 5:
                return (uint16 *)(seg_bp + ((uint16)(cpu.ebp + *(uint32 *)((ip += 4) - 4))));
                break;
            case 6:
                return (uint16 *)(seg + ((uint16)(cpu.esi + *(uint32 *)((ip += 4) - 4))));
                break;
            case 7:
                return (uint16 *)(seg + ((uint16)(cpu.edi + *(uint32 *)((ip += 4) - 4))));
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return (uint16 *)(seg + ((uint16)(cpu.bx + cpu.si + *(uint16 *)((ip += 2) - 2))));
                break;
            case 1:
                return (uint16 *)(seg + ((uint16)(cpu.bx + cpu.di + *(uint16 *)((ip += 2) - 2))));
                break;
            case 2:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + cpu.si + *(uint16 *)((ip += 2) - 2))));
                break;
            case 3:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + cpu.di + *(uint16 *)((ip += 2) - 2))));
                break;
            case 4:
                return (uint16 *)(seg + ((uint16)(cpu.si + *(uint16 *)((ip += 2) - 2))));
                break;
            case 5:
                return (uint16 *)(seg + ((uint16)(cpu.di + *(uint16 *)((ip += 2) - 2))));
                break;
            case 6:
                return (uint16 *)(seg_bp + ((uint16)(cpu.bp + *(uint16 *)((ip += 2) - 2))));
                break;
            case 7:
                return (uint16 *)(seg + ((uint16)(cpu.bx + *(uint16 *)((ip += 2) - 2))));
                break;
            }
        }
        break;
    }

    return nullptr;
}

uint32 *rm32() {
    static int32 i; // r/m byte
    i = *ip;
    switch (i >> 6) {
    case 3:
        ip++;
        return reg32[i & 7];
        break;
    case 0:
        ip++;
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return (uint32 *)(seg + cpu.ax);
                break;
            case 1:
                return (uint32 *)(seg + cpu.cx);
                break;
            case 2:
                return (uint32 *)(seg + cpu.dx);
                break;
            case 3:
                return (uint32 *)(seg + cpu.bx);
                break;
            case 4:
                return (uint32 *)(seg + (uint16)sib_mod0());
                break;
            case 5:
                return (uint32 *)(seg + (*(uint16 *)((ip += 4) - 4)));
                break;
            case 6:
                return (uint32 *)(seg + cpu.si);
                break;
            case 7:
                return (uint32 *)(seg + cpu.di);
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return (uint32 *)(seg + ((uint16)(cpu.bx + cpu.si)));
                break;
            case 1:
                return (uint32 *)(seg + ((uint16)(cpu.bx + cpu.di)));
                break;
            case 2:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + cpu.si)));
                break;
            case 3:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + cpu.di)));
                break;
            case 4:
                return (uint32 *)(seg + cpu.si);
                break;
            case 5:
                return (uint32 *)(seg + cpu.di);
                break;
            case 6:
                return (uint32 *)(seg + (*(uint16 *)((ip += 2) - 2)));
                break;
            case 7:
                return (uint32 *)(seg + cpu.bx);
                break;
            }
        }
        break;
    case 1:
        ip++;
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return (uint32 *)(seg + ((uint16)(cpu.eax + *(int8 *)ip++)));
                break;
            case 1:
                return (uint32 *)(seg + ((uint16)(cpu.ecx + *(int8 *)ip++)));
                break;
            case 2:
                return (uint32 *)(seg + ((uint16)(cpu.edx + *(int8 *)ip++)));
                break;
            case 3:
                return (uint32 *)(seg + ((uint16)(cpu.ebx + *(int8 *)ip++)));
                break;
            case 4:
                i = sib();
                return (uint32 *)(seg + ((uint16)(i + *(int8 *)ip++)));
                break;
            case 5:
                return (uint32 *)(seg_bp + ((uint16)(cpu.ebp + *(int8 *)ip++)));
                break;
            case 6:
                return (uint32 *)(seg + ((uint16)(cpu.esi + *(int8 *)ip++)));
                break;
            case 7:
                return (uint32 *)(seg + ((uint16)(cpu.edi + *(int8 *)ip++)));
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return (uint32 *)(seg + ((uint16)(cpu.bx + cpu.si + *(int8 *)ip++)));
                break;
            case 1:
                return (uint32 *)(seg + ((uint16)(cpu.bx + cpu.di + *(int8 *)ip++)));
                break;
            case 2:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + cpu.si + *(int8 *)ip++)));
                break;
            case 3:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + cpu.di + *(int8 *)ip++)));
                break;
            case 4:
                return (uint32 *)(seg + ((uint16)(cpu.si + *(int8 *)ip++)));
                break;
            case 5:
                return (uint32 *)(seg + ((uint16)(cpu.di + *(int8 *)ip++)));
                break;
            case 6:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + *(int8 *)ip++)));
                break;
            case 7:
                return (uint32 *)(seg + ((uint16)(cpu.bx + *(int8 *)ip++)));
                break;
            }
        }
        break;
    case 2:
        ip++;
        if (emu_a32) {
            switch (i & 7) {
            case 0:
                return (uint32 *)(seg + ((uint16)(cpu.eax + *(uint32 *)((ip += 4) - 4))));
                break;
            case 1:
                return (uint32 *)(seg + ((uint16)(cpu.ecx + *(uint32 *)((ip += 4) - 4))));
                break;
            case 2:
                return (uint32 *)(seg + ((uint16)(cpu.edx + *(uint32 *)((ip += 4) - 4))));
                break;
            case 3:
                return (uint32 *)(seg + ((uint16)(cpu.ebx + *(uint32 *)((ip += 4) - 4))));
                break;
            case 4:
                i = sib();
                return (uint32 *)(seg + ((uint16)(i + *(uint32 *)((ip += 4) - 4))));
                break;
            case 5:
                return (uint32 *)(seg_bp + ((uint16)(cpu.ebp + *(uint32 *)((ip += 4) - 4))));
                break;
            case 6:
                return (uint32 *)(seg + ((uint16)(cpu.esi + *(uint32 *)((ip += 4) - 4))));
                break;
            case 7:
                return (uint32 *)(seg + ((uint16)(cpu.edi + *(uint32 *)((ip += 4) - 4))));
                break;
            }
        } else {
            switch (i & 7) {
            case 0:
                return (uint32 *)(seg + ((uint16)(cpu.bx + cpu.si + *(uint16 *)((ip += 2) - 2))));
                break;
            case 1:
                return (uint32 *)(seg + ((uint16)(cpu.bx + cpu.di + *(uint16 *)((ip += 2) - 2))));
                break;
            case 2:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + cpu.si + *(uint16 *)((ip += 2) - 2))));
                break;
            case 3:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + cpu.di + *(uint16 *)((ip += 2) - 2))));
                break;
            case 4:
                return (uint32 *)(seg + ((uint16)(cpu.si + *(uint16 *)((ip += 2) - 2))));
                break;
            case 5:
                return (uint32 *)(seg + ((uint16)(cpu.di + *(uint16 *)((ip += 2) - 2))));
                break;
            case 6:
                return (uint32 *)(seg_bp + ((uint16)(cpu.bp + *(uint16 *)((ip += 2) - 2))));
                break;
            case 7:
                return (uint32 *)(seg + ((uint16)(cpu.bx + *(uint16 *)((ip += 2) - 2))));
                break;
            }
        }
        break;
    }

    return nullptr;
}

uint8 *seg_es_ptr;
uint8 *seg_cs_ptr;
uint8 *seg_ss_ptr;
uint8 *seg_ds_ptr;
uint8 *seg_fs_ptr;
uint8 *seg_gs_ptr;

#define seg_es 0
#define seg_cs 1
#define seg_ss 2
#define seg_ds 3
#define seg_fs 4
#define seg_gs 5

#define op_r i & 7

void cpu_call() {

    static int32 i, i2, i3, x, x2, x3, y, y2, y3;
    static uint8 b, b2, b3;
    static uint8 *uint8p;
    static uint16 *uint16p;
    static uint32 *uint32p;
    static uint8 *dseg;
    static int32 r;
    ip = (uint8 *)&cmem[cpu.cs * 16 + cpu.ip];

    seg_es_ptr = (uint8 *)cmem + cpu.es * 16;
    seg_cs_ptr = (uint8 *)cmem + cpu.cs * 16;
    seg_ss_ptr = (uint8 *)cmem + cpu.ss * 16;
    seg_ds_ptr = (uint8 *)cmem + cpu.ds * 16;
    seg_fs_ptr = (uint8 *)cmem + cpu.fs * 16;
    seg_gs_ptr = (uint8 *)cmem + cpu.gs * 16;

next_opcode:
    emu_b32 = 0;
    emu_a32 = 0;
    seg = seg_ds_ptr;
    seg_bp = seg_ss_ptr;

    i = *ip++;

    // read any prefixes
    if (i == 0x66) {
        emu_b32 = 1;
        i = *ip++;
    }
    if (i == 0x26) {
        seg_bp = seg = seg_es_ptr;
        i = *ip++;
    }
    if (i == 0x2E) {
        seg_bp = seg = seg_cs_ptr;
        i = *ip++;
    }
    if (i == 0x36) {
        seg = seg_ss_ptr;
        i = *ip++;
    }
    if (i == 0x3E) {
        seg_bp = seg_ds_ptr;
        i = *ip++;
    }
    if (i == 0x64) {
        seg_bp = seg = seg_fs_ptr;
        i = *ip++;
    }
    if (i == 0x65) {
        seg_bp = seg = seg_gs_ptr;
        i = *ip++;
    }
    if (i == 0x67) {
        emu_a32 = 1;
        i = *ip++;
    }

    if (i == 0x0F)
        goto opcode_0F;

    r = *ip >> 3 & 7;

    // mov
    if (i != 0x8D) {
        if (i >= 0x88 && i <= 0x8E) {
            switch (i) {
            case 0x88: // /r r/m8,r8
                *rm8() = *reg8[r];
                break;
            case 0x89: // /r r/m16(32),r16(32)
                if (emu_b32)
                    *rm32() = *reg32[r];
                else
                    *rm16() = *reg16[r];
                break;
            case 0x8A: // /r r8,r/m8
                *reg8[r] = *rm8();
                break;
            case 0x8B: // /r r16(32),r/m16(32)
                if (emu_b32)
                    *reg32[r] = *rm32();
                else
                    *reg16[r] = *rm16();
                break;
            case 0x8C: // /r r/m16,Sreg
                *rm16() = *segreg[r];
                break;
            case 0x8E: // /r Sreg,r/m16
                *segreg[r] = *rm16();
                if (r == 0)
                    seg_es_ptr = (uint8 *)cmem + *segreg[r] * 16;
                // CS (r==1) cannot be set
                if (r == 2)
                    seg_ss_ptr = (uint8 *)cmem + *segreg[r] * 16;
                if (r == 3)
                    seg_ds_ptr = (uint8 *)cmem + *segreg[r] * 16;
                if (r == 4)
                    seg_fs_ptr = (uint8 *)cmem + *segreg[r] * 16;
                if (r == 5)
                    seg_gs_ptr = (uint8 *)cmem + *segreg[r] * 16;
                break;
            }
            goto done;
        }
    }
    if (i >= 0xA0 && i <= 0xA3) {
        switch (i) {
        case 0xA0: // al,moffs8
            cpu.al = *(seg + *(uint16 *)ip);
            ip += 2;
            break;
        case 0xA1: // (e)ax,moffs16(32)
            if (emu_b32) {
                cpu.eax = *(uint32 *)(seg + *(uint16 *)ip);
                ip += 2;
            } else {
                cpu.ax = *(uint16 *)(seg + *(uint16 *)ip);
                ip += 2;
            }
            break;
        case 0xA2: // moffs8,al
            *(seg + *(uint16 *)ip) = cpu.al;
            ip += 2;
            break;
        case 0xA3: // moffs16(32),(e)ax
            if (emu_b32) {
                *(uint32 *)(seg + *(uint16 *)ip) = cpu.eax;
                ip += 2;
            } else {
                *(uint16 *)(seg + *(uint16 *)ip) = cpu.ax;
                ip += 2;
            }
            break;
        }
        goto done;
    }
    if (i >= 0xB0 && i <= 0xB7) { // +rb reg8,imm8
        *reg8[op_r] = *ip++;
        goto done;
    }
    if (i >= 0xB8 && i <= 0xBF) { // +rw(rd) reg16(32),imm16(32)
        if (emu_b32) {
            *reg32[op_r] = *(uint32 *)ip;
            ip += 4;
        } else {
            *reg16[op_r] = *(uint16 *)ip;
            ip += 2;
        }
        goto done;
    }
    if (i == 0xC6) { // r/m8,imm8
        uint8p = rm8();
        *uint8p = *ip++;
        goto done;
    }
    if (i == 0xC7) { // r/m16(32),imm16(32)
        if (emu_b32) {
            uint32p = rm32();
            *uint32p = *(uint32 *)ip;
            ip += 4;
        } else {
            uint16p = rm16();
            *uint16p = *(uint16 *)ip;
            ip += 2;
        }
        goto done;
    }

    // ret (todo)
    if (i == 0xCB) { //(far)
        // assume return control (revise later)
        return;
    }
    if (i == 0xCA) { // imm16 (far)
        // assume return control (revise later)
        return;
    }

    // int (todo)
    if (i == 0xCD) {
        call_int(*ip++); // assume interrupt table is 0xFFFF
        goto done;
    }

    // push
    if (i == 0xFF) {
        if (emu_b32) {
            *((uint32 *)(seg_ss_ptr + (cpu.sp -= 4))) = *rm32();
        } else {
            *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *rm16();
        }
        goto done;
    }
    if (i >= 0x50 && i <= 0x57) { //+ /r r16(32)
        if (emu_b32) {
            *((uint32 *)(seg_ss_ptr + (cpu.sp -= 4))) = *reg32[op_r];
        } else {
            *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *reg16[op_r];
        }
        goto done;
    }
    if (i == 0x6A) { // imm8 (sign extended to 16 bits)
        *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = ((int8)*ip++);
        goto done;
    }
    if (i == 0x68) { // imm16(32)
        if (emu_b32) {
            *((uint32 *)(seg_ss_ptr + (cpu.sp -= 4))) = *(uint32 *)ip;
            ip += 4;
        } else {
            *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *(uint16 *)ip;
            ip += 2;
        }
        goto done;
    }
    if (i == 0x0E) { // CS
        *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *segreg[seg_cs];
        goto done;
    }
    if (i == 0x16) { // SS
        *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *segreg[seg_ss];
        goto done;
    }
    if (i == 0x1E) { // DS
        *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *segreg[seg_ds];
        goto done;
    }
    if (i == 0x06) { // ES
        *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *segreg[seg_es];
        goto done;
    }

    // pop
    if (i == 0x8F) {
        if (emu_b32) {
            *rm32() = *((uint32 *)(seg_ss_ptr - 4 + (cpu.sp += 4)));
        } else {
            *rm16() = *((uint16 *)(seg_ss_ptr - 2 + (cpu.sp += 2)));
        }
        goto done;
    }
    if (i >= 0x58 && i <= 0x5F) { //+rw(d) r16(32)
        if (emu_b32) {
            *reg32[op_r] = *((uint32 *)(seg_ss_ptr - 4 + (cpu.sp += 4)));
        } else {
            *reg16[op_r] = *((uint16 *)(seg_ss_ptr - 2 + (cpu.sp += 2)));
        }
        goto done;
    }
    if (i == 0x1F) { // DS
        *segreg[seg_ds] = *((uint16 *)(seg_ss_ptr - 2 + (cpu.sp += 2)));
        goto done;
    }
    if (i == 0x07) { // ES

        *segreg[seg_es] = *((uint16 *)(seg_ss_ptr - 2 + (cpu.sp += 2)));
        goto done;
    }
    if (i == 0x17) { // SS
        *segreg[seg_ss] = *((uint16 *)(seg_ss_ptr - 2 + (cpu.sp += 2)));
        goto done;
    }

    goto skip_0F_opcodes;
opcode_0F:
    i = *ip++;
    r = *ip >> 3 & 7; // required???

    // push
    if (i == 0xA0) {
        *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *segreg[seg_fs];
        goto done;
    }
    if (i == 0xA8) {
        *((uint16 *)(seg_ss_ptr + (cpu.sp -= 2))) = *segreg[seg_gs];
        goto done;
    }

    // pop
    if (i == 0xA1) { // FS
        *segreg[seg_fs] = *((uint16 *)(seg_ss_ptr - 2 + (cpu.sp += 2)));
        goto done;
    }
    if (i == 0xA9) { // GS
        *segreg[seg_gs] = *((uint16 *)(seg_ss_ptr - 2 + (cpu.sp += 2)));
        goto done;
    }

skip_0F_opcodes:

    i2 = ((i >> 4) & 15);
    if (i2 <= 9)
        i2 += 48;
    else
        i2 = i2 - 10 + 65;
    unknown_opcode_mess->chr[16] = i2;
    i2 = i & 15;
    if (i2 <= 9)
        i2 += 48;
    else
        i2 = i2 - 10 + 65;
    unknown_opcode_mess->chr[17] = i2;
    gui_alert((const char *)unknown_opcode_mess->chr, "X86 Error", "ok");
    exit(86);
done:
    if (*ip)
        goto next_opcode;

    exit(cmem[0]);
}

int32 screen_last_valid = 0;
uint8 *screen_last = (uint8 *)malloc(1);
uint32 screen_last_size = 1;

uint64 asciicode_value = 0;
int32 asciicode_reading = 0;

int32 lock_display = 0;
int32 lock_display_required = 0;

// cost delay, made obsolete by managing thread priorities (consider removal)
#define cost_limit 10000
#define cost_delay 0
uint32 cost = 0;

int64 build_int64(uint32 val2, uint32 val1) {
    static int64 val;
    val = val2;
    val <<= 32;
    val |= val1;
    return val;
}

uint64 build_uint64(uint32 val2, uint32 val1) {
    static uint64 val;
    val = val2;
    val <<= 32;
    val |= val1;
    return val;
}

// nb. abbreviations are used in variable names to save typing, here are some of the expansions
// cmem=conventional memory
// qbs=qbick basic string (refers to the emulation of quick basic strings)
// sp=stack pointer
// dblock=a 64K memory block in conventional memory holding single variables and strings
extern uint8 *cmem_static_pointer;
uint8 *cmem_static_base = &cmem[0] + 1280 + 65536;
extern uint8 *cmem_dynamic_base;
//[1280][DBLOCK][STATIC-><-DYNAMIC][A000-]

extern uint32 qbs_cmem_sp; //=256;
extern uint32 cmem_sp;     //=65536;
extern uint64 *nothingvalue;

uint8 wait_needed = 1;

int32 full_screen = 0;      // 0,1(stretched/closest),2(1:1)
int32 full_screen_set = -1; // 0(windowed),1(stretched/closest),2(1:1)

int32 vertical_retrace_in_progress = 0;
int32 vertical_retrace_happened = 0;

uint8 lock_subsystem = 0;

extern uint8 close_program; //=0;
uint8 program_wait = 0;

extern uint8 suspend_program;
extern uint8 stop_program;

int32 global_counter = 0;
extern double last_line;
void end(void);

void sub__echo(qbs *message);

void unlockvWatchHandle() {
    if (vwatch > 0)
        vwatch = -1;
}

int32 vWatchHandle() {
    return vwatch;
}

void sub__assert(int32 expression, qbs *assert_message, int32 passed) {
    if (asserts == 0)
        return;
    if (expression == 0) {
        if (console == 1 && passed == 1) {
            sub__echo(assert_message);
            error(315);
            return;
        }
        error(314);
    }
    return;
}

void end() {
    dont_call_sub_gl = 1;
    exit_ok |= 1;
    while (!stop_program)
        Sleep(16);
    while (1)
        Sleep(16);
}

int32 stop_program_state() {
    return stop_program;
}

// MEM_STATIC memory manager
/*
    mem_static uses a pointer called mem_static_pointer to allocate linear memory.
    It can also change mem_static_pointer back to a previous location, effectively erasing
    any memory after that point.
    Because memory cannot be guaranteed to be allocated in exactly the same location
    after realloc which QB64 requires to keep functionality of previous pointers when
    the current block of memory is full QB64 creates an entirely new block, much larger
    than the previous block (at least 2x), and "writes-off" the previous block as un-
    reclaimable memory. This tradeoff is worth the speed it recovers.
    This allocation strategy can be shown as follows: (X=1MB)
    X
    XX
    XXXX
    XXXXXXXX
    XXXXXXXXXXXXXXXX
    etc.
*/
uint32 mem_static_size;
extern uint8 *mem_static;
extern uint8 *mem_static_pointer;
extern uint8 *mem_static_limit;

uint8 *mem_static_malloc(uint32 size) {
    size += 7;
    size -= (size & 7); // align to 8 byte boundary
    if ((mem_static_pointer += size) < mem_static_limit)
        return mem_static_pointer - size;
    mem_static_size = (mem_static_size << 1) + size;
    mem_static = (uint8 *)malloc(mem_static_size);
    if (!mem_static)
        error(504);
    mem_static_pointer = mem_static + size;
    mem_static_limit = mem_static + mem_static_size;
    return mem_static_pointer - size;
}

void mem_static_restore(uint8 *restore_point) {
    if ((restore_point >= mem_static) && (restore_point <= mem_static_limit)) {
        mem_static_pointer = restore_point;
    } else {
        // if restore_point is not in the current block, use t=start of current block as a new base
        mem_static_pointer = mem_static;
    }
}

// CMEM_FAR_DYNAMIC memory manager
/*
    (uses a custom "links" based memory manager)
*/
//           &HA000    DBLOCK SIZE        DBLOCK OFFSET
//           655360 - (65536            + 1280         )=588544 links possible
// links limited to 588544/4=147136 (do not have enough links if avg. block size less than 4 bytes)
// stores blocks, not free memory, because blocks are easier to identify
// always scanned from beginning to end, so prev. pointer is unnecessary
struct cmem_dynamic_link_type {
    uint8 *offset;
    uint8 *top;
    uint32 size;
    uint32 i;
    cmem_dynamic_link_type *next;
};

cmem_dynamic_link_type cmem_dynamic_link[147136 + 1]; //+1 is added because array is used from index 1

// i=cmem_dynamic_next_link++; if (i>=147136) error(257);//not enough blocks
// newlink=(cmem_dynamic_link_type*)&cmem_dynamic_link[i];

cmem_dynamic_link_type *cmem_dynamic_link_first = NULL;
int32 cmem_dynamic_next_link = 0;
int32 cmem_dynamic_free_link = 0;
uint32 cmem_dynamic_free_list[147136];

uint8 *cmem_dynamic_malloc(uint32 size) {
    static int32 i;
    static uint8 *top;
    static cmem_dynamic_link_type *link;
    static cmem_dynamic_link_type *newlink;
    static cmem_dynamic_link_type *prev_link;
    if (size > 65536)
        error(505); //>64K
    // to avoid mismatches between offsets, all 0-byte blocks are given the special offset A000h (the top of the heap)
    if (!size)
        return (&cmem[0] + 655360); // top of heap
    // forces blocks to be multiples of 16 bytes so they align with segment boundaries
    if (size & 15)
        size = size - (size & 15) + 16;
    // is a space large enough between existing blocks available?
    //(if not, memory will be allocated at bottom of heap)
    top = &cmem[0] + 655360; // top is the base of the higher block
    prev_link = NULL;
    if ((link = cmem_dynamic_link_first)) {
    cmem_dynamic_findspace:
        if ((top - link->top) >= size) { // gpf
            // found free space
            goto cmem_dynamic_make_new_link;
        }
        prev_link = link;
        top = link->offset; // set top to the base of current block for future comparisons
        if ((link = link->next))
            goto cmem_dynamic_findspace;
    }
    // no space between existing blocks is large enough, alloc below 'top'
    if ((top - cmem_static_pointer) < size)
        error(506); // a large enough block cannot be created!
    cmem_dynamic_base = top - size;
// get a new link index
cmem_dynamic_make_new_link:
    if (cmem_dynamic_free_link) {
        i = cmem_dynamic_free_list[cmem_dynamic_free_link--];
    } else {
        i = cmem_dynamic_next_link++;
        if (i >= 147136)
            error(507); // not enough blocks
    }
    newlink = (cmem_dynamic_link_type *)&cmem_dynamic_link[i];
    // set link info
    newlink->i = i;
    newlink->offset = top - size;
    newlink->size = size;
    newlink->top = top;
    // attach below prev_link
    if (prev_link) {
        newlink->next = prev_link->next; // NULL if none
        prev_link->next = newlink;
    } else {
        newlink->next = cmem_dynamic_link_first; // NULL if none
        cmem_dynamic_link_first = newlink;
    }
    return newlink->offset;
}

void cmem_dynamic_free(uint8 *block) {
    static cmem_dynamic_link_type *link;
    static cmem_dynamic_link_type *prev_link;
    if (!cmem_dynamic_link_first)
        return;
    if (!block)
        return;
    if (block == (&cmem[0] + 655360))
        return; // to avoid mismatches between offsets, all 0-byte blocks are given the special offset A000h (the top of the heap)
    prev_link = NULL;
    link = cmem_dynamic_link_first;
check_next:
    if (link->offset == block) {
        // unlink
        if (prev_link) {
            prev_link->next = link->next;
        } else {
            cmem_dynamic_link_first = link->next;
        }
        // free link
        cmem_dynamic_free_link++;
        cmem_dynamic_free_list[cmem_dynamic_free_link] = link->i;
        // memory freed successfully!
        return;
    }
    prev_link = link;
    if ((link = link->next))
        goto check_next;
    return;
}

uint8 *defseg = &cmem[1280]; // set to base of DBLOCK

void sub_defseg(int32 segment, int32 passed) {
    if (is_error_pending())
        return;
    if (!passed) {
        defseg = &cmem[1280];
        return;
    }

    if ((segment < -65536) || (segment > 65535)) { // same range as QB checks
        error(6);
    } else {
        defseg = &cmem[0] + ((uint16)segment) * 16;
    }
}

int32 func_peek(int32 offset) {
    if ((offset < -65536) || (offset > 65535)) { // same range as QB checks
        error(6);
        return 0;
    }
    return defseg[(uint16)offset];
}

void sub_poke(int32 offset, int32 value) {
    if (is_error_pending())
        return;
    if ((offset < -65536) || (offset > 65535)) { // same range as QB checks
        error(6);
        return;
    }
    defseg[(uint16)offset] = value;
}

int32 array_ok = 1; // kept to compile legacy versions

// gosub-return handling
extern uint32 next_return_point; //=0;
extern uint32 *return_point;     //=(uint32*)malloc(4*16384);
extern uint32 return_points;     //=16384;

void more_return_points() {
    if (return_points > 2147483647)
        error(256);
    return_points *= 2;
    return_point = (uint32 *)realloc(return_point, return_points * 4);
    if (return_point == NULL)
        error(256);
}

uint8 keyon[65536];

qbs *singlespace;

qbs *func_varptr_helper(uint8 type, uint16 offset) {
    //*creates a 3 byte string using the values given
    qbs *tqbs;
    tqbs = qbs_new(3, 1);
    tqbs->chr[0] = type;
    tqbs->chr[1] = offset & 255;
    tqbs->chr[2] = offset >> 8;
    return tqbs;
}

qbs *qbs_inkey() {
    if (is_error_pending())
        return qbs_new(0, 1);
    qbs *tqbs;
    // Sleep(0);
    tqbs = qbs_new(2, 1);
    if (cmem[0x41a] != cmem[0x41c]) {
        tqbs->chr[0] = cmem[0x400 + cmem[0x41a]];
        tqbs->chr[1] = cmem[0x400 + cmem[0x41a] + 1];
        if (tqbs->chr[0]) {
            tqbs->len = 1;
        } else {
            if (tqbs->chr[1] == 0)
                tqbs->len = 1;
        }
        cmem[0x41a] += 2;
        if (cmem[0x41a] == 62)
            cmem[0x41a] = 30;
    } else {
        tqbs->len = 0;
    }
    return tqbs;
}

void sub__keyclear(int32 buf, int32 passed) {
    if (is_error_pending())
        return;
    if (passed && (buf > 3 || buf < 1))
        error(5);
    //  Sleep(10);
    if ((buf == 1 && passed) || !passed) {
        // INKEY$ buffer
        cmem[0x41a] = 30;
        cmem[0x41b] = 0; // head
        cmem[0x41c] = 30;
        cmem[0x41d] = 0; // tail
    }
    if ((buf == 2 && passed) || !passed) {
        //_KEYHIT buffer
        keyhit_nextfree = 0;
        keyhit_next = 0;
    }
    if ((buf == 3 && passed) || !passed) {
        // INP(&H60) buffer
        port60h_events = 0;
    }
#ifdef QB64_WINDOWS
    // Windows Console Buffer
    FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
#endif
}

// QBG BLOCK
int32 qbg_mode = -1; //-1 means not initialized!
int32 qbg_text_only;
// text & graphics modes
int32 qbg_height_in_characters, qbg_width_in_characters;
int32 qbg_top_row, qbg_bottom_row;
int32 qbg_cursor_x, qbg_cursor_y;
int32 qbg_character_height, qbg_character_width;
uint32 qbg_color, qbg_background_color;
// text mode ONLY
int32 qbg_cursor_show;
int32 qbg_cursor_firstvalue, qbg_cursor_lastvalue; // these values need revision
// graphics modes ONLY
int32 qbg_width, qbg_height;
float qbg_x, qbg_y;
int32 qbg_bits_per_pixel, qbg_pixel_mask; // for monochrome modes 1b, for 16 color 1111b, for 256 color 11111111b
int32 qbg_bytes_per_pixel;
int32 qbg_clipping_or_scaling; // 1=clipping, 2=clipping and scaling
int32 qbg_view_x1, qbg_view_y1, qbg_view_x2, qbg_view_y2;
int32 qbg_view_offset_x, qbg_view_offset_y;
float qbg_scaling_x, qbg_scaling_y;
float qbg_scaling_offset_x, qbg_scaling_offset_y;
float qbg_window_x1, qbg_window_y1, qbg_window_x2, qbg_window_y2;
int32 qbg_pages;
uint32 *qbg_pageoffsets;
int32 *qbg_cursor_x_previous; // used to recover old cursor position
int32 *qbg_cursor_y_previous;
int32 qbg_active_page;
uint8 *qbg_active_page_offset;
int32 qbg_visual_page;
uint8 *qbg_visual_page_offset;
int32 qbg_color_assign[256]; // for modes with quasi palettes!
uint32 pal_mode10[2][9];

int32 lineclip_draw; // 1=draw, 0=don't draw
int32 lineclip_x1, lineclip_y1, lineclip_x2, lineclip_y2;
int32 lineclip_skippixels; // the number of pixels from x1,y1 which won't be drawn

void lineclip(int32 x1, int32 y1, int32 x2, int32 y2, int32 xmin, int32 ymin, int32 xmax, int32 ymax) {
    static double mx, my, y, x, d;
    static int32 xdis, ydis;
    lineclip_skippixels = 0;

    if (x1 >= xmin) {
        if (x1 <= xmax) {
            if (y1 >= ymin) {
                if (y1 <= ymax) { //(x1,y1) onscreen?
                    if (x1 == x2)
                        if (y1 == y2)
                            goto singlepoint; // is it a single point? (needed to avoid "division by 0" errors)
                    goto gotx1y1;
                }
            }
        }
    }

    //(x1,y1) offscreen...

    if (x1 == x2)
        if (y1 == y2) {
            lineclip_draw = 0;
            return;
        } // offscreen single point

    // ignore entirely offscreen lines requiring no further calculations
    if (x1 < xmin)
        if (x2 < xmin) {
            lineclip_draw = 0;
            return;
        }
    if (x1 > xmax)
        if (x2 > xmax) {
            lineclip_draw = 0;
            return;
        }
    if (y1 < ymin)
        if (y2 < ymin) {
            lineclip_draw = 0;
            return;
        }
    if (y1 > ymax)
        if (y2 > ymax) {
            lineclip_draw = 0;
            return;
        }

    mx = (x2 - x1) / std::fabs((double)(y2 - y1));
    my = (y2 - y1) / std::fabs((double)(x2 - x1));
    // right wall from right
    if (x1 > xmax) {
        if (mx < 0) {
            y = (double)y1 + ((double)x1 - (double)xmax) * my;
            if (y >= ymin) {
                if (y <= ymax) {
                    // double space indented values calculate pixels to skip
                    xdis = x1;
                    ydis = y1;
                    x1 = xmax;
                    y1 = qbr_float_to_long(y);
                    xdis = abs(xdis - x1);
                    ydis = abs(ydis - y1);
                    if (xdis >= ydis)
                        lineclip_skippixels = xdis;
                    else
                        lineclip_skippixels = ydis;
                    goto gotx1y1;
                }
            }
        }
    }
    // left wall from left
    if (x1 < xmin) {
        if (mx > 0) {
            y = (double)y1 + ((double)xmin - (double)x1) * my;
            if (y >= ymin) {
                if (y <= ymax) {
                    // double space indented values calculate pixels to skip
                    xdis = x1;
                    ydis = y1;
                    x1 = xmin;
                    y1 = qbr_float_to_long(y);
                    xdis = abs(xdis - x1);
                    ydis = abs(ydis - y1);

                    if (xdis >= ydis)
                        lineclip_skippixels = xdis;
                    else
                        lineclip_skippixels = ydis;
                    goto gotx1y1;
                }
            }
        }
    }
    // top wall from top
    if (y1 < ymin) {
        if (my > 0) {
            x = (double)x1 + ((double)ymin - (double)y1) * mx;
            if (x >= xmin) {
                if (x <= xmax) {
                    // double space indented values calculate pixels to skip
                    xdis = x1;
                    ydis = y1;
                    x1 = qbr_float_to_long(x);
                    y1 = ymin;
                    xdis = abs(xdis - x1);
                    ydis = abs(ydis - y1);
                    if (xdis >= ydis)
                        lineclip_skippixels = xdis;
                    else
                        lineclip_skippixels = ydis;
                    goto gotx1y1;
                }
            }
        }
    }
    // bottom wall from bottom
    if (y1 > ymax) {
        if (my < 0) {
            x = (double)x1 + ((double)y1 - (double)ymax) * mx;
            if (x >= xmin) {
                if (x <= xmax) {
                    // double space indented values calculate pixels to skip
                    xdis = x1;
                    ydis = y1;
                    x1 = qbr_float_to_long(x);
                    y1 = ymax;
                    xdis = abs(xdis - x1);
                    ydis = abs(ydis - y1);
                    if (xdis >= ydis)
                        lineclip_skippixels = xdis;
                    else
                        lineclip_skippixels = ydis;
                    goto gotx1y1;
                }
            }
        }
    }
    lineclip_draw = 0;
    return;
gotx1y1:

    if (x2 >= xmin) {
        if (x2 <= xmax) {
            if (y2 >= ymin) {
                if (y2 <= ymax) {
                    goto gotx2y2;
                }
            }
        }
    }

    mx = (x1 - x2) / std::fabs((double)(y1 - y2));
    my = (y1 - y2) / std::fabs((double)(x1 - x2));
    // right wall from right
    if (x2 > xmax) {
        if (mx < 0) {
            y = (double)y2 + ((double)x2 - (double)xmax) * my;
            if (y >= ymin) {
                if (y <= ymax) {
                    x2 = xmax;
                    y2 = qbr_float_to_long(y);
                    goto gotx2y2;
                }
            }
        }
    }
    // left wall from left
    if (x2 < xmin) {
        if (mx > 0) {
            y = (double)y2 + ((double)xmin - (double)x2) * my;
            if (y >= ymin) {
                if (y <= ymax) {
                    x2 = xmin;
                    y2 = qbr_float_to_long(y);
                    goto gotx2y2;
                }
            }
        }
    }
    // top wall from top
    if (y2 < ymin) {
        if (my > 0) {
            x = (double)x2 + ((double)ymin - (double)y2) * mx;
            if (x >= xmin) {
                if (x <= xmax) {
                    x2 = qbr_float_to_long(x);
                    y2 = ymin;
                    goto gotx2y2;
                }
            }
        }
    }
    // bottom wall from bottom
    if (y2 > ymax) {
        if (my < 0) {
            x = (double)x2 + ((double)y2 - (double)ymax) * mx;
            if (x >= xmin) {
                if (x <= xmax) {
                    x2 = qbr_float_to_long(x);
                    y2 = ymax;
                    goto gotx2y2;
                }
            }
        }
    }
    lineclip_draw = 0;
    return;
gotx2y2:
singlepoint:
    lineclip_draw = 1;
    lineclip_x1 = x1;
    lineclip_y1 = y1;
    lineclip_x2 = x2;
    lineclip_y2 = y2;

    return;
}

void qbg_palette(uint32 attribute, uint32 col, int32 passed) {
    static int32 r, g, b;
    if (is_error_pending())
        return;
    if (!passed) {
        restorepalette(write_page);
        return;
    }

    // 32-bit
    if (write_page->bytes_per_pixel == 4)
        goto error;

    attribute &= 255; // patch to support QBASIC overflow "bug"

    if ((write_page->compatible_mode == 13) || (write_page->compatible_mode == 256)) {
        if (col & 0xFFC0C0C0)
            goto error; // 11111111110000001100000011000000b
        r = col & 63;
        g = (col >> 8) & 63;
        b = (col >> 16) & 63;
        r = qbr((double)r * 4.063492f - 0.4999999f);
        g = qbr((double)g * 4.063492f - 0.4999999f);
        b = qbr((double)b * 4.063492f - 0.4999999f);
        write_page->pal[attribute] = b + g * 256 + r * 65536;
        // Upgraded from (((col<<2)&0xFF)<<16)+(((col>>6)&0xFF)<<8)+((col>>14)&0xFF)
        return;
    }

    if (write_page->compatible_mode == 12) {
        if (attribute > 15)
            goto error;
        if (col & 0xFFC0C0C0)
            goto error; // 11111111110000001100000011000000b
        r = col & 63;
        g = (col >> 8) & 63;
        b = (col >> 16) & 63;
        r = qbr((double)r * 4.063492f - 0.4999999f);
        g = qbr((double)g * 4.063492f - 0.4999999f);
        b = qbr((double)b * 4.063492f - 0.4999999f);
        write_page->pal[attribute] = b + g * 256 + r * 65536;
        return;
    }

    if (write_page->compatible_mode == 11) {
        if (attribute > 1)
            goto error;
        if (col & 0xFFC0C0C0)
            goto error; // 11111111110000001100000011000000b
        r = col & 63;
        g = (col >> 8) & 63;
        b = (col >> 16) & 63;
        r = qbr((double)r * 4.063492f - 0.4999999f);
        g = qbr((double)g * 4.063492f - 0.4999999f);
        b = qbr((double)b * 4.063492f - 0.4999999f);
        write_page->pal[attribute] = b + g * 256 + r * 65536;
        return;
    }

    if (write_page->compatible_mode == 10) {
        if (attribute > 3)
            goto error;
        if ((col < 0) || (col > 8))
            goto error;
        write_page->pal[attribute + 4] = col;
        return;
    }

    if (write_page->compatible_mode == 9) {
        if (attribute > 15)
            goto error;
        if ((col < 0) || (col > 63))
            goto error;
        write_page->pal[attribute] = palette_64[col];
        return;
    }

    if (write_page->compatible_mode == 8) {
        if (attribute > 15)
            goto error;
        if ((col < 0) || (col > 15))
            goto error;
        write_page->pal[attribute] = palette_256[col];
        return;
    }

    if (write_page->compatible_mode == 7) {
        if (attribute > 15)
            goto error;
        if ((col < 0) || (col > 15))
            goto error;
        write_page->pal[attribute] = palette_256[col];
        return;
    }

    if (write_page->compatible_mode == 2) {
        if (attribute > 1)
            goto error;
        if ((col < 0) || (col > 15))
            goto error;
        write_page->pal[attribute] = palette_256[col];
        return;
    }

    if (write_page->compatible_mode == 1) {
        if (attribute > 15)
            goto error;
        if ((col < 0) || (col > 15))
            goto error;
        write_page->pal[attribute] = palette_256[col];
        return;
    }

    if (write_page->compatible_mode == 0) {
        if (attribute > 15)
            goto error;
        if ((col < 0) || (col > 63))
            goto error;
        write_page->pal[attribute] = palette_64[col];
        return;
    }

error:
    error(5);
    return;
}

void qbg_sub_color(uint32 col1, uint32 col2, uint32 bordercolor, int32 i, int32 passed) {
    if (is_error_pending())
        return;
    if (!passed) {
        // performs no action if nothing passed (as in QBASIC for some modes)
        return;
    }

    int32 opi = -1; // originally passed image
    int32 sod = -1; // saved old destination
    if (passed & 8) {
        opi = i;
        if (i >= 0) { // validate i
            validatepage(i);
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    }
    if (opi != -1) { // set given image as destination
        sod = func__dest();
        sub__dest(opi);
    }

    if (write_page->console) {
#ifdef QB64_WINDOWS
        HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
        int color = col2 * 16 + col1;
        SetConsoleTextAttribute(output, color);
#else
        // Exactly how the colour is rendered depends on your terminal emulator and
        // colour palette. Themes and user-customisation aside, the first 16 colours
        // line up with the old VGA colour scheme.
        // Most terminal emulators can handle 8 bit colour, see
        // https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit for the 8 bit colour palette.
        if ((passed & 1 && (col1 > 255 || col1 < 0)) || (passed & 2 && (col2 > 255 || col2 < 0)))
            goto error;

        if (passed & 1)
            printf("\033[38;5;%dm", col1);

        if (passed & 2)
            printf("\033[48;5;%dm", col2);
#endif
        goto done;
    }
    if (write_page->compatible_mode == 32) {
        if (passed & 4)
            goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->background_color = col2;
        goto done;
    }
    if (write_page->compatible_mode == 256) {
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 255)
                goto error;
        if (passed & 2)
            if (col2 > 255)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->background_color = col2;
        goto done;
    }
    if (write_page->compatible_mode == 13) {
        // if (passed&6) goto error;
        // if (col1>255) goto error;
        // write_page->color=col1;
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 255)
                goto error;
        if (passed & 2)
            if (col2 > 255)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->background_color = col2;
        goto done;
    }
    if (write_page->compatible_mode == 12) {
        // if (passed&6) goto error;
        // if (col1>15) goto error;
        // write_page->color=col1;
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 15)
                goto error;
        if (passed & 2)
            if (col2 > 15)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->background_color = col2;
        goto done;
    }
    if (write_page->compatible_mode == 11) {
        // if (passed&6) goto error;
        // if (col1>1) goto error;
        // write_page->color=col1;
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 1)
                goto error;
        if (passed & 2)
            if (col2 > 1)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->background_color = col2;
        goto done;
    }
    if (write_page->compatible_mode == 10) {
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 3)
                goto error;
        if (passed & 2)
            if (col2 > 8)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        // if (passed&2) ..._color_assign[0]=col2;
        if (passed & 2)
            write_page->pal[4] = col2;
        goto done;
    }
    if (write_page->compatible_mode == 9) {
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 15)
                goto error;
        if (passed & 2)
            if (col2 > 63)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->pal[0] = palette_64[col2];
        goto done;
    }
    if (write_page->compatible_mode == 8) {
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 15)
                goto error;
        if (passed & 2)
            if (col2 > 15)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->pal[0] = palette_256[col2];
        goto done;
    }
    if (write_page->compatible_mode == 7) {
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 15)
                goto error;
        if (passed & 2)
            if (col2 > 15)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->pal[0] = palette_256[col2];
        goto done;
    }
    if (write_page->compatible_mode == 2) {
        if (passed & 4)
            goto error;
        if (passed & 1)
            if (col1 > 1)
                goto error;
        if (passed & 2)
            if (col2 > 15)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->pal[0] = palette_256[col2];
        goto done;
    }
    if (write_page->compatible_mode == 1) {
        if (passed & 4)
            goto error;
        if (passed & 1) {
            if (col1 > 15)
                goto error;
            write_page->pal[0] = palette_256[col1];
        }
        if (passed & 2) {
            if (col2 & 1) {
                write_page->pal[1] = palette_256[3];
                write_page->pal[2] = palette_256[5];
                write_page->pal[3] = palette_256[7];
            } else {
                write_page->pal[1] = palette_256[2];
                write_page->pal[2] = palette_256[4];
                write_page->pal[3] = palette_256[6];
            }
        }
        goto done;
    }
    if (write_page->compatible_mode == 0) {
        if (passed & 1)
            if (col1 > 31)
                goto error;
        if (passed & 2)
            if (col2 > 15)
                goto error;
        if (passed & 1)
            write_page->color = col1;
        if (passed & 2)
            write_page->background_color = col2 & 7;
        goto done;
    }
error:
    error(5);
done:
    if (opi != -1)
        sub__dest(sod); // reset old destination, if required
    return;
}

void defaultcolors() {
    write_page->color = 15;
    write_page->background_color = 0;
    if (write_page->compatible_mode == 0) {
        write_page->color = 7;
        write_page->background_color = 0;
    }
    if (write_page->compatible_mode == 1) {
        write_page->color = 3;
        write_page->background_color = 0;
    }
    if (write_page->compatible_mode == 2) {
        write_page->color = 1;
        write_page->background_color = 0;
    }
    if (write_page->compatible_mode == 10) {
        write_page->color = 3;
        write_page->background_color = 0;
    }
    if (write_page->compatible_mode == 11) {
        write_page->color = 1;
        write_page->background_color = 0;
    }
    if (write_page->compatible_mode == 32) {
        write_page->color = 0xFFFFFFFF;
        write_page->background_color = 0xFF000000;
    }
    write_page->draw_color = write_page->color;
    return;
}

// Note: Cannot be used to setup page 0, just to validate it
void validatepage(int32 n) {
    static int32 i, i2;
    // add new page indexes if necessary
    if (n >= pages) {
        i = n + 1;
        page = (int32 *)realloc(page, i * 4);
        memset(page + pages, 0, (i - pages) * 4);
        pages = i;
    }
    // create page at index n if none exists
    if (!page[n]) {
        // graphical (assumed)
        i = page[0];
        i2 = imgnew(img[i].width, img[i].height, img[i].compatible_mode);
        // modify based on page 0's attributes
        // i. link palette to page 0's palette (if necessary)
        if (img[i2].bytes_per_pixel != 4) {
            free(img[i2].pal);
            img[i2].flags ^= IMG_FREEPAL;
            img[i2].pal = img[i].pal;
        }
        // ii. set flags
        img[i2].flags |= IMG_SCREEN;
        // iii. inherit font
        selectfont(img[i].font, &img[i2]);
        // text
        //...
        page[n] = i2;
    }
    return;
} // validate_page

void qbg_screen(int32 mode, int32 color_switch, int32 active_page, int32 visual_page, int32 refresh, int32 passed) {
    if (is_error_pending())
        return;

    if (width8050switch) {
        if ((passed != 1) || mode)
            width8050switch = 0;
    }

    static int32 i, i2, i3, x, y, f, p;
    static img_struct *im;
    static int32 prev_width_in_characters, prev_height_in_characters;

    static int32 last_active_page = 0; // used for active page settings migration

    i = 0; // update flags
    // 1=mode change required
    // 2=page change required (used only to see if an early exit without locking is possible)

    i2 = page[0];
    if (passed & 1) {   // mode
        if (mode < 0) { // custom screen
            i3 = -mode;
            if (i3 >= nextimg) {
                error(258);
                return;
            } // within valid range?
            if (!img[i3].valid) {
                error(258);
                return;
            } // valid?
            if (i3 != i2)
                i = 1; // is mode changing?
        } else {
            if (mode == 3)
                goto error;
            if (mode == 4)
                goto error;
            if (mode == 5)
                goto error;
            if (mode == 6)
                goto error;
            if (mode > 13)
                goto error;
            // is mode changing?
            if (i2) {
                if (img[i2].compatible_mode != mode)
                    i = 1;
            } else
                i = 1;
            // force update if special parameters passed
            //(at present, only SCREEN 0 is ever called with these overrides, so handling
            // of these is done only in the SCREEN 0 section of the SCREEN sub)
            if ((sub_screen_width_in_characters != -1) || (sub_screen_height_in_characters != -1) || (sub_screen_font != -1))
                i = 1;
        }
    }

    if (passed & 4) { // active page
        if (active_page < 0)
            goto error;
        if (!(passed & 8)) { // if visual page not specified, set it to the active page
            passed |= 8;
            visual_page = active_page;
        }
        if (!(i & 1)) { // mode not changing
            // validate the passed active page, then see if it is the currently selected page
            validatepage(active_page);
            i2 = page[active_page];
            if ((i2 != read_page_index) || (i2 != write_page_index))
                i |= 2;
        }
    } // passed&4

    if (passed & 8) { // visual page
        i3 = visual_page;
        if (i3 < 0)
            goto error;
        if (!(i & 1)) { // mode not changing
            validatepage(visual_page);
            i2 = page[visual_page];
            if (i2 != display_page_index)
                i |= 2;
        }
    } // passed&8

    // if no changes need to be made exit before locking
    if (!i)
        return;

    if (autodisplay) {
        if (lock_display_required) { // on init of main (), attempting a lock would create an infinite loop
            if (i & 1) {             // avoid locking when only changing the screen page
                if (lock_display == 0)
                    lock_display = 1; // request lock
                while (lock_display != 2) {
                    Sleep(0);
                }
            }
        }
    }

    screen_last_valid = 0; // ignore cache used to update the screen on next update

    if (passed & 1) { // mode
        if (i & 1) {  // mode change necessary

            // calculate previous width & height if possible
            prev_width_in_characters = 0;
            prev_height_in_characters = 0;
            if ((i = page[0])) { // currently in a screen mode?
                im = &img[i];
                if (!im->compatible_mode) {
                    prev_width_in_characters = im->width;
                    prev_height_in_characters = im->height;
                } else {
                    x = fontwidth[im->font];
                    if (!x)
                        x = 1;
                    prev_width_in_characters = im->width / x;
                    prev_height_in_characters = im->height / fontheight[im->font];
                }
            } // currently in a screen mode

            // free any previously allocated surfaces
            // free pages in reverse order
            if (page[0]) { // currently in a screen mode?
                for (i = 1; i < pages; i++) {
                    if ((i2 = page[i])) {
                        // manual delete, freeing video pages is usually illegal
                        if (img[i2].flags & IMG_FREEMEM)
                            free(img[i2].offset); // free pixel data
                        freeimg(i2);
                    } // i2
                } // i
                i = page[0];
                if (sub_screen_keep_page0) {
                    img[i].flags ^= IMG_SCREEN;
                } else {
                    if (img[i].flags & IMG_FREEMEM)
                        free(img[i].offset); // free pixel data
                    if (img[i].flags & IMG_FREEPAL)
                        free(img[i].pal); // free palette
                    freeimg(i);
                }
            } // currently in a screen mode
            sub_screen_keep_page0 = 0; // reset to default status

            pages = 1;
            page[0] = 0;

            if (mode < 0) { // custom screen
                i = -mode;
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
                sub_screen_keep_page0 = 1;
            }

            // 320 x 200 graphics
            // 40 x 25 text format, character box size of 8 x 8
            // Assignment of up to 256K colors to up to 256 attributes
            if (mode == 13) {
                i = imgframe(&cmem[655360], 320, 200, 13);
                memset(img[i].offset, 0, 320 * 200);
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 13

            // 640 x 480 graphics
            // 80 x 30 or 80 x 60 text format, character box size of 8 x 16 or 8 x 8
            // Assignment of up to 256K colors to 16 attributes
            if (mode == 12) {
                i = imgnew(640, 480, 12);
                if ((prev_width_in_characters == 80) && (prev_height_in_characters == 60))
                    selectfont(8, &img[i]); // override default font
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 12

            /*
                Screen 11
                \A6 640 x 480 graphics
                \A6 80 x 30 or 80 x 60 text format, character box size of 8 x 16 or 8 x 8
                \A6 Assignment of up to 256K colors to 2 attributes
            */
            if (mode == 11) {
                i = imgnew(640, 480, 11);
                if ((prev_width_in_characters == 80) && (prev_height_in_characters == 60))
                    selectfont(8, &img[i]); // override default font
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 11

            // SCREEN 10: 640 x 350 graphics, monochrome monitor only
            //  \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
            //  \A6 128K page size, page range is 0 (128K) or 0-1 (256K)
            //  \A6 Up to 9 pseudocolors assigned to 4 attributes
            /*
                'colors swap every half second!
                'using PALETTE does NOT swap color indexes
                '0 black-black
                '1 black-grey
                '2 black-white
                '3 grey-black
                '4 grey-grey
                '5 grey-white
                '6 white-black
                '7 white-grey
                '8 white-white
                '*IMPORTANT* QB sets initial values up different to default palette!
                '0 block-black(0)
                '1 grey-grey(4)
                '2 white-black(6)
                '3 white-white(8)
            */
            if (mode == 10) {
                i = imgnew(640, 350, 10);
                if ((prev_width_in_characters == 80) && (prev_height_in_characters == 43))
                    selectfont(8, &img[i]); // override default font
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];

            } // 10

            /*
                SCREEN 9: 640 x 350 graphics
                \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
                \A6 64K page size, page range is 0 (64K);
                128K page size, page range is 0 (128K) or 0-1 (256K)
                \A6 16 colors assigned to 4 attributes (64K adapter memory), or
                64 colors assigned to 16 attributes (more than 64K adapter memory)
            */
            if (mode == 9) {
                i = imgnew(640, 350, 9);
                if ((prev_width_in_characters == 80) && (prev_height_in_characters == 43))
                    selectfont(8, &img[i]); // override default font
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 9

            /*
                SCREEN 8: 640 x 200 graphics
                \A6 80 x 25 text format, 8 x 8 character box
                \A6 64K page size, page ranges are 0 (64K), 0-1 (128K), or 0-3 (246K)
                \A6 Assignment of 16 colors to any of 16 attributes
            */
            if (mode == 8) {
                i = imgnew(640, 200, 8);
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 8

            /*
                SCREEN 7: 320 x 200 graphics
                \A6 40 x 25 text format, character box size 8 x 8
                \A6 32K page size, page ranges are 0-1 (64K), 0-3 (128K), or 0-7 (256K)
                \A6 Assignment of 16 colors to any of 16 attributes
            */
            if (mode == 7) {
                i = imgnew(320, 200, 7);
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 7

            /*
                SCREEN 4:
                \A6 Supports Olivetti (R) Personal Computers models M24, M240, M28,
                M280, M380, M380/C, M380/T and AT&T (R) Personal Computers 6300
                series
                \A6 640 x 400 graphics
                \A6 80 x 25 text format, 8 x 16 character box
                \A6 1 of 16 colors assigned as the foreground color (selected by the
                COLOR statement); background is fixed at black.
            */
            // Note: QB64 will not support SCREEN 4

            /*
                SCREEN 3: Hercules adapter required, monochrome monitor only
                \A6 720 x 348 graphics
                \A6 80 x 25 text format, 9 x 14 character box
                \A6 2 screen pages (1 only if a second display adapter is installed)
                \A6 PALETTE statement not supported
            */
            // Note: QB64 will not support SCREEN 3

            /*
                SCREEN 2: 640 x 200 graphics
                \A6 80 x 25 text format with character box size of 8 x 8
                \A6 16 colors assigned to 2 attributes with EGA or VGA
            */
            if (mode == 2) {
                i = imgnew(640, 200, 2);
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 2

            /*
                SCREEN 1: 320 x 200 graphics
                \A6 40 x 25 text format, 8 x 8 character box
                \A6 16 background colors and one of two sets of 3 foreground colors assigned
                using COLOR statement with CGA
                \A6 16 colors assigned to 4 attributes with EGA or VGA
            */
            if (mode == 1) {
                i = imgnew(320, 200, 1);
                page[0] = i;
                img[i].flags |= IMG_SCREEN;
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 1

            /*
                MDPA, CGA, EGA, or VGA Adapter Boards
                SCREEN 0: Text mode only
                \A6 Either 40 x 25, 40 x 43, 40 x 50, 80 x 25, 80 x 43, or 80 x 50 text format
                with 8 x 8 character box size (8 x 14, 9 x 14, or 9 x 16 with EGA or VGA)
                \A6 16 colors assigned to 2 attributes
                \A6 16 colors assigned to any of 16 attributes (with CGA or EGA)
                \A6 64 colors assigned to any of 16 attributes (with EGA or VGA)
            */
            /*
                granularity from &HB800
                4096 in 80x25
                2048 in 40x25
                6880 in 80x43 (80x43x2=6880)
                3440 in 40x43 (40x43x2=3440)
                8000 in 80x50 (80x50x2=8000)
                4000 in 40x50 (40x50x2=4000)
            */
            if (mode == 0) {

                if ((sub_screen_width_in_characters != -1) && (sub_screen_height_in_characters != -1) && (sub_screen_font != -1)) {
                    x = sub_screen_width_in_characters;
                    y = sub_screen_height_in_characters;
                    f = sub_screen_font;
                    sub_screen_width_in_characters = -1;
                    sub_screen_height_in_characters = -1;
                    sub_screen_font = -1;
                    goto gotwidth;
                }
                if (sub_screen_width_in_characters != -1) {
                    x = sub_screen_width_in_characters;
                    sub_screen_width_in_characters = -1;
                    y = 25;
                    f = 16; // default
                    if (prev_height_in_characters == 43) {
                        y = 43;
                        f = 14;
                    }
                    if (prev_height_in_characters == 50) {
                        y = 50;
                        f = 8;
                    }
                    if (x == 40)
                        f++;
                    goto gotwidth;
                }
                if (sub_screen_height_in_characters != -1) {
                    y = sub_screen_height_in_characters;
                    sub_screen_height_in_characters = -1;
                    f = 16; // default
                    if (y == 43)
                        f = 14;
                    if (y == 50)
                        f = 8;
                    x = 80; // default
                    if (prev_width_in_characters == 40) {
                        f++;
                        x = 40;
                    }
                    goto gotwidth;
                }

                if ((prev_width_in_characters == 80) && (prev_height_in_characters == 50)) {
                    x = 80;
                    y = 50;
                    f = 8;
                    goto gotwidth;
                }
                if ((prev_width_in_characters == 40) && (prev_height_in_characters == 50)) {
                    x = 40;
                    y = 50;
                    f = 8 + 1;
                    goto gotwidth;
                }
                if ((prev_width_in_characters == 80) && (prev_height_in_characters == 43)) {
                    x = 80;
                    y = 43;
                    f = 8;
                    goto gotwidth;
                }
                if ((prev_width_in_characters == 40) && (prev_height_in_characters == 43)) {
                    x = 40;
                    y = 43;
                    f = 8 + 1;
                    goto gotwidth;
                }
                if ((prev_width_in_characters == 40) && (prev_height_in_characters == 25)) {
                    x = 40;
                    y = 25;
                    f = 16 + 1;
                    goto gotwidth;
                }
                x = 80;
                y = 25;
                f = 16;
            gotwidth:;
                i2 = x * y * 2; // default granularity
                // specific granularities which cannot be calculated
                if ((x == 40) && (y == 25) && (f = (16 + 1)))
                    i2 = 2048;
                if ((x == 80) && (y == 25) && (f = 16))
                    i2 = 4096;
                p = 65536 / i2; // number of pages to allocate in cmem
                if (p > 8)
                    p = 8; // limit cmem pages to 8
                // make sure 8 page indexes exist
                if (7 >= pages) {
                    i = 7 + 1;
                    page = (int32 *)realloc(page, i * 4);
                    memset(page + pages, 0, (i - pages) * 4);
                    pages = i;
                }
                for (i3 = 0; i3 < 8; i3++) {
                    if (i3 < p) {
                        i = imgframe(&cmem[753664 + i2 * i3], x, y, 0);
                    } else {
                        i = imgnew(x, y, 0);
                    }
                    selectfont(f, &img[i]);
                    img[i].flags |= IMG_SCREEN;
                    page[i3] = i;
                }
                // text-clear 64K after seg. &HB800
                for (i = 0; i < 65536; i += 2) {
                    cmem[753664 + i] = 32;
                    cmem[753664 + i + 1] = 7;
                } // init. 64K of memory after B800
                i = page[0];
                display_page_index = i;
                display_page = &img[i];
                write_page_index = i;
                write_page = &img[i];
                read_page_index = i;
                read_page = &img[i];
            } // 0

            write_page->draw_ta = 0.0;
            write_page->draw_scale = 1.0; // reset DRAW attributes (of write_page)

            last_active_page = 0;

            key_display_redraw = 1;
            key_update();

        } // setmode
    } // passed MODE

    // note: changing the active or visual page reselects the default colors!
    if (passed & 4) { // SCREEN ?,?,X,? (active_page)
        i = active_page;
        validatepage(i);
        i = page[i];
        if ((write_page_index != i) || (read_page_index != i)) {
            write_page_index = i;
            write_page = &img[i];
            read_page_index = i;
            read_page = &img[i];
            defaultcolors();
            // reset VIEW PRINT state
            write_page->top_row = 1;
            if (!write_page->text)
                write_page->bottom_row = (write_page->height / write_page->font);
            else
                write_page->bottom_row = write_page->height;
            if (write_page->cursor_y > write_page->bottom_row)
                write_page->cursor_y = 1;
            write_page->bottom_row--;
            if (write_page->bottom_row <= 0)
                write_page->bottom_row = 1;

            // active page migration
            // note: transfers any screen settings which are maintained during a QBASIC active page switch
            if (last_active_page != active_page) {
                static img_struct *old_page;
                old_page = &img[page[last_active_page]];
                // WINDOW settings
                /*
                    SCREEN 7
                    WINDOW (0, 0)-(1, 1)
                    SCREEN 7, , 1, 1
                    PSET (.5, .5), 14
                */
                // VIEW settings
                /*
                    SCREEN 7
                    VIEW SCREEN (50, 50)-(100, 100)
                    SCREEN 7, , 1, 1
                    LINE (0, 0)-(1000, 1000), 1, BF
                */
                // GRAPHICS CURSOR LOCATION
                //(proven)
                // NOT MAINTAINED:
                // X color settings (for both text and graphics)
                // X text cursor location
                // X draw color (reset, as in QBASIC, by defaultcolors())
                if (!write_page->text) {
                    memcpy(&write_page->apm_p1, &old_page->apm_p1, (uint32)(&write_page->apm_p2 - &write_page->apm_p1));
                }
                last_active_page = active_page;
            } // active page migration
        }

    } // passed&4

    if (passed & 8) { // SCREEN ?,?,?,X (visual_page)
        i = visual_page;
        validatepage(i);
        i = page[i];
        if (display_page_index != i) {
            display_page_index = i;
            display_page = &img[i];
            defaultcolors();
            // reset VIEW PRINT state
            write_page->top_row = 1;
            if (!write_page->text)
                write_page->bottom_row = (write_page->height / write_page->font);
            else
                write_page->bottom_row = write_page->height;
            if (write_page->cursor_y > write_page->bottom_row)
                write_page->cursor_y = 1;
            write_page->bottom_row--;
            if (write_page->bottom_row <= 0)
                write_page->bottom_row = 1;
        }
    } // passed&8

    if (autodisplay) {
        if (lock_display_required)
            lock_display = 0; // release lock
    }

    return;
error:
    error(5);
    return;
} // screen (end)

void sub_pcopy(int32 src, int32 dst) {
    if (is_error_pending())
        return;
    static img_struct *s, *d;
    // validate
    if (src >= 0) {
        validatepage(src);
        s = &img[page[src]];
    } else {
        src = -src;
        if (src >= nextimg)
            goto error;
        s = &img[src];
        if (!s->valid)
            goto error;
    }
    if (dst >= 0) {
        validatepage(dst);
        d = &img[page[dst]];
    } else {
        dst = -dst;
        if (dst >= nextimg)
            goto error;
        d = &img[dst];
        if (!d->valid)
            goto error;
    }
    if (s == d)
        return;
    if (s->bytes_per_pixel != d->bytes_per_pixel)
        goto error;
    if ((s->height != d->height) || (s->width != d->width))
        goto error;
    if (s->bytes_per_pixel == 1) {
        if (d->mask < s->mask)
            goto error; // cannot copy onto a palette image with less colors
    }
    memcpy(d->offset, s->offset, d->width * d->height * d->bytes_per_pixel);
    return;
error:

    error(5);
    return;
}

void qbsub_width(int32 option, int32 value1, int32 value2, int32 value3, int32 value4, int32 passed) {
    //[{#|LPRINT}][?],[?]
    static int32 i, i2;

    if (is_error_pending())
        return;

    if (option == 0) { // WIDTH [?][,?]

        width8050switch = 0;

        static uint32 col, col2;

        // used to restore scaling after simple font changes
        // QBASIC/4.5/7.1: PMAP uses old scaling values after WIDTH change
        static float window_x1, window_y1, window_x2, window_y2;

        // Specifics:
        // MODE 0: Changes the resolution based on the desired width
        //        Horizontal width of 1 to 40 uses a double width font
        //        Heights from 1 to 42 use font height 16 pixels
        //        Heights from 43 to 49 use font height 14 pixels
        //        Heights from 50 to ? use font height 8 pixels
        // MODES 1-13: The resolution IS NOT CHANGED
        //            The font is changed to a font usually available for that screen
        //            mode, if available, that fits the given dimensions EXACTLY
        //            If not possible, it may jump back to SCREEN 0 in some instances
        //            just as it did in QBASIC
        // 256/32 BIT MODES: The font is unchanged
        //                  The resolution is changed using the currently selected font
        // note:
        // COLOR selection is kept, all other values are lost (if staying in same "mode")
        static int32 f, f2, width, height;

        width = value1;
        height = value2;

#ifdef QB64_WINDOWS
        if (write_page->console) {
            SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
            HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
            HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
            CONSOLE_SCREEN_BUFFER_INFO cl_bufinfo;

            GetConsoleScreenBufferInfo(cl_conout, &cl_bufinfo); // get the screen buffer information, for blank entries

            if (width <= 0)
                width = cl_bufinfo.srWindow.Right - cl_bufinfo.srWindow.Left + 1;
            ; // if width is omitted, then use existing width
            if (height <= 0)
                height = cl_bufinfo.srWindow.Bottom - cl_bufinfo.srWindow.Top + 1;
            ; // if height is omitted, then use existing height
            if (value3 <= 0)
                value3 = cl_bufinfo.dwSize.X; // if bufferwidth is omitted, then use existing buffer width
            if (value4 <= 0)
                value4 = cl_bufinfo.dwSize.Y; // same as above, but for height
            if (value3 < value1)
                value3 = value1; // don't make the buffer width smaller than the console width itself
            if (value4 < value2)
                value4 = value2; // and don't make that buffer height smaller than the console height

            SMALL_RECT rect = {0, 0, (SHORT)(width - 1), (SHORT)(height - 1)};
            COORD bufferSize = {(SHORT)value3, (SHORT)value4};
            SetConsoleScreenBufferSize(hConsole, bufferSize); // set the buffer
            SetConsoleWindowInfo(hConsole, TRUE, &rect);      // set the console itself
            return;
        }
#endif

        if ((!(passed & 1)) && (!(passed & 2)))
            goto error; // cannot omit both arguments

        if ((write_page->compatible_mode == 32) || (write_page->compatible_mode == 256)) {

            if (!(passed & 1)) { // width omitted
                width = write_page->width;
            } else {
                if (width <= 0)
                    goto error;
                i = fontwidth[write_page->font];
                if (!i)
                    i = 1;
                width *= i;
            }

            if (!(passed & 2)) { // height omitted
                height = write_page->height;
            } else {
                if (height <= 0)
                    goto error;
                height *= fontheight[write_page->font];
            }

            // width & height are now the desired dimensions

            if ((width == write_page->width) && (height == write_page->height))
                return; // no change required

            if (write_page->flags & IMG_SCREEN) {
                // delete pages 1-?
                for (i = 1; i < pages; i++) {
                    if ((i2 = page[i])) {
                        if (display_page_index == i2) {
                            display_page_index = page[0];
                            display_page = &img[display_page_index];
                        }
                        if (read_page_index == i2) {
                            read_page_index = display_page_index;
                            read_page = display_page;
                        }
                        if (write_page_index == i2) {
                            write_page_index = display_page_index;
                            write_page = display_page;
                        }
                        // manual delete, freeing video pages is usually illegal
                        if (img[i2].flags & IMG_FREEMEM)
                            free(img[i2].offset); // free pixel data
                        freeimg(i2);
                    }
                } // i
            }

            if (autodisplay) {
                if (write_page->flags & IMG_SCREEN) {
                    if (lock_display_required) {
                        if (lock_display == 0)
                            lock_display = 1;
                        while (lock_display != 2) {
                            Sleep(0);
                        }
                    }
                }
            }

            col = write_page->color;
            col2 = write_page->background_color;
            f = write_page->font;
            // change resolution
            write_page->width = width;
            write_page->height = height;
            if (write_page->flags & IMG_FREEMEM) {
                free(write_page->offset); // free pixel data
                write_page->offset = (uint8 *)calloc(width * height * write_page->bytes_per_pixel, 1);
            } else { // frame?
                memset(write_page->offset, 0, width * height * write_page->bytes_per_pixel);
            }
            imgrevert(write_page_index);
            write_page->color = col;
            write_page->background_color = col2;
            selectfont(f, write_page);

            if (autodisplay) {
                if (write_page->flags & IMG_SCREEN) {
                    if (lock_display_required)
                        lock_display = 0; // release lock
                }
            }

            return;

        } // 32/256

        if (!(passed & 1)) { // width omitted
            if (height <= 0)
                goto error;

            if (!write_page->compatible_mode) { // 0
                f = 8;
                if (height <= 49)
                    f = 14;
                if (height <= 42)
                    f = 16;
                width = write_page->width;
                if (width <= 40)
                    f++;
                if ((write_page->font == f) && (write_page->height == height))
                    return; // no change
                sub_screen_height_in_characters = height;
                sub_screen_width_in_characters = width;
                sub_screen_font = f;
                qbg_screen(0, 0, 0, 0, 0, 1);
                return;
            } // 0

            if (((write_page->compatible_mode >= 1) && (write_page->compatible_mode <= 8)) || (write_page->compatible_mode == 13)) {
                if (write_page->height == height * 8) { // correct resolution
                    if (write_page->font == 8)
                        return; // correct font, no change required
                    if (write_page->flags & IMG_SCREEN) {
                        // delete pages 1-?
                        for (i = 1; i < pages; i++) {
                            if ((i2 = page[i])) {
                                if (display_page_index == i2) {
                                    display_page_index = page[0];
                                    display_page = &img[display_page_index];
                                }
                                if (read_page_index == i2) {
                                    read_page_index = display_page_index;
                                    read_page = display_page;
                                }
                                if (write_page_index == i2) {
                                    write_page_index = display_page_index;
                                    write_page = display_page;
                                }
                                // manual delete, freeing video pages is usually illegal
                                if (img[i2].flags & IMG_FREEMEM)
                                    free(img[i2].offset); // free pixel data
                                freeimg(i2);
                            }
                        } // i
                    }
                    col = write_page->color;
                    col2 = write_page->background_color;
                    imgrevert(write_page_index);
                    write_page->color = col;
                    write_page->background_color = col2;
                    selectfont(8, write_page);
                    return;
                } // correct resolution
                // fall through
            } // modes 1-8

            /*
                SCREEN 9: 640 x 350 graphics
                \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
                \A6 64K page size, page range is 0 (64K);
                128K page size, page range is 0 (128K) or 0-1 (256K)
                \A6 16 colors assigned to 4 attributes (64K adapter memory), or
                64 colors assigned to 16 attributes (more than 64K adapter memory)
                SCREEN 10: 640 x 350 graphics, monochrome monitor only
                \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
                \A6 128K page size, page range is 0 (128K) or 0-1 (256K)
                \A6 Up to 9 pseudocolors assigned to 4 attributes
            */
            if ((write_page->compatible_mode >= 9) && (write_page->compatible_mode <= 10)) {
                f = 0;
                if (write_page->height == height * 8)
                    f = 8;
                if (write_page->height == height * 14)
                    f = 14;
                if ((height == 43) && (write_page->height == 350))
                    f = 8; //?x350,8x8
                if (f) {   // correct resolution
                    if (write_page->font == f)
                        return; // correct font, no change required
                    if (write_page->flags & IMG_SCREEN) {
                        // delete pages 1-?
                        for (i = 1; i < pages; i++) {
                            if ((i2 = page[i])) {
                                if (display_page_index == i2) {
                                    display_page_index = page[0];
                                    display_page = &img[display_page_index];
                                }
                                if (read_page_index == i2) {
                                    read_page_index = display_page_index;
                                    read_page = display_page;
                                }
                                if (write_page_index == i2) {
                                    write_page_index = display_page_index;
                                    write_page = display_page;
                                }
                                // manual delete, freeing video pages is usually illegal
                                if (img[i2].flags & IMG_FREEMEM)
                                    free(img[i2].offset); // free pixel data
                                freeimg(i2);
                            }
                        } // i
                    }
                    col = write_page->color;
                    col2 = write_page->background_color;
                    window_x1 = write_page->window_x1;
                    window_x2 = write_page->window_x2;
                    window_y1 = write_page->window_y1;
                    window_y2 = write_page->window_y2;
                    imgrevert(write_page_index);
                    qbg_sub_window(window_x1, window_y1, window_x2, window_y2, 1 + 2);
                    write_page->clipping_or_scaling = 0;
                    write_page->color = col;
                    write_page->background_color = col2;
                    selectfont(f, write_page);
                    return;
                } // correct resolution
                // fall through
            } // modes 9,10

            if ((write_page->compatible_mode >= 11) && (write_page->compatible_mode <= 12)) {
                f = 0;
                if (write_page->height == height * 8)
                    f = 8;
                if (write_page->height == height * 16)
                    f = 16;
                if (f) { // correct resolution
                    if (write_page->font == f)
                        return; // correct font, no change required
                    if (write_page->flags & IMG_SCREEN) {
                        // delete pages 1-?
                        for (i = 1; i < pages; i++) {
                            if ((i2 = page[i])) {
                                if (display_page_index == i2) {
                                    display_page_index = page[0];
                                    display_page = &img[display_page_index];
                                }
                                if (read_page_index == i2) {
                                    read_page_index = display_page_index;
                                    read_page = display_page;
                                }
                                if (write_page_index == i2) {
                                    write_page_index = display_page_index;
                                    write_page = display_page;
                                }
                                // manual delete, freeing video pages is usually illegal
                                if (img[i2].flags & IMG_FREEMEM)
                                    free(img[i2].offset); // free pixel data
                                freeimg(i2);
                            }
                        } // i
                    }
                    col = write_page->color;
                    col2 = write_page->background_color;
                    window_x1 = write_page->window_x1;
                    window_x2 = write_page->window_x2;
                    window_y1 = write_page->window_y1;
                    window_y2 = write_page->window_y2;
                    imgrevert(write_page_index);
                    qbg_sub_window(window_x1, window_y1, window_x2, window_y2, 1 + 2);
                    write_page->clipping_or_scaling = 0;
                    write_page->color = col;
                    write_page->background_color = col2;
                    selectfont(f, write_page);
                    return;
                } // correct resolution
                // fall through
            } // modes 11,12

            // fall through:
            if ((height == 25) || (height == 50) || (height == 43)) {
                sub_screen_height_in_characters = height;
                qbg_screen(0, 0, 0, 0, 0, 1);
                return;
            }

            goto error;

        } // width omitted

        if (!(passed & 2)) { // height omitted

            if (width <= 0)
                goto error;

            if (!write_page->compatible_mode) { // 0
                height = write_page->height;
                f = 8;
                if (height <= 49)
                    f = 14;
                if (height <= 42)
                    f = 16;
                if (width <= 40)
                    f++;
                if ((write_page->font == f) && (write_page->width == width))
                    return; // no change
                sub_screen_height_in_characters = height;
                sub_screen_width_in_characters = width;
                sub_screen_font = f;
                qbg_screen(0, 0, 0, 0, 0, 1);
                return;
            } // 0

            if (((write_page->compatible_mode >= 1) && (write_page->compatible_mode <= 8)) || (write_page->compatible_mode == 13)) {
                if (write_page->width == width * 8) { // correct resolution
                    if (write_page->font == 8)
                        return; // correct font, no change required
                    if (write_page->flags & IMG_SCREEN) {
                        // delete pages 1-?
                        for (i = 1; i < pages; i++) {
                            if ((i2 = page[i])) {
                                if (display_page_index == i2) {
                                    display_page_index = page[0];
                                    display_page = &img[display_page_index];
                                }
                                if (read_page_index == i2) {
                                    read_page_index = display_page_index;
                                    read_page = display_page;
                                }
                                if (write_page_index == i2) {
                                    write_page_index = display_page_index;
                                    write_page = display_page;
                                }
                                // manual delete, freeing video pages is usually illegal
                                if (img[i2].flags & IMG_FREEMEM)
                                    free(img[i2].offset); // free pixel data
                                freeimg(i2);
                            }
                        } // i
                    }
                    col = write_page->color;
                    col2 = write_page->background_color;
                    imgrevert(write_page_index);
                    write_page->color = col;
                    write_page->background_color = col2;
                    selectfont(8, write_page);
                    return;
                } // correct resolution
                // fall through
            } // modes 1-8

            /*
                SCREEN 9: 640 x 350 graphics
                \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
                \A6 64K page size, page range is 0 (64K);
                128K page size, page range is 0 (128K) or 0-1 (256K)
                \A6 16 colors assigned to 4 attributes (64K adapter memory), or
                64 colors assigned to 16 attributes (more than 64K adapter memory)
                SCREEN 10: 640 x 350 graphics, monochrome monitor only
                \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
                \A6 128K page size, page range is 0 (128K) or 0-1 (256K)
                \A6 Up to 9 pseudocolors assigned to 4 attributes
            */
            if ((write_page->compatible_mode >= 9) && (write_page->compatible_mode <= 10)) {
                f = 0;
                if (write_page->width == width * 8)
                    f = 8;
                if (f) { // correct resolution
                    f2 = fontheight[write_page->font];
                    if (f2 > 8)
                        f = 14;
                    if (write_page->font == f)
                        return; // correct font, no change required
                    if (write_page->flags & IMG_SCREEN) {
                        // delete pages 1-?
                        for (i = 1; i < pages; i++) {
                            if ((i2 = page[i])) {
                                if (display_page_index == i2) {
                                    display_page_index = page[0];
                                    display_page = &img[display_page_index];
                                }
                                if (read_page_index == i2) {
                                    read_page_index = display_page_index;
                                    read_page = display_page;
                                }
                                if (write_page_index == i2) {
                                    write_page_index = display_page_index;
                                    write_page = display_page;
                                }
                                // manual delete, freeing video pages is usually illegal
                                if (img[i2].flags & IMG_FREEMEM)
                                    free(img[i2].offset); // free pixel data
                                freeimg(i2);
                            }
                        } // i
                    }
                    col = write_page->color;
                    col2 = write_page->background_color;
                    window_x1 = write_page->window_x1;
                    window_x2 = write_page->window_x2;
                    window_y1 = write_page->window_y1;
                    window_y2 = write_page->window_y2;
                    imgrevert(write_page_index);
                    qbg_sub_window(window_x1, window_y1, window_x2, window_y2, 1 + 2);
                    write_page->clipping_or_scaling = 0;
                    write_page->color = col;
                    write_page->background_color = col2;
                    selectfont(f, write_page);
                    return;
                } // correct resolution
                // fall through
            } // modes 9,10

            if ((write_page->compatible_mode >= 11) && (write_page->compatible_mode <= 12)) {
                f = 0;
                if (write_page->width == width * 8)
                    f = 8;
                if (f) { // correct resolution
                    f2 = fontheight[write_page->font];
                    if (f2 > 8)
                        f = 16;
                    if (write_page->font == f)
                        return; // correct font, no change required
                    if (write_page->flags & IMG_SCREEN) {
                        // delete pages 1-?
                        for (i = 1; i < pages; i++) {
                            if ((i2 = page[i])) {
                                if (display_page_index == i2) {
                                    display_page_index = page[0];
                                    display_page = &img[display_page_index];
                                }
                                if (read_page_index == i2) {
                                    read_page_index = display_page_index;
                                    read_page = display_page;
                                }
                                if (write_page_index == i2) {
                                    write_page_index = display_page_index;
                                    write_page = display_page;
                                }
                                // manual delete, freeing video pages is usually illegal
                                if (img[i2].flags & IMG_FREEMEM)
                                    free(img[i2].offset); // free pixel data
                                freeimg(i2);
                            }
                        } // i
                    }
                    col = write_page->color;
                    col2 = write_page->background_color;
                    window_x1 = write_page->window_x1;
                    window_x2 = write_page->window_x2;
                    window_y1 = write_page->window_y1;
                    window_y2 = write_page->window_y2;
                    imgrevert(write_page_index);
                    qbg_sub_window(window_x1, window_y1, window_x2, window_y2, 1 + 2);
                    write_page->clipping_or_scaling = 0;
                    write_page->color = col;
                    write_page->background_color = col2;
                    selectfont(f, write_page);
                    return;
                } // correct resolution
                // fall through
            } // modes 11,12

            // fall through:
            if ((width == 40) || (width == 80)) {
                sub_screen_width_in_characters = width;
                qbg_screen(0, 0, 0, 0, 0, 1);
                return;
            }

            goto error;

        } // height omitted

        // both height & width passed

        if ((width <= 0) || (height <= 0))
            goto error;

        if (!write_page->compatible_mode) { // 0
            f = 8;
            if (height <= 49)
                f = 14;
            if (height <= 42)
                f = 16;
            if (width <= 40)
                f++;
            if ((write_page->font == f) && (write_page->width == width) && (write_page->height == height))
                return; // no change
            sub_screen_height_in_characters = height;
            sub_screen_width_in_characters = width;
            sub_screen_font = f;
            qbg_screen(0, 0, 0, 0, 0, 1);
            return;
        } // 0

        if (((write_page->compatible_mode >= 1) && (write_page->compatible_mode <= 8)) || (write_page->compatible_mode == 13)) {
            if ((write_page->width == width * 8) && (write_page->height == height * 8)) { // correct resolution
                if (write_page->font == 8)
                    return; // correct font, no change required
                if (write_page->flags & IMG_SCREEN) {
                    // delete pages 1-?
                    for (i = 1; i < pages; i++) {
                        if ((i2 = page[i])) {
                            if (display_page_index == i2) {
                                display_page_index = page[0];
                                display_page = &img[display_page_index];
                            }
                            if (read_page_index == i2) {
                                read_page_index = display_page_index;
                                read_page = display_page;
                            }
                            if (write_page_index == i2) {
                                write_page_index = display_page_index;
                                write_page = display_page;
                            }
                            // manual delete, freeing video pages is usually illegal
                            if (img[i2].flags & IMG_FREEMEM)
                                free(img[i2].offset); // free pixel data
                            freeimg(i2);
                        }
                    } // i
                }
                col = write_page->color;
                col2 = write_page->background_color;
                imgrevert(write_page_index);
                write_page->color = col;
                write_page->background_color = col2;
                selectfont(8, write_page);
                return;
            } // correct resolution
            // fall through
        } // modes 1-8

        /*
            SCREEN 9: 640 x 350 graphics
            \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
            \A6 64K page size, page range is 0 (64K);
            128K page size, page range is 0 (128K) or 0-1 (256K)
            \A6 16 colors assigned to 4 attributes (64K adapter memory), or
            64 colors assigned to 16 attributes (more than 64K adapter memory)
            SCREEN 10: 640 x 350 graphics, monochrome monitor only
            \A6 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
            \A6 128K page size, page range is 0 (128K) or 0-1 (256K)
            \A6 Up to 9 pseudocolors assigned to 4 attributes
        */
        if ((write_page->compatible_mode >= 9) && (write_page->compatible_mode <= 10)) {
            f = 0;
            if (write_page->width == width * 8) {
                if (write_page->height == height * 8)
                    f = 8;
                if (write_page->height == height * 14)
                    f = 14;
                if ((height == 43) && (write_page->height == 350))
                    f = 8; //?x350,8x8
            }
            if (f) { // correct resolution
                if (write_page->font == f)
                    return; // correct font, no change required
                if (write_page->flags & IMG_SCREEN) {
                    // delete pages 1-?
                    for (i = 1; i < pages; i++) {
                        if ((i2 = page[i])) {
                            if (display_page_index == i2) {
                                display_page_index = page[0];
                                display_page = &img[display_page_index];
                            }
                            if (read_page_index == i2) {
                                read_page_index = display_page_index;
                                read_page = display_page;
                            }
                            if (write_page_index == i2) {
                                write_page_index = display_page_index;
                                write_page = display_page;
                            }
                            // manual delete, freeing video pages is usually illegal
                            if (img[i2].flags & IMG_FREEMEM)
                                free(img[i2].offset); // free pixel data
                            freeimg(i2);
                        }
                    } // i
                }
                col = write_page->color;
                col2 = write_page->background_color;
                window_x1 = write_page->window_x1;
                window_x2 = write_page->window_x2;
                window_y1 = write_page->window_y1;
                window_y2 = write_page->window_y2;
                imgrevert(write_page_index);
                qbg_sub_window(window_x1, window_y1, window_x2, window_y2, 1 + 2);
                write_page->clipping_or_scaling = 0;
                write_page->color = col;
                write_page->background_color = col2;
                selectfont(f, write_page);
                return;
            } // correct resolution
            // fall through
        } // modes 9,10

        if ((write_page->compatible_mode >= 11) && (write_page->compatible_mode <= 12)) {
            f = 0;
            if (write_page->width == width * 8) {

                if (write_page->height == height * 8)
                    f = 8;

                if (write_page->height == height * 16)
                    f = 16;
            }
            if (f) { // correct resolution
                if (write_page->font == f)
                    return; // correct font, no change required
                if (write_page->flags & IMG_SCREEN) {
                    // delete pages 1-?
                    for (i = 1; i < pages; i++) {
                        if ((i2 = page[i])) {
                            if (display_page_index == i2) {
                                display_page_index = page[0];
                                display_page = &img[display_page_index];
                            }
                            if (read_page_index == i2) {
                                read_page_index = display_page_index;
                                read_page = display_page;
                            }
                            if (write_page_index == i2) {
                                write_page_index = display_page_index;
                                write_page = display_page;
                            }
                            // manual delete, freeing video pages is usually illegal
                            if (img[i2].flags & IMG_FREEMEM)
                                free(img[i2].offset); // free pixel data
                            freeimg(i2);
                        }
                    } // i
                }
                col = write_page->color;
                col2 = write_page->background_color;
                window_x1 = write_page->window_x1;
                window_x2 = write_page->window_x2;
                window_y1 = write_page->window_y1;
                window_y2 = write_page->window_y2;
                imgrevert(write_page_index);
                qbg_sub_window(window_x1, window_y1, window_x2, window_y2, 1 + 2);
                write_page->clipping_or_scaling = 0;
                write_page->color = col;
                write_page->background_color = col2;
                selectfont(f, write_page);
                return;
            } // correct resolution
            // fall through
        } // modes 11,12

        // fall through:
        if ((width == 40) || (width == 80)) {
            if ((height == 25) || (height == 50) || (height == 43)) {
                sub_screen_width_in_characters = width;
                sub_screen_height_in_characters = height;
                f = 16;
                if (height == 43)
                    f = 14;
                if (height == 50)
                    f = 8;
                if (width == 40)
                    f++;
                sub_screen_font = f;
                qbg_screen(0, 0, 0, 0, 0, 1);
                return;
            }

            goto error;

        } // WIDTH [?][,?]

    } // option==0

    if (option == 2) { // LPRINT
        if (passed != 1)
            goto error;
        if ((value1 < 1) || (value1 > 255))
            goto error;
        width_lprint = value1;
        return;
    } // option==2

    // file/device?
    //...

error:
    error(5);
    return;
}

void pset_and_clip(int32 x, int32 y, uint32 col) {

    if ((x >= write_page->view_x1) && (x <= write_page->view_x2) && (y >= write_page->view_y1) && (y <= write_page->view_y2)) {

        static uint8 *cp;
        static uint32 *o32;
        static uint32 destcol;
        if (write_page->bytes_per_pixel == 1) {
            write_page->offset[y * write_page->width + x] = col & write_page->mask;
            return;
        } else {

            if (write_page->alpha_disabled) {
                write_page->offset32[y * write_page->width + x] = col;
                return;
            }
            switch (col & 0xFF000000) {
            case 0xFF000000: // 100% alpha, so regular pset (fast)
                write_page->offset32[y * write_page->width + x] = col;
                return;
                break;
            case 0x0: // 0%(0) alpha, so no pset (very fast)
                return;
                break;
            case 0x80000000: //~50% alpha (optimized)
                o32 = write_page->offset32 + (y * write_page->width + x);
                *o32 = (((*o32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend128[*o32 >> 24] << 24);
                return;
                break;
            case 0x7F000000: //~50% alpha (optimized)
                o32 = write_page->offset32 + (y * write_page->width + x);
                *o32 = (((*o32 & 0xFEFEFE) + (col & 0xFEFEFE)) >> 1) + (ablend127[*o32 >> 24] << 24);
                return;
                break;
            default: // other alpha values (uses a lookup table)
                o32 = write_page->offset32 + (y * write_page->width + x);
                destcol = *o32;
                cp = cblend + (col >> 24 << 16);
                *o32 = cp[(col << 8 & 0xFF00) + (destcol & 255)] + (cp[(col & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                       (cp[(col >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(col >> 24) + (destcol >> 16 & 0xFF00)] << 24);
            };
        }

    } // within viewport
    return;
}

void qb32_boxfill(float x1f, float y1f, float x2f, float y2f, uint32 col) {
    static int32 x1, y1, x2, y2, i, width, img_width, x, y, d_width, a, a2, v1, v2, v3;
    static uint8 *p, *cp, *cp2, *cp3;
    static uint32 *lp, *lp_last, *lp_first;
    static uint32 *doff32, destcol;

    // resolve coordinates
    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            x1 = qbr_float_to_long(x1f * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            y1 = qbr_float_to_long(y1f * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
            x2 = qbr_float_to_long(x2f * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            y2 = qbr_float_to_long(y2f * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            x1 = qbr_float_to_long(x1f) + write_page->view_offset_x;
            y1 = qbr_float_to_long(y1f) + write_page->view_offset_y;
            x2 = qbr_float_to_long(x2f) + write_page->view_offset_x;
            y2 = qbr_float_to_long(y2f) + write_page->view_offset_y;
        }
    } else {
        x1 = qbr_float_to_long(x1f);
        y1 = qbr_float_to_long(y1f);
        x2 = qbr_float_to_long(x2f);
        y2 = qbr_float_to_long(y2f);
    }

    // swap coordinates (if necessary)
    if (x1 > x2) {
        i = x1;
        x1 = x2;
        x2 = i;
    }
    if (y1 > y2) {
        i = y1;
        y1 = y2;
        y2 = i;
    }

    // exit without rendering if necessary
    if (x2 < write_page->view_x1)
        return;
    if (x1 > write_page->view_x2)
        return;
    if (y2 < write_page->view_y1)
        return;
    if (y1 > write_page->view_y2)
        return;

    // crop coordinates
    if (x1 < write_page->view_x1)
        x1 = write_page->view_x1;
    if (y1 < write_page->view_y1)
        y1 = write_page->view_y1;
    if (x1 > write_page->view_x2)
        x1 = write_page->view_x2;
    if (y1 > write_page->view_y2)
        y1 = write_page->view_y2;
    if (x2 < write_page->view_x1)
        x2 = write_page->view_x1;
    if (y2 < write_page->view_y1)
        y2 = write_page->view_y1;
    if (x2 > write_page->view_x2)
        x2 = write_page->view_x2;
    if (y2 > write_page->view_y2)
        y2 = write_page->view_y2;

    if (write_page->bytes_per_pixel == 1) {
        col &= write_page->mask;
        width = x2 - x1 + 1;
        img_width = write_page->width;
        p = write_page->offset + y1 * write_page->width + x1;
        i = y2 - y1 + 1;
    loop:
        memset(p, col, width);
        p += img_width;
        if (--i)
            goto loop;
        return;
    } // 1

    // assume 32-bit
    // optimized
    // alpha disabled or full alpha?
    a = col >> 24;
    if ((write_page->alpha_disabled) || (a == 255)) {
        width = x2 - x1 + 1;
        y = y2 - y1 + 1;
        img_width = write_page->width;
        // build first line pixel by pixel
        lp_first = write_page->offset32 + y1 * img_width + x1;
        lp = lp_first - 1;
        lp_last = lp + width;
        while (lp++ < lp_last)
            *lp = col;
        // copy remaining lines
        lp = lp_first;
        width *= 4;
        while (y--) {
            memcpy(lp, lp_first, width);
            lp += img_width;
        }
        return;
    }
    // no alpha?
    if (!a)
        return;
    // half alpha?
    img_width = write_page->width;
    doff32 = write_page->offset32 + y1 * img_width + x1;
    width = x2 - x1 + 1;
    d_width = img_width - width;
    if (a == 128) {
        col &= 0xFEFEFE;
        y = y2 - y1 + 1;
        while (y--) {
            x = width;
            while (x--) {
                *doff32++ = (((*doff32 & 0xFEFEFE) + col) >> 1) + (ablend128[*doff32 >> 24] << 24);
            }
            doff32 += d_width;
        }
        return;
    }
    if (a == 127) {
        col &= 0xFEFEFE;
        y = y2 - y1 + 1;
        while (y--) {
            x = width;
            while (x--) {
                *doff32++ = (((*doff32 & 0xFEFEFE) + col) >> 1) + (ablend127[*doff32 >> 24] << 24);
            }
            doff32 += d_width;
        }
        return;
    }
    // ranged alpha
    cp = cblend + (a << 16);
    a2 = a << 8;
    cp3 = cp + (col >> 8 & 0xFF00);
    cp2 = cp + (col & 0xFF00);
    cp += (col << 8 & 0xFF00);
    y = y2 - y1 + 1;
    while (y--) {
        x = width;
        while (x--) {
            destcol = *doff32;
            *doff32++ = cp[destcol & 255] + (cp2[destcol >> 8 & 255] << 8) + (cp3[destcol >> 16 & 255] << 16) + (ablend[(destcol >> 24) + a2] << 24);
        }
        doff32 += d_width;
    }
    return;
}

void fast_boxfill(int32 x1, int32 y1, int32 x2, int32 y2, uint32 col) {
    // assumes:
    // actual coordinates passed
    // left->right, top->bottom order
    // on-screen
    static int32 i, width, img_width, x, y, d_width, a, a2, v1, v2, v3;
    static uint8 *p, *cp, *cp2, *cp3;
    static uint32 *lp, *lp_last, *lp_first;
    static uint32 *doff32, destcol;

    if (write_page->bytes_per_pixel == 1) {
        col &= write_page->mask;
        width = x2 - x1 + 1;
        img_width = write_page->width;
        p = write_page->offset + y1 * write_page->width + x1;
        i = y2 - y1 + 1;
    loop:
        memset(p, col, width);
        p += img_width;
        if (--i)
            goto loop;
        return;
    } // 1

    // assume 32-bit
    // optimized
    // alpha disabled or full alpha?
    a = col >> 24;
    if ((write_page->alpha_disabled) || (a == 255)) {

        width = x2 - x1 + 1;
        y = y2 - y1 + 1;
        img_width = write_page->width;
        // build first line pixel by pixel
        lp_first = write_page->offset32 + y1 * img_width + x1;
        lp = lp_first - 1;
        lp_last = lp + width;
        while (lp++ < lp_last)
            *lp = col;
        // copy remaining lines
        lp = lp_first;
        width *= 4;
        while (y--) {
            memcpy(lp, lp_first, width);
            lp += img_width;
        }
        return;
    }
    // no alpha?
    if (!a)
        return;
    // half alpha?
    img_width = write_page->width;
    doff32 = write_page->offset32 + y1 * img_width + x1;
    width = x2 - x1 + 1;
    d_width = img_width - width;
    if (a == 128) {
        col &= 0xFEFEFE;
        y = y2 - y1 + 1;
        while (y--) {
            x = width;
            while (x--) {
                *doff32++ = (((*doff32 & 0xFEFEFE) + col) >> 1) + (ablend128[*doff32 >> 24] << 24);
            }
            doff32 += d_width;
        }
        return;
    }
    if (a == 127) {
        col &= 0xFEFEFE;
        y = y2 - y1 + 1;
        while (y--) {
            x = width;
            while (x--) {
                *doff32++ = (((*doff32 & 0xFEFEFE) + col) >> 1) + (ablend127[*doff32 >> 24] << 24);
            }
            doff32 += d_width;
        }
        return;
    }
    // ranged alpha
    cp = cblend + (a << 16);
    a2 = a << 8;
    cp3 = cp + (col >> 8 & 0xFF00);
    cp2 = cp + (col & 0xFF00);
    cp += (col << 8 & 0xFF00);
    y = y2 - y1 + 1;
    while (y--) {
        x = width;
        while (x--) {
            destcol = *doff32;
            *doff32++ = cp[destcol & 255] + (cp2[destcol >> 8 & 255] << 8) + (cp3[destcol >> 16 & 255] << 16) + (ablend[(destcol >> 24) + a2] << 24);
        }
        doff32 += d_width;
    }
    return;
}

// copied from qb32_line with the following modifications
// i. pre-WINDOW'd & VIEWPORT'd int32 co-ordinates
// ii. all references to style & lineclip_skippixels commented
// iii. declaration of x1,y1,x2,y2,x1f,y1f changed, some declarations removed
void fast_line(int32 x1, int32 y1, int32 x2, int32 y2, uint32 col) {
    static int32 l, l2, mi;
    static float m, x1f, y1f;

    lineclip(x1, y1, x2, y2, write_page->view_x1, write_page->view_y1, write_page->view_x2, write_page->view_y2);

    // style=(style&65535)+(style<<16);
    // lineclip_skippixels&=15;
    // style=_lrotl(style,lineclip_skippixels);

    if (lineclip_draw) {
        l = abs(lineclip_x1 - lineclip_x2);
        l2 = abs(lineclip_y1 - lineclip_y2);
        if (l > l2) {

            // x-axis distance is larger
            y1f = lineclip_y1;
            if (l) { // following only applies if drawing more than one pixel
                m = ((float)lineclip_y2 - (float)lineclip_y1) / (float)l;
                if (lineclip_x2 >= lineclip_x1)
                    mi = 1;
                else
                    mi = -1; // direction of change
            }
            l++;
            while (l--) {
                if (y1f < 0)
                    lineclip_y1 = y1f - 0.5f;
                else
                    lineclip_y1 = y1f + 0.5f;

                // if ((style=_lrotl(style,1))&1){
                pset(lineclip_x1, lineclip_y1, col);
                //}

                lineclip_x1 += mi;
                y1f += m;
            }

        } else {

            // y-axis distance is larger
            x1f = lineclip_x1;
            if (l2) { // following only applies if drawing more than one pixel
                m = ((float)lineclip_x2 - (float)lineclip_x1) / (float)l2;
                if (lineclip_y2 >= lineclip_y1)
                    mi = 1;
                else
                    mi = -1; // direction of change
            }
            l2++;
            while (l2--) {
                if (x1f < 0)
                    lineclip_x1 = x1f - 0.5f;
                else
                    lineclip_x1 = x1f + 0.5f;
                // if ((style=_lrotl(style,1))&1){
                pset(lineclip_x1, lineclip_y1, col);
                //}
                lineclip_y1 += mi;
                x1f += m;
            }
        }

    } // lineclip_draw
    return;
}

void qb32_line(float x1f, float y1f, float x2f, float y2f, uint32 col, uint32 style) {
    static int32 x1, y1, x2, y2, l, l2, mi;
    static float m;

    // resolve coordinates
    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            x1 = qbr_float_to_long(x1f * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            y1 = qbr_float_to_long(y1f * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
            x2 = qbr_float_to_long(x2f * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            y2 = qbr_float_to_long(y2f * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            x1 = qbr_float_to_long(x1f) + write_page->view_offset_x;
            y1 = qbr_float_to_long(y1f) + write_page->view_offset_y;
            x2 = qbr_float_to_long(x2f) + write_page->view_offset_x;
            y2 = qbr_float_to_long(y2f) + write_page->view_offset_y;
        }
    } else {
        x1 = qbr_float_to_long(x1f);
        y1 = qbr_float_to_long(y1f);
        x2 = qbr_float_to_long(x2f);
        y2 = qbr_float_to_long(y2f);
    }

    lineclip(x1, y1, x2, y2, write_page->view_x1, write_page->view_y1, write_page->view_x2, write_page->view_y2);

    style = (style & 65535) + (style << 16);
    lineclip_skippixels &= 15;
    style = rotateLeft(style, lineclip_skippixels);

    if (lineclip_draw) {
        l = abs(lineclip_x1 - lineclip_x2);
        l2 = abs(lineclip_y1 - lineclip_y2);
        if (l > l2) {

            // x-axis distance is larger
            y1f = lineclip_y1;
            if (l) { // following only applies if drawing more than one pixel
                m = ((float)lineclip_y2 - (float)lineclip_y1) / (float)l;
                if (lineclip_x2 >= lineclip_x1)
                    mi = 1;
                else
                    mi = -1; // direction of change
            }
            l++;
            while (l--) {
                if (y1f < 0)
                    lineclip_y1 = y1f - 0.5f;
                else
                    lineclip_y1 = y1f + 0.5f;

                if ((style = rotateLeft(style, 1)) & 1) {
                    pset(lineclip_x1, lineclip_y1, col);
                }

                lineclip_x1 += mi;
                y1f += m;
            }

        } else {

            // y-axis distance is larger
            x1f = lineclip_x1;
            if (l2) { // following only applies if drawing more than one pixel
                m = ((float)lineclip_x2 - (float)lineclip_x1) / (float)l2;
                if (lineclip_y2 >= lineclip_y1)
                    mi = 1;
                else
                    mi = -1; // direction of change
            }
            l2++;
            while (l2--) {
                if (x1f < 0)
                    lineclip_x1 = x1f - 0.5f;
                else
                    lineclip_x1 = x1f + 0.5f;
                if ((style = rotateLeft(style, 1)) & 1) {
                    pset(lineclip_x1, lineclip_y1, col);
                }
                lineclip_y1 += mi;
                x1f += m;
            }
        }

    } // lineclip_draw
    return;
}

void sub_line(float x1, float y1, float x2, float y2, uint32 col, int32 bf, uint32 style, int32 passed) {
    if (is_error_pending())
        return;
    if (write_page->text) {
        error(5);
        return;
    }
    /*
        '"[[{STEP}](?,?)]-[{STEP}](?,?)[,[?][,[{B|BF}][,?]]]"
        LINE -(10, 10) 'flags: 0
        LINE (0, 0)-(10, 10) 'flags: 1
        LINE -STEP(10, 10) 'flags: 2
        LINE STEP(0, 0)-(10, 10) 'flags: 1+4
    */

    // adjust coordinates and qb graphics cursor position based on STEP
    if (passed & 1) {
        if (passed & 4) {
            x1 = write_page->x + x1;
            y1 = write_page->y + y1;
        }
        write_page->x = x1;
        write_page->y = y1;
    } else {
        x1 = write_page->x;
        y1 = write_page->y;
    }
    if (passed & 2) {
        x2 = write_page->x + x2;
        y2 = write_page->y + y2;
    }
    write_page->x = x2;
    write_page->y = y2;

    if (bf == 0) { // line
        if ((passed & 16) == 0)
            style = 0xFFFF;
        if ((passed & 8) == 0)
            col = write_page->color;
        write_page->draw_color = col;
        qb32_line(x1, y1, x2, y2, col, style);
        return;
    }

    if (bf == 1) { // rectangle
        if ((passed & 16) == 0)
            style = 0xFFFF;
        if ((passed & 8) == 0)
            col = write_page->color;
        write_page->draw_color = col;
        qb32_line(x1, y1, x2, y1, col, style);
        qb32_line(x2, y1, x2, y2, col, style);
        qb32_line(x2, y2, x1, y2, col, style);
        qb32_line(x1, y2, x1, y1, col, style);
        return;
    }

    if (bf == 2) { // filled box
        if ((passed & 8) == 0)
            col = write_page->color;
        write_page->draw_color = col;
        qb32_boxfill(x1, y1, x2, y2, col);
        return;
    }

} // sub_line

// 3 paint routines exist for color (not textured) filling
// i) 8-bit
// ii) 32-bit no-alpha
// iii) 32-bit
// simple comparisons are used, the alpha value is part of that comparison in all cases
// even if blending is disabled (a fixed color is likely to have a fixed alpha value anyway),
// and this allows for filling alpha regions

// 32-bit WITH BENDING
void sub_paint32(float x, float y, uint32 fillcol, uint32 bordercol, int32 passed) {

    // uses 2 buffers, a and b, and swaps between them for reading and creating
    static uint32 a_n = 0;
    static uint16 *a_x = (uint16 *)malloc(2 * 65536), *a_y = (uint16 *)malloc(2 * 65536);
    static uint8 *a_t = (uint8 *)malloc(65536);
    static uint32 b_n = 0;
    static uint16 *b_x = (uint16 *)malloc(2 * 65536), *b_y = (uint16 *)malloc(2 * 65536);
    static uint8 *b_t = (uint8 *)malloc(65536);
    static uint8 *done = (uint8 *)calloc(640 * 480, 1);
    static int32 ix, iy, i, t, x2, y2;
    static uint32 offset;
    static uint8 *cp;
    static uint16 *sp;
    // overrides
    static int32 done_size = 640 * 480;
    static uint32 *qbg_active_page_offset;                                      // override
    static int32 qbg_width, qbg_view_x1, qbg_view_y1, qbg_view_x2, qbg_view_y2; // override
    static uint32 *doff32, destcol;

    if ((passed & 2) == 0)
        fillcol = write_page->color;
    if ((passed & 4) == 0)
        bordercol = fillcol;
    write_page->draw_color = fillcol;

    if (passed & 1) {
        write_page->x += x;
        write_page->y += y;
    } else {
        write_page->x = x;
        write_page->y = y;
    }

    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            ix = qbr_float_to_long(write_page->x * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            ix = qbr_float_to_long(write_page->x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y) + write_page->view_offset_y;
        }
    } else {
        ix = qbr_float_to_long(write_page->x);
        iy = qbr_float_to_long(write_page->y);
    }

    // return if offscreen
    if ((ix < write_page->view_x1) || (iy < write_page->view_y1) || (ix > write_page->view_x2) || (iy > write_page->view_y2)) {
        return;
    }

    // overrides
    qbg_active_page_offset = write_page->offset32;
    qbg_width = write_page->width;
    qbg_view_x1 = write_page->view_x1;
    qbg_view_y1 = write_page->view_y1;
    qbg_view_x2 = write_page->view_x2;
    qbg_view_y2 = write_page->view_y2;
    i = write_page->width * write_page->height;
    if (i > done_size) {
        free(done);
        done = (uint8 *)calloc(i, 1);
    }

    // return if first point is the bordercolor
    if (qbg_active_page_offset[iy * qbg_width + ix] == bordercol)
        return;

    // create first node
    a_x[0] = ix;
    a_y[0] = iy;
    a_t[0] = 15;
    // types:
    //&1=check left
    //&2=check right
    //&4=check above
    //&8=check below

    a_n = 1;
    // qbg_active_page_offset[iy*qbg_width+ix]=fillcol;
    offset = iy * qbg_width + ix;
    //--------plot pixel--------
    doff32 = qbg_active_page_offset + offset;
    switch (fillcol & 0xFF000000) {
    case 0xFF000000:
        *doff32 = fillcol;
        break;
    case 0x0:
        // doff32;
        break;
    case 0x80000000:
        *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
        break;
    case 0x7F000000:
        *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
        break;
    default:
        destcol = *doff32;
        cp = cblend + (fillcol >> 24 << 16);
        *doff32 = cp[(fillcol << 8 & 0xFF00) + (destcol & 255)] + (cp[(fillcol & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                  (cp[(fillcol >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(fillcol >> 24) + (destcol >> 16 & 0xFF00)] << 24);
    }; // switch
    //--------done plot pixel--------
    done[iy * qbg_width + ix] = 1;

nextpass:
    b_n = 0;
    for (i = 0; i < a_n; i++) {
        t = a_t[i];
        ix = a_x[i];
        iy = a_y[i];

        // left
        if (t & 1) {
            x2 = ix - 1;
            y2 = iy;
            if (x2 >= qbg_view_x1) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        //--------plot pixel--------
                        doff32 = qbg_active_page_offset + offset;
                        switch (fillcol & 0xFF000000) {
                        case 0xFF000000:
                            *doff32 = fillcol;
                            break;
                        case 0x0:
                            // doff32;
                            break;
                        case 0x80000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
                            break;
                        case 0x7F000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
                            break;
                        default:
                            destcol = *doff32;
                            cp = cblend + (fillcol >> 24 << 16);
                            *doff32 = cp[(fillcol << 8 & 0xFF00) + (destcol & 255)] + (cp[(fillcol & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                                      (cp[(fillcol >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(fillcol >> 24) + (destcol >> 16 & 0xFF00)] << 24);
                        }; // switch
                        //--------done plot pixel--------
                        b_t[b_n] = 13;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // right
        if (t & 2) {
            x2 = ix + 1;
            y2 = iy;
            if (x2 <= qbg_view_x2) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        //--------plot pixel--------
                        doff32 = qbg_active_page_offset + offset;
                        switch (fillcol & 0xFF000000) {
                        case 0xFF000000:
                            *doff32 = fillcol;
                            break;
                        case 0x0:
                            // doff32;
                            break;
                        case 0x80000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
                            break;
                        case 0x7F000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
                            break;
                        default:
                            destcol = *doff32;
                            cp = cblend + (fillcol >> 24 << 16);
                            *doff32 = cp[(fillcol << 8 & 0xFF00) + (destcol & 255)] + (cp[(fillcol & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                                      (cp[(fillcol >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(fillcol >> 24) + (destcol >> 16 & 0xFF00)] << 24);
                        }; // switch
                        //--------done plot pixel--------
                        b_t[b_n] = 14;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // above
        if (t & 4) {
            x2 = ix;
            y2 = iy - 1;
            if (y2 >= qbg_view_y1) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        //--------plot pixel--------
                        doff32 = qbg_active_page_offset + offset;
                        switch (fillcol & 0xFF000000) {
                        case 0xFF000000:
                            *doff32 = fillcol;
                            break;
                        case 0x0:
                            // doff32;
                            break;
                        case 0x80000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
                            break;
                        case 0x7F000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
                            break;
                        default:
                            destcol = *doff32;
                            cp = cblend + (fillcol >> 24 << 16);
                            *doff32 = cp[(fillcol << 8 & 0xFF00) + (destcol & 255)] + (cp[(fillcol & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                                      (cp[(fillcol >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(fillcol >> 24) + (destcol >> 16 & 0xFF00)] << 24);
                        }; // switch
                        //--------done plot pixel--------
                        b_t[b_n] = 7;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // below
        if (t & 8) {
            x2 = ix;
            y2 = iy + 1;
            if (y2 <= qbg_view_y2) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        //--------plot pixel--------
                        doff32 = qbg_active_page_offset + offset;
                        switch (fillcol & 0xFF000000) {
                        case 0xFF000000:
                            *doff32 = fillcol;
                            break;
                        case 0x0:
                            // doff32;
                            break;
                        case 0x80000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend128[*doff32 >> 24] << 24);
                            break;
                        case 0x7F000000:
                            *doff32 = (((*doff32 & 0xFEFEFE) + (fillcol & 0xFEFEFE)) >> 1) + (ablend127[*doff32 >> 24] << 24);
                            break;
                        default:
                            destcol = *doff32;
                            cp = cblend + (fillcol >> 24 << 16);
                            *doff32 = cp[(fillcol << 8 & 0xFF00) + (destcol & 255)] + (cp[(fillcol & 0xFF00) + (destcol >> 8 & 255)] << 8) +
                                      (cp[(fillcol >> 8 & 0xFF00) + (destcol >> 16 & 255)] << 16) + (ablend[(fillcol >> 24) + (destcol >> 16 & 0xFF00)] << 24);
                        }; // switch
                        //--------done plot pixel--------
                        b_t[b_n] = 11;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

    } // i

    // no new nodes?
    if (b_n == 0) {
        memset(done, 0, write_page->width * write_page->height); // cleanup
        return;                                                  // finished!
    }

    // swap a & b arrays
    sp = a_x;
    a_x = b_x;
    b_x = sp;
    sp = a_y;
    a_y = b_y;
    b_y = sp;
    cp = a_t;
    a_t = b_t;
    b_t = cp;
    a_n = b_n;

    goto nextpass;
}

// 32-bit NO ALPHA BENDING
void sub_paint32x(float x, float y, uint32 fillcol, uint32 bordercol, int32 passed) {

    // uses 2 buffers, a and b, and swaps between them for reading and creating
    static uint32 a_n = 0;
    static uint16 *a_x = (uint16 *)malloc(2 * 65536), *a_y = (uint16 *)malloc(2 * 65536);
    static uint8 *a_t = (uint8 *)malloc(65536);
    static uint32 b_n = 0;
    static uint16 *b_x = (uint16 *)malloc(2 * 65536), *b_y = (uint16 *)malloc(2 * 65536);
    static uint8 *b_t = (uint8 *)malloc(65536);
    static uint8 *done = (uint8 *)calloc(640 * 480, 1);
    static int32 ix, iy, i, t, x2, y2;
    static uint32 offset;
    static uint8 *cp;
    static uint16 *sp;
    // overrides
    static int32 done_size = 640 * 480;
    static uint32 *qbg_active_page_offset;                                      // override
    static int32 qbg_width, qbg_view_x1, qbg_view_y1, qbg_view_x2, qbg_view_y2; // override

    if ((passed & 2) == 0)
        fillcol = write_page->color;
    if ((passed & 4) == 0)
        bordercol = fillcol;
    write_page->draw_color = fillcol;

    if (passed & 1) {
        write_page->x += x;
        write_page->y += y;
    } else {
        write_page->x = x;
        write_page->y = y;
    }

    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            ix = qbr_float_to_long(write_page->x * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            ix = qbr_float_to_long(write_page->x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y) + write_page->view_offset_y;
        }
    } else {
        ix = qbr_float_to_long(write_page->x);
        iy = qbr_float_to_long(write_page->y);
    }

    // return if offscreen
    if ((ix < write_page->view_x1) || (iy < write_page->view_y1) || (ix > write_page->view_x2) || (iy > write_page->view_y2)) {
        return;
    }

    // overrides
    qbg_active_page_offset = write_page->offset32;
    qbg_width = write_page->width;
    qbg_view_x1 = write_page->view_x1;
    qbg_view_y1 = write_page->view_y1;
    qbg_view_x2 = write_page->view_x2;
    qbg_view_y2 = write_page->view_y2;
    i = write_page->width * write_page->height;
    if (i > done_size) {
        free(done);
        done = (uint8 *)calloc(i, 1);
    }

    // return if first point is the bordercolor
    if (qbg_active_page_offset[iy * qbg_width + ix] == bordercol)
        return;

    // create first node
    a_x[0] = ix;
    a_y[0] = iy;
    a_t[0] = 15;
    // types:
    //&1=check left
    //&2=check right
    //&4=check above
    //&8=check below

    a_n = 1;
    qbg_active_page_offset[iy * qbg_width + ix] = fillcol;
    done[iy * qbg_width + ix] = 1;

nextpass:
    b_n = 0;
    for (i = 0; i < a_n; i++) {
        t = a_t[i];
        ix = a_x[i];
        iy = a_y[i];

        // left
        if (t & 1) {
            x2 = ix - 1;
            y2 = iy;
            if (x2 >= qbg_view_x1) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 13;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // right
        if (t & 2) {
            x2 = ix + 1;
            y2 = iy;
            if (x2 <= qbg_view_x2) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 14;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // above
        if (t & 4) {
            x2 = ix;
            y2 = iy - 1;
            if (y2 >= qbg_view_y1) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 7;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // below
        if (t & 8) {
            x2 = ix;
            y2 = iy + 1;
            if (y2 <= qbg_view_y2) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 11;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

    } // i

    // no new nodes?
    if (b_n == 0) {
        memset(done, 0, write_page->width * write_page->height); // cleanup
        return;                                                  // finished!
    }

    // swap a & b arrays
    sp = a_x;
    a_x = b_x;
    b_x = sp;
    sp = a_y;
    a_y = b_y;
    b_y = sp;
    cp = a_t;
    a_t = b_t;
    b_t = cp;
    a_n = b_n;

    goto nextpass;
}

// 8-bit (default entry point)
void sub_paint(float x, float y, uint32 fillcol, uint32 bordercol, qbs *backgroundstr, int32 passed) {
    if (is_error_pending())
        return;
    if (write_page->text) {
        error(5);
        return;
    }
    if (passed & 8) {
        error(5);
        return;
    }

    if (write_page->bytes_per_pixel == 4) {
        if (write_page->alpha_disabled) {
            sub_paint32x(x, y, fillcol, bordercol, passed);
            return;
        } else {
            sub_paint32(x, y, fillcol, bordercol, passed);
            return;
        }
    }

    // uses 2 buffers, a and b, and swaps between them for reading and creating
    static uint32 a_n = 0;
    static uint16 *a_x = (uint16 *)malloc(2 * 65536), *a_y = (uint16 *)malloc(2 * 65536);
    static uint8 *a_t = (uint8 *)malloc(65536);
    static uint32 b_n = 0;
    static uint16 *b_x = (uint16 *)malloc(2 * 65536), *b_y = (uint16 *)malloc(2 * 65536);
    static uint8 *b_t = (uint8 *)malloc(65536);
    static uint8 *done = (uint8 *)calloc(640 * 480, 1);
    static int32 ix, iy, i, t, x2, y2;
    static uint32 offset;
    static uint8 *cp;
    static uint16 *sp;
    // overrides
    static int32 done_size = 640 * 480;
    static uint8 *qbg_active_page_offset;                                       // override
    static int32 qbg_width, qbg_view_x1, qbg_view_y1, qbg_view_x2, qbg_view_y2; // override

    if ((passed & 2) == 0)
        fillcol = write_page->color;
    if ((passed & 4) == 0)
        bordercol = fillcol;
    fillcol &= write_page->mask;
    bordercol &= write_page->mask;
    write_page->draw_color = fillcol;

    if (passed & 1) {
        write_page->x += x;
        write_page->y += y;
    } else {
        write_page->x = x;
        write_page->y = y;
    }

    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            ix = qbr_float_to_long(write_page->x * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            ix = qbr_float_to_long(write_page->x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y) + write_page->view_offset_y;
        }
    } else {
        ix = qbr_float_to_long(write_page->x);
        iy = qbr_float_to_long(write_page->y);
    }

    // return if offscreen
    if ((ix < write_page->view_x1) || (iy < write_page->view_y1) || (ix > write_page->view_x2) || (iy > write_page->view_y2)) {
        return;
    }

    // overrides
    qbg_active_page_offset = write_page->offset;
    qbg_width = write_page->width;
    qbg_view_x1 = write_page->view_x1;
    qbg_view_y1 = write_page->view_y1;
    qbg_view_x2 = write_page->view_x2;
    qbg_view_y2 = write_page->view_y2;
    i = write_page->width * write_page->height;
    if (i > done_size) {
        free(done);
        done = (uint8 *)calloc(i, 1);
    }

    // return if first point is the bordercolor
    if (qbg_active_page_offset[iy * qbg_width + ix] == bordercol)
        return;

    // create first node
    a_x[0] = ix;
    a_y[0] = iy;
    a_t[0] = 15;
    // types:
    //&1=check left
    //&2=check right
    //&4=check above
    //&8=check below

    a_n = 1;
    qbg_active_page_offset[iy * qbg_width + ix] = fillcol;
    done[iy * qbg_width + ix] = 1;

nextpass:
    b_n = 0;
    for (i = 0; i < a_n; i++) {
        t = a_t[i];
        ix = a_x[i];
        iy = a_y[i];

        // left
        if (t & 1) {
            x2 = ix - 1;
            y2 = iy;
            if (x2 >= qbg_view_x1) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 13;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // right
        if (t & 2) {
            x2 = ix + 1;
            y2 = iy;
            if (x2 <= qbg_view_x2) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 14;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // above
        if (t & 4) {
            x2 = ix;
            y2 = iy - 1;
            if (y2 >= qbg_view_y1) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 7;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

        // below
        if (t & 8) {
            x2 = ix;
            y2 = iy + 1;
            if (y2 <= qbg_view_y2) {
                offset = y2 * qbg_width + x2;
                if (!done[offset]) {
                    done[offset] = 1;
                    if (qbg_active_page_offset[offset] != bordercol) {
                        qbg_active_page_offset[offset] = fillcol;
                        b_t[b_n] = 11;
                        b_x[b_n] = x2;
                        b_y[b_n] = y2;
                        b_n++; // add new node
                    }
                }
            }
        }

    } // i

    // no new nodes?
    if (b_n == 0) {
        memset(done, 0, write_page->width * write_page->height); // cleanup
        return;                                                  // finished!
    }

    // swap a & b arrays
    sp = a_x;
    a_x = b_x;
    b_x = sp;
    sp = a_y;
    a_y = b_y;
    b_y = sp;
    cp = a_t;
    a_t = b_t;
    b_t = cp;
    a_n = b_n;

    goto nextpass;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

void getptsize_1bpp(const qbs *pt, int32 *sx, int32 *sy) {
    *sx = 8;
    *sy = pt->len;
}

uint32 getptcol_1bpp(const qbs *pt, int32 x, int32 y) {
    return (pt->chr[y] >> (7 - x)) & 1;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

void getptsize_2bpp(const qbs *pt, int32 *sx, int32 *sy) {
    *sx = 4;
    *sy = pt->len;
}

uint32 getptcol_2bpp(const qbs *pt, int32 x, int32 y) {
    return (pt->chr[y] >> ((3 - x) << 1)) & 0x03;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

void getptsize_4bpp(const qbs *pt, int32 *sx, int32 *sy) {
    *sx = 8;
    *sy = pt->len >> 2;
    if (((*sy) << 2) < pt->len)
        ++*sy;
}

uint32 getptcol_4bpp(const qbs *pt, int32 x, int32 y) {
    int quadstart = (y << 2);
    uint32 clr = 0;
    for (uint8 i = 0; i < 4; ++i) {
        uint8 byte = ((i + quadstart) >= pt->len ? 0 : pt->chr[i + quadstart]);
        if (byte & (1 << (7 - x)))
            clr |= (1 << i);
    }
    return clr;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

void getptsize_8bpp(const qbs *pt, int32 *sx, int32 *sy) {
    *sx = 1;
    *sy = pt->len;
}

uint32 getptcol_8bpp(const qbs *pt, int x, int y) {
    return pt->chr[y];
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

void sub_paint(float x, float y, qbs *fillstr, uint32 bordercol, qbs *backgroundstr, int32 passed) {
    if (is_error_pending())
        return;

    // uses 2 buffers, a and b, and swaps between them for reading and creating
    static uint32 a_n = 0;
    static uint16 *a_x = (uint16 *)malloc(2 * 65536), *a_y = (uint16 *)malloc(2 * 65536);
    static uint8 *a_t = (uint8 *)malloc(65536);
    static uint32 b_n = 0;
    static uint16 *b_x = (uint16 *)malloc(2 * 65536), *b_y = (uint16 *)malloc(2 * 65536);
    static uint8 *b_t = (uint8 *)malloc(65536);
    static uint8 *done = (uint8 *)calloc(640 * 480, 1);
    static int32 ix, iy, i, t, x2, y2;
    static uint32 offset;
    static uint8 *cp;
    static uint16 *sp;
    static int32 done_size = 640 * 480;

    if (qbg_text_only) {
        error(5);
        return;
    }
    if ((passed & 2) == 0) {
        error(5);
        return;
    } // must be called with this parameter!

    if (fillstr->len == 0) {
        error(5);
        return;
    }

    static uint8 tile[8][64];
    static int32 sx, sy;
    void (*getptsize)(const qbs *pt, int32 *sx, int32 *sy) = getptsize_4bpp;
    uint32 (*getptcol)(const qbs *pt, int32 x, int32 y) = getptcol_4bpp;

    switch (write_page->compatible_mode) {
    case 1:
        getptsize = getptsize_2bpp;
        getptcol = getptcol_2bpp;
        break;
    case 2:
        getptsize = getptsize_1bpp;
        getptcol = getptcol_1bpp;
        break;
    case 7:
    case 8:
    case 9:
    case 12:
        getptsize = getptsize_4bpp;
        getptcol = getptcol_4bpp;
        break;
    case 13:
        getptsize = getptsize_8bpp;
        getptcol = getptcol_8bpp;
        break;
    }

    getptsize(fillstr, &sx, &sy);
    for (int x = 0; x < sx; ++x)
        for (int y = 0; y < sy; ++y)
            tile[x][y] = getptcol(fillstr, x, y);

    bordercol &= write_page->mask;

    if (passed & 1) {
        write_page->x += x;
        write_page->y += y;
    } else {
        write_page->x = x;
        write_page->y = y;
    }

    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            ix = qbr_float_to_long(write_page->x * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            ix = qbr_float_to_long(write_page->x) + write_page->view_offset_x;
            iy = qbr_float_to_long(write_page->y) + write_page->view_offset_y;
        }
    } else {
        ix = qbr_float_to_long(write_page->x);
        iy = qbr_float_to_long(write_page->y);
    }

    // return if offscreen
    if ((ix < write_page->view_x1) || (iy < write_page->view_y1) || (ix > write_page->view_x2) || (iy > write_page->view_y2)) {
        return;
    }

    // overrides
    qbg_active_page_offset = write_page->offset;
    qbg_width = write_page->width;
    qbg_view_x1 = write_page->view_x1;
    qbg_view_y1 = write_page->view_y1;
    qbg_view_x2 = write_page->view_x2;
    qbg_view_y2 = write_page->view_y2;
    i = write_page->width * write_page->height;
    if (i > done_size) {
        free(done);
        done = (uint8 *)calloc(i, 1);
    }

    // The original color of the starting location
    uint32_t startingColor = qbg_active_page_offset[iy * qbg_width + ix];

    bool borderColorProvided = passed & 4;

    // Exit early if we're already at the border color
    if (borderColorProvided && qbg_active_page_offset[iy * qbg_width + ix] == bordercol)
        return;

    // create first node
    a_x[0] = ix;
    a_y[0] = iy;
    a_t[0] = 15;

    a_n = 1;
    qbg_active_page_offset[iy * qbg_width + ix] = tile[ix % sx][iy % sy];
    done[iy * qbg_width + ix] = 1;

    // Each index maps to a direction, in the order:
    // Left, Right, Up, Down
    int32_t xdelta[4] = {-1, 1, 0, 0};
    int32_t ydelta[4] = {0, 0, -1, 1};

    // The bits indicate the directions that should be checked for the next
    // pixel, we ignore the direction we came from.
    uint32_t dirCheckMap[4] = {
        1 | 4 | 8, // Left, Up, Down
        2 | 4 | 8, // Right, Up, Down
        1 | 2 | 4, // Left, Right, Up
        1 | 2 | 8, // Left, Right, Down
    };

    while (true) {
        b_n = 0;
        for (i = 0; i < a_n; i++) {
            t = a_t[i];
            ix = a_x[i];
            iy = a_y[i];

            for (int k = 0; k < 4; k++) {
                if ((t & (1 << k)) == 0)
                    continue;

                x2 = ix + xdelta[k];
                y2 = iy + ydelta[k];

                // Verify dimensions are within bounds
                if (x2 >= qbg_view_x1 && x2 <= qbg_view_x2 && y2 >= qbg_view_y1 && y2 <= qbg_view_y2) {
                    offset = y2 * qbg_width + x2;

                    // Check that we haven't done this pixel yet
                    if (!done[offset]) {
                        done[offset] = 1;

                        // We either check that we didn't hit the border color
                        // (if provided), or that we're still the starting
                        // color.
                        if ((borderColorProvided && qbg_active_page_offset[offset] != bordercol) ||
                            (!borderColorProvided && qbg_active_page_offset[offset] == startingColor)) {

                            qbg_active_page_offset[offset] = tile[x2 % sx][y2 % sy];
                            b_t[b_n] = dirCheckMap[k];
                            b_x[b_n] = x2;
                            b_y[b_n] = y2;
                            b_n++; // add new node
                        }
                    }
                }
            }
        }

        // no new nodes?
        if (b_n == 0) {
            memset(done, 0, write_page->width * write_page->height); // cleanup
            return;                                                  // finished!
        }

        // swap a & b arrays
        sp = a_x;
        a_x = b_x;
        b_x = sp;

        sp = a_y;
        a_y = b_y;
        b_y = sp;

        cp = a_t;
        a_t = b_t;
        b_t = cp;

        a_n = b_n;
    }
}

void sub_circle(double x, double y, double r, uint32 col, double start, double end, double aspect, int32 passed) {
    //                                                &2         &4           &8         &16
    //[{STEP}](?,?),?[,[?][,[?][,[?][,?]]]]
    if (is_error_pending())
        return;

    // data
    static double pi = 3.1415926535897932, pi2 = 6.2831853071795865;
    static int32 line_to_start, line_from_end;
    static int32 ix, iy; // integer screen co-ordinates of circle's centre
    static double xspan, yspan;
    static double c; // circumference
    static double px, py;
    static double sinb, cosb; // second angle used in double-angle-formula
    static int32 pixels;
    static double tmp;
    static int32 tmpi;
    static int32 i;
    static int32 exclusive;
    static double arc1, arc2, arc3, arc4, arcinc;
    static double px2, py2;
    static int32 x2, y2;
    static int32 x3, y3;
    static int32 lastplotted_x2, lastplotted_y2;
    static int32 lastchecked_x2, lastchecked_y2;

    if (write_page->text) {
        error(5);
        return;
    }

    // lines to & from centre
    if (!(passed & 4))
        start = 0;
    if (!(passed & 8))
        end = pi2;
    line_to_start = 0;
    if (start < 0) {
        line_to_start = 1;
        start = -start;
    }
    line_from_end = 0;
    if (end < 0) {
        line_from_end = 1;
        end = -end;
    }

    // error checking
    if (start > pi2) {
        error(5);
        return;
    }
    if (end > pi2) {
        error(5);
        return;
    }

    // when end<start, the arc of the circle that wouldn't have been drawn if start & end
    // were swapped is drawn
    exclusive = 0;
    if (end < start) {
        tmp = start;
        start = end;
        end = tmp;
        tmpi = line_to_start;
        line_to_start = line_from_end;
        line_from_end = tmpi;
        exclusive = 1;
    }

    // calc. centre
    if (passed & 1) {
        x = write_page->x + x;
        y = write_page->y + y;
    }
    write_page->x = x;
    write_page->y = y; // set graphics cursor position to circle's centre

    r = x + r; // the difference between x & x+r in pixels will be the radius in pixels
    // resolve coordinates (but keep as floats)
    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            x = x * write_page->scaling_x + write_page->scaling_offset_x + write_page->view_offset_x;
            y = y * write_page->scaling_y + write_page->scaling_offset_y + write_page->view_offset_y;
            r = r * write_page->scaling_x + write_page->scaling_offset_x + write_page->view_offset_x;
        } else {
            x = x + write_page->view_offset_x;
            y = y + write_page->view_offset_y;
            r = r + write_page->view_offset_x;
        }
    }
    if (x < 0)
        ix = x - 0.5;
    else
        ix = x + 0.5;
    if (y < 0)
        iy = y - 0.5;
    else
        iy = y + 0.5;
    r = std::fabs(r - x); // r is now a radius in pixels

    // adjust vertical and horizontal span of the circle based on aspect ratio
    xspan = r;
    yspan = r;
    if (!(passed & 16)) {
        aspect = 1; // Note: default aspect ratio is 1:1 for QB64 specific modes (256/32)
        if (write_page->compatible_mode == 1)
            aspect = 4.0 * (200.0 / 320.0) / 3.0;
        if (write_page->compatible_mode == 2)
            aspect = 4.0 * (200.0 / 640.0) / 3.0;
        if (write_page->compatible_mode == 7)
            aspect = 4.0 * (200.0 / 320.0) / 3.0;
        if (write_page->compatible_mode == 8)
            aspect = 4.0 * (200.0 / 640.0) / 3.0;
        if (write_page->compatible_mode == 9)
            aspect = 4.0 * (350.0 / 640.0) / 3.0;
        if (write_page->compatible_mode == 10)
            aspect = 4.0 * (350.0 / 640.0) / 3.0;
        if (write_page->compatible_mode == 11)
            aspect = 4.0 * (480.0 / 640.0) / 3.0;
        if (write_page->compatible_mode == 12)
            aspect = 4.0 * (480.0 / 640.0) / 3.0;
        if (write_page->compatible_mode == 13)
            aspect = 4.0 * (200.0 / 320.0) / 3.0;
        // Old method: aspect=4.0*((double)write_page->height/(double)write_page->width)/3.0;
    }
    if (aspect >= 0) {
        if (aspect < 1) {
            // aspect: 0 to 1
            yspan *= aspect;
        }
        if (aspect > 1) {
            // aspect: 1 to infinity
            xspan /= aspect;
        }
    } else {
        if (aspect > -1) {
            // aspect: -1 to 0
            yspan *= (1 + aspect);
        }
        // if aspect<-1 no change is required
    }

    // skip everything if none of the circle is inside current viwport
    if ((x + xspan + 0.5) < write_page->view_x1)
        return;
    if ((y + yspan + 0.5) < write_page->view_y1)
        return;
    if ((x - xspan - 0.5) > write_page->view_x2)
        return;
    if ((y - yspan - 0.5) > write_page->view_y2)
        return;

    if (!(passed & 2))
        col = write_page->color;
    write_page->draw_color = col;

    // pre-set/pre-calculate values
    c = pi2 * r;
    pixels = c / 4.0 + 0.5;
    arc1 = 0;
    arc2 = pi;
    arc3 = pi;
    arc4 = pi2;
    arcinc = (pi / 2) / (double)pixels;
    sinb = std::sin(arcinc);
    cosb = std::cos(arcinc);
    lastplotted_x2 = -1;
    lastchecked_x2 = -1;
    i = 0;

    if (line_to_start) {
        px = std::cos(start);
        py = std::sin(start);
        x2 = px * xspan + 0.5;
        y2 = py * yspan - 0.5;
        fast_line(ix, iy, ix + x2, iy - y2, col);
    }

    px = 1;
    py = 0;

drawcircle:
    x2 = px * xspan + 0.5;
    y2 = py * yspan - 0.5;

    if (i == 0) {
        lastchecked_x2 = x2;
        lastchecked_y2 = y2;
        goto plot;
    }

    if ((abs(x2 - lastplotted_x2) >= 2) || (abs(y2 - lastplotted_y2) >= 2)) {
    plot:
        if (exclusive) {
            if ((arc1 <= start) || (arc1 >= end)) {
                pset_and_clip(ix + lastchecked_x2, iy + lastchecked_y2, col);
            }
            if ((arc2 <= start) || (arc2 >= end)) {
                pset_and_clip(ix - lastchecked_x2, iy + lastchecked_y2, col);
            }
            if ((arc3 <= start) || (arc3 >= end)) {
                pset_and_clip(ix - lastchecked_x2, iy - lastchecked_y2, col);
            }
            if ((arc4 <= start) || (arc4 >= end)) {
                pset_and_clip(ix + lastchecked_x2, iy - lastchecked_y2, col);
            }
        } else { // inclusive
            if ((arc1 >= start) && (arc1 <= end)) {
                pset_and_clip(ix + lastchecked_x2, iy + lastchecked_y2, col);
            }
            if ((arc2 >= start) && (arc2 <= end)) {
                pset_and_clip(ix - lastchecked_x2, iy + lastchecked_y2, col);
            }
            if ((arc3 >= start) && (arc3 <= end)) {
                pset_and_clip(ix - lastchecked_x2, iy - lastchecked_y2, col);
            }
            if ((arc4 >= start) && (arc4 <= end)) {
                pset_and_clip(ix + lastchecked_x2, iy - lastchecked_y2, col);
            }
        }
        if (i > pixels)
            goto allplotted;
        lastplotted_x2 = lastchecked_x2;
        lastplotted_y2 = lastchecked_y2;
    }
    lastchecked_x2 = x2;
    lastchecked_y2 = y2;

    if (i <= pixels) {
        i++;
        if (i > pixels)
            goto plot;
        px2 = px * cosb + py * sinb;
        py = py * cosb - px * sinb;
        px = px2;
        if (i) {
            arc1 += arcinc;
            arc2 -= arcinc;
            arc3 += arcinc;
            arc4 -= arcinc;
        }
        goto drawcircle;
    }
allplotted:

    if (line_from_end) {
        px = std::cos(end);
        py = std::sin(end);
        x2 = px * xspan + 0.5;
        y2 = py * yspan - 0.5;
        fast_line(ix, iy, ix + x2, iy - y2, col);
    }

} // sub_circle

uint32 point(int32 x, int32 y) { // does not clip!
    if (read_page->bytes_per_pixel == 1) {
        return read_page->offset[y * read_page->width + x] & read_page->mask;
    } else {
        return read_page->offset32[y * read_page->width + x];
    }
    return NULL;
}

double func_point(float x, float y, int32 passed) {
    static int32 x2, y2, i;

    if (!passed) {
        if (write_page->text) {
            error(5);
            return 0;
        }
        i = qbr_float_to_long(x);
        if ((i < 0) || (i > 3)) {
            error(5);
            return 0;
        }
        switch (i) {
        case 0:
            if (write_page->clipping_or_scaling == 2) {
                return qbr_float_to_long(write_page->x * write_page->scaling_x + write_page->scaling_offset_x);
            }
            return qbr_float_to_long(write_page->x);
            break;
        case 1:
            if (write_page->clipping_or_scaling == 2) {
                return qbr_float_to_long(write_page->y * write_page->scaling_y + write_page->scaling_offset_y);
            }
            return qbr_float_to_long(write_page->y);
            break;
        case 2:
            return write_page->x;
            break;
        case 3:
            return write_page->y;
            break;
        default:
            error(5);
            return 0;
        }
    } //! passed

    if (read_page->text) {
        error(5);
        return 0;
    }
    if (read_page->clipping_or_scaling) {
        if (read_page->clipping_or_scaling == 2) {
            x2 = qbr_float_to_long(x * read_page->scaling_x + read_page->scaling_offset_x) + read_page->view_offset_x;
            y2 = qbr_float_to_long(y * read_page->scaling_y + read_page->scaling_offset_y) + read_page->view_offset_y;
        } else {
            x2 = qbr_float_to_long(x) + read_page->view_offset_x;
            y2 = qbr_float_to_long(y) + read_page->view_offset_y;
        }
    } else {
        x2 = qbr_float_to_long(x);
        y2 = qbr_float_to_long(y);
    }
    if (x2 >= read_page->view_x1) {
        if (x2 <= read_page->view_x2) {
            if (y2 >= read_page->view_y1) {
                if (y2 <= read_page->view_y2) {
                    return point(x2, y2);
                }
            }
        }
    }
    return -1;
}

void sub_pset(float x, float y, uint32 col, int32 passed) {
    if (is_error_pending())
        return;
    static int32 x2, y2;
    if (!write_page->compatible_mode) {
        error(5);
        return;
    }
    // Special Format: [{STEP}](?,?),[?]
    if (passed & 1) {
        write_page->x += x;
        write_page->y += y;
    } else {
        write_page->x = x;
        write_page->y = y;
    }
    if (!(passed & 2))
        col = write_page->color;
    write_page->draw_color = col;
    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            x2 = qbr(write_page->x * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            y2 = qbr(write_page->y * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            x2 = qbr(write_page->x) + write_page->view_offset_x;
            y2 = qbr(write_page->y) + write_page->view_offset_y;
        }
        if (x2 >= write_page->view_x1) {
            if (x2 <= write_page->view_x2) {
                if (y2 >= write_page->view_y1) {
                    if (y2 <= write_page->view_y2) {
                        pset(x2, y2, col);
                    }
                }
            }
        }
        return;
    } else {
        x2 = qbr(write_page->x);
        if (x2 >= 0) {
            if (x2 < write_page->width) {
                y2 = qbr(write_page->y);
                if (y2 >= 0) {
                    if (y2 < write_page->height) {
                        pset(x2, y2, col);
                    }
                }
            }
        }
    }
    return;
}

void sub_preset(float x, float y, uint32 col, int32 passed) {
    if (is_error_pending())
        return;
    if (!(passed & 2)) {
        col = write_page->background_color;
        passed |= 2;
    }
    sub_pset(x, y, col, passed);
    return;
}

int32 img_printchr = 0;
int32 img_printchr_i;
int32 img_printchr_x;
int32 img_printchr_y;
char *img_printchr_offset;

void printchr(int32 character) {
    static uint32 x, x2, y, y2, w, h, z, z2, z3, a, a2, a3, color, background_color, f;
    static uint32 *lp;
    static uint8 *cp;
    static img_struct *im;

    im = write_page;
    color = im->color;
    background_color = im->background_color;

    if (im->text) {
        im->offset[(((im->cursor_y - 1) * im->width + im->cursor_x - 1)) << 1] = character;
        im->offset[((((im->cursor_y - 1) * im->width + im->cursor_x - 1)) << 1) + 1] = (color & 0xF) + background_color * 16 + (color & 16) * 8;
        return;
    }

    // precalculations

    f = im->font;
    x = fontwidth[f];
    if (x)
        x *= (im->cursor_x - 1);
    else
        x = im->cursor_x - 1;
    y = (im->cursor_y - 1) * fontheight[f];
    h = fontheight[f];
    if ((fontflags[f] & FONT_LOAD_UNICODE) == 0)
        character &= 255; // unicodefontsupport

    // if (mode==1) img[i].print_mode=3;//fill
    // if (mode==2) img[i].print_mode=1;//keep
    // if (mode==3) img[i].print_mode=2;//only

    if (f >= 32) { // custom font

        // 8-bit / alpha-disabled 32-bit / dont-blend(alpha may still be applied)
        if ((im->bytes_per_pixel == 1) || ((im->bytes_per_pixel == 4) && (im->alpha_disabled)) || (fontflags[f] & FONT_LOAD_DONTBLEND)) {

            // render character
            static int32 ok;
            static uint8 *rt_data;
            static int32 rt_w, rt_h;
            ok = FontRenderTextASCII(font[f], (uint8 *)&character, 1, 1, &rt_data, &rt_w, &rt_h);
            if (!ok)
                return;

            w = rt_w;

            switch (im->print_mode) {
            case 3:
                for (y2 = 0; y2 < h; y2++) {
                    cp = rt_data + y2 * w;
                    for (x2 = 0; x2 < w; x2++) {
                        if (*cp++)
                            pset(x + x2, y + y2, color);
                        else
                            pset(x + x2, y + y2, background_color);
                    }
                }
                break;
            case 1:
                for (y2 = 0; y2 < h; y2++) {
                    cp = rt_data + y2 * w;
                    for (x2 = 0; x2 < w; x2++) {
                        if (*cp++)
                            pset(x + x2, y + y2, color);
                    }
                }
                break;
            case 2:
                for (y2 = 0; y2 < h; y2++) {
                    cp = rt_data + y2 * w;
                    for (x2 = 0; x2 < w; x2++) {
                        if (!(*cp++))
                            pset(x + x2, y + y2, background_color);
                    }
                }
                break;
            default:
                break;
            }

            free(rt_data);
            return;
        } // 1-8 bit
        // assume 32-bit blended

        a = (color >> 24) + 1;
        a2 = (background_color >> 24) + 1;
        z = color & 0xFFFFFF;
        z2 = background_color & 0xFFFFFF;

        // render character
        static int32 ok;
        static uint8 *rt_data;
        static int32 rt_w, rt_h;
        ok = FontRenderTextASCII(font[f], (uint8 *)&character, 1, 0, &rt_data, &rt_w, &rt_h);
        if (!ok)
            return;

        w = rt_w;

        switch (im->print_mode) {
        case 3:

            static float r1, g1, b1, alpha1, r2, g2, b2, alpha2;
            alpha1 = (color >> 24) & 255;
            r1 = (color >> 16) & 255;
            g1 = (color >> 8) & 255;
            b1 = color & 255;
            alpha2 = (background_color >> 24) & 255;
            r2 = (background_color >> 16) & 255;
            g2 = (background_color >> 8) & 255;
            b2 = background_color & 255;
            static float dr, dg, db, da;
            dr = r2 - r1;
            dg = g2 - g1;
            db = b2 - b1;
            da = alpha2 - alpha1;
            static float cw; // color weight multiplier, avoids seeing black when transitioning from RGBA(?,?,?,255) to RGBA(0,0,0,0)
            if (alpha1)
                cw = alpha2 / alpha1;
            else
                cw = 100000;
            static float d;

            for (y2 = 0; y2 < h; y2++) {
                cp = rt_data + y2 * w;
                for (x2 = 0; x2 < w; x2++) {

                    d = *cp++;
                    d = 255 - d;
                    d /= 255.0;
                    static float r3, g3, b3, alpha3;
                    alpha3 = alpha1 + da * d;
                    d *= cw;
                    if (d > 1.0)
                        d = 1.0;
                    r3 = r1 + dr * d;
                    g3 = g1 + dg * d;
                    b3 = b1 + db * d;
                    static int32 r4, g4, b4, alpha4;
                    r4 = qbr_float_to_long(r3);
                    g4 = qbr_float_to_long(g3);
                    b4 = qbr_float_to_long(b3);
                    alpha4 = qbr_float_to_long(alpha3);
                    pset(x + x2, y + y2, b4 + (g4 << 8) + (r4 << 16) + (alpha4 << 24));
                }
            }
            break;
        case 1:
            for (y2 = 0; y2 < h; y2++) {
                cp = rt_data + y2 * w;
                for (x2 = 0; x2 < w; x2++) {
                    z3 = *cp++;
                    if (z3)
                        pset(x + x2, y + y2, ((z3 * a) >> 8 << 24) + z);
                }
            }
            break;
        case 2:
            for (y2 = 0; y2 < h; y2++) {
                cp = rt_data + y2 * w;
                for (x2 = 0; x2 < w; x2++) {
                    z3 = *cp++;
                    if (z3 != 255)
                        pset(x + x2, y + y2, (((255 - z3) * a2) >> 8 << 24) + z2);
                }
            }
            break;
        default:
            break;
        }
        free(rt_data);
        return;
    } // custom font

    // default fonts
    if (im->font == 8)
        cp = &charset8x8[character][0][0];
    if (im->font == 14)
        cp = &charset8x16[character][1][0];
    if (im->font == 16)
        cp = &charset8x16[character][0][0];
    switch (im->print_mode) {
    case 3:
        for (y2 = 0; y2 < h; y2++) {
            for (x2 = 0; x2 < 8; x2++) {
                if (*cp++)
                    pset(x + x2, y + y2, color);
                else
                    pset(x + x2, y + y2, background_color);
            }
        }
        break;
    case 1:
        for (y2 = 0; y2 < h; y2++) {
            for (x2 = 0; x2 < 8; x2++) {
                if (*cp++)
                    pset(x + x2, y + y2, color);
            }
        }
        break;
    case 2:
        for (y2 = 0; y2 < h; y2++) {
            for (x2 = 0; x2 < 8; x2++) {
                if (!(*cp++))
                    pset(x + x2, y + y2, background_color);
            }
        }
        break;
    default:
        break;
    } // z
    return;

} // printchr

/// @brief Returns the width of a character in pixels.
/// Note: Only called by qbs_print().
/// Supports "UNICODE" _LOADFONT option
/// @param character The character to return the width for (this can be UTF32)
/// @return The width in pixels
int32_t chrwidth(uint32_t character) {
    auto im = write_page;
    auto f = im->font;
    auto w = fontwidth[f];
    if (w)
        return w;

    // Custom font
    // a740g: No need to render just to find the pixel length
    if ((fontflags[f] & FONT_LOAD_UNICODE)) { // UNICODE character
        w = FontPrintWidthUTF32(font[f], (char32_t *)&character, 1);
    } else { // ASCII character
        character &= 255;
        w = FontPrintWidthASCII(font[f], (uint8_t *)&character, 1);
    }

    return w;
}

void newline() {
    static uint32 *lp;
    static uint16 *sp;
    static int32 z, z2;

    // move cursor to new line
    write_page->cursor_y++;
    write_page->cursor_x = 1;

    // scroll up screen if necessary
    if (write_page->cursor_y > write_page->bottom_row) {

        if (lprint) {
            sub__printimage(lprint_image);
            sub_cls(NULL, 15, 2);
            lprint_buffered = 0;
            return;
        }

        if (write_page->text) {
            // text
            // move lines up
            memmove(write_page->offset + (write_page->top_row - 1) * 2 * write_page->width, write_page->offset + write_page->top_row * 2 * write_page->width,
                    (write_page->bottom_row - write_page->top_row) * 2 * write_page->width);
            // erase bottom line
            z2 = (write_page->color & 0xF) + (write_page->background_color & 7) * 16 + (write_page->color & 16) * 8;
            z2 <<= 8;
            z2 += 32;
            sp = ((uint16 *)(write_page->offset + (write_page->bottom_row - 1) * 2 * write_page->width));
            z = write_page->width;
            while (z--)
                *sp++ = z2;
        } else {
            // graphics
            // move lines up
            memmove(write_page->offset + (write_page->top_row - 1) * write_page->bytes_per_pixel * write_page->width * fontheight[write_page->font],
                    write_page->offset + write_page->top_row * write_page->bytes_per_pixel * write_page->width * fontheight[write_page->font],
                    (write_page->bottom_row - write_page->top_row) * write_page->bytes_per_pixel * write_page->width * fontheight[write_page->font]);
            // erase bottom line
            if (write_page->bytes_per_pixel == 1) {
                memset(write_page->offset + (write_page->bottom_row - 1) * write_page->width * fontheight[write_page->font], write_page->background_color,
                       write_page->width * fontheight[write_page->font]);
            } else {
                // assume 32-bit
                z2 = write_page->background_color;
                lp = write_page->offset32 + (write_page->bottom_row - 1) * write_page->width * fontheight[write_page->font];
                z = write_page->width * fontheight[write_page->font];
                while (z--)
                    *lp++ = z2;
            }
        } // graphics
        write_page->cursor_y = write_page->bottom_row;
    } // scroll up

} // newline

void makefit(qbs *text) {
    static int32 w, x, x2, x3;
    if (write_page->holding_cursor)
        return;
    if (write_page->cursor_x != 1) { // if already at left-most, nothing more can be done
        if (write_page->text) {
            if ((write_page->cursor_x + text->len - 1) > write_page->width)
                newline();
        } else {
            w = func__printwidth(text, NULL, NULL);
            x = fontwidth[write_page->font];
            if (!x)
                x = 1;
            x = x * (write_page->cursor_x - 1);
            if ((x + w) > write_page->width)
                newline();
        }
    }
}

void lprint_makefit(qbs *text) {
    // stub
    // makefit(text);
}

void tab() {
    static int32 x, x2, w;

    // tab() on a held-cursor only sets the cursor to the left hand position of the next line
    if (write_page->holding_cursor) {
        newline();
        write_page->holding_cursor = 0;
        return;
    }

#ifdef QB64_WINDOWS // if Windows console
    if (write_page->console) {
        if (func_pos(0) > write_page->width - 10) {
            printf("\n");
            return;
        } else {
            do {
                printf(" ");
            } while (func_pos(0) % 10 != 0);
        }
        return;
    }
#endif

    // text
    if (write_page->text) {
        qbs_print(singlespace, 0);
    text:
        if (write_page->cursor_x != 1) {
            if (((write_page->cursor_x - 1) % 14) || (write_page->cursor_x > (write_page->width - 13))) {
                if (write_page->cursor_x < write_page->width) {
                    qbs_print(singlespace, 0);
                    goto text;
                }
            }
        } //!=1
        return;
    }

    x = fontwidth[write_page->font];
    if (!x) {

        // variable width
        x = write_page->cursor_x - 1;
        x2 = (x / 112 + 1) * 112;      // next position
        if (x2 >= write_page->width) { // it doesn't fit on line
            // box fill x to end of line with background color

            if (write_page->print_mode != 1) // only draw the box if _PRINTMODE != _KEEPBACKGROUND
                fast_boxfill(x, (write_page->cursor_y - 1) * fontheight[write_page->font], write_page->width - 1,
                             write_page->cursor_y * fontheight[write_page->font] - 1, write_page->background_color);

            newline();
        } else { // fits on line
            // box fill x to x2-1 with background color

            if (write_page->print_mode != 1) // only draw the box if _PRINTMODE != _KEEPBACKGROUND
                fast_boxfill(x, (write_page->cursor_y - 1) * fontheight[write_page->font], x2 - 1, write_page->cursor_y * fontheight[write_page->font] - 1,
                             write_page->background_color);

            write_page->cursor_x = x2;
        }

    } else {

        // fixed width
        w = write_page->width / x;

        qbs_print(singlespace, 0);
    fixwid:
        if (write_page->cursor_x != 1) {
            if (((write_page->cursor_x - 1) % 14) || (write_page->cursor_x > (w - 13))) {
                if (write_page->cursor_x < w) {
                    qbs_print(singlespace, 0);
                    goto fixwid;
                }
            }
        } //!=1
    }
    return;
}

int32 func_lpos(int32 lpt) {
    // lpt values: 0 = LPT1, 1 = LPT1, 2 = LPT2, 3 = LPT3
    if ((lpt < 0) || (lpt > 3)) {
        error(5);
        return 0;
    }
    return lpos;
}

void qbs_lprint(qbs *str, int32 finish_on_new_line) {
    static int32 old_dest;
    while (lprint_locked)
        Sleep(64);
    lprint = 1;
    old_dest = func__dest();
    if (!lprint_image) {
        lprint_image = func__newimage(640, 960, 13, 1);
        sub__dest(lprint_image);
        sub_cls(NULL, 15, 2);
        sub__font(16, NULL, 0);
        qbg_sub_color(0, 15, NULL, NULL, 3);
        qbg_sub_view_print(1, 60, 1);
    } else {
        sub__dest(lprint_image);
    }
    lprint_buffered = 1;
    lprint_last = func_timer(0.001, 1);
    qbs_print(str, finish_on_new_line);
    sub__dest(old_dest);
    lprint = 0;
}

int32 no_control_characters = 0;
int32 no_control_characters2 = 0;

void sub__controlchr(int32 onoff) {
    if (onoff == 2)
        no_control_characters2 = 1;
    else
        no_control_characters2 = 0;
}

int32 func__controlchr() {
    return -no_control_characters2;
}

void qbs_print(qbs *str, int32 finish_on_new_line) {
    if (is_error_pending())
        return;
    int32 i, i2, entered_new_line, x, x2, y, y2, z, z2, w;
    entered_new_line = 0;
    static uint32 character;

    if (write_page->console) {
        static qbs *strz;
        if (!strz)
            strz = qbs_new(0, 0);
        qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
        if (finish_on_new_line)
            std::cout << (char *)strz->chr << std::endl;
        else
            std::cout << (char *)strz->chr;
#ifndef QB64_WINDOWS
        std::cout.flush();
#endif
        return;
    }

    /*
        if (!str->len){
        if (!newline) return;//no action required
        if (write_page->holding_cursor){//extra CR required before return
        write_page->holding_cursor=0;
        i=-1;
        write_page->cursor_x++;
        goto print_unhold_cursor;
        }
        }

        if (!str->len) goto null_length;

        if (write_page->holding_cursor){
        write_page->holding_cursor=0;
        i=-1;
        write_page->cursor_x++;
        goto print_unhold_cursor;
        }
    */

    // holding cursor?
    if (write_page->holding_cursor) {
        if (str->len) {
            write_page->holding_cursor = 0;
            newline();
        } else {
            // null length print string
            if (finish_on_new_line)
                write_page->holding_cursor = 0; // new line will be entered automatically
        }
    }

    for (i = 0; i < str->len; i++) {

        character = str->chr[i];

        if (fontflags[write_page->font] & FONT_LOAD_UNICODE) { // unicode font
            if (i > (str->len - 4))
                break; // not enough data for a utf32 encoding
            character = *((int32 *)(&str->chr[i]));
            i += 3;
        }

        if (lprint)
            lprint_buffered = 1;
        entered_new_line = 0; // beginning a new line was the last action (so don't add a new one)

        // ###special characters

        if (no_control_characters || no_control_characters2)
            goto skip_control_characters;

        if (character == 28) {
            // advance one cursor position
            if (lprint) {
                if (lpos < width_lprint)
                    lpos++;
            }
            // can cursor advance?
            if (write_page->cursor_y >= write_page->bottom_row) {
                if (write_page->text) {
                    if (write_page->cursor_x >= write_page->width)
                        goto skip;
                } else {
                    if (fontwidth[write_page->font]) {
                        if (write_page->cursor_x >= (write_page->width / fontwidth[write_page->font]))
                            goto skip;
                    } else {
                        if (write_page->cursor_x >= write_page->width)
                            goto skip;
                    }
                }
            }
            write_page->cursor_x++;
            if (write_page->text) {
                if (write_page->cursor_x > write_page->width) {
                    write_page->cursor_y++;
                    write_page->cursor_x = 1;
                }
            } else {
                if (fontwidth[write_page->font]) {
                    if (write_page->cursor_x > (write_page->width / fontwidth[write_page->font])) {
                        write_page->cursor_y++;
                        write_page->cursor_x = 1;
                    }
                } else {
                    if (write_page->cursor_x > write_page->width) {
                        write_page->cursor_y++;
                        write_page->cursor_x = 1;
                    }
                }
            }
            goto skip;
        }

        if (character == 29) {
            // go back one cursor position
            if (lprint) {
                if (lpos > 1)
                    lpos--;
            }
            // can cursor go back?
            if ((write_page->cursor_y == write_page->top_row) || (write_page->cursor_y > write_page->bottom_row)) {
                if (write_page->cursor_x == 1)
                    goto skip;
            }
            write_page->cursor_x--;
            if (write_page->cursor_x < 1) {
                write_page->cursor_y--;
                if (write_page->text) {
                    write_page->cursor_x = write_page->width;
                } else {
                    if (fontwidth[write_page->font]) {
                        write_page->cursor_x = write_page->width / fontwidth[write_page->font];
                    } else {
                        write_page->cursor_x = write_page->width;
                    }
                }
            }
            goto skip;
        }

        if (character == 30) {
            // previous row, same column
            // no change if cursor not within view print boundaries
            if ((write_page->cursor_y > write_page->top_row) && (write_page->cursor_y <= write_page->bottom_row)) {
                write_page->cursor_y--;
            }
            goto skip;
        }

        if (character == 31) {
            // next row, same column
            // no change if cursor not within view print boundaries
            if ((write_page->cursor_y >= write_page->top_row) && (write_page->cursor_y < write_page->bottom_row)) {
                write_page->cursor_y++;
            }
            goto skip;
        }

        if (character == 12) { // aka form feed
            if (lprint) {
                sub__printimage(lprint_image);
                lprint_buffered = 0;
            }
            // clears text viewport
            // clears bottom row
            // moves cursor to top-left of text viewport
            sub_cls(NULL, NULL, 0);
            if (lprint)
                lpos = 1;
            goto skip;
        }

        if (character == 11) {
            write_page->cursor_x = 1;
            write_page->cursor_y = write_page->top_row;
            if (lprint)
                lpos = 1;
            goto skip;
        }

        if (character == 9) {
            // moves to next multiple of 8 (always advances at least one space)
            if (!fontwidth[write_page->font]) {
                // variable width!
                x = write_page->cursor_x - 1;
                x2 = (x / 64 + 1) * 64;        // next position
                if (x2 >= write_page->width) { // it doesn't fit on line
                    // box fill x to end of line with background color

                    if (write_page->print_mode != 1) // only draw the box if _PRINTMODE != _KEEPBACKGROUND
                        fast_boxfill(x, (write_page->cursor_y - 1) * fontheight[write_page->font], write_page->width - 1,
                                     write_page->cursor_y * fontheight[write_page->font] - 1, write_page->background_color);

                    newline();
                    entered_new_line = 1;
                } else { // fits on line
                    // box fill x to x2-1 with background color

                    if (write_page->print_mode != 1) // only draw the box if _PRINTMODE != _KEEPBACKGROUND
                        fast_boxfill(x, (write_page->cursor_y - 1) * fontheight[write_page->font], x2 - 1,
                                     write_page->cursor_y * fontheight[write_page->font] - 1, write_page->background_color);

                    write_page->cursor_x = x2;
                }
                goto skip;
            } else {
                if (write_page->cursor_x % 8) { // next cursor position not a multiple of 8
                    i--;                        // more spaces will be required
                }
                character = 32; // override character 9
            }
        } // 9

        if (character == 7) {
            sub_beep();
            goto skip;
        }

        if ((character == 10) || (character == 13)) {
            newline();
            if (lprint)
                lpos = 1;
            // note: entered_new_line not set because these carriage returns compound on each other
            goto skip;
        }

    skip_control_characters:

        // ###check if character fits on line, if not move to next line
        //(only applies to non-fixed width fonts)
        if (!fontwidth[write_page->font]) { // unpredictable width
            w = chrwidth(character);
            if ((write_page->cursor_x - 1 + w) > write_page->width) {
                newline();
                // entered_new_line not set, a character will follow
            }
        }

        // ###print the character
        printchr(character);

        // ###advance lpos, begin new line if necessary
        if (lprint) {
            lpos++;
            if (lpos > width_lprint) {
                newline();
                entered_new_line = 1;
                lpos = 1;
                goto skip; // skip cursor advancement and checking because new line entered
            }
        }

        // ###advance cursor
        if (fontwidth[write_page->font]) {
            write_page->cursor_x++;
        } else {
            write_page->cursor_x += w;
        }

        // ###check if another character could fit at cursor_x's location
        if (write_page->compatible_mode) { // graphics
            x = fontwidth[write_page->font];
            if (!x)
                x = 1;
            x2 = x * (write_page->cursor_x - 1);
            if (x2 > (write_page->width - x)) {
                if (!finish_on_new_line) {
                    if (i == (str->len - 1)) { // last character
                        // move horizontal cursor back to right-most valid position
                        write_page->cursor_x = write_page->width / x;
                        write_page->holding_cursor = 1;
                        goto held_cursor;
                    }
                }
                newline();
                entered_new_line = 1;
            }
        } else { // text
            if (write_page->cursor_x > write_page->width) {
                if (!finish_on_new_line) {
                    if (i == (str->len - 1)) {  // last character
                        write_page->cursor_x--; // move horizontal cursor back to right-most valid position
                        write_page->holding_cursor = 1;
                        goto held_cursor;
                    }
                }
                newline();
                entered_new_line = 1;
            }
        }
    held_cursor:

    skip:;

        /*
            tabbing1:


            write_page->cursor_x++;



            //hold cursor?
            if (write_page->cursor_x>qbg_width_in_characters){//past last x position
            if (!newline){//don't need a new line
            if (i==(str->len-1)){//last character
            write_page->cursor_x--;
            write_page->holding_cursor=1;
            goto hold_cursor;
            }
            }
            }




            qbs_print_skipchar:;

            print_unhold_cursor:

            if (write_page->cursor_x>qbg_width_in_characters){
            qbs_print_newline:
            newlineadded=1;

            if (write_page->cursor_y==qbg_height_in_characters) write_page->cursor_y=qbg_bottom_row;

            write_page->cursor_y++;
            write_page->cursor_x=1;



            if (write_page->cursor_y>qbg_bottom_row){
            //move screen space within view print up 1 row
            //if (qbg_mode==13){

            ///memmove(&cmem[655360+(qbg_top_row-1)*2560],&cmem[655360+qbg_top_row*2560],(qbg_bottom_row-qbg_top_row)*2560);
            ///memset(&cmem[655360+(qbg_bottom_row-1)*2560],0,2560);
            if (qbg_text_only){

            memmove(qbg_active_page_offset+(qbg_top_row-1)*qbg_width_in_characters*2,
            qbg_active_page_offset+(qbg_top_row)*qbg_width_in_characters*2,
            (qbg_bottom_row-qbg_top_row)*qbg_width_in_characters*2);
            for (i2=0;i2<qbg_width_in_characters;i2++){
            qbg_active_page_offset[(qbg_bottom_row-1)*qbg_width_in_characters*2+i2*2]=32;
            qbg_active_page_offset[(qbg_bottom_row-1)*qbg_width_in_characters*2+i2*2+1]=7;

            }

            }else{
            memmove(qbg_active_page_offset+(qbg_top_row-1)*qbg_bytes_per_pixel*qbg_width*qbg_character_height,
            qbg_active_page_offset+qbg_top_row*qbg_bytes_per_pixel*qbg_width*qbg_character_height,
            (qbg_bottom_row-qbg_top_row)*qbg_bytes_per_pixel*qbg_width*qbg_character_height);
            memset(qbg_active_page_offset+(qbg_bottom_row-1)*qbg_bytes_per_pixel*qbg_width*qbg_character_height,0,qbg_bytes_per_pixel*qbg_width*qbg_character_height);
            }




            write_page->cursor_y=qbg_bottom_row;
            }



            }
        */

    } // i

null_length:
    if (finish_on_new_line && (!entered_new_line))
        newline();
    if (lprint) {
        if (finish_on_new_line)
            lpos = 1;
    }

    /*
        null_length:

        //begin new line?
        if (newline&&(!newlineadded)) {newline=0; goto qbs_print_newline;}

        //hold cursor
        hold_cursor:
    */

    return;
}

void qbg_sub_window(float x1, float y1, float x2, float y2, int32 passed) {
    //                  &1
    //(passed&2)->SCREEN
    if (is_error_pending())
        return;
    static float i;
    static float old_x, old_y;

    if (write_page->text)
        goto qbg_sub_window_error;
    if ((!(passed & 1)) && (passed & 2))
        goto qbg_sub_window_error; // SCREEN passed without any other arguments!

    // backup current qbg_x & qbg_y coordinates relative to viewport, not window
    if (write_page->clipping_or_scaling == 2) {
        old_x = write_page->x * write_page->scaling_x + write_page->scaling_offset_x;
        old_y = write_page->y * write_page->scaling_y + write_page->scaling_offset_y;
    } else {
        old_x = write_page->x;
        old_y = write_page->y;
    }

    if (passed & 1) {
        if (x1 == x2)
            goto qbg_sub_window_error;
        if (y1 == y2)
            goto qbg_sub_window_error;
        // sort so x1 & y1 contain the lower values
        if (x1 > x2) {
            i = x1;
            x1 = x2;
            x2 = i;
        }
        if (y1 > y2) {
            i = y1;
            y1 = y2;
            y2 = i;
        }
        if (!(passed & 2)) {
            i = y1;
            y1 = y2;
            y2 = i;
        }
        // Note: Window's coordinates are not based on prev. WINDOW values
        write_page->clipping_or_scaling = 2;
        if (passed & 2) {
            // WINDOW SCREEN uses the active viewport dimensions and offset behavior.
            write_page->scaling_x = ((float)(write_page->view_x2 - write_page->view_x1)) / (x2 - x1);
            write_page->scaling_y = ((float)(write_page->view_y2 - write_page->view_y1)) / (y2 - y1);
            write_page->scaling_offset_x = write_page->view_x1 - write_page->view_offset_x - x1 * write_page->scaling_x;
            write_page->scaling_offset_y = write_page->view_y1 - write_page->view_offset_y - y1 * write_page->scaling_y;
        } else {
            if ((write_page->view_offset_x == write_page->view_x1) && (write_page->view_offset_y == write_page->view_y1)) {
                // Regular VIEW uses viewport-relative coordinates, so WINDOW should scale against that viewport.
                write_page->scaling_x = ((float)(write_page->view_x2 - write_page->view_x1)) / (x2 - x1);
                write_page->scaling_y = ((float)(write_page->view_y2 - write_page->view_y1)) / (y2 - y1);
                write_page->scaling_offset_x = write_page->view_x1 - write_page->view_offset_x - x1 * write_page->scaling_x;
                write_page->scaling_offset_y = write_page->view_y1 - write_page->view_offset_y - y1 * write_page->scaling_y;
            } else {
                // VIEW SCREEN keeps window coordinates screen-based.
                write_page->scaling_x = ((float)(write_page->width - 1)) / (x2 - x1);
                write_page->scaling_y = ((float)(write_page->height - 1)) / (y2 - y1);
                write_page->scaling_offset_x = -x1 * write_page->scaling_x;
                write_page->scaling_offset_y = -y1 * write_page->scaling_y;
            }
        }
        write_page->window_x1 = x1;
        write_page->window_x2 = x2;
        write_page->window_y1 = y1;
        write_page->window_y2 = y2;

        if (x1 == 0) {
            if (y1 == 0) {
                if (x2 == (write_page->width - 1)) {
                    if (y2 == (write_page->height - 1)) {
                        if ((write_page->scaling_x == 1) && (write_page->scaling_y == 1)) {
                            if ((write_page->scaling_offset_x == 0) && (write_page->scaling_offset_y == 0)) {
                                goto qbg_sub_window_restore_default;
                            }
                        }
                    }
                }
            }
        }

        // adjust qbg_x & qbg_y according to new window
        write_page->x = (old_x - write_page->scaling_offset_x) / write_page->scaling_x;
        write_page->y = (old_y - write_page->scaling_offset_y) / write_page->scaling_y;

        return;
    } else {
    // restore default WINDOW coordinates
    qbg_sub_window_restore_default:
        write_page->clipping_or_scaling = 1;
        write_page->scaling_x = 1;
        write_page->scaling_y = 1;
        write_page->scaling_offset_x = 0;
        write_page->scaling_offset_y = 0;
        write_page->window_x1 = 0;
        write_page->window_x2 = write_page->width - 1;
        write_page->window_y1 = 0;
        write_page->window_y2 = write_page->height - 1;
        if (write_page->view_x1 == 0) {
            if (write_page->view_y1 == 0) {
                if (write_page->view_x2 == (write_page->width - 1)) {
                    if (write_page->view_y2 == (write_page->height - 1)) {
                        if (write_page->view_offset_x == 0) {
                            if (write_page->view_offset_y == 0) {
                                write_page->clipping_or_scaling = 0;
                            }
                        }
                    }
                }
            }
        }

        // adjust qbg_x & qbg_y according to new window
        write_page->x = old_x;
        write_page->y = old_y;

        return;
    }
qbg_sub_window_error:
    error(5);
    return;
}

void qbg_sub_view_print(int32 topline, int32 bottomline, int32 passed) {
    if (is_error_pending())
        return;

    static int32 maxrows;
    maxrows = write_page->height;
    if (!write_page->text)
        maxrows /= fontheight[write_page->font];

    if (!passed) { // topline and bottomline not passed
        write_page->top_row = 1;
        write_page->bottom_row = maxrows;
        write_page->cursor_y = 1;
        write_page->cursor_x = 1;
        write_page->holding_cursor = 0;
        return;
    }

    if (topline <= 0)
        goto error;
    if (topline > maxrows)
        goto error;
    if (bottomline < topline)
        goto error;
    if (bottomline > maxrows)
        goto error;

    write_page->top_row = topline;
    write_page->bottom_row = bottomline;
    write_page->cursor_y = write_page->top_row;
    write_page->cursor_x = 1;
    write_page->holding_cursor = 0;
    return;

error:
    error(5);
    return;
}

void qbg_sub_view(int32 x1, int32 y1, int32 x2, int32 y2, int32 fillcolor, int32 bordercolor, int32 passed) {
    //   &1                                   &4              &8
    //    (passed&2)->coords_relative_to_screen
    if (is_error_pending())
        return;
    // format: [{SCREEN}][(?,?)-(?,?)],[?],[?]
    // bordercolor draws a line AROUND THE OUTSIDE of the specified viewport
    // the current WINDOW settings do not affect inputted x,y values
    // the current VIEW settings do not affect inputted x,y values
    // REMEMBER! Recalculate WINDOW values based on new viewport dimensions
    int32 i;

    // PRE-ERROR CHECKING
    if (passed & 1) {
        if (x1 < 0)
            goto error;
        if (x1 >= write_page->width)
            goto error;
        if (y1 < 0)
            goto error;
        if (y1 >= write_page->height)
            goto error;
        if (x2 < 0)
            goto error;
        if (x2 >= write_page->width)
            goto error;
        if (y2 < 0)
            goto error;
        if (y2 >= write_page->height)
            goto error;
    } else {
        if (passed & 2)
            goto error;
        if (passed & 4)
            goto error;
        if (passed & 8)
            goto error;
    }

    // reset DRAW attributes
    write_page->draw_ta = 0.0;
    write_page->draw_scale = 1.0;

    if (passed & 1) {
        // force x1,y1 to be the top left corner
        if (x2 < x1) {
            i = x1;
            x1 = x2;
            x2 = i;
        }
        if (y2 < y1) {
            i = y1;
            y1 = y2;
            y2 = i;
        }

        write_page->view_x1 = x1;
        write_page->view_y1 = y1;
        write_page->view_x2 = x2;
        write_page->view_y2 = y2;
        if ((passed & 2) == 0) {
            write_page->view_offset_x = x1;
            write_page->view_offset_y = y1;
        } else {
            write_page->view_offset_x = 0;
            write_page->view_offset_y = 0;
        }
        if (!write_page->clipping_or_scaling)
            write_page->clipping_or_scaling = 1;
    } else {
        // no arguments passed
        write_page->view_x1 = 0;
        write_page->view_y1 = 0;
        write_page->view_x2 = write_page->width - 1;
        write_page->view_y2 = write_page->height - 1;
        write_page->view_offset_x = 0;
        write_page->view_offset_y = 0;
        if (write_page->clipping_or_scaling == 1)
            write_page->clipping_or_scaling = 0;
    }

    if (passed & 4) { // fillcolor
        qb32_boxfill(write_page->window_x1, write_page->window_y1, write_page->window_x2, write_page->window_y2, fillcolor);
    }

    if (passed & 8) { // bordercolor
        static int32 bx, by;
        by = write_page->view_y1 - 1;
        if ((by >= 0) && (by < write_page->height)) {
            for (bx = write_page->view_x1 - 1; bx <= write_page->view_x2; bx++) {
                if ((bx >= 0) && (bx < write_page->width)) {
                    pset(bx, by, bordercolor);
                }
            }
        }
        by = write_page->view_y2 + 1;
        if ((by >= 0) && (by < write_page->height)) {
            for (bx = write_page->view_x1 - 1; bx <= write_page->view_x2; bx++) {
                if ((bx >= 0) && (bx < write_page->width)) {
                    pset(bx, by, bordercolor);
                }
            }
        }
        bx = write_page->view_x1 - 1;
        if ((bx >= 0) && (bx < write_page->width)) {
            for (by = write_page->view_y1 - 1; by <= write_page->view_y2; by++) {
                if ((by >= 0) && (by < write_page->height)) {
                    pset(bx, by, bordercolor);
                }
            }
        }
        bx = write_page->view_x2 + 1;
        if ((bx >= 0) && (bx < write_page->width)) {
            for (by = write_page->view_y1 - 1; by <= (write_page->view_y2 + 1); by++) {
                if ((by >= 0) && (by < write_page->height)) {
                    pset(bx, by, bordercolor);
                }
            }
        }
    }

    return;
error:
    error(5);
    return;
}

void qbg_sub_locate(int32 row, int32 column, int32 cursor, int32 start, int32 stop, int32 passed);

void sub_clsDest(int32 method, uint32 use_color, int32 dest, int32 passed) {
    int32 tempDest;
    if (passed & 4) {
        tempDest = func__dest(); // get the old dest
        sub__dest(dest);         // set the new dest
    }

    sub_cls(method, use_color, passed & 3); // call this regardless if we change the dest or not, just strip out that value first.

    if (passed & 4) {
        sub__dest(tempDest);
    } // restore the old dest
}

void sub_cls(int32 method, uint32 use_color, int32 passed) {
    if (is_error_pending())
        return;
    static int32 characters, i;
    static uint16 *sp;
    static uint16 clearvalue;

    if (write_page->console) {
#ifdef QB64_WINDOWS
        system("cls");                    // lazy but works
        qbg_sub_locate(1, 1, 0, 0, 0, 3); // is this really necessary?
#else
        if (passed & 2)
            qbg_sub_color(0, use_color, 0, 0, 2);
        std::cout << "\033[2J";
        qbg_sub_locate(1, 1, 0, 0, 0, 3);
#endif
        return;
    }

    // validate
    if (passed & 2) {
        if (write_page->bytes_per_pixel != 4) {
            if (use_color > write_page->mask)
                goto error;
        }
    } else {
        use_color = write_page->background_color;
    }

    if (passed & 1) {
        if ((method > 2) || (method < 0))
            goto error;
    }

    // all CLS methods reset the cursor position
    write_page->cursor_y = write_page->top_row;
    write_page->cursor_x = 1;

    // all CLS methods reset DRAW attributes
    write_page->draw_ta = 0.0;
    write_page->draw_scale = 1.0;

    if (write_page->text) {
        // precalculate a (int16) value which can be used to clear the screen
        clearvalue = (write_page->color & 0xF) + (use_color & 7) * 16 + (write_page->color & 16) * 8;
        clearvalue <<= 8;
        clearvalue += 32;
    }

    if ((passed & 1) == 0) { // no method specified
        // video mode: clear only graphics viewport
        // text mode: clear text view port AND the bottom line
        if (write_page->text) {
            // text view port
            characters = write_page->width * (write_page->bottom_row - write_page->top_row + 1);
            sp = (uint16 *)&write_page->offset[(write_page->top_row - 1) * write_page->width * 2];
            for (i = 0; i < characters; i++) {
                sp[i] = clearvalue;
            }
            // bottom line
            characters = write_page->width;
            sp = (uint16 *)&write_page->offset[(write_page->height - 1) * write_page->width * 2];
            for (i = 0; i < characters; i++) {
                sp[i] = clearvalue;
            }
            key_display_redraw = 1;
            key_update();
            return;
        } else { // graphics
            // graphics view port
            if (write_page->bytes_per_pixel == 1) { // 8-bit
                if (write_page->clipping_or_scaling) {
                    qb32_boxfill(write_page->window_x1, write_page->window_y1, write_page->window_x2, write_page->window_y2, use_color);
                } else { // fast method (no clipping/scaling)
                    memset(write_page->offset, use_color, write_page->width * write_page->height);
                }
            } else { // 32-bit
                i = write_page->alpha_disabled;
                write_page->alpha_disabled = 1;
                if (write_page->clipping_or_scaling) {
                    qb32_boxfill(write_page->window_x1, write_page->window_y1, write_page->window_x2, write_page->window_y2, use_color);
                } else { // fast method (no clipping/scaling)
                    fast_boxfill(0, 0, write_page->width - 1, write_page->height - 1, use_color);
                }
                write_page->alpha_disabled = i;
            }
        }

        if (write_page->clipping_or_scaling == 2) {
            write_page->x = ((float)(write_page->view_x2 - write_page->view_x1 + 1)) / write_page->scaling_x / 2.0f + write_page->scaling_offset_x;
            write_page->y = ((float)(write_page->view_y2 - write_page->view_y1 + 1)) / write_page->scaling_y / 2.0f + write_page->scaling_offset_y;
        } else {
            write_page->x = ((float)(write_page->view_x2 - write_page->view_x1 + 1)) / 2.0f;
            write_page->y = ((float)(write_page->view_y2 - write_page->view_y1 + 1)) / 2.0f;
        }

        key_display_redraw = 1;
        key_update();
        return;
    }

    if (method == 0) { // clear everything
        if (write_page->text) {
            characters = write_page->height * write_page->width;
            sp = (uint16 *)write_page->offset;
            for (i = 0; i < characters; i++) {
                sp[i] = clearvalue;
            }
            key_display_redraw = 1;
            key_update();
            return;
        } else {
            if (write_page->bytes_per_pixel == 1) {
                memset(write_page->offset, use_color, write_page->width * write_page->height);
            } else {
                i = write_page->alpha_disabled;
                write_page->alpha_disabled = 1;
                fast_boxfill(0, 0, write_page->width - 1, write_page->height - 1, use_color);
                write_page->alpha_disabled = i;
            }
        }

        if (write_page->clipping_or_scaling == 2) {
            write_page->x = ((float)(write_page->view_x2 - write_page->view_x1 + 1)) / write_page->scaling_x / 2.0f + write_page->scaling_offset_x;
            write_page->y = ((float)(write_page->view_y2 - write_page->view_y1 + 1)) / write_page->scaling_y / 2.0f + write_page->scaling_offset_y;
        } else {
            write_page->x = ((float)(write_page->view_x2 - write_page->view_x1 + 1)) / 2.0f;
            write_page->y = ((float)(write_page->view_y2 - write_page->view_y1 + 1)) / 2.0f;
        }

        key_display_redraw = 1;
        key_update();
        return;
    }

    if (method == 1) { // ONLY clear the graphics viewport
        if (write_page->text)
            return;
        // graphics view port
        if (write_page->bytes_per_pixel == 1) { // 8-bit
            if (write_page->clipping_or_scaling) {
                qb32_boxfill(write_page->window_x1, write_page->window_y1, write_page->window_x2, write_page->window_y2, use_color);
            } else { // fast method (no clipping/scaling)
                memset(write_page->offset, use_color, write_page->width * write_page->height);
            }
        } else { // 32-bit
            i = write_page->alpha_disabled;
            write_page->alpha_disabled = 1;
            if (write_page->clipping_or_scaling) {
                qb32_boxfill(write_page->window_x1, write_page->window_y1, write_page->window_x2, write_page->window_y2, use_color);
            } else { // fast method (no clipping/scaling)
                fast_boxfill(0, 0, write_page->width - 1, write_page->height - 1, use_color);
            }
            write_page->alpha_disabled = i;
        }

        if (write_page->clipping_or_scaling == 2) {
            write_page->x = ((float)(write_page->view_x2 - write_page->view_x1 + 1)) / write_page->scaling_x / 2.0f + write_page->scaling_offset_x;
            write_page->y = ((float)(write_page->view_y2 - write_page->view_y1 + 1)) / write_page->scaling_y / 2.0f + write_page->scaling_offset_y;
        } else {
            write_page->x = ((float)(write_page->view_x2 - write_page->view_x1 + 1)) / 2.0f;
            write_page->y = ((float)(write_page->view_y2 - write_page->view_y1 + 1)) / 2.0f;
        }

        key_display_redraw = 1;
        key_update();
        return;
    }

    if (method == 2) { // ONLY clear the VIEW PRINT range text viewport
        if (write_page->text) {
            // text viewport
            characters = write_page->width * (write_page->bottom_row - write_page->top_row + 1);
            sp = (uint16 *)&write_page->offset[(write_page->top_row - 1) * write_page->width * 2];
            for (i = 0; i < characters; i++) {
                sp[i] = clearvalue;
            }
            // should not and does not redraw KEY bar
            return;
        } else {
            // text viewport
            if (write_page->bytes_per_pixel == 1) { // 8-bit
                memset(&write_page->offset[write_page->width * fontheight[write_page->font] * (write_page->top_row - 1)], use_color,
                       write_page->width * fontheight[write_page->font] * (write_page->bottom_row - write_page->top_row + 1));
            } else { // 32-bit
                i = write_page->alpha_disabled;
                write_page->alpha_disabled = 1;
                fast_boxfill(0, fontheight[write_page->font] * (write_page->top_row - 1), write_page->width - 1,
                             fontheight[write_page->font] * write_page->bottom_row - 1, use_color);
                write_page->alpha_disabled = i;
            }
            // should not and does not redraw KEY bar
            return;
        }
    }

    return;
error:
    error(5);
    return;
}

void qbg_sub_locate(int32 row, int32 column, int32 cursor, int32 start, int32 stop, int32 passed) {
    static int32 h, w, i;
    if (is_error_pending())
        return;

    if (write_page->console) {
#ifdef QB64_WINDOWS // If trying to locate with windows console
        CONSOLE_SCREEN_BUFFER_INFO cl_bufinfo;
        SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
        HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
        GetConsoleScreenBufferInfo(cl_conout, &cl_bufinfo);
        if (column == 0)
            column = cl_bufinfo.dwCursorPosition.X + 1;
        if (row == 0)
            row = cl_bufinfo.dwCursorPosition.Y + 1;
        COORD pos = {(SHORT)(column - 1), (SHORT)(row - 1)};
        HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleCursorPosition(output, pos);
#else
        // We don't have a good way of getting the current cursor position, so we ignore any LOCATEs
        // that don't give an absolute position.
        if (!(passed & 1 && passed & 2))
            return;
        printf("\033[%d;%dH", row, column);
#endif
        return;
    }

    // calculate height & width in characters
    if (write_page->compatible_mode) {
        h = write_page->height / fontheight[write_page->font];
        if (fontwidth[write_page->font]) {
            w = write_page->width / fontwidth[write_page->font];
        } else {
            w = write_page->width;
        }
    } else {
        h = write_page->height;
        w = write_page->width;
    }

    // PRE-ERROR CHECKING
    if (passed & 1) {
        if (row < write_page->top_row)
            goto error;
        if ((row != h) && (row > write_page->bottom_row)) {
            if (width8050switch) { // note: can assume WIDTH 80,25 as no SCREEN or WIDTH statements have been called
                width8050switch = 0;
                if (row <= 50) {
                    if (passed & 2) {
                        if (column < 1)
                            goto error;
                        if (column > w)
                            goto error;
                    }
                    char *buffer;
                    uint32 c, c2;
                    buffer = (char *)malloc(80 * 25 * 2);
                    c = write_page->color;
                    c2 = write_page->background_color;
                    memcpy(buffer, &cmem[0xB8000], 80 * 25 * 2);
                    qbsub_width(0, 80, 50, 0, 0, 3);
                    memcpy(&cmem[0xB8000], buffer, 80 * 25 * 2);
                    write_page->color = c;
                    write_page->background_color = c2;
                    free(buffer);
                    goto width8050switch_done;
                }
            }
            goto error;
        }
    }
width8050switch_done:
    if (passed & 2) {
        if (column < 1)
            goto error;
        if (column > w)
            goto error;
    }
    if (passed & 4) {
        if (cursor < 0)
            goto error;
        if (cursor > 1)
            goto error;
    }
    if (passed & 8) {
        if (start < 0)
            goto error;
        if (start > 31)
            goto error;
    }
    if (passed & 16) {
        if (stop < 0)
            goto error;
        if (stop > 31)
            goto error;
    }

    if (passed & 1) {
        write_page->cursor_y = row;
        write_page->holding_cursor = 0;
    }
    if (passed & 2) {
        write_page->cursor_x = column;
        write_page->holding_cursor = 0;
    }
    if ((passed & 3) == 0) {
        if (write_page->holding_cursor)
            write_page->holding_cursor = 2; // special case[forum/index.php?topic=5255.0]
    }

    if (passed & 4) {
        if (cursor)
            cursor = 1;
        write_page->cursor_show = cursor;
        if (write_page->flags & IMG_SCREEN) { // page-linked attribute
            for (i = 0; i < pages; i++) {
                if (page[i])
                    img[i].cursor_show = cursor;
            }
        } // IMG_SCREEN
    } // passed&4

    if (passed & 8) {
        write_page->cursor_firstvalue = start;
    } else {
        start = write_page->cursor_firstvalue;
    }
    if (passed & 16) {
        write_page->cursor_lastvalue = stop;
    } else {
        stop = write_page->cursor_lastvalue;
    }
    if (passed & (8 + 16)) {
        if (write_page->flags & IMG_SCREEN) { // page-linked attribute
            for (i = 0; i < pages; i++) {
                if (page[i]) {
                    img[i].cursor_firstvalue = start;
                    img[i].cursor_lastvalue = stop;
                }
            } // i
        } // IMG_SCREEN
    }

    return;

error:
    error(5);
    return;
}

// input helper functions:
uint64 hexoct2uint64_value;

int32 hexoct2uint64(qbs *h) {
    // returns 0 = failed
    //         1 = HEX value (default if unspecified)
    //         2 = OCT value
    //         3 = BIN value
    static int32 i, i2;
    static uint64 result;
    result = 0;
    static int32 type;
    type = 0;
    hexoct2uint64_value = 0;
    if (!h->len)
        return 1;
    if (h->chr[0] != 38)
        return 0; // not "&"
    if (h->len == 1)
        return 1; // "&" received, but awaiting further input
    i = h->chr[1];
    if ((i == 72) || (i == 104))
        type = 1; // "H" or "h"
    if ((i == 79) || (i == 111))
        type = 2; // "O" or "o"
    if ((i == 66) || (i == 98))
        type = 3; // "B" or "b"
    if (!type)
        return 0;
    if (h->len == 2)
        return type;

    // unsigned _int64 max = 18446744073709551615   (decimal, 20 chars)
    //                     = 1111111 etc., max. 64x (binary, 64 chars)
    //                     = 1777777777777777777777 (octal, 22 chars)
    //                     = FFFFFFFFFFFFFFFF       (hex, 16 chars)

    if (type == 1) {
        if (h->len > 18)
            return 0; // larger than int64
        for (i = 2; i < h->len; i++) {
            result <<= 4;
            i2 = h->chr[i];
            //           0      -      9       /       A      -      F       /       a      -      f
            if (((i2 >= 48) && (i2 <= 57)) || ((i2 >= 65) && (i2 <= 70)) || ((i2 >= 97) && (i2 <= 102))) {
                if (i2 >= 97)
                    i2 -= 32;
                if (i2 >= 65)
                    i2 -= 7;
                i2 -= 48;
                // i2 is now a values between 0 and 15
                result += i2;
            } else
                return 0; // invalid character
        } // i
        hexoct2uint64_value = result;
        return 1;
    } // type == 1

    if (type == 2) {
        if (h->len > 24)
            return 0; // larger than int64
        if (h->len == 24) {
            if ((h->chr[2] != 48) && (h->chr[2] != 49))
                return 0; // larger than int64
        }
        for (i = 2; i < h->len; i++) {
            result <<= 3;
            i2 = h->chr[i];
            if ((i2 >= 48) && (i2 <= 55)) { // 0-7
                i2 -= 48;
                result += i2;
            } else
                return 0; // invalid character
        } // i
        hexoct2uint64_value = result;
        return 2;
    } // type == 2

    if (type == 3) {
        if (h->len > 66)
            return 0; // larger than int64
        for (i = 2; i < h->len; i++) {
            result <<= 1;
            i2 = h->chr[i];
            if ((i2 >= 48) && (i2 <= 49)) { // 0-1
                i2 -= 48;
                result += i2;
            } else
                return 0; // invalid character
        } // i
        hexoct2uint64_value = result;
        return 3;
    } // type == 3

    return 0;
}

// input method (complex, calls other qbs functions)
const char *uint64_max[] = {"18446744073709551615"};
const char *int64_max[] = {"9223372036854775807"};
const char *int64_max_neg[] = {"9223372036854775808"};
const char *single_max[] = {"3402823"};
const char *single_max_neg[] = {"1401298"};
const char *double_max[] = {"17976931"};
const char *double_max_neg[] = {"4940656"};
uint8 significant_digits[1024];
int32 num_significant_digits;

extern void *qbs_input_variableoffsets[257];
extern int32 qbs_input_variabletypes[257];
qbs *qbs_input_arguements[257];
int32 cursor_show_last;

void qbs_input(int32 numvariables, uint8 newline) {
    if (is_error_pending())
        return;
    int32 i, i2, i3, i4, i5, i6, chr;

    static int32 autodisplay_backup;
    autodisplay_backup = autodisplay;
    autodisplay = 1;

    static int32 source_backup;
    source_backup = func__source();
    sub__source(func__dest());

    // duplicate dest image so any changes can be reverted
    static int32 dest_image, dest_image_temp, dest_image_holding_cursor;
    dest_image = func__copyimage(func__dest(), NULL, NULL);
    if (dest_image == -1)
        error(516); // out of memory
    dest_image_temp = func__copyimage(func__dest(), NULL, NULL);
    if (dest_image_temp == -1)
        error(517); // out of memory
    static int32 dest_image_cursor_x, dest_image_cursor_y;
    dest_image_cursor_x = write_page->cursor_x;
    dest_image_cursor_y = write_page->cursor_y;
    dest_image_holding_cursor = write_page->holding_cursor;

    uint32 qbs_tmp_base = qbs_tmp_list_nexti;

    static int32 lineinput;
    lineinput = 0;
    if (qbs_input_variabletypes[1] & ISSTRING) {
        if (qbs_input_variabletypes[1] & 512) {
            qbs_input_variabletypes[1] = -512;
            lineinput = 1;
        }
    }

    cursor_show_last = write_page->cursor_show;
    write_page->cursor_show = 1;

    int32 addspaces;
    addspaces = 0;
    qbs *inpstr = qbs_new(0, 0);  // not temp so must be freed
    qbs *inpstr2 = qbs_new(0, 0); // not temp so must be freed
    qbs *key = qbs_new(0, 0);     // not temp so must be freed
    qbs *c = qbs_new(1, 0);       // not temp so must be freed

    for (i = 1; i <= numvariables; i++)
        qbs_input_arguements[i] = qbs_new(0, 0);

    // init all passed variables to 0 or ""
    for (i = 1; i <= numvariables; i++) {

        if (qbs_input_variabletypes[i] & ISSTRING) { // STRING
            if (((qbs *)qbs_input_variableoffsets[i])->fixed) {
                memset(((qbs *)qbs_input_variableoffsets[i])->chr, 32, ((qbs *)qbs_input_variableoffsets[i])->len);
            } else {
                ((qbs *)qbs_input_variableoffsets[i])->len = 0;
            }
        }

        if ((qbs_input_variabletypes[i] & ISOFFSETINBITS) == 0) { // reg. numeric variable
            memset(qbs_input_variableoffsets[i], 0, (qbs_input_variabletypes[i] & 511) >> 3);
        }

        // bit referenced?

    } // i

qbs_input_next:

    int32 argn, firstchr, toomany;
    toomany = 0;
    argn = 1;
    i = 0;
    i2 = 0;
    qbs_input_arguements[1]->len = 0;
    firstchr = 1;
qbs_input_sep_arg:

    if (i < inpstr->len) {

        if (inpstr->chr[i] == 44) { //","
            if (i2 != 1) {          // not in the middle of a string
                if (!lineinput) {
                    i2 = 0;
                    argn = argn + 1;
                    if (argn > numvariables) {
                        toomany = 1;
                        goto qbs_input_sep_arg_done;
                    }
                    qbs_input_arguements[argn]->len = 0;
                    firstchr = 1;
                    goto qbs_input_next_arg;
                }
            }
        }

        if (inpstr->chr[i] == 34) { //"
            if (firstchr) {
                if (!lineinput) {
                    i2 = 1; // requires closure
                    firstchr = 0;
                    goto qbs_input_next_arg;
                }
            }
            if (i2 == 1) {
                i2 = 2;
                goto qbs_input_next_arg;
            }
        }

        if (i2 == 2) {
            goto backspace; // INVALID! Cannot have any characters after a closed "..."
        }

        c->chr[0] = inpstr->chr[i];
        qbs_set(qbs_input_arguements[argn], qbs_add(qbs_input_arguements[argn], c));

        firstchr = 0;
    qbs_input_next_arg:;
        i++;
        goto qbs_input_sep_arg;
    }
qbs_input_sep_arg_done:
    if (toomany)
        goto backspace;

    // validate current arguments
    // ASSUME LEADING & TRAILING SPACES REMOVED!
    uint8 valid;
    uint8 neg;
    int32 completewith;
    int32 l;
    uint8 *cp, *cp2;
    uint64 max, max_neg, multiple, value;
    uint64 hexvalue;

    completewith = -1;
    valid = 1;
    l = qbs_input_arguements[argn]->len;
    cp = qbs_input_arguements[argn]->chr;
    neg = 0;

    if ((qbs_input_variabletypes[argn] & ISSTRING) == 0) {
        if ((qbs_input_variabletypes[argn] & ISFLOAT) == 0) {
            if ((qbs_input_variabletypes[argn] & 511) <= 32) { // cannot handle INTEGER64 variables using this method!
                int64 finalvalue;
                // it's an integer variable!
                finalvalue = 0;
                if (l == 0) {
                    completewith = 48;
                    goto typechecked_integer;
                }
                // calculate max & max_neg (i4 used to store number of bits)
                i4 = qbs_input_variabletypes[argn] & 511;
                max = 1;
                max <<= i4;
                max--;

                // check for hex/oct/bin
                if ((i3 = hexoct2uint64(qbs_input_arguements[argn]))) {
                    hexvalue = hexoct2uint64_value;
                    if (hexvalue > max) {
                        valid = 0;
                        goto typechecked;
                    }
                    // i. check max num of "digits" required to represent a value, if more exist cull excess
                    // ii. set completewith value (if necessary)
                    if (i3 == 1) {
                        value = max;
                        i = 0;
                        for (i2 = 1; i2 <= 16; i2++) {
                            if (value & 0xF)
                                i = i2;
                            value >>= 4;
                        }
                        if (l > (2 + i)) {
                            valid = 0;
                            goto typechecked;
                        }
                        if (l == 1)
                            completewith = 72; //"H"
                        if (l == 2)
                            completewith = 48; //"0"
                    }
                    if (i3 == 2) {
                        value = max;
                        i = 0;
                        for (i2 = 1; i2 <= 22; i2++) {
                            if (value & 0x7)
                                i = i2;
                            value >>= 3;
                        }
                        if (l > (2 + i)) {
                            valid = 0;
                            goto typechecked;
                        }
                        if (l == 1)
                            completewith = 79; //"O"
                        if (l == 2)
                            completewith = 48; //"0"
                    }
                    if (i3 == 3) {
                        value = max;
                        i = 0;
                        for (i2 = 1; i2 <= 64; i2++) {
                            if (value & 0x1)
                                i = i2;
                            value >>= 1;
                        }
                        if (l > (2 + i)) {
                            valid = 0;
                            goto typechecked;
                        }
                        if (l == 1)
                            completewith = 66; //"B"
                        if (l == 2)
                            completewith = 48; //"0"
                    }
                    finalvalue = hexvalue;
                    goto typechecked_integer;
                }

                // max currently contains the largest UNSIGNED value possible, adjust as necessary
                if (qbs_input_variabletypes[argn] & ISUNSIGNED) {
                    max_neg = 0;
                } else {
                    max >>= 1;
                    max_neg = max + 1;
                }
                // check for - sign
                i2 = 0;
                if ((qbs_input_variabletypes[argn] & ISUNSIGNED) == 0) {
                    if (cp[i2] == 45) { //"-"
                        if (l == 1) {
                            completewith = 48;
                            goto typechecked_integer;
                        }
                        i2++;
                        neg = 1;
                    }
                }
                // after a leading 0 no other digits are possible, return an error if this is the case
                if (cp[i2] == 48) {
                    if (l > (i2 + 1)) {
                        valid = 0;
                        goto typechecked;
                    }
                }
                // scan the "number"...
                multiple = 1;
                value = 0;
                for (i = l - 1; i >= i2; i--) {
                    i3 = cp[i] - 48;
                    if ((i3 >= 0) && (i3 <= 9)) {
                        value += multiple * i3;
                        if (qbs_input_variabletypes[argn] & ISUNSIGNED) {
                            if (value > max) {
                                valid = 0;
                                goto typechecked;
                            }
                        } else {
                            if (neg) {
                                if (value > max_neg) {
                                    valid = 0;
                                    goto typechecked;
                                }
                            } else {
                                if (value > max) {
                                    valid = 0;
                                    goto typechecked;
                                }
                            }
                        }
                    } else {
                        valid = 0;
                        goto typechecked;
                    }
                    multiple *= 10;
                } // next i
                if (neg)
                    finalvalue = -value;
                else
                    finalvalue = value;
            typechecked_integer:
                // set variable to finalvalue
                if ((qbs_input_variabletypes[argn] & ISOFFSETINBITS) == 0) { // reg. numeric variable
                    memcpy(qbs_input_variableoffsets[argn], &finalvalue, (qbs_input_variabletypes[argn] & 511) >> 3);
                }
                goto typechecked;
            }
        }
    }

    if (qbs_input_variabletypes[argn] & ISSTRING) {
        if (((qbs *)qbs_input_variableoffsets[argn])->fixed) {
            if (l > ((qbs *)qbs_input_variableoffsets[argn])->len) {
                valid = 0;
                goto typechecked;
            }
        }
        qbs_set((qbs *)qbs_input_variableoffsets[argn], qbs_input_arguements[argn]);
        goto typechecked;
    }

    // INTEGER64 type
    // int64 range:          \969223372036854775808 to  9223372036854775807
    // uint64 range: 0                    to 18446744073709551615
    if ((qbs_input_variabletypes[argn] & ISSTRING) == 0) {
        if ((qbs_input_variabletypes[argn] & ISFLOAT) == 0) {
            if ((qbs_input_variabletypes[argn] & 511) == 64) {
                if (l == 0) {
                    completewith = 48;
                    *(int64 *)qbs_input_variableoffsets[argn] = 0;
                    goto typechecked;
                }

                // check for hex/oct/bin
                if ((i3 = hexoct2uint64(qbs_input_arguements[argn]))) {
                    hexvalue = hexoct2uint64_value;
                    if (hexvalue > max) {
                        valid = 0;
                        goto typechecked;
                    }
                    // set completewith value (if necessary)
                    if (i3 == 1)
                        if (l == 1)
                            completewith = 72; //"H"
                    if (i3 == 2)
                        if (l == 1)
                            completewith = 79; //"O"
                    if (i3 == 3)
                        if (l == 1)
                            completewith = 66; //"B"
                    if (l == 2)
                        completewith = 48; //"0"
                    *(uint64 *)qbs_input_variableoffsets[argn] = hexvalue;
                    goto typechecked;
                }

                // check for - sign
                i2 = 0;
                if ((qbs_input_variabletypes[argn] & ISUNSIGNED) == 0) {
                    if (cp[i2] == 45) { //"-"
                        if (l == 1) {
                            completewith = 48;
                            *(int64 *)qbs_input_variableoffsets[argn] = 0;
                            goto typechecked;
                        }
                        i2++;
                        neg = 1;
                    }
                }
                // after a leading 0 no other digits are possible, return an error if this is the case
                if (cp[i2] == 48) {
                    if (l > (i2 + 1)) {
                        valid = 0;
                        goto typechecked;
                    }
                }
                // count how many digits are in the number
                i4 = 0;
                for (i = l - 1; i >= i2; i--) {
                    i3 = cp[i] - 48;
                    if ((i3 < 0) || (i3 > 9)) {
                        valid = 0;
                        goto typechecked;
                    }
                    i4++;
                } // i
                if (qbs_input_variabletypes[argn] & ISUNSIGNED) {
                    if (i4 < 20)
                        goto typechecked_int64;
                    if (i4 > 20) {
                        valid = 0;
                        goto typechecked;
                    }

                    cp2 = (uint8 *)uint64_max[0];
                } else {
                    if (i4 < 19)
                        goto typechecked_int64;
                    if (i4 > 19) {
                        valid = 0;
                        goto typechecked;
                    }
                    if (neg)
                        cp2 = (uint8 *)int64_max_neg[0];
                    else
                        cp2 = (uint8 *)int64_max[0];
                }
                // number of digits valid, but exact value requires checking
                cp = qbs_input_arguements[argn]->chr;
                for (i = 0; i < i4; i++) {
                    if (cp[i + i2] < cp2[i])
                        goto typechecked_int64;
                    if (cp[i + i2] > cp2[i]) {
                        valid = 0;
                        goto typechecked;
                    }
                }
            typechecked_int64:
                // add character 0 to end to make it a null terminated string
                c->chr[0] = 0;
                qbs_set(qbs_input_arguements[argn], qbs_add(qbs_input_arguements[argn], c));
                if (qbs_input_variabletypes[argn] & ISUNSIGNED) {
                    sscanf((char *)qbs_input_arguements[argn]->chr, "%" SCNu64, (uint64_t *)qbs_input_variableoffsets[argn]);
                } else {
                    sscanf((char *)qbs_input_arguements[argn]->chr, "%" SCNd64, (int64_t *)qbs_input_variableoffsets[argn]);
                }
                goto typechecked;
            }
        }
    }

    // check ISFLOAT type?
    //[-]9999[.]9999[E/D][+/-]99999
    if (qbs_input_variabletypes[argn] & ISFLOAT) {
        static int32 digits_before_point;
        static int32 digits_after_point;
        static int32 zeros_after_point;
        static int32 neg_power;
        digits_before_point = 0;
        digits_after_point = 0;
        neg_power = 0;
        value = 0;
        zeros_after_point = 0;
        num_significant_digits = 0;

        // set variable to 0
        if ((qbs_input_variabletypes[argn] & 511) == 32)
            *(float *)qbs_input_variableoffsets[argn] = 0;
        if ((qbs_input_variabletypes[argn] & 511) == 64)
            *(double *)qbs_input_variableoffsets[argn] = 0;
        if ((qbs_input_variabletypes[argn] & 511) == 256)
            *(long double *)qbs_input_variableoffsets[argn] = 0;

        // begin with a generic assessment, regardless of whether it is single, double or float
        if (l == 0) {
            completewith = 48;
            goto typechecked;
        }

        // check for hex/oct/bin
        if ((i3 = hexoct2uint64(qbs_input_arguements[argn]))) {
            hexvalue = hexoct2uint64_value;
            // set completewith value (if necessary)
            if (i3 == 1)
                if (l == 1)
                    completewith = 72; //"H"
            if (i3 == 2)
                if (l == 1)
                    completewith = 79; //"O"
            if (i3 == 3)
                if (l == 1)
                    completewith = 66; //"B"
            if (l == 2)
                completewith = 48; //"0"
            // nb. because VC6 didn't support...
            // error C2520: conversion from uint64 to double not implemented, use signed int64
            // I've implemented a work-around so correct values will be returned
            static int64 transfer;
            transfer = 0x7FFFFFFF;
            transfer <<= 32;
            transfer |= 0xFFFFFFFF;
            while (hexvalue > transfer) {
                hexvalue -= transfer;
                if ((qbs_input_variabletypes[argn] & 511) == 32)
                    *(float *)qbs_input_variableoffsets[argn] += transfer;
                if ((qbs_input_variabletypes[argn] & 511) == 64)
                    *(double *)qbs_input_variableoffsets[argn] += transfer;
                if ((qbs_input_variabletypes[argn] & 511) == 256)
                    *(long double *)qbs_input_variableoffsets[argn] += transfer;
            }
            transfer = hexvalue;
            if ((qbs_input_variabletypes[argn] & 511) == 32)
                *(float *)qbs_input_variableoffsets[argn] += transfer;
            if ((qbs_input_variabletypes[argn] & 511) == 64)
                *(double *)qbs_input_variableoffsets[argn] += transfer;
            if ((qbs_input_variabletypes[argn] & 511) == 256)
                *(long double *)qbs_input_variableoffsets[argn] += transfer;
            goto typechecked;
        }

        // check for - sign
        i2 = 0;
        if (cp[i2] == 45) { //"-"
            if (l == 1) {
                completewith = 48;
                goto typechecked;
            }
            i2++;
            neg = 1;
        }
        // if it starts with 0, it may only have one leading 0
        if (cp[i2] == 48) {
            if (l > (i2 + 1)) {
                i2++;
                if (cp[i2] == 46)
                    goto decimal_point;
                valid = 0;
                goto typechecked; // expected a decimal point
                // nb. of course, user could have typed D or E BUT there is no point
                //    calculating 0 to the power of anything!
            } else
                goto typechecked; // validate, as no other data is required
        }
        // scan digits before decimal place
        for (i = i2; i < l; i++) {
            i3 = cp[i];
            if ((i3 == 68) || (i3 == (68 + 32)) || (i3 == 69) || (i3 == (69 + 32))) { // d,D,e,E?
                if (i == i2) {
                    valid = 0;
                    goto typechecked;
                } // cannot begin with d,D,e,E!
                i2 = i;
                goto exponent;
            }
            if (i3 == 46) {
                i2 = i;
                goto decimal_point;
            } // nb. it can begin with a decimal point!
            i3 -= 48;
            if ((i3 < 0) || (i3 > 9)) {
                valid = 0;
                goto typechecked;
            }
            digits_before_point++;
            // nb. because leading 0 is handled differently, all digits are significant
            significant_digits[num_significant_digits] = i3 + 48;
            num_significant_digits++;
        }
        goto assess_float;
    ////////////////////////////////
    decimal_point:;
        i4 = 1;
        if (i2 == (l - 1)) {
            completewith = 48;
            goto assess_float;
        }
        i2++;
        for (i = i2; i < l; i++) {
            i3 = cp[i];
            if ((i3 == 68) || (i3 == (68 + 32)) || (i3 == 69) || (i3 == (69 + 32))) { // d,D,e,E?
                if (num_significant_digits) {
                    if (i == i2) {
                        valid = 0;
                        goto typechecked;
                    } // cannot begin with d,D,e,E just after a decimal point!
                    i2 = i;
                    goto exponent;
                }
            }
            i3 -= 48;
            if ((i3 < 0) || (i3 > 9)) {
                valid = 0;
                goto typechecked;
            }
            if (i3)
                i4 = 0;
            if (i4)
                zeros_after_point++;
            digits_after_point++;
            if ((num_significant_digits) || i3) {
                significant_digits[num_significant_digits] = i3 + 48;
                num_significant_digits++;
            }
        } // i
        goto assess_float;
    ////////////////////////////////
    exponent:;

        // ban d/D for SINGLE precision input
        if ((qbs_input_variabletypes[argn] & 511) == 32) { // SINGLE
            i3 = cp[i2];
            if ((i3 == 68) || (i3 == (68 + 32))) { // d/D
                valid = 0;
                goto typechecked;
            }
        }
        // correct "D" notation for c++ scanf
        i3 = cp[i2];
        if ((i3 == 68) || (i3 == (68 + 32))) { // d/D
            cp[i2] = 69;                       //"E"
        }
        if (i2 == (l - 1)) {
            completewith = 48;
            goto assess_float;
        }
        i2++;
        // check for optional + or -
        i3 = cp[i2];
        if (i3 == 45) { //"-"
            if (i2 == (l - 1)) {
                completewith = 48;
                goto assess_float;
            }
            neg_power = 1;
            i2++;
        }
        if (i3 == 43) { //"+"
            if (i2 == (l - 1)) {
                completewith = 48;
                goto assess_float;
            }
            i2++;
        }
        // nothing valid after a leading 0
        if (cp[i2] == 48) { // 0
            if (l > (i2 + 1)) {
                valid = 0;
                goto typechecked;
            }
        }
        multiple = 1;
        value = 0;
        for (i = l - 1; i >= i2; i--) {
            i3 = cp[i] - 48;
            if ((i3 >= 0) && (i3 <= 9)) {

                value += multiple * i3;
            } else {
                valid = 0;
                goto typechecked;
            }
            multiple *= 10;
        } // i
    //////////////////////////
    assess_float:;
        // nb. 0.???? means digits_before_point==0

        if ((qbs_input_variabletypes[argn] & 511) == 32) { // SINGLE
            // QB:           \B13.402823    E+38 to \B11.401298    E-45
            // WIKIPEDIA:    \B13.4028234   E+38 to ?
            // OTHER SOURCE: \B13.402823466 E+38 to \B11.175494351 E-38
            if (neg_power)
                value = -value;
            // special case->single 0 after point
            if ((zeros_after_point == 1) && (digits_after_point == 1)) {
                digits_after_point = 0;
                zeros_after_point = 0;
            }
            // upper overflow check
            // i. check that value doesn't consist solely of 0's
            if (zeros_after_point > 43) {
                valid = 0;
                goto typechecked;
            } // cannot go any further without reversal by exponent
            if ((digits_before_point == 0) && (digits_after_point == zeros_after_point))
                goto nooverflow_float;
            // ii. calculate the position of the first WHOLE digit (in i)
            i = digits_before_point;
            if (!i)
                i = -zeros_after_point;
            /*EXAMPLES:
                1.0         i=1
                12.0        i=2
                0.1         i=0
                0.01        i=-1
            */
            i = i + value; // apply exponent
            if (i > 39) {
                valid = 0;
                goto typechecked;
            }
            // nb. the above blocks the ability to type a long-int32 number and use a neg exponent
            //    to validate it
            //********IMPORTANT: if i==39 then the first 7 digits MUST be scanned!!!
            if (i == 39) {
                cp2 = (uint8 *)single_max[0];
                i2 = num_significant_digits;
                if (i2 > 7)
                    i2 = 7;
                for (i3 = 0; i3 < i2; i3++) {
                    if (significant_digits[i3] > *cp2) {
                        valid = 0;
                        goto typechecked;
                    }
                    if (significant_digits[i3] < *cp2)
                        break;
                    cp2++;
                }
            }
            // check for pointless levels of precision (eg. 1.21351273512653625116212!)
            if (digits_after_point) {
                if (digits_before_point) {
                    if ((digits_after_point + digits_before_point) > 8) {
                        valid = 0;
                        goto typechecked;
                    }
                } else {
                    if ((digits_after_point - zeros_after_point) > 8) {
                        valid = 0;
                        goto typechecked;
                    }
                }
            }
            // check for "under-flow"
            if (i < -44) {
                valid = 0;
                goto typechecked;
            }
            //********IMPORTANT: if i==-44 then the first 7 digits MUST be scanned!!!
            if (i == -44) {
                cp2 = (uint8 *)single_max_neg[0];
                i2 = num_significant_digits;
                if (i2 > 7)
                    i2 = 7;
                for (i3 = 0; i3 < i2; i3++) {
                    if (significant_digits[i3] < *cp2) {
                        valid = 0;
                        goto typechecked;
                    }
                    if (significant_digits[i3] > *cp2)
                        break;
                    cp2++;
                }
            }
        nooverflow_float:;
            c->chr[0] = 0;
            qbs_set(qbs_input_arguements[argn], qbs_add(qbs_input_arguements[argn], c));
            sscanf((char *)qbs_input_arguements[argn]->chr, "%f", (float *)qbs_input_variableoffsets[argn]);
            goto typechecked;
        }

        if ((qbs_input_variabletypes[argn] & 511) == 64) { // DOUBLE
            // QB: Double (15-digit) precision \B11.7976931 D+308 to \B14.940656 D-324
            // WIKIPEDIA:    \B11.7976931348623157 D+308 to ???
            // OTHER SOURCE: \B11.7976931348623157 D+308 to \B12.2250738585072014E-308

            if (neg_power)
                value = -value;
            // special case->single 0 after point
            if ((zeros_after_point == 1) && (digits_after_point == 1)) {
                digits_after_point = 0;
                zeros_after_point = 0;
            }
            // upper overflow check
            // i. check that value doesn't consist solely of 0's
            if (zeros_after_point > 322) {
                valid = 0;
                goto typechecked;
            } // cannot go any further without reversal by exponent
            if ((digits_before_point == 0) && (digits_after_point == zeros_after_point))
                goto nooverflow_double;
            // ii. calculate the position of the first WHOLE digit (in i)
            i = digits_before_point;
            if (!i)
                i = -zeros_after_point;
            i = i + value; // apply exponent
            if (i > 309) {
                valid = 0;
                goto typechecked;
            }
            // nb. the above blocks the ability to type a long-int32 number and use a neg exponent
            //    to validate it
            //********IMPORTANT: if i==309 then the first 8 digits MUST be scanned!!!
            if (i == 309) {
                cp2 = (uint8 *)double_max[0];
                i2 = num_significant_digits;
                if (i2 > 8)
                    i2 = 8;
                for (i3 = 0; i3 < i2; i3++) {
                    if (significant_digits[i3] > *cp2) {
                        valid = 0;
                        goto typechecked;
                    }
                    if (significant_digits[i3] < *cp2)
                        break;
                    cp2++;
                }
            }
            // check for pointless levels of precision (eg. 1.21351273512653625116212!)
            if (digits_after_point) {
                if (digits_before_point) {
                    if ((digits_after_point + digits_before_point) > 16) {
                        valid = 0;
                        goto typechecked;
                    }
                } else {
                    if ((digits_after_point - zeros_after_point) > 16) {
                        valid = 0;
                        goto typechecked;
                    }
                }
            }
            // check for "under-flow"
            if (i < -323) {
                valid = 0;
                goto typechecked;
            }
            //********IMPORTANT: if i==-323 then the first 7 digits MUST be scanned!!!
            if (i == -323) {
                cp2 = (uint8 *)double_max_neg[0];
                i2 = num_significant_digits;
                if (i2 > 7)
                    i2 = 7;
                for (i3 = 0; i3 < i2; i3++) {
                    if (significant_digits[i3] < *cp2) {
                        valid = 0;
                        goto typechecked;
                    }
                    if (significant_digits[i3] > *cp2)
                        break;
                    cp2++;
                }
            }
        nooverflow_double:;
            c->chr[0] = 0;
            qbs_set(qbs_input_arguements[argn], qbs_add(qbs_input_arguements[argn], c));
            sscanf((char *)qbs_input_arguements[argn]->chr, "%lf", (double *)qbs_input_variableoffsets[argn]);
            goto typechecked;
        }

        if ((qbs_input_variabletypes[argn] & 511) == 256) { // FLOAT
            // at present, there is no defined limit for FLOAT type numbers, so no restrictions
            // are applied!
            c->chr[0] = 0;
            qbs_set(qbs_input_arguements[argn], qbs_add(qbs_input_arguements[argn], c));

            // sscanf((char*)qbs_input_arguements[argn]->chr,"%lf",(long double*)qbs_input_variableoffsets[argn]);
            static double sscanf_fix;
            sscanf((char *)qbs_input_arguements[argn]->chr, "%lf", &sscanf_fix);
            *(long double *)qbs_input_variableoffsets[argn] = sscanf_fix;
        }

    } // ISFLOAT

// undefined/uncheckable types fall through as valid!
typechecked:;
    if (!valid)
        goto backspace;

    qbs_set(inpstr2, inpstr);

    // input a key

qbs_input_invalidinput:

    static int32 showing_cursor;
    showing_cursor = 0;

qbs_input_wait_for_key:

    // toggle box cursor
    if (!write_page->text) {
        i = 1;
        if ((write_page->font >= 32) || (write_page->compatible_mode == 256) || (write_page->compatible_mode == 32)) {
            if (GetTicks() & 512)
                i = 0;
        }
        if (i != showing_cursor) {
            showing_cursor ^= 1;
            static int32 x, y, x2, y2, fx, fy, alpha, cw;
            static uint32 c;
            alpha = write_page->alpha_disabled;
            write_page->alpha_disabled = 1;
            fy = fontheight[write_page->font];
            fx = fontwidth[write_page->font];
            if (!fx)
                fx = 1;
            cw = fx;
            if ((write_page->font >= 32) || (write_page->compatible_mode == 256) || (write_page->compatible_mode == 32))
                cw = 1;
            y2 = (write_page->cursor_y - 1) * fy;
            for (y = 0; y < fy; y++) {
                x2 = (write_page->cursor_x - 1) * fx;
                for (x = 0; x < cw; x++) {
                    pset(x2, y2, point(x2, y2) ^ write_page->color);
                    x2++;
                }
                y2++;
            }
            write_page->alpha_disabled = alpha;
        }
    } //! write_page->text

    if (addspaces) {
        addspaces--;
        c->chr[0] = 32;
        qbs_set(key, c);
    } else {

        if (write_page->console) {
            qbs_set(key, qbs_new_txt(""));
            chr = fgetc(stdin);
            if (chr != EOF) {
                if (chr == '\n')
                    chr = 13;
                qbs_set(key, qbs_new_txt(" "));
                key->chr[0] = chr;
            } else {
                Sleep(10);
            }
        } else {
            Sleep(10);
            qbs_set(key, qbs_inkey());
        }

        qbs_cleanup(qbs_tmp_base, 0);
    }
    if (stop_program)
        return;
    if (key->len != 1)
        goto qbs_input_wait_for_key;

    // remove box cursor
    if (!write_page->text) {
        if (showing_cursor) {
            showing_cursor = 0;
            static int32 x, y, x2, y2, fx, fy, cw, alpha;
            static uint32 c;
            alpha = write_page->alpha_disabled;
            write_page->alpha_disabled = 1;
            fy = fontheight[write_page->font];
            fx = fontwidth[write_page->font];
            if (!fx)
                fx = 1;
            cw = fx;
            if ((write_page->font >= 32) || (write_page->compatible_mode == 256) || (write_page->compatible_mode == 32))
                cw = 1;
            y2 = (write_page->cursor_y - 1) * fy;
            for (y = 0; y < fy; y++) {
                x2 = (write_page->cursor_x - 1) * fx;
                for (x = 0; x < cw; x++) {
                    pset(x2, y2, point(x2, y2) ^ write_page->color);
                    x2++;
                }
                y2++;
            }
            write_page->alpha_disabled = alpha;
        }
    } //! write_page->text

    // input should disallow certain characters
    if (key->chr[0] == 7) {
        qbs_print(key, 0);
        goto qbs_input_next;
    } // beep!
    if (key->chr[0] == 10)
        goto qbs_input_next; // linefeed
    if (key->chr[0] == 9) {  // tab
        i = 8 - (inpstr2->len & 7);
        addspaces = i;
        goto qbs_input_next;
    }
    // other ASCII chars that cannot be printed
    if (key->chr[0] == 11)
        goto qbs_input_next;
    if (key->chr[0] == 12)
        goto qbs_input_next;
    if (key->chr[0] == 28)
        goto qbs_input_next;
    if (key->chr[0] == 29)
        goto qbs_input_next;
    if (key->chr[0] == 30)
        goto qbs_input_next;
    if (key->chr[0] == 31)
        goto qbs_input_next;

    if (key->chr[0] == 13) {
        // assume input is valid

        // autofinish (if necessary)

        // assume all parts entered

        for (i = 1; i <= numvariables; i++) {
            qbs_free(qbs_input_arguements[i]);
        }

        if (newline) {
            c->len = 0;
            if (!write_page->console)
                qbs_print(c, 1);
        }
        qbs_free(c);
        qbs_free(inpstr);
        qbs_free(inpstr2);
        qbs_free(key);

        write_page->cursor_show = cursor_show_last;

        sub__freeimage(dest_image, 1);
        sub__freeimage(dest_image_temp, 1);

        if (autodisplay_backup == 0) {
            autodisplay = -1; // toggle request
            while (autodisplay)
                Sleep(1);
        }

        sub__source(source_backup);

        return;
    }

    if (key->chr[0] == 8) { // backspace
    backspace:
        if (!inpstr->len)
            goto qbs_input_invalidinput;
        inpstr->len--;
        i2 = func__dest();                      // backup current dest
        sub_pcopy(dest_image, dest_image_temp); // copy original background to temp
        // write characters to temp
        sub__dest(dest_image_temp);
        write_page->cursor_x = dest_image_cursor_x;
        write_page->cursor_y = dest_image_cursor_y;
        write_page->holding_cursor = dest_image_holding_cursor;
        for (i = 0; i < inpstr->len; i++) {
            key->chr[0] = inpstr->chr[i];
            qbs_print(key, 0);
        }
        sub__dest(i2);
        // copy temp to dest
        sub_pcopy(dest_image_temp, i2);
        // update cursor
        write_page->cursor_x = img[-dest_image_temp].cursor_x;
        write_page->cursor_y = img[-dest_image_temp].cursor_y;
        goto qbs_input_next;
    }

    // affect inpstr2 with key
    qbs_set(inpstr2, qbs_add(inpstr2, key));

    // perform actual update
    if (!write_page->console)
        qbs_print(key, 0);

    qbs_set(inpstr, inpstr2);

    goto qbs_input_next;

} // qbs_input

int32 unsupported_port_accessed = 0;

int32 H3C7_palette_register_read_index = 0;
int32 H3C8_palette_register_index = 0;
int32 H3C9_next = 0;
int32 H3C9_read_next = 0;

int32 H3C0_blink_enable = 1;

void sub__blink(int32 onoff) {
    if (onoff == 1)
        H3C0_blink_enable = 1;
    else
        H3C0_blink_enable = 0;
}

int32 func__blink() {
    return -H3C0_blink_enable;
}

uintptr_t func__windowhandle() {
#ifdef QB64_WINDOWS
#    ifdef DEPENDENCY_CONSOLE_ONLY
    if (!window_handle) {
        char pszConsoleTitle[1024];
        GetConsoleTitle(pszConsoleTitle, 1024);
        window_handle = FindWindow(NULL, pszConsoleTitle);
    }
    return (ptrszint)window_handle;
#    endif

    OPTIONAL_GLUT(0);
    return (ptrszint)window_handle;
#else
    return 0;
#endif
}

qbs *func__title() {
    if (!window_title) {
        return qbs_new_txt("");
    } else {
        return qbs_new_txt((char *)window_title);
    }
}

void set_foreground_window(ptrszint i) {
#ifdef QB64_WINDOWS
    BOOL result = SetForegroundWindow((HWND)i);
#endif
    return;
}

int32 func__hasfocus() {
#ifdef QB64_GUI
#    ifdef QB64_WINDOWS
    return -((HWND)func__windowhandle() == GetForegroundWindow());
#    elif defined(QB64_LINUX)
    return window_focused;
#    endif
#endif
    return -1;
}

void sub_out(int32 port, int32 data) {
    if (is_error_pending())
        return;
    unsupported_port_accessed = 0;
    port = port & 65535;
    data = data & 255;

    if (port == 0x3C0) {
        H3C0_blink_enable = data & (1 << 3);
        goto done;
    }

    if (port == 0x3C7) { //&H3C7, set palette register read index
        H3C7_palette_register_read_index = data;
        H3C9_read_next = 0;
        goto done;
    }
    if (port == 968) { //&H3C8, set palette register write index
        H3C8_palette_register_index = data;
        H3C9_next = 0;
        goto done;
    }
    if (port == 969) { //&H3C9, set palette color
        data = data & 63;
        if (write_page->pal) {    // avoid NULL pointer
            if (H3C9_next == 0) { // red
                write_page->pal[H3C8_palette_register_index] &= 0xFF00FFFF;
                write_page->pal[H3C8_palette_register_index] += (qbr((double)data * 4.063492f - 0.4999999f) << 16);
            }
            if (H3C9_next == 1) { // green
                write_page->pal[H3C8_palette_register_index] &= 0xFFFF00FF;
                write_page->pal[H3C8_palette_register_index] += (qbr((double)data * 4.063492f - 0.4999999f) << 8);
            }
            if (H3C9_next == 2) { // blue
                write_page->pal[H3C8_palette_register_index] &= 0xFFFFFF00;
                write_page->pal[H3C8_palette_register_index] += qbr((double)data * 4.063492f - 0.4999999f);
            }
        }
        H3C9_next = H3C9_next + 1;
        if (H3C9_next == 3) {
            H3C9_next = 0;
            H3C8_palette_register_index = H3C8_palette_register_index + 1;
            H3C8_palette_register_index &= 0xFF;
        }
        goto done;
    }

    unsupported_port_accessed = 1;
done:
    return;
error:
    error(5);
}

uint32 rnd_seed = 327680;
uint32 rnd_seed_first = 327680; // Note: must contain the same value as rnd_seed

void sub_randomize(double seed, int32 passed) {
    if (is_error_pending())
        return;

    if (passed == 3) { // USING
        // Dim As Uinteger m = cptr(Uinteger Ptr, @n)[1]
        static uint32 m;
        m = ((uint32 *)&seed)[1];
        // m Xor= (m Shr 16)
        m ^= (m >> 16);
        // rnd_seed = (m And &hffff) Shl 8 Or (rnd_seed And &hff)
        rnd_seed = ((m & 0xffff) << 8) | (rnd_seed_first & 0xff); // Note: rnd_seed changed to rnd_seed_first
        return;
    }

    if (passed == 1) {
        // Dim As Uinteger m = cptr(Uinteger Ptr, @n)[1]
        static uint32 m;
        m = ((uint32 *)&seed)[1];
        // m Xor= (m Shr 16)
        m ^= (m >> 16);
        // rnd_seed = (m And &hffff) Shl 8 Or (rnd_seed And &hff)
        rnd_seed = ((m & 0xffff) << 8) | (rnd_seed & 0xff);
        return;
    }

    qbs_print(qbs_new_txt("Random-number seed (-32768 to 32767)? "), 0);
    static int16 integerseed;
    qbs_input_variabletypes[1] = 16; // id.t=16 'a signed 16 bit integer
    qbs_input_variableoffsets[1] = &integerseed;
    qbs_input(1, 1);
    // rnd_seed = (m And &hffff) Shl 8 Or (rnd_seed And &hff) 'nb. same as above
    rnd_seed = ((integerseed & 0xffff) << 8) | (rnd_seed & 0xff);
    return;
}

float func_rnd(float n, int32 passed) {
    if (is_error_pending())
        return 0;

    static uint32 m;
    if (!passed)
        n = 1.0f;
    if (n != 0.0) {
        if (n < 0.0) {
            m = *((uint32 *)&n);
            rnd_seed = (m & 0xFFFFFF) + ((m & 0xFF000000) >> 24);
        }
        rnd_seed = (rnd_seed * 16598013 + 12820163) & 0xFFFFFF;
    }
    return (double)rnd_seed / 0x1000000;
}

void sub__fps(double fps, int32 passed) {
    // passed=1 means _AUTO
    // passed=2 means use fps
    if (is_error_pending())
        return;
    if (passed != 1 && passed != 2) {
        error(5);
        return;
    }
    if (passed == 1) {
        auto_fps = 1; //_AUTO
    }
    if (passed == 2) {
        if (fps < 1) {
            error(5);
            return;
        }
        if (fps > 200)
            fps = 200;
        max_fps = fps;
        auto_fps = 0;
    }
}

int32 generic_put(int32 i, int32 offset, uint8 *cp, int32 bytes) {
    // note: generic_put & generic_get have been made largely redundant by gfs_read & gfs_write
    //      offset is a byte-offset from base 0 (-1=current pos)
    //      generic_put has been kept 32-bit for compatibility
    //      the return value of generic_put is always 0
    //      though errors are handled, generic_put should only be called in error-less situations
    if (is_error_pending())
        return 0;
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return 0;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (!gfs->write) {
        error(75);
        return 0;
    } // Path/file access error
    static int32 e;
    e = gfs_write(i, offset, (uint8 *)cp, bytes);
    if (e) {
        if (e == -2) {
            error(258);
            return 0;
        } // invalid handle
        if (e == -3) {
            error(54);
            return 0;
        } // bad file mode
        if (e == -4) {
            error(5);
            return 0;
        } // illegal function call
        if (e == -7) {
            error(70);
            return 0;
        } // permission denied
        error(75);
        return 0; // assume[-9]: path/file access error
    }
    return 0;
}

int32 generic_get_bytes_read;

int32 generic_get(int32 i, int32 offset, uint8 *cp, int32 bytes) {
    // note: generic_put & generic_get have been made largely redundant by gfs_read & gfs_write
    //      offset is a byte-offset from base 0 (-1=current pos)
    //      generic_get has been kept 32-bit for compatibility
    //      the return value of generic_get is always 0
    //      though errors are handled, generic_get should only be called in error-less situations
    generic_get_bytes_read = 0;
    if (is_error_pending())
        return 0;
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return 0;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (!gfs->read) {
        error(75);
        return 0;
    } // Path/file access error
    static int32 e;
    e = gfs_read(i, offset, (uint8 *)cp, bytes);
    generic_get_bytes_read = gfs_read_bytes();
    if (e) {
        if (e != -10) { // note: on eof, unread buffer area becomes NULL
            if (e == -2) {
                error(258);
                return 0;
            } // invalid handle
            if (e == -3) {
                error(54);
                return 0;
            } // bad file mode
            if (e == -4) {
                error(5);
                return 0;
            } // illegal function call
            if (e == -7) {
                error(70);
                return 0;
            } // permission denied
            error(75);
            return 0; // assume[-9]: path/file access error
        }
    }
    return 0;
}

void sub_open(qbs *name, int32 type, int32 access, int32 sharing, int32 i, int64 record_length, int32 passed) {
    if (is_error_pending())
        return;
    //?[{FOR RANDOM|FOR BINARY|FOR INPUT|FOR OUTPUT|FOR APPEND}]
    // 1 2
    //[{ACCESS READ WRITE|ACCESS READ|ACCESS WRITE}]
    //  3
    //[{SHARED|LOCK READ WRITE|LOCK READ|LOCK WRITE}]{AS}[#]?[{LEN =}?]
    //  4                                                   5        6[1]
    static int32 x;
    static int32 g_access, g_restrictions, g_how;

    if (type == 0)
        type = 1;
    if (passed)
        if ((record_length == 0) || (record_length < -1)) {
            error(5);
            return;
        } // Illegal function call
    // note: valid record_length values are allowable but ignored by QB for non-RANDOM modes too!

    x = gfs_fileno_valid(i);
    if (x == -2) {
        error(52);
        return;
    } // Bad file name or number
    if (x == 1) {
        error(55);
        return;
    } // File already open

    if (type <= 2) {
        g_access = 3;
        g_restrictions = 0;
        g_how = 3;
    }
    if (type == 3) {
        g_access = 1;
        g_restrictions = 0;
        g_how = 0;
    }
    if (type == 4) {
        g_access = 2;
        g_restrictions = 0;
        g_how = 2;
    }
    if (type == 5) {
        g_access = 2;
        g_restrictions = 0;
        g_how = 1;
    }

    if (access == 1)
        g_access = 3;
    if (access == 2)
        g_access = 1;
    if (access == 3)
        g_access = 2;
    if (access && (g_how == 3))
        g_how = 1; // undefined access not possible when ACCESS is explicitly specified

    if (sharing == 1)
        g_restrictions = 0;
    if (sharing == 2)
        g_restrictions = 3;
    if (sharing == 3)
        g_restrictions = 1;
    if (sharing == 4)
        g_restrictions = 2;
    // note: In QB, opening a file already open for OUTPUT/APPEND created the 'file already open' error.

    //      However, from a new cmd window (or a SHELLed QB program) it can be opened!
    //      So it is not a true OS restriction/lock, just a block applied internally by QB.
    //      This is currently unsupported by QB64.

    x = gfs_open(name, g_access, g_restrictions, g_how);
    if (x < 0) {
        if (x == -5) {
            error(53);
            return;
        }
        if (x == -6) {
            error(76);
            return;
        }
        if (x == -7) {
            error(70);
            return;
        }
        if (x == -8) {
            error(68);
            return;
        }
        if (x == -11) {
            error(64);
            return;
        }
        if (x == -12) {
            error(54);
            return;
        }
        error(53);
        return; // default assumption: 'file not found'
    }

    gfs_fileno_use(i, x);

    static gfs_file_struct *f;
    f = gfs_get_file_struct(x);

    f->type = type;
    if (type == 5)
        f->type = 4;

    f->column = 1;

    if (type == 1) { // set record length
        f->record_length = 128;
        if (passed)
            if (record_length != -1)
                f->record_length = record_length;
        f->field_buffer = (uint8 *)calloc(f->record_length, 1);
    }

    if (type == 5) { // seek eof
        static int64 x64;
        x64 = gfs_lof(x);
        if (x64 > 0)
            gfs_setpos(x, x64); // not an error and not null length
    }

    if (type == 3) { // check if eof character, CHR$(26), is the first byte and set EOF accordingly
        static int64 x64;
        x64 = gfs_lof(x);
        if (x64) {
            // read first byte
            static uint8 c;
            auto e = gfs_read(x, -1, &c, 1);
            if (e) {
                // if (e==-10) return -1;
                // if (e==-2){error(258); return -2;}//invalid handle
                // if (e==-3){error(54); return -2;}//bad file mode
                // if (e==-4){error(5); return -2;}//illegal function call
                if (e == -7) {
                    error(70);
                    return;
                } // permission denied
                error(75);
                return; // assume[-9]: path/file access error
            }
            if (c == 26) {
                gfs_get_file_struct(x)->eof_passed = 1; // set EOF flag
            }
            gfs_setpos(x, 0);
        }
    } // type==3
}

void sub_open_gwbasic(qbs *typestr, int32 i, qbs *name, int64 record_length, int32 passed) {
    if (is_error_pending())
        return;
    static int32 a, type;
    if (!typestr->len) {
        error(54);
        return;
    } // bad file mode
    a = typestr->chr[0] & 223;
    type = 0;
    if (a == 82)
        type = 1; // R
    if (a == 66)
        type = 2; // B
    if (a == 73)
        type = 3; // I
    if (a == 79)
        type = 4; // O
    if (a == 65)
        type = 5; // A
    if (!type) {
        error(54);
        return;
    } // bad file mode
    if (passed) {
        sub_open(name, type, NULL, NULL, i, record_length, 1);
    } else {
        sub_open(name, type, NULL, NULL, i, NULL, 0);
    }
}

void sub_close(int32 i2, int32 passed) {
    if (is_error_pending())
        return;
    int32 i, x; //<--RECURSIVE function - do not make this static

    if (passed) {

        if (i2 < 0) { // special handle
            // determine which close procedure to call
            x = -(i2 + 1);
            static stream_struct *st;
            static special_handle_struct *sh;
            sh = (special_handle_struct *)list_get(special_handles, x);
            if (!sh)
                return;

            switch (sh->type) {
            case special_handle_type::Stream:
                st = (stream_struct *)sh->index;

                if (st->type == stream_type::Tcp)
                    connection_close(x);

                break;

            case special_handle_type::Host:
                connection_close(x);
                break;

            case special_handle_type::Http:
                libqb_http_close(x);
                break;

            case special_handle_type::Invalid:
                // TODO: Check if anything needs to be done here
                break;
            }

            return;
        } // special handle

        if (gfs_fileno_valid(i2) == 1)
            gfs_close(gfs_get_fileno(i2));
        return;

    } // passed

    // special handles

    for (i = 1; i <= special_handles->indexes; i++) {
        if (vwatch > 0 && vwatch == i) {
            // keep connection to the IDE open for $DEBUG mode
        } else {
            sub_close(-i - 1, 1);
        }
    }

    gfs_close_all_files();
} // close

int32 file_input_chr(int32 i) {
    // returns the ASCII value of the character (0-255)
    // returns -1 if eof reached (error to be externally handled)
    // returns -2 for other errors (internally handled), the calling function should abort

    static uint8 c;
    auto e = gfs_read(i, -1, &c, 1);
    if (e) {
        if (e == -10)
            return -1;
        if (e == -2) {
            error(258);
            return -2;
        } // invalid handle
        if (e == -3) {
            error(54);
            return -2;
        } // bad file mode
        if (e == -4) {
            error(5);
            return -2;
        } // illegal function call
        if (e == -7) {
            error(70);
            return -2;
        } // permission denied
        error(75);
        return -2; // assume[-9]: path/file access error
    }
    if (c == 26) { // eof character (go back 1 byte so subsequent reads will re-encounter the eof character)
        gfs_setpos(i, gfs_getpos(i) - 1);
        gfs_get_file_struct(i)->eof_passed = 1; // also set EOF flag
        return -1;
    }
    return c;
}

void file_input_skip1310(int32 i, int32 c) {
    // assumes a character of value 13 or 10 has just been read (passed)
    // peeks next character and skips it too if it is a corresponding 13 or 10 pair
    static int32 nextc;
    nextc = file_input_chr(i);
    if (nextc == -2)
        return;
    if (nextc == -1)
        return;
    if (((c == 10) && (nextc != 13)) || ((c == 13) && (nextc != 10))) {
        gfs_setpos(i, gfs_getpos(i) - 1); // go back 1 character
    } else {
        // check next character for EOF CHR$(26)
        nextc = file_input_chr(i);
        if (nextc == -2)
            return;
        if (nextc == -1)
            return;
        gfs_setpos(i, gfs_getpos(i) - 1); // go back 1 character
    }
}

void file_input_nextitem(int32 i, int32 lastc) {
    if (i < 0)
        return;
    // this may require reversing a bit too!
    int32 c, nextc;
    c = lastc;
nextchr:
    if (c == -1)
        return;
    if (c == 32) {
        nextc = file_input_chr(i);
        if (nextc == -2)
            return;
        if (nextc == -1)
            return;
        if ((nextc != 32) && (nextc != 44) && (nextc != 10) && (nextc != 13)) {
            gfs_setpos(i, gfs_getpos(i) - 1);
            return;
        } else {
            c = nextc;
            goto nextchr;
        }
    }
    if (c == 44)
        return;                   //,
    if ((c == 10) || (c == 13)) { // lf cr
        file_input_skip1310(i, c);
        return;
    }
    c = file_input_chr(i);
    if (c == -2)
        return;
    goto nextchr;
}

uint8 sub_file_print_spaces[32];

void sub_file_print(int32 i, qbs *str, int32 extraspace, int32 tab, int32 newline) {
    if (is_error_pending())
        return;
    static int32 x, x2, x3, x4;
    static int32 e;

    // tcp/ip?
    // note: spacing considerations such as 'extraspace' & 'tab' are ignored
    if (i < 0) {

        return;
    }
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);

    if (gfs->scrn == 1) {
        qbs_print(str, newline);
        return;
    };

    if (gfs->type != 4) {
        error(54);
        return;
    } // Bad file mode
    if (!gfs->write) {
        error(75);
        return;
    } // Path/file access error

    e = gfs_write(i, -1, str->chr, str->len);
    if (e) {
        if (e == -2) {
            error(258);
            return;
        } // invalid handle
        if (e == -3) {
            error(54);
            return;
        } // bad file mode
        if (e == -4) {
            error(5);
            return;
        } // illegal function call
        if (e == -7) {
            error(70);
            return;
        } // permission denied
        error(75);
        return; // assume[-9]: path/file access error
    }

    // move column if carriage return found in content
    static int32 stri, strl;
    static uint8 c;
    strl = str->len;
    for (stri = 0; stri < strl; stri++) {
        c = str->chr[stri];
        if ((c != 13) && (c != 10)) {
            gfs->column++;
        } else {
            if (c == 13)
                gfs->column = 1;
        }
    }

    // add extra spaces as needed
    static int32 nspaces;
    static int16 cr_lf = 13 + 10 * 256;
    nspaces = 0;
    if (extraspace) {
        nspaces++;
        gfs->column++;
    }
    if (tab) {
        // a space MUST be added
        nspaces++;
        gfs->column++;
        x = (gfs->column - 1) % 14;
        if (x != 0) {
            x = 14 - x;
            nspaces += x;
            gfs->column += x;
        }
    }
    if (nspaces) {

        e = gfs_write(i, -1, sub_file_print_spaces, nspaces);
        if (e) {
            if (e == -2) {
                error(258);
                return;
            } // invalid handle
            if (e == -3) {
                error(54);
                return;
            } // bad file mode
            if (e == -4) {
                error(5);
                return;
            } // illegal function call
            if (e == -7) {
                error(70);
                return;
            } // permission denied
            error(75);
            return; // assume[-9]: path/file access error
        }
    }
    if (newline) {

        e = gfs_write(i, -1, (uint8 *)&cr_lf, 2);
        if (e) {
            if (e == -2) {
                error(258);
                return;
            } // invalid handle
            if (e == -3) {
                error(54);
                return;
            } // bad file mode
            if (e == -4) {
                error(5);
                return;
            } // illegal function call
            if (e == -7) {
                error(70);
                return;
            } // permission denied
            error(75);
            return; // assume[-9]: path/file access error
        }

        gfs->column = 1;
    }
}

// number limits
const char *range_int64_max[] = {"9223372036854775807"};     // 19 digits
const char *range_int64_neg_max[] = {"9223372036854775808"}; // 19 digits
const char *range_uint64_max[] = {"18446744073709551615"};   // 20 digits
const char *range_float_max[] = {"17976931348623157"};       // 17 digits

// universal number representation
uint16 n_digits;
uint8 n_digit[256];
int64 n_exp; // if 0, there is one digit in front of the decimal place
uint8 n_neg; // if 1, the number is negative
uint8 n_hex; // if 1, the digits are in hexadecimal and n_exp should be ignored
             // if 2, the digits are in octal and n_exp should be ignored
             // if 3, the digits are in binary and n_exp should be ignored
             //(consider revising variable name n_hex)

int32 n_roundincrement() {
    static int32 i, i2, i3;
    if (n_digits == 0)
        return 0;
    if (n_digits > (n_exp + 1)) { // numbers exist after the decimal point
        i = n_digit[n_exp + 1] - 48;
        if (i >= 5)
            return 1;
    }
    return 0;
}

long double n_float_value;

int32 n_float() {
    // return value: Bit 0=successful
    // data
    static uint8 built[256];
    static int64 value;
    uint64 uvalue;
    static int32 i, i2, i3;
    static uint8 *max;
    max = (uint8 *)range_float_max[0];
    n_float_value = 0;
    value = 0;
    uvalue = 0;
    if (n_digits == 0)
        return 1;
    // hex?
    if (n_hex == 1) {
        if (n_digits > 16)
            return 0;
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i];
            if ((i2 >= 48) && (i2 <= 57))
                i2 -= 48;
            if ((i2 >= 65) && (i2 <= 70))
                i2 -= 55;
            if ((i2 >= 97) && (i2 <= 102))
                i2 -= 87;
            value <<= 4;
            value |= i2;
        }
        n_float_value = value;
        return 1;
    }
    // oct?
    if (n_hex == 2) {
        if (n_digits >= 22) {
            if ((n_digits > 22) || (n_digit[0] > 49))
                return 0;
        }
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i] - 48;
            value <<= 3;
            value |= i2;
        }
        n_float_value = value;
        return 1;
    }
    // bin?
    if (n_hex == 3) {
        if (n_digits > 64)
            return 0;
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i] - 48;
            value <<= 1;
            value |= i2;
        }
        n_float_value = value;
        return 1;
    }

    // max range check (+-1.7976931348623157E308)
    if (n_exp > 308)
        return 0; // overflow
    if (n_exp == 308) {
        i2 = n_digits;
        if (i2 > 17)
            i2 = 17;
        for (i = 0; i < i2; i++) {
            if (n_digit[i] > max[i])
                return 0; // overflow
            if (n_digit[i] < max[i])
                break;
        }
    }
    // too close to 0?
    if (n_exp < -324)
        return 1;
    // read & return value (via C++ function)
    // build number
    i = 0;
    if (n_neg) {
        built[i] = 45;
        i++;
    } //-
    built[i] = n_digit[0];
    i++;
    built[i] = 46;
    i++; //.
    if (n_digits == 1) {
        built[i] = 48;
        i++; // 0
    } else {
        i3 = n_digits;
        if (i3 > 17)
            i3 = 17;
        for (i2 = 1; i2 < i3; i2++) {
            built[i] = n_digit[i2];
            i++;
        }
    }
    built[i] = 69;
    i++; // E
    i2 = sprintf((char *)&built[i], "%" PRId64, n_exp);
    i = i + i2;
    built[i] = 0; // NULL terminate for sscanf

    static double sscanf_fix;
    sscanf((char *)&built[0], "%lf", &sscanf_fix);
    n_float_value = sscanf_fix;

    return 1;
}

int64 n_int64_value;

int32 n_int64() {
    // return value: Bit 0=successful
    // data
    static int64 value;
    uint64 uvalue;
    static int32 i, i2;
    static uint8 *max;
    static uint8 *neg_max;
    static int64 v0 = build_int64(0x80000000, 0x00000000);
    static int64 v1 = build_int64(0x7FFFFFFF, 0xFFFFFFFF);
    max = (uint8 *)range_int64_max[0];
    neg_max = (uint8 *)range_int64_neg_max[0];
    n_int64_value = 0;
    value = 0;
    uvalue = 0;
    if (n_digits == 0)
        return 1;
    // hex
    if (n_hex == 1) {
        if (n_digits > 16)
            return 0;
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i];
            if ((i2 >= 48) && (i2 <= 57))
                i2 -= 48;
            if ((i2 >= 65) && (i2 <= 70))
                i2 -= 55;
            if ((i2 >= 97) && (i2 <= 102))
                i2 -= 87;
            value <<= 4;
            value |= i2;
        }
        n_int64_value = value;
        return 1;
    }
    // oct
    if (n_hex == 2) {
        if (n_digits >= 22) {

            if ((n_digits > 22) || (n_digit[0] > 49))
                return 0;
        }
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i] - 48;
            value <<= 3;
            value |= i2;
        }
        n_int64_value = value;
        return 1;
    }
    // bin
    if (n_hex == 3) {
        if (n_digits > 64)
            return 0;
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i] - 48;
            value <<= 1;
            value |= i2;
        }
        n_int64_value = value;
        return 1;
    }

    // range check: int64 (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
    if (n_exp > 18)
        return 0; // overflow
    if (n_exp == 18) {
        i2 = n_digits;
        if (i2 > 19)
            i2 = 19; // only scan integeral digits
        for (i = 0; i < i2; i++) {
            if (n_neg) {
                if (n_digit[i] > neg_max[i])
                    return 0; // overflow
                if (n_digit[i] < neg_max[i])
                    break;
            } else {
                if (n_digit[i] > max[i])
                    return 0; // overflow
                if (n_digit[i] < max[i])
                    break;
            }
        }
    }
    // calculate integeral value
    i2 = n_digits;
    if (i2 > (n_exp + 1))
        i2 = n_exp + 1;
    for (i = 0; i < (n_exp + 1); i++) {
        uvalue *= 10;
        if (i < i2)
            uvalue = uvalue + (n_digit[i] - 48);
    }
    if (n_neg) {
        value = -uvalue;
    } else {
        value = uvalue;
    }
    // apply rounding
    if (n_roundincrement()) {
        if (n_neg) {
            if (value == v0)
                return 0;
            value--;
        } else {
            if (value == v1)
                return 0;
            value++;
        }
    }
    // return value
    n_int64_value = value;
    return 1;
}

uint64 n_uint64_value;

int32 n_uint64() {
    // return value: Bit 0=successful
    // data
    static int64 value;
    uint64 uvalue;
    static int32 i, i2;
    static uint8 *max;
    static int64 v0 = build_uint64(0xFFFFFFFF, 0xFFFFFFFF);
    max = (uint8 *)range_uint64_max[0];
    n_uint64_value = 0;
    value = 0;
    uvalue = 0;
    if (n_digits == 0)
        return 1;
    // hex
    if (n_hex == 1) {
        if (n_digits > 16)
            return 0;
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i];
            if ((i2 >= 48) && (i2 <= 57))
                i2 -= 48;
            if ((i2 >= 65) && (i2 <= 70))
                i2 -= 55;
            if ((i2 >= 97) && (i2 <= 102))
                i2 -= 87;
            uvalue <<= 4;
            uvalue |= i2;
        }
        n_uint64_value = uvalue;
        return 1;
    }
    // oct
    if (n_hex == 2) {
        if (n_digits >= 22) {
            if ((n_digits > 22) || (n_digit[0] > 49))
                return 0;
        }
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i] - 48;
            uvalue <<= 3;
            uvalue |= i2;
        }
        n_uint64_value = uvalue;
        return 1;
    }
    // bin
    if (n_hex == 3) {
        if (n_digits > 64)
            return 0;
        for (i = 0; i < n_digits; i++) {
            i2 = n_digit[i] - 48;
            uvalue <<= 1;
            uvalue |= i2;
        }
        n_uint64_value = uvalue;
        return 1;
    }

    // negative?
    if (n_neg) {
        if (n_exp >= 0)
            return 0; // cannot return a negative number!
    }
    // range check: int64 (0 to 18446744073709551615)
    if (n_exp > 19)
        return 0; // overflow
    if (n_exp == 19) {
        i2 = n_digits;
        if (i2 > 20)
            i2 = 20; // only scan integeral digits
        for (i = 0; i < i2; i++) {
            if (n_digit[i] > max[i])
                return 0; // overflow
            if (n_digit[i] < max[i])
                break;
        }
    }
    // calculate integeral value
    i2 = n_digits;
    if (i2 > (n_exp + 1))
        i2 = n_exp + 1;
    for (i = 0; i < (n_exp + 1); i++) {
        uvalue *= 10;
        if (i < i2)
            uvalue = uvalue + (n_digit[i] - 48);
    }
    // apply rounding
    if (n_roundincrement()) {
        if (n_neg) {
            return 0;
        } else {
            if (uvalue == v0)
                return 0;
            uvalue++;
        }
    }
    // return value
    n_uint64_value = uvalue;

    return 1;
}

int32 n_inputnumberfromdata(uint8 *data, ptrszint *data_offset, ptrszint data_size) {
    // return values:
    // 0=success!
    // 1=Overflow
    // 2=Out of DATA
    // 3=Syntax error
    // note: when read fails the data_offset MUST be restored to its old position

    // data
    static int32 i, i2;
    static int32 step, c;
    static int32 exponent_digits;
    static uint8 negate_exponent;
    static int64 exponent_value;
    static int32 return_value;

    return_value = 1; // overflow (default)
    step = 0;
    negate_exponent = 0;
    exponent_value = 0;
    exponent_digits = 0;

    // prepare universal number representation
    n_digits = 0;
    n_exp = 0;
    n_neg = 0;
    n_hex = 0;

    // Out of DATA?
    if (*data_offset >= data_size)
        return 2;

    // read character
    c = data[*data_offset];

    // hex/oct/bin
    if (c == 38) { // "&"
        (*data_offset)++;
        if (*data_offset >= data_size)
            return 3; // Syntax error (missing H/O/B after &)
        c = data[*data_offset];
        if (c == 44) {
            (*data_offset)++;
            return 3; // Syntax error (missing H/O/B after &)
        }
        if ((c == 72) || (c == 104)) { // "H" or "h"
        nexthexchr:
            (*data_offset)++;
            if (*data_offset >= data_size)
                goto gotnumber;
            c = data[*data_offset];
            if (c == 44) {
                (*data_offset)++;
                goto gotnumber;
            }
            if (((c >= 48) && (c <= 57)) || ((c >= 65) && (c <= 70)) || ((c >= 97) && (c <= 102))) { // 0-9 or A-F or a-f
                if (step == 5)
                    return 3; // Syntax error (digits after type suffix)
                if (n_digits == 256)
                    return 1; // Overflow
                n_digit[n_digits] = c;
                n_digits++;
                n_hex = 1;
                goto nexthexchr;
            }
            if ((c == 33) || (c == 35) || (c == 36) || (c == 37) || (c == 38) || (c == 96) || (c == 126)) { // type suffix
                if (step <= 5) {
                    step = 5;
                    goto nexthexchr;
                }
            }
            return 3; // Syntax error (invalid HEX char)
        }
        if ((c == 79) || (c == 111)) { // "O" or "o"
        nexthexchr2:
            (*data_offset)++;
            if (*data_offset >= data_size)
                goto gotnumber;
            c = data[*data_offset];
            if (c == 44) {
                (*data_offset)++;
                goto gotnumber;
            }
            if ((c >= 48) && (c <= 55)) { // 0-7
                if (step == 5)
                    return 3; // Syntax error (digits after type suffix)
                if (n_digits == 256)
                    return 1; // Overflow
                n_digit[n_digits] = c;
                n_digits++;
                n_hex = 2;
                goto nexthexchr2;
            }
            if ((c == 33) || (c == 35) || (c == 36) || (c == 37) || (c == 38) || (c == 96) || (c == 126)) { // type suffix
                if (step <= 5) {
                    step = 5;
                    goto nexthexchr2;
                }
            }
            return 3; // Syntax error (invalid OCT char)
        }
        if ((c == 66) || (c == 98)) { // "B" or "b"
        nexthexchr3:
            (*data_offset)++;
            if (*data_offset >= data_size)
                goto gotnumber;
            c = data[*data_offset];
            if (c == 44) {
                (*data_offset)++;
                goto gotnumber;
            }
            if ((c >= 48) && (c <= 49)) { // 0-1
                if (step == 5)
                    return 3; // Syntax error (digits after type suffix)
                if (n_digits == 256)
                    return 1; // Overflow
                n_digit[n_digits] = c;
                n_digits++;
                n_hex = 3;
                goto nexthexchr3;
            }
            if ((c == 33) || (c == 35) || (c == 36) || (c == 37) || (c == 38) || (c == 96) || (c == 126)) { // type suffix
                if (step <= 5) {
                    step = 5;
                    goto nexthexchr3;
                }
            }
            return 3; // Syntax error (invalid BIN char)
        }
        return 3; // Syntax error (missing H/O/B after &)
    } // "&"

readnextchr:
    if (c == 44) {
        (*data_offset)++;
        goto gotnumber;
    }

    if (c == 45) { //-
        if (step == 0) {
            n_neg = 1;
            step = 1;
            goto nextchr;
        } // sign before integer part
        if (step == 3) {
            negate_exponent = 1;
            step = 4;
            goto nextchr;
        } // exponent sign
        return 3; // Syntax error (no - allowed in fraction part of number or after type suffix)
    }

    if (c == 43) { //+
        if (step == 0) {
            step = 1;
            goto nextchr;
        } // sign before integer part
        if (step == 3) {
            step = 4;
            goto nextchr;
        } // exponent sign
        return 3; // Syntax error (no + allowed in fraction part of number or after type suffix)
    }

    if ((c >= 48) && (c <= 57)) { // 0-9
        if (step == 5)
            return 3; // Syntax error (digit after type suffix)

        if (step <= 1) { // before decimal point
            step = 1;
            if (n_digits || (c > 48)) {
                if (n_digits)
                    n_exp++;
                if (n_digits == 256)
                    return 1; // Overflow
                n_digit[n_digits] = c;
                n_digits++;
            }
        }

        if (step == 2) { // after decimal point
            if ((n_digits == 0) && (c == 48))
                n_exp--;
            if ((n_digits) || (c > 48)) {
                if (n_digits == 256)
                    return 1; // Overflow
                n_digit[n_digits] = c;
                n_digits++;
            }
        }

        if (step >= 3) { // exponent
            step = 4;
            if ((exponent_digits) || (c > 48)) {
                if (exponent_digits == 18)
                    return 1; // Overflow
                exponent_value *= 10;
                exponent_value = exponent_value + (c - 48);
                exponent_digits++;
            }
        }

        goto nextchr;
    }

    if (c == 46) { //.
        if (step > 1)
            return 3; // Syntax error (multiple . or after type suffix)
        if (n_digits == 0)
            n_exp = -1;
        step = 2;
        goto nextchr;
    }

    if ((c == 68) || (c == 69) || (c == 70) || (c == 100) || (c == 101) || (c == 102)) { // D,E,F,d,e,f
        if (step > 2)
            return 3; // Syntax error (multiple exponents or after type suffix)
        step = 3;
        goto nextchr;
    }

    if ((c == 33) || (c == 35) || (c == 36) || (c == 37) || (c == 38) || (c == 96) || (c == 126)) { // type suffix
        if (step <= 5) {
            step = 5;
            goto nextchr;
        }
    }
    return 3; // Syntax error (invalid number char)
nextchr:
    (*data_offset)++;
    if (*data_offset >= data_size)
        goto gotnumber;
    c = data[*data_offset];
    goto readnextchr;

gotnumber:;
    if (negate_exponent)
        n_exp -= exponent_value;
    else
        n_exp += exponent_value; // complete exponent
    if (n_digits == 0) {
        n_exp = 0;
        n_neg = 0;
    } // clarify number
    return 0; // success
}

int32 n_inputnumberfromfile(int32 fileno) {
    // return values:
    // 0=success
    // 1=overflow
    // 2=eof
    // 3=failed (no further errors)

    // data
    static int32 i, i2;
    static int32 step, c;
    static int32 exponent_digits;
    static uint8 negate_exponent;
    static int64 exponent_value;
    static int32 return_value;

    // tcp/ip specific data
    static qbs *str, *character;
    int32 nextc, x, x2, x3, x4;
    int32 i1;
    int32 inspeechmarks;
    static uint8 *ucbuf;
    static uint32 ucbufsiz;
    static int32 info;

    if (fileno >= 0) {
        if (gfs_fileno_valid(fileno) != 1) {
            error(52);
            return 3;
        } // Bad file name or number
        fileno = gfs_get_fileno(fileno); // convert fileno to gfs index
        static gfs_file_struct *gfs;
        gfs = gfs_get_file_struct(fileno);
        if (gfs->type != 3) {
            error(54);
            return 3;
        } // Bad file mode
        if (!gfs->read) {
            error(75);
            return 3;
        } // Path/file access error
    }

    return_value = 1; // overflow (default)
    step = 0;
    negate_exponent = 0;
    exponent_value = 0;
    exponent_digits = 0;

    // prepare universal number representation
    n_digits = 0;
    n_exp = 0;
    n_neg = 0;
    n_hex = 0;

    // skip any leading spaces
    do {
        c = file_input_chr(fileno);
        if (c == -2)
            return 3;
        if (c == -1) {
            return_value = 2;
            goto error;
        } // input past end of file
    } while (c == 32);

    // hex/oct/bin
    if (c == 38) { // "&"
        c = file_input_chr(fileno);
        if (c == -2)
            return 3;
        if (c == -1)
            goto gotnumber;
        if ((c == 72) || (c == 104)) { // "H" or "h"
        nexthexchr:
            c = file_input_chr(fileno);
            if (c == -2)
                return 3;
            if (((c >= 48) && (c <= 57)) || ((c >= 65) && (c <= 70)) || ((c >= 97) && (c <= 102))) { // 0-9 or A-F or a-f
                if (n_digits == 256)
                    goto error; // overflow
                n_digit[n_digits] = c;
                n_digits++;
                n_hex = 1;
                goto nexthexchr;
            }
            goto gotnumber;
        }
        if ((c == 79) || (c == 111)) { // "O" or "o"
        nexthexchr2:
            c = file_input_chr(fileno);
            if (c == -2)
                return 3;
            if ((c >= 48) && (c <= 55)) { // 0-7
                if (n_digits == 256)
                    goto error; // overflow
                n_digit[n_digits] = c;
                n_digits++;
                n_hex = 2;
                goto nexthexchr2;
            }
            goto gotnumber;
        }
        if ((c == 66) || (c == 98)) { // "B" or "b"
        nexthexchr3:
            c = file_input_chr(fileno);
            if (c == -2)
                return 3;
            if ((c >= 48) && (c <= 49)) { // 0-1
                if (n_digits == 256)
                    goto error; // overflow
                n_digit[n_digits] = c;
                n_digits++;
                n_hex = 3;
                goto nexthexchr3;
            }
            goto gotnumber;
        }
        goto gotnumber;
    } // "&"

readnextchr:
    if (c == -1)
        goto gotnumber;

    if (c == 45) { //-
        if (step == 0) {
            n_neg = 1;
            step = 1;
            goto nextchr;
        }
        if (step == 3) {
            negate_exponent = 1;
            step = 4;
            goto nextchr;
        }
        goto gotnumber;
    }

    if (c == 43) { //+
        if (step == 0) {
            step = 1;
            goto nextchr;
        }
        if (step == 3) {
            step = 4;
            goto nextchr;
        }
        goto gotnumber;
    }

    if ((c >= 48) && (c <= 57)) { // 0-9

        if (step <= 1) { // before decimal point
            step = 1;
            if (n_digits || (c > 48)) {
                if (n_digits)
                    n_exp++;
                if (n_digits == 256)
                    goto error; // overflow
                n_digit[n_digits] = c;
                n_digits++;
            }
        }

        if (step == 2) { // after decimal point
            if ((n_digits == 0) && (c == 48))
                n_exp--;
            if ((n_digits) || (c > 48)) {
                if (n_digits == 256)
                    goto error; // overflow
                n_digit[n_digits] = c;
                n_digits++;
            }
        }

        if (step >= 3) { // exponent
            step = 4;
            if ((exponent_digits) || (c > 48)) {
                if (exponent_digits == 18)
                    goto error; // overflow
                exponent_value *= 10;
                exponent_value = exponent_value + (c - 48);
                exponent_digits++;
            }
        }

        goto nextchr;
    }

    if (c == 46) { //.
        if (step > 1)
            goto gotnumber;
        if (n_digits == 0)
            n_exp = -1;
        step = 2;
        goto nextchr;
    }

    if ((c == 68) || (c == 69) || (c == 70) || (c == 100) || (c == 101) || (c == 102)) { // D,E,F,d,e,f
        if (step > 2)
            goto gotnumber;
        step = 3;
        goto nextchr;
    }

    goto gotnumber; // invalid character
nextchr:
    c = file_input_chr(fileno);
    if (c == -2)
        return 3;
    goto readnextchr;

gotnumber:;
    if (negate_exponent)
        n_exp -= exponent_value;
    else
        n_exp += exponent_value; // complete exponent
    if (n_digits == 0) {
        n_exp = 0;
        n_neg = 0;
    } // clarify number
    file_input_nextitem(fileno, c);
    return 0; // success

error:
    file_input_nextitem(fileno, c);
    if (fileno < 0) {
    }
    return return_value;
}

void revert_input_check() {}

void sub_file_input_string(int32 fileno, qbs *deststr) {
    if (is_error_pending())
        return;
    static qbs *str, *character;
    int32 c, nextc, x, x2, x3, x4;
    int32 i, i1;
    int32 inspeechmarks;
    static uint8 *ucbuf;
    static uint32 ucbufsiz;
    static int32 info;

    // tcp/ip?
    // note: spacing considerations are ignored
    if (fileno < 0) {
        return;
    }

    if (gfs_fileno_valid(fileno) != 1) {
        error(52);
        return;
    } // Bad file name or number
    fileno = gfs_get_fileno(fileno); // convert fileno to gfs index

    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(fileno);
    if (gfs->type != 3) {
        error(54);
        return;
    } // Bad file mode
    if (!gfs->read) {
        error(75);
        return;
    } // Path/file access error

    str = qbs_new(0, 0);
    // skip whitespace (spaces & tabs)
    do {
        c = file_input_chr(fileno);
        if (c == -2)
            return;
        if (c == -1) {
            qbs_set(deststr, str);
            qbs_free(str);
            error(62); // input past end of file
            return;
        }
    } while ((c == 32) || (c == 9));
    // quoted string?
    inspeechmarks = 0;
    if (c == 34) { //"
        inspeechmarks = 1;
        c = file_input_chr(fileno);
    }
    // read string
    character = qbs_new(1, 0);
nextchr:
    if (c == -2)
        return;
    if (c == -1)
        goto gotstr;
    if (inspeechmarks) {
        if (c == 34)
            goto gotstr; //"
    } else {
        if (c == 44)
            goto gotstr; //,
        if (c == 10)
            goto gotstr;
        if (c == 13)
            goto gotstr;
    }
    character->chr[0] = c;
    qbs_set(str, qbs_add(str, character));
    c = file_input_chr(fileno);
    goto nextchr;
gotstr:
    // cull trailing whitespace
    if (!inspeechmarks) {
    cullstr:
        if (str->len) {
            if ((str->chr[str->len - 1] == 32) || (str->chr[str->len - 1] == 9)) {
                str->len--;
                goto cullstr;
            }
        }
    }
nextstr:
    // scan until next item (or eof) reached
    if (c == -2)
        return;
    if (c == -1)
        goto returnstr;
    if (c == 44)
        goto returnstr;
    if ((c == 10) || (c == 13)) { // lf cr
        file_input_skip1310(fileno, c);
        goto returnstr;
    }
    c = file_input_chr(fileno);
    goto nextstr;
// return string
returnstr:
    qbs_set(deststr, str);
    qbs_free(str);
    qbs_free(character);
    return;
}

int64 func_file_input_int64(int32 fileno) {
    if (is_error_pending())
        return 0;
    static int32 i;
    i = n_inputnumberfromfile(fileno);
    if (i == 1) {
        error(6);
        return 0;
    } // overflow
    if (i == 2) {
        error(62);
        return 0;
    } // input past end of file
    if (i == 3)
        return 0; // failed
    if (n_int64())
        return n_int64_value;
    error(6); // overflow
    return 0;
}

uint64 func_file_input_uint64(int32 fileno) {
    if (is_error_pending())
        return 0;
    static int32 i;
    i = n_inputnumberfromfile(fileno);
    if (i == 1) {
        error(6);
        return 0;
    } // overflow
    if (i == 2) {
        error(62);
        return 0;
    } // input past end of file
    if (i == 3)
        return 0; // failed
    if (n_uint64())
        return n_uint64_value;
    error(6); // overflow
    return 0;
}

void sub_read_string(uint8 *data, ptrszint *data_offset, ptrszint data_size, qbs *deststr) {
    if (is_error_pending())
        return;
    static qbs *str, *character;
    static int32 c, inspeechmarks;
    str = qbs_new(0, 0);
    character = qbs_new(1, 0);
    inspeechmarks = 0;

    if (*data_offset >= data_size) {
        error(4);
        goto gotstr;
    } // Out of DATA

    c = data[*data_offset];
nextchr:

    if (c == 44) { //,
        if (inspeechmarks != 1) {
            (*data_offset)++;
            goto gotstr;
        }
    }
    if (inspeechmarks == 2) {
        error(4);
        str->len = 0;
        goto gotstr;
    } // syntax error (expected , after closing " unless at end of data in which " is assumed by QB)

    if (c == 34) { //"
        if (inspeechmarks) {
            inspeechmarks = 2;
            goto skipchr;
        }
        if (!str->len) {
            inspeechmarks = 1;
            goto skipchr;
        }
    }

    character->chr[0] = c;
    qbs_set(str, qbs_add(str, character));
skipchr:

    (*data_offset)++;
    if (*data_offset >= data_size)
        goto gotstr;
    c = data[*data_offset];
    goto nextchr;

gotstr:
    qbs_set(deststr, str);
    qbs_free(str);
    qbs_free(character);
    return;
}

long double func_read_float(uint8 *data, ptrszint *data_offset, ptrszint data_size, int32 typ) {
    if (is_error_pending())
        return 0;
    static int32 i;
    static int64 maxval, minval;
    static int64 value;
    static ptrszint old_data_offset;
    old_data_offset = *data_offset;
    i = n_inputnumberfromdata(data, data_offset, data_size);

    // return values:
    // 0=success!
    // 1=Overflow
    // 2=Out of DATA
    // 3=Syntax error
    // note: when read fails the data_offset MUST be restored to its old position
    if (i == 1) { // Overflow
        goto overflow;
    }
    if (i == 2) { // Out of DATA
        error(4);
        return 0;
    }
    if (i == 3) { // Syntax error
        *data_offset = old_data_offset;
        error(2);
        return 0;
    }

    if (!n_float())
        goto overflow; // returns n_float_value

    // range test & return value
    if (typ & ISFLOAT) {
        if ((typ & 511) >= 64)
            return n_float_value;
        if (n_float_value > 3.402823466E+38)
            goto overflow;
        return n_float_value;
    } else {
        if (n_float_value < (-(9.2233720368547758E+18)))
            goto overflow; // too low to store!
        if (n_float_value > 9.2233720368547758E+18)
            goto overflow; // too high to store!
        value = qbr(n_float_value);
        if ((typ & ISUNSIGNED) || n_hex) {
            maxval = (((int64)1) << (typ & 511)) - 1;
            minval = 0;
        } else {
            maxval = (((int64)1) << ((typ & 511) - 1)) - 1;
            minval = -(((int64)1) << ((typ & 511) - 1));
        }
        if ((value > maxval) || (value < minval))
            goto overflow;

        if (((typ & ISUNSIGNED) == 0) && n_hex) {            // signed hex/oct/bin/...
            if ((((int64)1) << ((typ & 511) - 1)) & value) { // if top bit is set, set all bits above it to form a negative value
                value = (maxval ^ ((int64)-1)) + value;
            }
        }

        return value;
    }

overflow:
    *data_offset = old_data_offset;
    error(6);
    return 0;
}

int64 func_read_int64(uint8 *data, ptrszint *data_offset, ptrszint data_size) {
    if (is_error_pending())
        return 0;
    static int32 i;
    static ptrszint old_data_offset;
    old_data_offset = *data_offset;
    i = n_inputnumberfromdata(data, data_offset, data_size);
    // return values:
    // 0=success!
    // 1=Overflow
    // 2=Out of DATA
    // 3=Syntax error
    // note: when read fails the data_offset MUST be restored to its old position
    if (i == 1) { // Overflow
        goto overflow;
    }
    if (i == 2) { // Out of DATA
        error(4);
        return 0;
    }
    if (i == 3) { // Syntax error
        *data_offset = old_data_offset;
        error(2);
        return 0;
    }
    if (n_int64())
        return n_int64_value;
overflow:
    *data_offset = old_data_offset;
    error(6);
    return 0;
}

uint64 func_read_uint64(uint8 *data, ptrszint *data_offset, ptrszint data_size) {
    if (is_error_pending())
        return 0;
    static int32 i;
    static ptrszint old_data_offset;
    old_data_offset = *data_offset;
    i = n_inputnumberfromdata(data, data_offset, data_size);
    // return values:
    // 0=success!
    // 1=Overflow
    // 2=Out of DATA
    // 3=Syntax error
    // note: when read fails the data_offset MUST be restored to its old position
    if (i == 1) { // Overflow
        goto overflow;
    }
    if (i == 2) { // Out of DATA
        error(4);
        return 0;
    }
    if (i == 3) { // Syntax error
        *data_offset = old_data_offset;
        error(2);
        return 0;
    }
    if (n_uint64())
        return n_uint64_value;
overflow:
    *data_offset = old_data_offset;
    error(6);
    return 0;
}

long double func_file_input_float(int32 fileno, int32 typ) {
    if (is_error_pending())
        return 0;
    static int32 i;
    static int64 maxval, minval;
    static int64 value;
    i = n_inputnumberfromfile(fileno);
    if (i == 1) {
        error(6);
        return 0;
    } // overflow
    if (i == 2) {
        error(62);
        return 0;
    } // input past end of file
    if (i == 3)
        return 0; // failed
    if (!n_float()) {
        error(6);
        return 0;
    } // returns n_float_value
    // range test & return value
    if (typ & ISFLOAT) {
        if ((typ & 511) >= 64)
            return n_float_value;
        if (n_float_value > 3.402823466E+38) {
            error(6);
            return 0;
        }
        return n_float_value;
    } else {
        if (n_float_value < (-(9.2233720368547758E+18))) {
            error(6);
            return 0;
        } // too low to store!
        if (n_float_value > 9.2233720368547758E+18) {
            error(6);
            return 0;
        } // too high to store!
        value = qbr(n_float_value);
        if (typ & ISUNSIGNED) {
            maxval = (((int64)1) << (typ & 511)) - 1;
            minval = 0;
        } else {
            maxval = (((int64)1) << ((typ & 511) - 1)) - 1;
            minval = -(((int64)1) << ((typ & 511) - 1));
        }
        if ((value > maxval) || (value < minval)) {
            error(6);
            return 0;
        }

        if (((typ & ISUNSIGNED) == 0) && n_hex) {            // signed hex/oct/bin/...
            if ((((int64)1) << ((typ & 511) - 1)) & value) { // if top bit is set, set all bits above it to form a negative value
                value = (maxval ^ ((int64)-1)) + value;
            }
        }

        return value;
    }
} // func_file_input_float

void *byte_element(uint64 offset, int32 length) {
    if (length < 0)
        length = 0; // some calculations may result in negative values which mean 0 (no bytes available)
    // add structure to xms stack (byte_element structures are never stored in cmem!)
    void *p;
    if ((mem_static_pointer += 12) < mem_static_limit)
        p = mem_static_pointer - 12;
    else
        p = mem_static_malloc(12);
    *((uint64 *)p) = offset;
    ((uint32 *)p)[2] = length;
    return p;
}

void *byte_element(uint64 offset, int32 length, byte_element_struct *info) {
    if (length < 0)
        length = 0; // some calculations may result in negative values which mean 0 (no bytes available)
    info->length = length;
    info->offset = offset;
    return info;
}

void call_interrupt(int32 intno, void *inregs, void *outregs) {
    if (is_error_pending())
        return;
    static byte_element_struct *ele;
    static uint16 *sp;
    /* testing only
        static qbs* s=NULL;
        if (s==NULL) s=qbs_new(0,0);
        qbs_set(s,qbs_str(ele->length));
        MessageBox2(NULL,(char*)s->chr,"CALL INTERRUPT: size",MB_OK|MB_SYSTEMMODAL);
        qbs_set(s,qbs_str( ((uint8*)(ele->offset))[0] ));
        MessageBox2(NULL,(char*)s->chr,"CALL INTERRUPT: value",MB_OK|MB_SYSTEMMODAL);
    */
    /* reference
        TYPE RegType
        AX AS INTEGER
        BX AS INTEGER
        CX AS INTEGER
        DX AS INTEGER
        BP AS INTEGER
        SI AS INTEGER
        DI AS INTEGER
        FLAGS AS INTEGER
        END TYPE
    */
    // error checking
    ele = (byte_element_struct *)outregs;
    if (ele->length < 16) {
        error(5);
        return;
    } // Illegal function call
    ele = (byte_element_struct *)inregs;
    if (ele->length < 16) {
        error(5);
        return;
    } // Illegal function call
    // load virtual registers
    sp = (uint16 *)(ele->offset);
    cpu.ax = sp[0];
    cpu.bx = sp[1];
    cpu.cx = sp[2];
    cpu.dx = sp[3];
    cpu.bp = sp[4];
    cpu.si = sp[5];
    cpu.di = sp[6];
    // note: flags ignored (revise)
    call_int(intno);
    // save virtual registers
    ele = (byte_element_struct *)outregs;
    sp = (uint16 *)(ele->offset);
    sp[0] = cpu.ax;
    sp[1] = cpu.bx;
    sp[2] = cpu.cx;
    sp[3] = cpu.dx;
    sp[4] = cpu.bp;
    sp[5] = cpu.si;
    sp[6] = cpu.di;
    // note: flags ignored (revise)
    return;
}

void call_interruptx(int32 intno, void *inregs, void *outregs) {
    if (is_error_pending())
        return;
    static byte_element_struct *ele;
    static uint16 *sp;
    /* reference
        TYPE RegTypeX
        AX AS INTEGER
        BX AS INTEGER
        CX AS INTEGER
        DX AS INTEGER
        BP AS INTEGER
        SI AS INTEGER
        DI AS INTEGER
        FLAGS AS INTEGER
        DS AS INTEGER
        ES AS INTEGER
        END TYPE
    */
    // error checking
    ele = (byte_element_struct *)outregs;
    if (ele->length < 20) {
        error(5);
        return;
    } // Illegal function call
    ele = (byte_element_struct *)inregs;
    if (ele->length < 20) {
        error(5);
        return;
    } // Illegal function call
    // load virtual registers
    sp = (uint16 *)(ele->offset);
    cpu.ax = sp[0];
    cpu.bx = sp[1];
    cpu.cx = sp[2];
    cpu.dx = sp[3];
    cpu.bp = sp[4];
    cpu.si = sp[5];
    cpu.di = sp[6];
    // note: flags ignored (revise)
    cpu.ds = sp[8];
    cpu.es = sp[9];
    call_int(intno);
    // save virtual registers
    ele = (byte_element_struct *)outregs;
    sp = (uint16 *)(ele->offset);
    sp[0] = cpu.ax;
    sp[1] = cpu.bx;
    sp[2] = cpu.cx;
    sp[3] = cpu.dx;
    sp[4] = cpu.bp;
    sp[5] = cpu.si;
    sp[6] = cpu.di;
    // note: flags ignored (revise)
    sp[8] = cpu.ds;
    sp[9] = cpu.es;
    return;
}

void sub_get(int32 i, int64 offset, void *element, int32 passed) {
    if (is_error_pending())
        return;
    static byte_element_struct *ele;
    static int32 x, x2;

    if (i < 0) { // special handle?
        stream_struct *st;
        special_handle_struct *sh;

        x = -(i + 1);

        sh = (special_handle_struct *)list_get(special_handles, x);
        if (!sh) {
            error(52);
            return;
        }

        switch (sh->type) {
        case special_handle_type::Stream:
            st = (stream_struct *)sh->index;

            stream_update(st);
            ele = (byte_element_struct *)element;
            if (st->in_size < ele->length) {
                st->eof = 1;
                return;
            }

            st->eof = 0;
            memcpy((void *)(ele->offset), st->in, ele->length);
            x2 = st->in_size - ele->length;
            if (x2)
                memmove(st->in, st->in + ele->length, x2);
            st->in_size -= ele->length;
            break;

        case special_handle_type::Http:
            ele = (byte_element_struct *)element;

            // If there's not enough data in the internal buffer to fill this
            // fixed length request, then report EOF
            if (libqb_http_get_fixed(x, (char *)ele->offset, ele->length) == -1)
                sh->index = 1;
            else
                sh->index = 2; // Force EOF to report we're not at the end

            break;

        default:
            error(52);
            break;
        }

        return;
    } // special handle

    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (gfs->type > 2) {
        error(54);
        return;
    } // Bad file mode
    if (!gfs->read) {
        error(75);
        return;
    } // Path/file access error

    ele = (byte_element_struct *)element;

    if (gfs->type == 1) { // RANDOM
        if (ele->length > gfs->record_length) {
            error(59);
            return;
        } // Bad record length
        if (passed) {
            offset--;
            if (offset < 0) {
                error(63);
                return;
            } // Bad record number
            offset *= gfs->record_length;
        } else {
            offset = -1;
        }
    } else { // BINARY
        if (passed) {
            offset--;
            if (offset < 0) {
                error(63);
                return;
            } // Bad record number
        } else {
            offset = -1;
        }
    }

    static int32 e;

    e = gfs_read(i, offset, (uint8 *)ele->offset, ele->length);
    if (e) {
        if (e != -10) { // note: on eof, unread buffer area becomes NULL
            if (e == -2) {
                error(258);
                return;
            } // invalid handle
            if (e == -3) {
                error(54);
                return;
            } // bad file mode
            if (e == -4) {
                error(5);
                return;
            } // illegal function call
            if (e == -7) {
                error(70);
                return;
            } // permission denied
            error(75);
            return; // assume[-9]: path/file access error
        }
    }

    // seek to beginning of next field
    if (gfs->type == 1) {
        if (e != -10) { // note: seek index not advanced if record did not exist
            if (ele->length < gfs->record_length) {
                if (offset != -1) {
                    e = gfs_setpos(i, offset + gfs->record_length);
                } else {
                    e = gfs_setpos(i, gfs_getpos(i) - ele->length + gfs->record_length);
                }
                if (e) {
                    error(54);
                    return;
                } // assume[-3]: bad file mode
            }
        } // e!=-10
    }

} // get

void sub_get2(int32 i, int64 offset, qbs *str, int32 passed) {
    if (is_error_pending())
        return;
    static int32 x, x2, x3, x4;

    if (i < 0) {          // special handle?
        if (str->fixed) { // following method is only for variable length strings
            static byte_element_struct tbyte_element_struct;
            sub_get(i, offset, byte_element((uint64)str->chr, str->len, &tbyte_element_struct), passed);
            return;
        }
        x = -(i + 1);
        static stream_struct *st;
        static qbs *tqbs;
        static special_handle_struct *sh;
        sh = (special_handle_struct *)list_get(special_handles, x);
        if (!sh) {
            error(52);
            return;
        }

        size_t length = 0;

        switch (sh->type) {
        case special_handle_type::Stream:
            st = (stream_struct *)sh->index;
            stream_update(st);

            tqbs = qbs_new(st->in_size, 1);
            if (st->in_size)
                memcpy(tqbs->chr, st->in, st->in_size);

            st->in_size = 0;
            st->eof = 0;
            qbs_set(str, tqbs);
            break;

        case special_handle_type::Http:
            sh->index = 0; // Reset EOF flag, this allows EOF to be calculated as normal

            libqb_http_get_length(x, &length);

            tqbs = qbs_new(length, 1);
            if (length)
                length = libqb_http_get(x, (char *)tqbs->chr, &length);

            qbs_set(str, tqbs);
            break;

        default:
            error(52);
            break;
        }

        return;
    } // special handle

    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (gfs->type > 2) {
        error(54);
        return;
    } // Bad file mode
    if (!gfs->read) {
        error(75);
        return;
    } // Path/file access error

    if (gfs->type == 2) { // BINARY (use normal sub_get)
        static byte_element_struct tbyte_element_struct;
        sub_get(gfs->fileno, offset, byte_element((uint64)str->chr, str->len, &tbyte_element_struct), passed);
        return;
    }

    if (gfs->record_length < 2) {
        error(59);
        return;
    } // Bad record length

    if (passed) {
        offset--;
        if (offset < 0) {
            error(63);
            return;
        } // Bad record number
        offset *= gfs->record_length;
    } else {
        offset = -1;
    }

    static int32 e;

    static uint8 *data;
    static uint64 l, bytes;
    data = (uint8 *)malloc(gfs->record_length);
    e = gfs_read(i, offset, data, gfs->record_length); // read the whole record (including header & data)
    if (e) {
        if (e != -10) { // note: on eof, unread buffer area becomes NULL
            if (e == -2) {
                error(258);
                return;
            } // invalid handle
            if (e == -3) {
                error(54);
                return;
            } // bad file mode
            if (e == -4) {
                error(5);
                return;
            } // illegal function call
            if (e == -7) {
                error(70);
                return;
            } // permission denied
            error(75);
            return; // assume[-9]: path/file access error
        }
    }

    bytes = gfs_read_bytes(); // note: any unread part of the buffer is set to NULL (by gfs_read) and is treated as valid record data
    if (!bytes) {
        qbs_set(str, qbs_new(0, 1));
        free(data);
        return;
    } // as in QB when 0 bytes read, NULL string returned and (as no bytes read) no seek advancement

    // seek to beginning of next field
    // note: advancement occurs even if e==-10 (eof reached)
    if (bytes < gfs->record_length) {
        if (offset != -1) {
            e = gfs_setpos(i, offset + gfs->record_length);
        } else {
            e = gfs_setpos(i, gfs_getpos(i) - bytes + gfs->record_length);
        }
        if (e) {
            error(54);
            free(data);
            return;
        } // assume[-3]: bad file mode
    }

    x = 2; // offset where string data will be read from
    l = ((uint16 *)data)[0];
    if (l & 32768) {
        if (gfs->record_length < 8) { // record length is too small to read the length!
            // restore seek to original location
            if (offset != -1) {
                e = gfs_setpos(i, offset);
            } else {
                e = gfs_setpos(i, gfs_getpos(i) - gfs->record_length);
            }
            error(59);
            free(data);
            return; // Bad record length
        }
        x = 8;
        l = (l & 0x7FFF) + (((((uint64 *)data)[0]) >> 16) << 15);
    }

    // can record_length cannot fit the required string data?
    if ((gfs->record_length - x2) < l) {
        // restore seek to original location
        if (offset != -1) {
            e = gfs_setpos(i, offset);
        } else {
            e = gfs_setpos(i, gfs_getpos(i) - gfs->record_length);
        }
        error(59);
        free(data);
        return; // Bad record length
    }

    qbs_set(str, qbs_new_txt_len((char *)(data + x), l));
    free(data);
}

void sub_put(int32 i, int64 offset, void *element, int32 passed) {
    if (is_error_pending())
        return;
    static byte_element_struct *ele;
    static int32 x, x2;

    if (i < 0) { // special handle?
        x = -(i + 1);
        static special_handle_struct *sh;
        sh = (special_handle_struct *)list_get(special_handles, x);
        if (!sh) {
            error(52);
            return;
        }

        static stream_struct *st;

        switch (sh->type) {
        case special_handle_type::Stream:
            st = (stream_struct *)sh->index;
            ele = (byte_element_struct *)element;
            stream_out(st, (void *)ele->offset, ele->length);
            break;

        default:
            error(52);
            break;
        }

        return;
    } // special handle

    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (gfs->type > 2) {
        error(54);
        return;
    } // Bad file mode
    if (!gfs->write) {
        error(75);
        return;
    } // Path/file access error

    ele = (byte_element_struct *)element;

    if (gfs->type == 1) { // RANDOM
        if (ele->length > gfs->record_length) {
            error(59);
            return;
        } // Bad record length
        if (passed) {
            offset--;
            if (offset < 0) {
                error(63);
                return;
            } // Bad record number
            offset *= gfs->record_length;
        } else {
            offset = -1;
        }
    } else { // BINARY
        if (passed) {
            offset--;
            if (offset < 0) {
                error(63);
                return;
            } // Bad record number
        } else {
            offset = -1;
        }
    }

    static int32 e;

    e = gfs_write(i, offset, (uint8 *)ele->offset, ele->length);
    if (e) {
        if (e == -2) {
            error(258);
            return;
        } // invalid handle
        if (e == -3) {
            error(54);
            return;
        } // bad file mode
        if (e == -4) {
            error(5);
            return;
        } // illegal function call
        if (e == -7) {
            error(70);
            return;
        } // permission denied
        error(75);
        return; // assume[-9]: path/file access error
    }

    // seek to beginning of next field
    if (gfs->type == 1) {
        if (ele->length < gfs->record_length) {
            if (offset != -1) {
                e = gfs_setpos(i, offset + gfs->record_length);
            } else {
                e = gfs_setpos(i, gfs_getpos(i) - ele->length + gfs->record_length);
            }
            if (e)
                error(54);
            return; // assume[-3]: bad file mode
        }
    }
}

// put2 adds a 2-4 byte length descriptor to the data
//(used to PUT variable length strings in RANDOM mode)
void sub_put2(int32 i, int64 offset, void *element, int32 passed) {
    if (is_error_pending())
        return;
    static byte_element_struct *ele;
    static int32 x;
    static uint8 *data;

    if (i < 0) {                             // special handle?
        sub_put(i, offset, element, passed); //(use standard put call)
        return;
    }

    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (gfs->type > 2) {
        error(54);
        return;
    } // Bad file mode
    if (!gfs->write) {
        error(75);
        return;
    } // Path/file access error

    if (gfs->type == 2) { // BINARY (use normal sub_put)
        sub_put(gfs->fileno, offset, element, passed);
        return;
    }

    // RANDOM
    static uint64 l;
    static int64 lmask;
    lmask = -1;
    lmask >>= 16;
    ele = (byte_element_struct *)element;
    l = ele->length; // note: ele->length is currently 32-bit, but sub_put2 is 64-bit compliant
    //{15}{1}[{48}]
    if (l > 32767) {
        data = (uint8 *)malloc(l + 8);
        memcpy(&data[8], (void *)(ele->offset), l);
        ((uint64 *)data)[0] = 0;
        ((uint16 *)data)[0] = (l & 32767) + 32768;
        l = ((l >> 15) & lmask);
        ((uint64 *)(data + 2))[0] |= l;
        ele->length += 8;
    } else {
        data = (uint8 *)malloc(l + 2);
        memcpy(&data[2], (void *)(ele->offset), l);
        ((uint16 *)data)[0] = l;
        ele->length += 2;
    }
    ele->offset = (uint64)&data[0];
    sub_put(gfs->fileno, offset, element, passed);
    free(data);

} // put2

void sub_graphics_get(float x1f, float y1f, float x2f, float y2f, void *element, uint32 mask, int32 passed) {
    //"[{STEP}](?,?)-[{STEP}](?,?),?[,?]"
    //   &1            &2            &4
    if (is_error_pending())
        return;

    static int32 x1, y1, x2, y2, z, w, h, bits, x, y, bytes, sx, sy, x3, y3, z2;
    static uint32 col, off, col1, col2, col3, col4, byte;

    if (read_page->text) {
        error(5);
        return;
    }

    // change coordinates according to step
    if (passed & 1) {
        x1f = read_page->x + x1f;
        y1f = read_page->y + y1f;
    }
    read_page->x = x1f;
    read_page->y = y1f;
    if (passed & 2) {
        x2f = read_page->x + x2f;
        y2f = read_page->y + y2f;
    }
    read_page->x = x2f;
    read_page->y = y2f;

    // resolve coordinates
    if (read_page->clipping_or_scaling) {
        if (read_page->clipping_or_scaling == 2) {
            x1 = qbr_float_to_long(x1f * read_page->scaling_x + read_page->scaling_offset_x) + read_page->view_offset_x;
            y1 = qbr_float_to_long(y1f * read_page->scaling_y + read_page->scaling_offset_y) + read_page->view_offset_y;
            x2 = qbr_float_to_long(x2f * read_page->scaling_x + read_page->scaling_offset_x) + read_page->view_offset_x;
            y2 = qbr_float_to_long(y2f * read_page->scaling_y + read_page->scaling_offset_y) + read_page->view_offset_y;
        } else {
            x1 = qbr_float_to_long(x1f) + read_page->view_offset_x;
            y1 = qbr_float_to_long(y1f) + read_page->view_offset_y;
            x2 = qbr_float_to_long(x2f) + read_page->view_offset_x;
            y2 = qbr_float_to_long(y2f) + read_page->view_offset_y;
        }
    } else {
        x1 = qbr_float_to_long(x1f);
        y1 = qbr_float_to_long(y1f);
        x2 = qbr_float_to_long(x2f);
        y2 = qbr_float_to_long(y2f);
    }

    // swap coordinates if reversed
    if (x2 < x1) {
        z = x1;
        x1 = x2;
        x2 = z;
    }
    if (y2 < y1) {
        z = y1;
        y1 = y2;
        y2 = z;
    }

    sx = read_page->width;
    sy = read_page->height;

    // boundary checking (if no mask colour was passed)
    if (!(passed & 4)) {
        if ((x1 < 0) || (y1 < 0) || (x2 >= sx) || (y2 >= sy)) {
            error(5);
            return;
        }
    }

    static byte_element_struct *ele;
    ele = (byte_element_struct *)element;
    static uint16 *dimensions;
    dimensions = (uint16 *)(ele->offset);
    static uint8 *cp, *cp1, *cp2, *cp3, *cp4;
    cp = (uint8 *)(ele->offset + 4);
    static uint32 *lp;
    lp = (uint32 *)(ele->offset + 4);

    w = x2 - x1 + 1;
    h = y2 - y1 + 1;
    bits = read_page->bits_per_pixel;

    if (bits == 1) {
        mask &= 1;
        z = (w + 7) >> 3;
        bytes = z * h + 4;
        if (bytes > ele->length) {
            error(5);
            return;
        }
        dimensions[0] = w;
        dimensions[1] = h;
        for (y = y1; y <= y2; y++) {
            z2 = 128;
            col2 = 0;
            off = y * sx + x1;
            for (x = x1; x <= x2; x++) {
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy))
                    col = read_page->offset[off];
                else
                    col = mask;
                if (col)
                    col2 |= z2;
                z2 >>= 1;
                if (!z2) {
                    z2 = 128;
                    *cp++ = col2;
                    col2 = 0;
                }
                off++;
            }
            if (z2 != 128)
                *cp++ = col2;
        }
        return;
    } // 1

    if (bits == 2) {
        mask &= 3;
        z = (w + 7) >> 3;
        bytes = z * h + 4;
        if (bytes > ele->length) {
            error(5);
            return;
        }
        dimensions[0] = w * 2;
        dimensions[1] = h;
        for (y = y1; y <= y2; y++) {
            byte = 0;
            x3 = 0;
            off = y * sx + x1;
            for (x = x1; x <= x2; x++) {
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy))
                    col = read_page->offset[off];
                else
                    col = mask;
                byte <<= 2;
                byte |= col;
                if ((x3 & 3) == 3) {
                    *cp++ = byte;
                    byte = 0;
                }
                x3++;
                off++;
            }
            if (x3 & 3)
                *cp++ = col2;
        }
        return;
    } // 2

    if (bits == 4) {
        mask &= 15;
        z = (w + 7) >> 3;
        bytes = z * 4 * h + 4;
        if (bytes > ele->length) {
            error(5);
            return;
        }
        dimensions[0] = w;
        dimensions[1] = h;
        y3 = 0;
        for (y = y1; y <= y2; y++) {
            z2 = 128;
            off = y * sx + x1;
            cp1 = cp + y3 * z * 4;
            cp2 = cp + y3 * z * 4 + z;
            cp3 = cp + y3 * z * 4 + z * 2;
            cp4 = cp + y3 * z * 4 + z * 3;
            col1 = 0;
            col2 = 0;
            col3 = 0;
            col4 = 0;
            for (x = x1; x <= x2; x++) {
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy))
                    col = read_page->offset[off];
                else
                    col = mask;
                if (col & 1)
                    col1 |= z2;
                if (col & 2)
                    col2 |= z2;
                if (col & 4)
                    col3 |= z2;
                if (col & 8)
                    col4 |= z2;
                z2 >>= 1;
                if (!z2) {
                    z2 = 128;
                    *cp1++ = col1;
                    *cp2++ = col2;
                    *cp3++ = col3;
                    *cp4++ = col4;
                    col1 = 0;
                    col2 = 0;
                    col3 = 0;
                    col4 = 0;
                }
                off++;
            }
            if (z2 != 128) {
                *cp1 = col1;
                *cp2 = col2;
                *cp3 = col3;
                *cp4 = col4;
            }
            y3++;
        }
        return;
    } // 4

    if (bits == 8) {
        mask &= 255;
        bytes = w * h + 4;
        if (bytes > ele->length) {
            error(5);
            return;
        }
        dimensions[0] = w * 8;
        dimensions[1] = h;
        for (y = y1; y <= y2; y++) {
            off = y * sx + x1;
            for (x = x1; x <= x2; x++) {
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy))
                    col = read_page->offset[off];
                else
                    col = mask;
                *cp++ = col;
                off++;
            }
        }
        return;
    } // 8

    if (bits == 32) {
        bytes = w * h * 4 + 4;
        if (bytes > ele->length) {
            error(5);
            return;
        }
        dimensions[0] = w;
        dimensions[1] = h; // note: width is left unmultiplied
        for (y = y1; y <= y2; y++) {
            off = y * sx + x1;
            for (x = x1; x <= x2; x++) {
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy))
                    col = read_page->offset32[off];
                else
                    col = mask;
                *lp++ = col;
                off++;
            }
        }
        return;
    } // 32

} // sub_graphics_get

void sub_graphics_put(float x1f, float y1f, void *element, int32 option, uint32 mask, int32 passed) {
    //"[{STEP}](?,?),?[,[{_CLIP}][{PSET|PRESET|AND|OR|XOR}][,?]]"
    // step->passed&1
    // clip->passed&2
    // mask->passed&4

    if (is_error_pending())
        return;

    static int32 step, clip;
    step = 0;
    clip = 0;
    if (passed & 1) {
        step = 1;
        passed -= 1;
    }
    if (passed & 2) {
        clip = 1;
        passed -= 2;
    }

    static int32 x1, y1, x2, y2, z, w, h, bits, x, y, bytes, sx, sy, x3, y3, z2;
    static uint32 col, off, col1, col2, col3, col4, byte, pixelmask;

    if (write_page->text) {
        error(5);
        return;
    }

    // change coordinates according to step
    if (step) {
        x1f += write_page->x;
        y1f += write_page->y;
        write_page->x = x1f;
        write_page->y = y1f;
    }

    // resolve coordinates
    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            x1 = qbr_float_to_long(x1f * write_page->scaling_x + write_page->scaling_offset_x) + write_page->view_offset_x;
            y1 = qbr_float_to_long(y1f * write_page->scaling_y + write_page->scaling_offset_y) + write_page->view_offset_y;
        } else {
            x1 = qbr_float_to_long(x1f) + write_page->view_offset_x;
            y1 = qbr_float_to_long(y1f) + write_page->view_offset_y;
        }
    } else {
        x1 = qbr_float_to_long(x1f);
        y1 = qbr_float_to_long(y1f);
    }

    sx = write_page->width;
    sy = write_page->height;
    bits = write_page->bits_per_pixel;

    static byte_element_struct *ele;
    ele = (byte_element_struct *)element;
    static uint16 *dimensions;
    dimensions = (uint16 *)(ele->offset);
    static uint8 *cp, *cp1, *cp2, *cp3, *cp4;
    cp = (uint8 *)(ele->offset + 4);
    static uint32 *lp;
    lp = (uint32 *)(ele->offset + 4);

    static uint8 *offp;
    static uint32 *off32p;

    if (4 > ele->length) {
        error(5);
        return;
    }

    // get dimensions
    w = dimensions[0];
    h = dimensions[1];
    z = w; //(used below)
    if (bits == 2) {
        if (w & 1) {
            error(5);
            return;
        } else
            w >>= 1;
    }
    if (bits == 8) {
        if (w & 7) {
            error(5);
            return;
        } else
            w >>= 3;
    }
    x2 = x1 + w - 1;
    y2 = y1 + h - 1;

    // boundary checking (if CLIP option was not used)
    if (!clip) {
        if ((x1 < 0) || (y1 < 0) || (x2 >= sx) || (y2 >= sy)) {
            error(5);
            return;
        }
    }

    // array size check (avoid reading unallocated memory)
    if (bits == 32)
        z *= 32;
    z = (z + 7) >> 3; // bits per row->bytes per row
    bytes = h * z;
    if (bits == 4)
        bytes *= 4;
    if ((bytes + 4) > ele->length) {
        error(5);
        return;
    }

    pixelmask = write_page->mask;

    if (bits == 1) {
        mask &= 1;
        y3 = 0;
        for (y = y1; y <= y2; y++) {
            offp = (uint8 *)write_page->offset + (y * sx + x1);
            x3 = 0;
            for (x = x1; x <= x2; x++) {
                if (!(x3--)) {
                    x3 = 7;
                    col2 = *cp++;
                }
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy)) {
                    col = (col2 >> x3) & 1;
                    if ((!passed) || (col != mask)) {
                        switch (option) {
                        case 0:
                            *offp ^= col;
                            break;
                        case 1:
                            *offp = col;
                            break;
                        case 2:
                            *offp = (~col) & pixelmask;
                            break;
                        case 3:
                            *offp &= col;
                            break;
                        case 4:
                            *offp |= col;
                            break;
                        case 5:
                            *offp ^= col;
                            break;
                        }
                    } // mask
                } // bounds
                offp++;
            }
            y3++;
        }
        return;
    } // 1

    if (bits == 2) {
        mask &= 3;
        y3 = 0;
        for (y = y1; y <= y2; y++) {
            offp = (uint8 *)write_page->offset + (y * sx + x1);
            x3 = 0;
            for (x = x1; x <= x2; x++) {
                if (!(x3--)) {
                    x3 = 3;
                    col2 = *cp++;
                }
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy)) {
                    col = (col2 >> (x3 << 1)) & 3;
                    if ((!passed) || (col != mask)) {
                        switch (option) {
                        case 0:
                            *offp ^= col;
                            break;
                        case 1:
                            *offp = col;
                            break;
                        case 2:
                            *offp = (~col) & pixelmask;
                            break;
                        case 3:
                            *offp &= col;
                            break;
                        case 4:
                            *offp |= col;
                            break;
                        case 5:
                            *offp ^= col;
                            break;
                        }
                    } // mask
                } // bounds
                offp++;
            }
            y3++;
        }
        return;
    } // 2

    if (bits == 4) {
        mask &= 15;
        y3 = 0;
        for (y = y1; y <= y2; y++) {
            offp = (uint8 *)write_page->offset + (y * sx + x1);
            cp1 = cp + y3 * z * 4;
            cp2 = cp + y3 * z * 4 + z;
            cp3 = cp + y3 * z * 4 + z * 2;

            cp4 = cp + y3 * z * 4 + z * 3;
            x3 = 0;
            for (x = x1; x <= x2; x++) {
                if (!(x3--)) {
                    x3 = 7;
                    col1 = *cp1++;
                    col2 = (*cp2++) << 1;
                    col3 = (*cp3++) << 2;
                    col4 = (*cp4++) << 3;
                }
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy)) {
                    col = ((col1 >> x3) & 1) | ((col2 >> x3) & 2) | ((col3 >> x3) & 4) | ((col4 >> x3) & 8);
                    if ((!passed) || (col != mask)) {
                        switch (option) {
                        case 0:
                            *offp ^= col;
                            break;
                        case 1:
                            *offp = col;
                            break;
                        case 2:
                            *offp = (~col) & pixelmask;
                            break;
                        case 3:
                            *offp &= col;
                            break;
                        case 4:
                            *offp |= col;
                            break;
                        case 5:
                            *offp ^= col;
                            break;
                        }
                    } // mask
                } // bounds
                offp++;
            }
            y3++;
        }
        return;
    } // 4

    if (bits == 8) {
        mask &= 255;
        for (y = y1; y <= y2; y++) {
            offp = (uint8 *)write_page->offset + (y * sx + x1);
            for (x = x1; x <= x2; x++) {
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy)) {
                    col = *cp;
                    if ((!passed) || (col != mask)) {
                        switch (option) {
                        case 0:
                            *offp ^= col;
                            break;
                        case 1:
                            *offp = col;
                            break;
                        case 2:
                            *offp = (~col) & pixelmask;
                            break;
                        case 3:
                            *offp &= col;
                            break;
                        case 4:
                            *offp |= col;
                            break;
                        case 5:
                            *offp ^= col;
                            break;
                        }
                    } // mask
                } // bounds
                offp++;
                cp++;
            }
        }
        return;
    } // 8

    if (bits == 32) {
        for (y = y1; y <= y2; y++) {
            off32p = (uint32 *)write_page->offset32 + (y * sx + x1);
            for (x = x1; x <= x2; x++) {
                if ((x >= 0) && (y >= 0) && (x < sx) && (y < sy)) {
                    col = *lp;
                    if ((!passed) || (col != mask)) {
                        switch (option) {
                        case 0:
                            *off32p ^= col;
                            break;
                        case 1:
                            *off32p = col;
                            break;
                        case 2:
                            *off32p = (~col) & pixelmask;
                            break;
                        case 3:
                            *off32p &= col;
                            break;
                        case 4:
                            *off32p |= col;
                            break;
                        case 5:
                            *off32p ^= col;
                            break;
                        }
                    } // mask
                } // bounds
                off32p++;
                lp++;
            }
        }
        return;
    } // 32

    /*
        static byte_element_struct *ele;
        ele=(byte_element_struct*)element;
        static int32 x,y;
        static int32 sx,sy,c,px,py;
        static uint8 *cp;
        static int32 *lp;

        sx=((uint16*)ele->offset)[0];
        sy=((uint16*)ele->offset)[1];
        cp=(uint8*)(ele->offset+4);
        lp=(int32*)cp;


        static int32 sizeinbytes;
        static int32 byte;
        static int32 bitvalue;
        static int32 bytesperrow;
        static int32 row2offset;
        static int32 row3offset;
        static int32 row4offset;

        static int32 longval;

        if (write_page->bits_per_pixel==8){
        mask&=255;
        //create error if not divisible by 8!
        sx>>=3;
        }

        if (write_page->bits_per_pixel==1){
        mask&=1;
        }

        if (write_page->bits_per_pixel==2){
        mask&=3;
        sx>>=1;
        }


        if (write_page->bits_per_pixel==4){
        mask&=15;
        bytesperrow=sx>>3; if (sx&7) bytesperrow++;
        row2offset=bytesperrow;
        row3offset=bytesperrow*2;
        row4offset=bytesperrow*3;
        }


        for (y=0;y<sy;y++){
        py=y1+y;

        if (write_page->bits_per_pixel==4){
        bitvalue=128;
        byte=0;
        }

        for (x=0;x<sx;x++){
        px=x1+x;


        //get colour
        if (write_page->bits_per_pixel==8){
        c=*cp;
        cp++;
        }

        if (write_page->bits_per_pixel==4){
        byte=x>>3;
        c=0;
        if (cp[byte]&bitvalue) c|=1;
        if (cp[row2offset+byte]&bitvalue) c|=2;
        if (cp[row3offset+byte]&bitvalue) c|=4;
        if (cp[row4offset+byte]&bitvalue) c|=8;
        bitvalue>>=1; if (bitvalue==0) bitvalue=128;
        }


        if (write_page->bits_per_pixel==1){
        if (!(x&7)){
        byte=*cp;
        cp++;
        }
        c=(byte&128)>>7; byte<<=1;
        }

        if (write_page->bits_per_pixel==2){
        if (!(x&3)){
        byte=*cp;
        cp++;
        }
        c=(byte&192)>>6; byte<<=2;
        }


        if ((px>=0)&&(px<write_page->width)&&(py>=0)&&(py<write_page->height)){

        //check color
        if (passed){
        if (c==mask) goto maskpixel;
        }


        //"pset" color

        //PUT[{STEP}](?,?),?[,[{PSET|PRESET|AND|OR|XOR}][,[?]]]
        //apply option

        if (option==1){
        write_page->offset[py*write_page->width+px]=c;
        }
        if (option==2){
        //PRESET=bitwise NOT
        write_page->offset[py*write_page->width+px]=(~c)&write_page->mask;
        }
        if (option==3){
        write_page->offset[py*write_page->width+px]&=c;
        }
        if (option==4){
        write_page->offset[py*write_page->width+px]|=c;
        }
        if ((option==5)||(option==0)){
        write_page->offset[py*write_page->width+px]^=c;
        }






        }
        maskpixel:;


        }//x


        if (write_page->bits_per_pixel==4) cp+=(bytesperrow*4);

        //if (_bits_per_pixel==1){
        // if (sx&7) cp++;
        //}

        //if (_bits_per_pixel==2){
        // if (sx&3) cp++;
        //}


        }//y
    */
}

int32 func_csrlin() {
#ifdef QB64_WINDOWS // Windows console CSRLIN support
    if (write_page->console) {
        CONSOLE_SCREEN_BUFFER_INFO cl_bufinfo;
        SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
        HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
        GetConsoleScreenBufferInfo(cl_conout, &cl_bufinfo);
        return cl_bufinfo.dwCursorPosition.Y + 1;
    }
#endif
    if (write_page->holding_cursor) {
        if (write_page->cursor_y >= write_page->bottom_row)
            return write_page->bottom_row;
        else
            return write_page->cursor_y + 1;
    }
    return write_page->cursor_y;
}

int32 func_pos(int32 ignore) {
#ifdef QB64_WINDOWS
    if (write_page->console) { // qb64 console CSRLIN
        CONSOLE_SCREEN_BUFFER_INFO cl_bufinfo;
        SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
        HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
        GetConsoleScreenBufferInfo(cl_conout, &cl_bufinfo);
        return cl_bufinfo.dwCursorPosition.X + 1;
    }
#endif
    if (write_page->holding_cursor)
        return 1;
    return write_page->cursor_x;
}

int32 sleep_break = 0;

void sub_sleep(int32 seconds, int32 passed) {
    if (is_error_pending())
        return;

    sleep_break = 0;
    double prev, ms, now, elapsed; // cannot be static
    prev = GetTicks();
    ms = 1000.0 * (double)seconds;

#ifdef QB64_WINDOWS

    if (read_page->console) {
        int32 junk = 0, junk2 = 0;
        DWORD dwRet;
        HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
        FlushConsoleInputBuffer(hStdin);

        do {
            uint32_t sleepms = 10;

            // Check if ms is less than 10, if so sleep for that many ms to
            // try to make the sleep a bit more accurate
            if (passed && ms < 10 && ms > 0)
                sleepms = ms;

            // Only sleep for a max of 10ms intervals so that we can pump the evnt() handler
            dwRet = WaitForSingleObject(hStdin, sleepms); // this should provide our pause

            if (dwRet == WAIT_OBJECT_0) { // this says the console had input
                junk = func__getconsoleinput();
                if (junk == 1) {                     // this is a valid keyboard event.  Let's exit SLEEP in the console.
                    Sleep(100);                      // Give the user time to remove their finger from the key, before clearing the buffer.
                    FlushConsoleInputBuffer(hStdin); // flush the keyboard buffer after, so we don't leave stray events to be processed (such as key up events).
                    return;
                } else { // we had an input event such as the mouse.  Ignore it and clear the buffer so we don't keep responding to mouse inputs
                    FlushConsoleInputBuffer(hStdin);
                }
            }

            // Handle periodic events (timers and some other things)
            evnt(0);

            // evnt() may trigger us to end sleep early
            if (sleep_break)
                return;
            if (stop_program)
                return;

            // We only update the ms count if we were provided a max number of
            // seconds to sleep
            if (passed) {
                now = GetTicks();
                if (now < prev)
                    return;           // value looped?
                elapsed = now - prev; // elapsed time since prev
                ms = ms - elapsed;
                prev = now;
            }

        } while (!passed || ms > 0); // We loop if we weren't provided a number of seconds to sleep, or if we reached the timeout

        // If we got here, then we timed out
        return;
    }
#endif

recalculate:
wait:
    evnt(0); // handle general events
    // exit condition checks/events
    if (sleep_break)
        return;
    if (stop_program)
        return;
    if (ms <= 0) { // untimed SLEEP
        Sleep(9);
        goto wait;
    }
    now = GetTicks();
    if (now < prev) { // value looped?
        return;
    }
    elapsed = now - prev; // elapsed time since prev
    if (elapsed < ms) {
        int64 wait; // cannot be static
        wait = ms - elapsed;
        if (!wait)
            wait = 1;
        if (wait >= 10) {
            Sleep(9);
            // recalculate time
            goto recalculate;
        } else {
            Sleep(wait);
        }
    }
    return;
}

ptrszint func_lbound(ptrszint *array, int32 index, int32 num_indexes) {
    if ((index < 1) || (index > num_indexes) || ((array[2] & 1) == 0)) {
        error(9);
        return 0;
    }
    index = num_indexes - index + 1;
    return array[4 * index];
}

ptrszint func_ubound(ptrszint *array, int32 index, int32 num_indexes) {
    if ((index < 1) || (index > num_indexes) || ((array[2] & 1) == 0)) {
        error(9);
        return 0;
    }
    index = num_indexes - index + 1;
    return array[4 * index] + array[4 * index + 1] - 1;
}

uint8 port60h_event[256];
int32 port60h_events = 0;

int32 func_inp(int32 port) {
    static int32 value;
    unsupported_port_accessed = 0;
    if ((port > 65535) || (port < -65536)) {
        error(6);
        return 0; // Overflow
    }
    port &= 0xFFFF;

    if (port == 0x3C9) {       // read palette
        if (write_page->pal) { // avoid NULL pointer
            // convert 0-255 value to 0-63 value
            if (H3C9_read_next == 0) { // red
                value = qbr_double_to_long((((double)((write_page->pal[H3C7_palette_register_read_index] >> 16) & 255)) / 3.984376 - 0.4999999f));
            }
            if (H3C9_read_next == 1) { // green
                value = qbr_double_to_long((((double)((write_page->pal[H3C7_palette_register_read_index] >> 8) & 255)) / 3.984376 - 0.4999999f));
            }
            if (H3C9_read_next == 2) { // blue
                value = qbr_double_to_long((((double)(write_page->pal[H3C7_palette_register_read_index] & 255)) / 3.984376 - 0.4999999f));
            }
            H3C9_read_next = H3C9_read_next + 1;
            if (H3C9_read_next == 3) {
                H3C9_read_next = 0;
                H3C7_palette_register_read_index = H3C7_palette_register_read_index + 1;
                H3C7_palette_register_read_index &= 0xFF;
            }
            return value;
        } //->pal
        return 0; // non-palette modes
    }

    /*
        3dAh (R):  Input Status #1 Register
        bit   0  Either Vertical or Horizontal Retrace active if set
        1  Light Pen has triggered if set
        2  Light Pen switch is open if set
        3  Vertical Retrace in progress if set
        4-5  Shows two of the 6 color outputs, depending on 3C0h index 12h.
        Attr: Bit 4-5:   Out bit 4  Out bit 5
        0          Blue       Red
        1        I Blue       Green
        2        I Red      I Green
    */
    if (port == 0x3DA) {
        value = 0;
        if (vertical_retrace_happened || vertical_retrace_in_progress) {
            vertical_retrace_happened = 0;
            value |= 8;
        }
        return value;
    }

    if (port == 0x60) {
        // return last scancode event
        if (port60h_events) {
            value = port60h_event[0];
            if (port60h_events > 1)
                memmove(port60h_event, port60h_event + 1, 255);
            port60h_events--;
            return value;
        } else {
            return port60h_event[0];
        }
    }

    unsupported_port_accessed = 1;
    return 0; // unknown port!
}

void sub_wait(int32 port, int32 andexpression, int32 xorexpression, int32 passed) {
    if (is_error_pending())
        return;
    // 1. read value from port
    // 2. value^=xorexpression (if passed!)
    // 3. value^=andexpression
    // IMPORTANT: Wait returns immediately if given port is unsupported by QB64 so program
    //           can continue
    static int32 value;

    // error & range checking
    if ((port > 65535) || (port < -65536)) {
        error(6);
        return; // Overflow
    }
    port &= 0xFFFF;
    if ((andexpression < -32768) || (andexpression > 65535)) {
        error(6);
        return; // Overflow
    }
    andexpression &= 0xFF;
    if (passed) {
        if ((xorexpression < -32768) || (xorexpression > 65535)) {
            error(6);
            return; // Overflow
        }
    }
    xorexpression &= 0xFF;

wait:
    value = func_inp(port);
    if (passed)
        value ^= xorexpression;
    value &= andexpression;
    if (value || unsupported_port_accessed || stop_program)
        return;
    Sleep(1);
    goto wait;
}

extern int32 tab_LPRINT;      // 1=dest is LPRINT image
extern int32 tab_spc_cr_size; //=1;//default
extern int32 tab_fileno;

qbs *func_tab(int32 pos) {
    if (is_error_pending())
        return qbs_new(0, 1);

    static int32 tab_LPRINT_olddest;
    if (tab_LPRINT) {
        if (!lprint_image)
            qbs_lprint(qbs_new(0, 1), 0); // send dummy data to init the LPRINT image
        tab_LPRINT_olddest = func__dest();
        sub__dest(lprint_image);
    }

    // returns a string to advance to the horizontal position "pos" on either
    // the current line or the next line.
    static int32 w, div, cursor;

    static int32 cr_size;      // a local in case file is SCRN
    cr_size = tab_spc_cr_size; // init to caller's value
    // calculate width in spaces & current position
    if (cr_size == 2) {
        // print to file
        div = 1;
        w = 2147483647;
        cursor = 1;
        // validate file
        static int32 i;
        i = tab_fileno;
        if (i < 0)
            goto invalid_file; // TCP/IP unsupported
        if (gfs_fileno_valid(i) != 1)
            goto invalid_file;                   // Bad file name or number
        i = gfs_get_fileno(i);                   // convert fileno to gfs index
        if (gfs_get_file_struct(i)->scrn == 1) { // going to screen, change the cr size
            cr_size = 1;
        } else {
            cursor = gfs_get_file_struct(i)->column;
        }
    invalid_file:;
    }
    if (cr_size == 1) {
        // print to surface
        if (write_page->text) {
            w = write_page->width;
            div = 1;
        } else {
            if (fontwidth[write_page->font]) {
                w = write_page->width / fontwidth[write_page->font];
                div = 1;
            } else {
                // w=func__printwidth(singlespace,NULL,0);
                w = write_page->width;
                div = func__printwidth(singlespace, NULL, 0);
            }
        }
        cursor = write_page->cursor_x;
    }

    static qbs *tqbs;
    if ((pos < -32768) || (pos > 32767)) {
        if (tab_LPRINT)
            sub__dest(tab_LPRINT_olddest);
        tqbs = qbs_new(0, 1);
        error(6);
        return tqbs; // Overflow
    }
    if (pos > w)
        pos %= w;
    if (pos < 1)
        pos = 1;
    static int32 size, spaces, cr;
    size = 0;
    spaces = 0;
    cr = 0;
    if (cursor > pos) {
        cr = 1;
        size = cr_size;
        spaces = pos / div;
        if (pos % div)
            spaces++;
        spaces--; // don't put a space on the dest position
        size += spaces;
    } else {
        spaces = (pos - cursor) / div;
        if ((pos - cursor) % div)
            spaces++;
        size = spaces;
    }
    // build custom string
    tqbs = qbs_new(size, 1);
    if (cr) {
        tqbs->chr[0] = 13;
        if (cr_size == 2)
            tqbs->chr[1] = 10;
        memset(&tqbs->chr[cr_size], 32, spaces);
    } else {
        memset(tqbs->chr, 32, spaces);
    }
    if (tab_LPRINT)
        sub__dest(tab_LPRINT_olddest);
    return tqbs;
}

qbs *func_spc(int32 spaces) {
    if (is_error_pending())
        return qbs_new(0, 1);

    static qbs *tqbs;
    if ((spaces < -32768) || (spaces > 32767)) {
        tqbs = qbs_new(0, 1);
        error(6);
        return tqbs;
    } // Overflow
    if (spaces < 0)
        spaces = 0;

    // for files, spc simply adds that many spaces
    if (tab_spc_cr_size == 2) { // files
        tqbs = qbs_new(spaces, 1);
        memset(tqbs->chr, 32, spaces);
        return tqbs;
    }

    static int32 tab_LPRINT_olddest;
    if (tab_LPRINT) {
        if (!lprint_image)
            qbs_lprint(qbs_new(0, 1), 0); // send dummy data to init the LPRINT image
        tab_LPRINT_olddest = func__dest();
        sub__dest(lprint_image);
    }

    // for screens, spc adds that many spaces MOD screen_width_in_characters
    // if 2 rows are bridged, the top row's characters are not printed to, just the lower
    static int32 x, x2;
    // calc spaces remaining on current screen row & MOD
    static int32 spaces_left_on_line;
    static qbs *onespace = NULL;
    if (!onespace) {
        onespace = qbs_new(1, 0);
        onespace->chr[0] = 32;
    }
    static int32 onespace_width; // for variable width fonts
    if (write_page->text) {
        spaces_left_on_line = write_page->width - write_page->cursor_x + 1;
        spaces %= write_page->width; // MOD
    } else {
        x = fontwidth[write_page->font];
        if (x) {
            x2 = write_page->width / x; // characters per row
            spaces_left_on_line = x2 - write_page->cursor_x + 1;
            spaces %= x2; // MOD
        } else {
            x2 = write_page->width - write_page->cursor_x + 1; // pixels left on row
            onespace_width = func__printwidth(onespace, NULL, 0);
            spaces_left_on_line = x2 / onespace_width;
            spaces %= (write_page->width / onespace_width); // MOD
        }
    }

    // build string
    if (spaces_left_on_line >= spaces) {
        tqbs = qbs_new(spaces, 1);
        memset(tqbs->chr, 32, spaces);
    } else {
        spaces -= spaces_left_on_line;
        tqbs = qbs_new(1 + spaces, 1);
        tqbs->chr[0] = 13;
        memset(tqbs->chr + 1, 32, spaces);
    }

    if (tab_LPRINT)
        sub__dest(tab_LPRINT_olddest);
    return tqbs;
}

float func_pmap(float val, int32 option) {
    static int32 x, y;
    if (is_error_pending())
        return 0;
    if (!write_page->text) {
        // note: for QBASIC/4.5/7.1 compatibility clipping_or_scaling check is skipped
        if (option == 0) {
            x = qbr_float_to_long(val * write_page->scaling_x + write_page->scaling_offset_x);
            return x;
        }
        if (option == 1) {
            y = qbr_float_to_long(val * write_page->scaling_y + write_page->scaling_offset_y);
            return y;
        }
        if (option == 2) {
            return (((double)qbr_float_to_long(val)) - write_page->scaling_offset_x) / write_page->scaling_x;
        }
        if (option == 3) {
            return (((double)qbr_float_to_long(val)) - write_page->scaling_offset_y) / write_page->scaling_y;
        }
    } //! write_page->text
    error(5);
    return 0;
}

uint32 func_screen(int32 y, int32 x, int32 returncol, int32 passed) {

    static uint8 chr[65536];
    static int32 x2, y2, x3, y3, i, i2, i3;
    static uint32 col, firstcol;
    uint8 *cp;

    if (!passed)
        returncol = 0;

#ifdef QB64_WINDOWS
    if (read_page->console) {
        SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
        HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
        COORD cp1 = {(SHORT)(x - 1), (SHORT)(y - 1)};
        DWORD t;
        uint16 a;
        if (returncol) {
            int x1 = ReadConsoleOutputAttribute(cl_conout, &a, 1, cp1, &t);
            return a;
        } else {
            ReadConsoleOutputCharacterA(cl_conout, (char *)&a, 1, cp1, &t);
            return a & 0xff;
        }
    }
#endif

    if (read_page->text) {
        // on screen?
        if ((y < 1) || (y > read_page->height)) {
            error(5);
            return 0;
        }
        if ((x < 1) || (x > read_page->width)) {
            error(5);
            return 0;
        }
        if (returncol) {
            return read_page->offset[((y - 1) * read_page->width + x - 1) * 2 + 1];
        }
        return read_page->offset[((y - 1) * read_page->width + x - 1) * 2];
    }

    // only 8x8,8x14,8x16 supported
    if ((read_page->font != 8) && (read_page->font != 14) && (read_page->font != 16)) {
        error(5);
        return 0;
    }

    // on screen?
    x2 = read_page->width / fontwidth[read_page->font];
    y2 = read_page->height / fontheight[read_page->font];
    if ((y < 1) || (y > y2)) {
        error(5);
        return 0;
    }
    if ((x < 1) || (x > x2)) {
        error(5);
        return 0;
    }

    // create "signature" of character on screen
    x--;
    y--;
    i = 0;
    i3 = 1;
    y3 = y * fontheight[read_page->font];
    for (y2 = 0; y2 < fontheight[read_page->font]; y2++) {
        x3 = x * fontwidth[read_page->font];
        for (x2 = 0; x2 < fontwidth[read_page->font]; x2++) {
            col = point(x3, y3);
            if (col) {
                if (i3) {
                    i3 = 0;
                    firstcol = col;
                }
                col = 255;
            }
            chr[i] = col;
            i++;
            x3++;
        }
        y3++;
    }

    if (i3) { // assume SPACE, no non-zero pixels were found
        if (returncol)
            return 1;
        return 32;
    }

    i3 = i; // number of bytes per character "signature"

    // compare signature with all ascii characters
    for (i = 0; i <= 255; i++) {
        if (read_page->font == 8)
            cp = &charset8x8[i][0][0];
        if (read_page->font == 14)
            cp = &charset8x16[i][1][0];
        if (read_page->font == 16)
            cp = &charset8x16[i][0][0];
        i2 = memcmp(cp, chr, i3);
        if (!i2) { // identical!
            if (returncol)
                return firstcol;
            return i;
        }
    }

    // no match found
    if (returncol)
        return 0;
    return 32;
}

void sub_bsave(qbs *filename, int32 offset, int32 size) {
    if (is_error_pending())
        return;
    static std::ofstream fh;

    static qbs *tqbs = NULL;
    if (!tqbs)
        tqbs = qbs_new(0, 0);
    static qbs *nullt = NULL;
    if (!nullt)
        nullt = qbs_new(1, 0);

    static int32 x;
    nullt->chr[0] = 0;
    if ((offset < -65536) || (offset > 65535)) {
        error(6);
        return;
    } // Overflow
    offset &= 0xFFFF;
    // note: QB64 allows a maximum of 65536 bytes, QB only allows 65535
    if ((size < -65536) || (size > 65536)) {
        error(6);
        return;
    } // Overflow
    if (size != 65536)
        size &= 0xFFFF;
    qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
    fh.open(filepath_fix_directory(tqbs), std::ios::binary | std::ios::out);
    if (!fh.is_open()) {
        error(64);
        return;
    } // Bad file name
    x = 253;
    fh.write((char *)&x, 1); // signature byte
    x = (defseg - &cmem[0]) / 16;
    fh.write((char *)&x, 2); // segment
    x = offset;
    fh.write((char *)&x, 2); // offset
    x = size;
    if (x > 65535)
        x = 0;                                 // if filesize>65542 then size=filesize-7
    fh.write((char *)&x, 2);                   // size
    fh.write((char *)(defseg + offset), size); // data
    fh.close();
}

void sub_bload(qbs *filename, int32 offset, int32 passed) {
    if (is_error_pending())
        return;
    static uint8 header[7];
    static std::ifstream fh;
    static qbs *tqbs = NULL;
    if (!tqbs)
        tqbs = qbs_new(0, 0);
    static qbs *nullt = NULL;
    if (!nullt)
        nullt = qbs_new(1, 0);

    static int32 x, file_seg, file_off, file_size;
    nullt->chr[0] = 0;
    if (passed) {
        if ((offset < -65536) || (offset > 65535)) {
            error(6);
            return;
        } // Overflow
        offset &= 0xFFFF;
    }
    qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
    fh.open(filepath_fix_directory(tqbs), std::ios::binary | std::ios::in);
    if (!fh.is_open()) {
        error(53);
        return;
    } // File not found
    fh.read((char *)header, 7);
    if (fh.gcount() != 7)
        goto error;
    if (header[0] != 253)
        goto error;
    file_seg = header[1] + header[2] * 256;
    file_off = header[3] + header[4] * 256;
    file_size = header[5] + header[6] * 256;
    if (file_size == 0) {
        fh.seekg(0, std::ios::end);
        file_size = fh.tellg();
        fh.seekg(7, std::ios::beg);
        file_size -= 7;
        if (file_size < 65536)
            file_size = 0;
    }
    if (passed) {
        fh.read((char *)(defseg + offset), file_size);
    } else {
        fh.read((char *)(&cmem[0] + file_seg * 16 + file_off), file_size);
    }
    if (fh.gcount() != file_size)
        goto error;
    fh.close();
    return;
error:
    fh.close();
    error(54); // Bad file mode
}

int64 func_lof(int32 i) {
    static int64 size;

    if (i < 0) { // special handle?
        static special_handle_struct *sh;
        uint64_t length;
        int x = -(i + 1);
        int err;

        sh = (special_handle_struct *)list_get(special_handles, x);
        if (!sh) {
            error(52);
            return 0;
        }

        static stream_struct *st;
        switch (sh->type) {
        case special_handle_type::Stream:
            st = (stream_struct *)sh->index;
            stream_update(st);
            return st->in_size;

        case special_handle_type::Http:
            err = libqb_http_get_content_length(x, &length);
            if (err != 0)
                return -1;

            return length;

        default:
            error(52);
            return 0;
        }
    } // special handle

    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return 0;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    size = gfs_lof(i);
    if (size < 0) {
        if (size == -2) {
            error(258);
            return 0;
        } // invalid handle
        if (size == -3) {
            error(54);
            return 0;
        } // bad file mode
        if (size == -4) {
            error(5);
            return 0;
        } // illegal function call
        error(75);
        return 0; // assume[-9]: path/file access error
    }
    return size;
}

int32 func_eof(int32 i) {
    static int32 pos, lof, x;

    if (i < 0) { // special handle?
        x = -(i + 1);
        static special_handle_struct *sh;
        sh = (special_handle_struct *)list_get(special_handles, x);
        if (!sh) {
            error(52);
            return 0;
        }

        static stream_struct *st;
        size_t length = 0;

        switch (sh->type) {
        case special_handle_type::Stream:
            st = (stream_struct *)sh->index;
            if (st->eof)
                return -1;
            return 0;

        case special_handle_type::Http:
            // If sh->index set it overrides the EOF() check. The caller will need to
            // use _CONNECTED() to determine whether more data could be coming.
            if (sh->index == 1)
                return -1;
            else if (sh->index == 2)
                return 0;

            // Only report EOF() if the program had read all incoming data and
            // the connection is finished.
            if (libqb_http_connected(x))
                return 0;

            libqb_http_get_length(x, &length);
            if (length != 0)
                return 0;

            return -1;

        default:
            error(52);
            return 0;
        }
    } // special handle

    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return 0;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (gfs->scrn) {
        error(5);
        return 0;
    }
    if (gfs->type != 3) { // uint8 type;//qb access method (1=RANDOM,2=BINARY,3=INPUT,4=OUTPUT)
        if (gfs_eof_passed(i) == 1)
            return -1;
    } else {
        if (gfs_eof_reached(i) == 1)
            return -1;
        if (gfs_eof_passed(i) == 1)
            return -1;
    }
    return 0;
}

int32 func__statusCode(int32 handle) {
    if (handle >= 0) {
        error(52);
        return -1;
    }

    int real_handle = -(handle + 1);

    special_handle_struct *sh = (special_handle_struct *)list_get(special_handles, real_handle);
    if (sh->type != special_handle_type::Http) {
        error(52);
        return -1;
    }

    int code = libqb_http_get_status_code(real_handle);

    if (code == -1) {
        // Connection isn't valid, it has no status code.
        error(9);
        return -1;
    }

    return code;
}

void sub_seek(int32 i, int64 pos) {
    if (is_error_pending())
        return;
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (gfs->type == 1) { // RANDOM
        pos--;
        if (pos < 0) {
            error(63);
            return;
        } // Bad record number
        pos *= gfs->record_length;
        pos++;
    }
    pos--;
    if (pos < 0) {
        error(63);
        return;
    } // Bad record number
    int32 e;
    e = gfs_setpos(i, pos);
    if (e < 0) {
        if (e == -2) {
            error(258);
            return;
        } // invalid handle
        if (e == -3) {
            error(54);
            return;
        } // bad file mode
        if (e == -4) {
            error(5);
            return;
        } // illegal function call
        error(75);
        return; // assume[-9]: path/file access error
    }
}

int64 func_seek(int32 i) {
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return 0;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);
    if (gfs->scrn)
        return 0;
    if (gfs->type == 1) { // RANDOM
        return gfs_getpos(i) / gfs->record_length + 1;
    }
    return gfs_getpos(i) + 1;
}

int64 func_loc(int32 i) {
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return 0;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);

    if (gfs->scrn) {
        error(5);
        return 0;
    }
    if (gfs->com_port) {
#ifdef QB64_WINDOWS
        static COMSTAT c;
        ZeroMemory(&c, sizeof(COMSTAT));
        static DWORD ignore;
        if (!ClearCommError(gfs->win_handle, &ignore, &c))
            return 0;
        return c.cbInQue; // bytes in COM input buffer
#endif
    }

    if (gfs->type == 1) { // RANDOM
        return gfs_getpos(i) / gfs->record_length;
    }
    if (gfs->type == 2) { // BINARY
        return gfs_getpos(i);
    }
    // APPEND/OUTPUT/INPUT
    return gfs_getpos(i) / 128;
}

qbs *func_input(int32 n, int32 i, int32 passed) {
    if (is_error_pending())
        return qbs_new(0, 1);
    static qbs *str, *str2;
    static int32 x, c;
    if (n < 0)
        str = qbs_new(0, 1);
    else
        str = qbs_new(n, 1);
    if (passed) {

        if (gfs_fileno_valid(i) != 1) {
            error(52);
            return str;
        } // Bad file name or number
        i = gfs_get_fileno(i); // convert fileno to gfs index
        static gfs_file_struct *gfs;
        gfs = gfs_get_file_struct(i);
        if ((gfs->type < 2) || (gfs->type > 3)) {
            error(62);
            return str;
        } // Input past end of file
        // note: RANDOM should be supported
        // note: Unusually, QB returns "Input past end of file" instead of "Bad file mode"
        if (!gfs->read) {
            error(75);
            return str;
        } // Path/file access error

        if (n < 0) {
            error(52);
            return str;
        } // Bad file name or number
        if (n == 0)
            return str;

        // INPUT -> Input past end of file at EOF or CHR$(26)
        //         unlike BINARY, partial strings cannot be returned
        //         (use input_file_chr and modify it to support CHR$(26)
        if (gfs->type == 3) {
            x = 0;
            do {
                c = file_input_chr(i);
                if (c == -1) {
                    error(62);
                    return str;
                } // Input past end of file
                if (c == -2) {
                    error(75);
                    return str;
                } // path/file access error
                str->chr[x] = c;

                if (gfs_get_file_struct(i)->eof_passed != 1) { // If we haven't declared the End of the File, check to see if the next byte is an EOF byte
                    c = file_input_chr(i);                     // read the next byte
                    if (gfs_get_file_struct(i)->eof_passed != 1) {
                        gfs_setpos(i, gfs_getpos(i) - 1);
                    } // and if it's not EOF, move our position back to where it should be
                }

                x++;
            } while (x < n);
            return str;
        }

        // BINARY -> If past EOF, returns a NULL length string!
        //          or as much of the data that was valid as possible
        //          Seek POS is only advanced as far as was read!
        //          Binary can read past CHR$(26)
        //          (simply call C's read statement and manage via .eof
        if (gfs->type == 2) {
            static int32 e;
            e = gfs_read(i, -1, str->chr, n);
            if (e) {
                if (e != -10) { // note: on eof, unread buffer area becomes NULL
                    str->len = 0;
                    if (e == -2) {
                        error(258);
                        return str;
                    } // invalid handle
                    if (e == -3) {
                        error(54);
                        return str;
                    } // bad file mode
                    if (e == -4) {
                        error(5);
                        return str;
                    } // illegal function call
                    if (e == -7) {
                        error(70);
                        return str;
                    } // permission denied
                    error(75);
                    return str; // assume[-9]: path/file access error
                }
            }
            str->len = gfs_read_bytes(); // adjust if not enough data was available
            return str;
        }

        // RANDOM (todo)

        return str;
    } else {
        // keyboard/piped
        //      For extended-two-byte character codes, only the first, CHR$(0), is returned and counts a 1 byte
        if (n < 0) {
            error(52);
            return str;
        }
        if (n == 0)
            return str;
        x = 0;
    waitforinput:
        str2 = qbs_inkey();
        if (str2->len) {
            str->chr[x] = str2->chr[0];
            x++;
        }
        qbs_free(str2);
        if (stop_program)
            return str;
        if (x < n) {
            evnt(0); // check for new events
            Sleep(10);
            goto waitforinput;
        }
        return str;
    }
}

void file_line_input_string_character(int32 filehandle, qbs *deststr) {
    static qbs *str, *character;
    int32 c, nextc;
    int32 inspeechmarks;

    str = qbs_new(0, 0);
    c = file_input_chr(filehandle);
    if (c == -2)
        return;
    if (c == -1) {
        qbs_set(deststr, str);
        qbs_free(str);
        error(62); // input past end of file
        return;
    }
    character = qbs_new(1, 0);
nextchr:
    if (c == -1)
        goto gotstr;
    if (c == 10)
        goto gotstr;
    if (c == 13)
        goto gotstr;
    character->chr[0] = c;
    qbs_set(str, qbs_add(str, character));
    c = file_input_chr(filehandle);
    if (c == -2)
        return;
    goto nextchr;
gotstr:
nextstr:
    // scan until next item (or eof) reached
    if (c == -1)
        goto returnstr;
    if ((c == 10) || (c == 13)) { // lf cr
        file_input_skip1310(filehandle, c);
        goto returnstr;
    }
    c = file_input_chr(filehandle);
    if (c == -2)
        return;
    goto nextstr;
// return string
returnstr:
    qbs_set(deststr, str);
    qbs_free(str);
    qbs_free(character);
    return;
}

void file_line_input_string_binary(int32 fileno, qbs *deststr) {
    int32 filebuf_size = 512;
    int32 filehandle;
    qbs *eol;

    filehandle = gfs_get_fileno(fileno); // convert fileno to gfs index
    eol = qbs_new_txt_len("\n", 1);

    int64 start_byte = func_seek(fileno);
    int64 filelength = func_lof(fileno);
    if (start_byte > filelength) {
        error(62); // input past end of file
        return;
    }
    qbs *buffer = qbs_new(filebuf_size, 0);
    qbs_set(deststr, qbs_new_txt_len("", 0));
    do {
        if (start_byte + filebuf_size > filelength)
            filebuf_size = filelength - start_byte + 1;
        qbs_set(buffer, func_space(qbr(filebuf_size)));

        sub_get2(fileno, start_byte, buffer, 1);
        int32 eol_pos = func_instr(0, buffer, eol, 0);
        if (eol_pos == 0) {
            if ((start_byte + filebuf_size) >= filelength) {
                qbs_set(deststr, buffer);
                gfs_setpos(filehandle, filelength);              // set the position right before the EOF marker
                gfs_get_file_struct(filehandle)->eof_passed = 1; // also set EOF flag;
                qbs_free(buffer);
                return;
            }
            filebuf_size += 512;
        } else {
            qbs_set(deststr, qbs_add(deststr, qbs_left(buffer, eol_pos - 1)));
            break;
        }
    } while (!func_eof(fileno));
    qbs_free(buffer);
    if (start_byte + deststr->len + 2 >= filelength) {   // if we've read to the end of the line
        gfs_setpos(filehandle, filelength);              // set the position right before the EOF marker
        gfs_get_file_struct(filehandle)->eof_passed = 1; // also set EOF flag;
        if (deststr->chr[deststr->len - 1] == '\r')
            qbs_set(deststr, qbs_left(deststr, deststr->len - 1));
        return;
    }
    gfs_setpos(filehandle, start_byte + deststr->len); // set the position at the end of the text
    if (deststr->chr[deststr->len - 1] == '\r')
        qbs_set(deststr, qbs_left(deststr, deststr->len - 1));
}

void sub_file_line_input_string(int32 fileno, qbs *deststr) {
    int32 filehandle;
    if (is_error_pending())
        return;
    if (gfs_fileno_valid(fileno) != 1) {
        error(52);
        return;
    } // Bad file name or number
    filehandle = gfs_get_fileno(fileno); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(filehandle);
    if (!gfs->read) {
        error(75);
        return;
    } // Path/file access error

    if (gfs->type == 2) {
        file_line_input_string_binary(fileno, deststr);
    } else if (gfs->type == 3) {
        file_line_input_string_character(filehandle, deststr);
    } else {
        error(54); // Bad file mode
    }
    return;
}

int32 func_freefile() {
    return gfs_fileno_freefile();
}

void sub__mousehide() {
#ifdef QB64_GUI
#    ifdef QB64_GLUT
    OPTIONAL_GLUT();
    libqb_glut_set_cursor(GLUT_CURSOR_NONE);
#    endif
#endif
    mouse_hiddden = -1;
}

#ifdef QB64_GLUT
int mouse_cursor_style = GLUT_CURSOR_LEFT_ARROW;
#else
int mouse_cursor_style = 1;
#endif

void sub__mouseshow(qbs *style, int32 passed) {
    if (is_error_pending())
        return;

#ifdef QB64_GLUT
    OPTIONAL_GLUT();

    static qbs *str = NULL;
    if (str == NULL)
        str = qbs_new(0, 0);
    if (passed) {
        qbs_set(str, qbs_ucase(style));
        if (qbs_equal(str, qbs_new_txt("DEFAULT"))) {
            mouse_cursor_style = GLUT_CURSOR_LEFT_ARROW;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("LINK"))) {
            mouse_cursor_style = GLUT_CURSOR_INFO;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("TEXT"))) {
            mouse_cursor_style = GLUT_CURSOR_TEXT;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("CROSSHAIR"))) {
            mouse_cursor_style = GLUT_CURSOR_CROSSHAIR;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("VERTICAL"))) {
            mouse_cursor_style = GLUT_CURSOR_UP_DOWN;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("HORIZONTAL"))) {
            mouse_cursor_style = GLUT_CURSOR_LEFT_RIGHT;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("TOPLEFT_BOTTOMRIGHT"))) {
            mouse_cursor_style = GLUT_CURSOR_TOP_LEFT_CORNER;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("TOPRIGHT_BOTTOMLEFT"))) {
            mouse_cursor_style = GLUT_CURSOR_TOP_RIGHT_CORNER;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("WAIT"))) {
            mouse_cursor_style = GLUT_CURSOR_WAIT;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("HELP"))) {
            mouse_cursor_style = GLUT_CURSOR_HELP;
            goto cursor_valid;
        }
        if (qbs_equal(str, qbs_new_txt("CYCLE")) || qbs_equal(str, qbs_new_txt("MOVE"))) {
            mouse_cursor_style = GLUT_CURSOR_CYCLE;
            goto cursor_valid;
        }
        error(5);
        return;
    }
cursor_valid:

    libqb_glut_set_cursor(mouse_cursor_style);
#endif
    mouse_hiddden = 0;
}

int32_t func__mousehidden() {
    return mouse_hiddden;
}

float func__mousemovementx() {
    mouse_message_queue_struct *queue = &mouse_message_queue;
    return queue->queue[queue->current].movementx;
}

float func__mousemovementy() {
    mouse_message_queue_struct *queue = &mouse_message_queue;
    return queue->queue[queue->current].movementy;
}

void sub__mousemove(float x, float y) {
#ifdef QB64_GLUT
    NEEDS_GLUT();

    int32 x2, y2, sx, sy;
    if (display_page->text) {
        sx = fontwidth[display_page->font] * display_page->width;
        sy = fontheight[display_page->font] * display_page->height;
        if (x < 0.5)
            goto error;
        if (y < 0.5)
            goto error;
        if (x > ((float)display_page->width) + 0.5)
            goto error;
        if (y > ((float)display_page->height) + 0.5)
            goto error;
        x -= 0.5;
        y -= 0.5;
        x = x * (float)fontwidth[display_page->font];
        y = y * (float)fontheight[display_page->font];
        x2 = qbr_float_to_long(x);
        y2 = qbr_float_to_long(y);
        if (x2 < 0)
            x2 = 0;
        if (y2 < 0)
            y2 = 0;
        if (x2 > sx - 1)
            x2 = sx - 1;
        if (y2 > sy - 1)
            y2 = sy - 1;
    } else {
        sx = display_page->width;
        sy = display_page->height;
        x2 = qbr_float_to_long(x);
        y2 = qbr_float_to_long(y);
        if (x2 < 0)
            goto error;
        if (y2 < 0)
            goto error;
        if (x2 > sx - 1)
            goto error;
        if (y2 > sy - 1)
            goto error;
    }

    // x2,y2 are pixel co-ordinates
    // adjust for fullscreen position as necessary:
    x2 *= environment_2d__screen_x_scale;
    y2 *= environment_2d__screen_y_scale;
    x2 += environment_2d__screen_x1;
    y2 += environment_2d__screen_y1;

    libqb_glut_warp_pointer(x2, y2);
    return;

error:
    error(5);
#endif
}

float func__mousex() {

    static int32 x, x2;
    static float f;

#ifdef QB64_WINDOWS
    if (read_page->console) {
        return consolemousex;
    }
#endif

    mouse_message_queue_struct *queue = &mouse_message_queue;
    x = queue->queue[queue->current].x;

    // calculate pixel offset of mouse within SCREEN using environment variables
    x -= environment_2d__screen_x1;
    x = qbr_float_to_long((((float)x + 0.5f) / environment_2d__screen_x_scale) - 0.5f);
    if (x < 0)
        x = 0;
    if (x >= environment_2d__screen_width)
        x = environment_2d__screen_width - 1;

    // restrict range to the current display page's range to avoid causing errors
    x2 = display_page->width;
    if (display_page->text)
        x2 *= fontwidth[display_page->font];
    if (x >= x2)
        x = x2 - 1;

    if (display_page->text) {
        f = x;
        x2 = fontwidth[display_page->font];
        f = f / (float)x2 + 0.5f;
        x2 = qbr_float_to_long(f);
        if (x2 > x)
            f -= 0.001f;
        if (x2 < x)
            f += 0.001f;
        return std::floor(f + 0.5);
    }

    return x;
}

float func__mousey() {

    static int32 y, y2;
    static float f;

#ifdef QB64_WINDOWS
    if (read_page->console) {
        return consolemousey;
    }
#endif

    mouse_message_queue_struct *queue = &mouse_message_queue;
    y = queue->queue[queue->current].y;

    // calculate pixel offset of mouse within SCREEN using environment variables
    y -= environment_2d__screen_y1;
    y = qbr_float_to_long((((float)y + 0.5f) / environment_2d__screen_y_scale) - 0.5f);
    if (y < 0)
        y = 0;
    if (y >= environment_2d__screen_height)
        y = environment_2d__screen_height - 1;

    // restrict range to the current display page's range to avoid causing errors
    y2 = display_page->height;
    if (display_page->text)
        y2 *= fontheight[display_page->font];
    if (y >= y2)
        y = y2 - 1;

    if (display_page->text) {
        f = y;
        y2 = fontheight[display_page->font];
        f = f / (float)y2 + 0.5f;
        y2 = qbr_float_to_long(f);
        if (y2 > y)
            f -= 0.001f;
        if (y2 < y)
            f += 0.001f;
        return std::floor(f + 0.5);
    }

    return y;
}

int32 func__mouseinput() {
    mouse_message_queue_struct *queue = &mouse_message_queue;
    if (queue->current == queue->last)
        return 0;
    int32 newIndex = queue->current + 1;
    if (newIndex > queue->lastIndex)
        newIndex = 0;
    queue->current = newIndex;
    return -1;
}

int32 func__mousebutton(int32 i) {
    if (i < 1) {
        error(5);
        return 0;
    }
#ifdef QB64_WINDOWS
    if (read_page->console) { // console may support up to 5 mouse buttons according to the documentation.
        if (i == 1)
            return consolebutton & 1;
        if (i == 2)
            return consolebutton & 2;
        if (i == 3)
            return consolebutton & 4;
        if (i == 4)
            return consolebutton & 8;
        if (i == 5)
            return consolebutton & 16;
        return 0;
    }
#endif

    if (i > 3)
        return 0; // current SDL only supports 3 mouse buttons!
    // swap indexes 2&3
    if (i == 2) {
        i = 3;
    } else {
        if (i == 3)
            i = 2;
    }
    mouse_message_queue_struct *queue = &mouse_message_queue;
    if (queue->queue[queue->current].buttons & (1 << (i - 1)))
        return -1;
    return 0;
}

int32 func__mousewheel() {
    static uint32 x;

#ifdef QB64_WINDOWS
    if (read_page->console) {
        if (consolebutton < -0x100)
            return -1;
        if (consolebutton > 0x100)
            return 1;
        return 0;
    }
#endif

    mouse_message_queue_struct *queue = &mouse_message_queue;
    x = queue->queue[queue->current].buttons;
    if ((x & (8 + 16)) == (8 + 16))
        return 0; // cancelled out change
    if (x & 8)
        return -1; // up
    if (x & 16)
        return 1; // down
    return 0;     // no change
}

extern uint16 call_absolute_offsets[256];

void call_absolute(int32 args, uint16 offset) {
    memset(&cpu, 0, sizeof(cpu_struct)); // flush cpu
    cpu.cs = ((defseg - cmem) >> 4);
    cpu.ip = offset;
    cpu.ss = 0xFFFF;
    cpu.sp = 0; // sp "loops" to <65536 after first push
    cpu.ds = 80;
    // push (near) arg offsets
    static int32 i;
    for (i = 0; i < args; i++) {
        cpu.sp -= 2;
        *(uint16 *)(cmem + cpu.ss * 16 + cpu.sp) = call_absolute_offsets[i];
    }
    // push ret segment, then push ret offset (both 0xFFFF to return control to QB64)
    cpu.sp -= 4;
    *(uint32 *)(cmem + cpu.ss * 16 + cpu.sp) = 0xFFFFFFFF;
    cpu_call();
}

void call_int(int32 i) {

    if (i == 0x33) {

        if (cpu.ax == 0) {
            cpu.ax = 0xFFFF; // mouse installed
            cpu.bx = 2;
            return;
        }

        if (cpu.ax == 1) {
            sub__mouseshow(NULL, 0);
            return;
        }
        if (cpu.ax == 2) {
            sub__mousehide();
            return;
        }
        if (cpu.ax == 3) {
            // return the current mouse status
            // buttons

            mouse_message_queue_struct *queue = &mouse_message_queue;

            // buttons
            cpu.bx = queue->queue[queue->last].buttons & 1;
            if (queue->queue[queue->last].buttons & 4)
                cpu.bx += 2;

            // x,y offsets
            static float mx, my;

            // temp override current message index to the most recent event
            static int32 current_mouse_message_backup;
            current_mouse_message_backup = queue->current;
            queue->current = queue->last;

            mx = func__mousex();
            my = func__mousey();

            // restore "current" message index
            queue->current = current_mouse_message_backup;

            cpu.cx = mx;
            cpu.dx = my;
            // double x-axis value for modes 1,7,13
            if ((display_page->compatible_mode == 1) || (display_page->compatible_mode == 7) || (display_page->compatible_mode == 13))
                cpu.cx *= 2;
            if (display_page->text) {
                // note: a range from 0 to columns*8-1 is returned regardless of the number of actual pixels
                cpu.cx = (mx - 0.5) * 8.0;
                if (cpu.cx >= (display_page->width * 8))
                    cpu.cx = (display_page->width * 8) - 1;
                // note: a range from 0 to rows*8-1 is returned regardless of the number of actual pixels
                // obsolete line of code:
                // cpu.dx=(((float)cpu.dx)/((float)(display_page->height*fontheight[display_page->font])))*((float)(display_page->height*8));//(mouse_y/height_in_pixels)*(rows*8)
                cpu.dx = (my - 0.5) * 8.0;
                if (cpu.dx >= (display_page->height * 8))
                    cpu.dx = (display_page->height * 8) - 1;
            }
            return;
        }

        if (cpu.ax == 7) { // horizontal min/max
            return;
        }
        if (cpu.ax == 8) { // vertical min/max
            return;
        }

        // MessageBox2(NULL,"Unknown MOUSE Sub-function","Call Interrupt Error",MB_OK|MB_SYSTEMMODAL);
        // exit(cpu.ax);

        return;
    }
}

// 2D PROTOTYPE QB64<->C CALLS

// Creating/destroying an image surface:

int32 func__newimage(int32 x, int32 y, int32 bpp, int32 passed) {
    static int32 i;
    if (is_error_pending())
        return 0;
    if (x <= 0 || y <= 0) {
        error(5);
        return 0;
    }
    if (!passed) {
        bpp = write_page->compatible_mode;
    } else {
        i = 0;
        if (bpp >= 0 && bpp <= 2)
            i = 1;
        if (bpp >= 7 && bpp <= 13)
            i = 1;
        if (bpp == 256)
            i = 1;
        if (bpp == 32)
            i = 1;
        if (!i) {
            error(5);
            return 0;
        }
    }
    i = imgnew(x, y, bpp);
    if (!i)
        return -1;
    if (!passed) {
        // adopt palette
        if (write_page->pal) {
            memcpy(img[i].pal, write_page->pal, 1024);
        }
        // adopt font
        sub__font(write_page->font, -i, 1);
        // adopt colors
        img[i].color = write_page->color;
        img[i].background_color = write_page->background_color;
        // adopt transparent color
        img[i].transparent_color = write_page->transparent_color;
        // adopt blend state
        img[i].alpha_disabled = write_page->alpha_disabled;
        // adopt print mode
        img[i].print_mode = write_page->print_mode;
    }
    return -i;
}

int32 func__copyimage(int32 i, int32 mode, int32 passed) {
    static int32 i2, bytes;
    static img_struct *s, *d;
    if (is_error_pending())
        return 0;
    // if (passed){
    if (i >= 0) { // validate i
        validatepage(i);
        i = page[i];
    } else {
        i = -i;
        if (i >= nextimg) {
            error(258);
            return 0;
        }
        if (!img[i].valid) {
            error(258);
            return 0;
        }
    }
    // }else{
    // i=write_page_index;
    // }

    s = &img[i];

    if (passed & 1) {
        if (mode != s->compatible_mode) {
            if (mode != 33 || s->compatible_mode != 32) {
                error(5);
                return -1;
            }
            // create new buffered hardware image
            i2 = new_hardware_img(s->width, s->height, (uint32 *)s->offset32, NEW_HARDWARE_IMG__BUFFER_CONTENT | NEW_HARDWARE_IMG__DUPLICATE_PROVIDED_BUFFER);
            return i2 + HARDWARE_IMG_HANDLE_OFFSET;
        }
    }

    // duplicate structure
    i2 = newimg();
    d = &img[i2];
    memcpy(d, s, sizeof(img_struct));
    // don't duplicate the memory lock (if any),
    //_MEMIMAGE needs to obtain a new lock for the copy
    img[i2].lock_id = NULL;
    img[i2].lock_offset = NULL;
    // duplicate pixel data
    bytes = d->width * d->height * d->bytes_per_pixel;
    d->offset = (uint8 *)malloc(bytes);
    if (!d->offset) {
        freeimg(i2);
        return -1;
    }
    memcpy(d->offset, s->offset, bytes);
    d->flags |= IMG_FREEMEM;
    // duplicate palette
    if (d->pal) {
        d->pal = (uint32 *)malloc(1024);
        if (!d->pal) {
            free(d->offset);
            freeimg(i2);
            return -1;
        }
        memcpy(d->pal, s->pal, 1024);
        d->flags |= IMG_FREEPAL;
    }
    // adjust flags
    if (d->flags & IMG_SCREEN)
        d->flags ^= IMG_SCREEN;
    // return new handle
    return -i2;
}

void sub__freeimage(int32 i, int32 passed) {
    if (is_error_pending())
        return;
    if (passed) {
        if (i >= 0) { // validate i
            error(5);
            return; // The SCREEN's pages cannot be freed!
        } else {

            auto himg = get_hardware_img(i);
            if (himg) {
                flush_old_hardware_commands();
                // add command to free image
                // create new command handle & structure
                int32 hgch = list_add(hardware_graphics_command_handles);
                hardware_graphics_command_struct *hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, hgch);
                hgc->remove = 0;
                // set command values
                hgc->command = HARDWARE_GRAPHICS_COMMAND__FREEIMAGE_REQUEST;
                hgc->src_img = get_hardware_img_index(i);
                himg->valid = 0;

                // queue the command
                hgc->next_command = 0;
                hgc->order = display_frame_order_next;
                if (last_hardware_command_added) {
                    hardware_graphics_command_struct *hgc2 =
                        (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, last_hardware_command_added);
                    hgc2->next_command = hgch;
                }
                last_hardware_command_added = hgch;
                if (first_hardware_command == 0)
                    first_hardware_command = hgch;

                return;
            }

            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    if (img[i].flags & IMG_SCREEN) {
        error(5);
        return;
    } // The SCREEN's pages cannot be freed!
    if (write_page_index == i)
        sub__dest(-display_page_index);
    if (read_page_index == i)
        sub__source(-display_page_index);
    if (img[i].flags & IMG_FREEMEM)
        free(img[i].offset); // free pixel data (potential crash here)
    if (img[i].flags & IMG_FREEPAL)
        free(img[i].pal); // free palette
    freeimg(i);
}

void freeallimages() {
    static int32 i;
    // note: handles 0 & -1(1) are reserved
    for (i = 2; i < nextimg; i++) {
        if (img[i].valid && i != abs(console_image)) {
            if ((img[i].flags & IMG_SCREEN) == 0) { // The SCREEN's pages cannot be freed!
                sub__freeimage(-i, 1);
            }
        } // valid
    } // i
}

// Selecting images:

void sub__source(int32 i) {
    if (is_error_pending())
        return;
    if (i >= 0) { // validate i
        validatepage(i);
        i = page[i];
    } else {
        i = -i;
        if (i >= nextimg) {
            error(258);
            return;
        }
        if (!img[i].valid) {
            error(258);
            return;
        }
    }
    read_page_index = i;
    read_page = &img[i];
}

void sub__dest(int32 i) {
    if (is_error_pending())
        return;
    if (i >= 0) { // validate i
        validatepage(i);
        i = page[i];
    } else {
        i = -i;
        if (i >= nextimg) {
            error(258);
            return;
        }
        if (!img[i].valid) {
            error(258);
            return;
        }
    }
    write_page_index = i;
    write_page = &img[i];
}

int32 func__source() {
    return -read_page_index;
}

int32 func__dest() {
    return -write_page_index;
}

int32 func__display() {
    return -display_page_index;
}

// Changing the settings of an image surface:

void sub__blend(int32 i, int32 passed) {
    if (is_error_pending())
        return;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            auto himg = get_hardware_img(i);
            if (himg) {
                himg->alpha_disabled = 0;
                return;
            }
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    if (img[i].bytes_per_pixel != 4) {
        error(5);
        return;
    }
    img[i].alpha_disabled = 0;
}

void sub__dontblend(int32 i, int32 passed) {
    if (is_error_pending())
        return;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            auto himg = get_hardware_img(i);
            if (himg) {
                himg->alpha_disabled = 1;
                return;
            }
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    if (img[i].bytes_per_pixel != 4)
        return;
    img[i].alpha_disabled = 1;
}

void sub__clearcolor(uint32 c, int32 i, int32 passed) {
    //--         _NONE->1       2       4
    // id.specialformat = "[{_NONE}][?][,?]"
    if (is_error_pending())
        return;
    static img_struct *im;
    static int32 z;
    static uint32 *lp, *last;
    static uint8 b_max, b_min, g_max, g_min, r_max, r_min;
    static uint8 *cp, *clast, v;
    if (passed & 4) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    im = &img[i];
    // text?
    if (im->text) {
        if (passed & 1) {
            if (passed & 2) {
                error(5);
                return;
            } // invalid options
            im->transparent_color = -1; // _CLEARCOLOR _NONE: disable transparency
            return;
        }
        if (!(passed & 2)) {
            error(5);
            return;
        } // invalid options
        if (c > 65535) {
            error(5);
            return;
        } // invalid cell value for a text surface
        im->transparent_color = (int32)c;
        return;
    }
    // palette?
    if (im->pal) {
        if (passed & 1) {
            if (passed & 2) {
                error(5);
                return;
            } // invalid options
            im->transparent_color = -1;
            return;
        }
        if (!(passed & 2)) {
            error(5);
            return;
        } // invalid options
        if (c > 255) {
            error(5);
            return;
        } // invalid color
        im->transparent_color = c;
        return;
    }
    // 32-bit? (alpha is ignored in this case)
    if (passed & 1) {
        if (passed & 2) {
            error(5);
            return;
        } // invalid options
        return; // no action
    }
    if (!(passed & 2)) {
        error(5);
        return;
    } // invalid options
    c &= 0xFFFFFF;
    last = im->offset32 + im->width * im->height;
    for (lp = im->offset32; lp < last; lp++) {
        if ((*lp & 0xFFFFFF) == c)
            *lp = c;
    }
    return;
}

// Changing/Using an image surface:

//_PUT "[(?,?)[-(?,?)]][,[?][,[?][,[(?,?)[-(?,?)]]]]]"
//(defined elsewhere)

//_IMGALPHA "?[,[?[{TO}?]][,?]]"
void sub__setalpha(int32 a, uint32 c, uint32 c2, int32 i, int32 passed) {
    //-->                             1        4        2
    static img_struct *im;
    static int32 z;
    static uint32 *lp, *last;
    static uint8 b_max, b_min, g_max, g_min, r_max, r_min, a_max, a_min;
    static uint8 *cp, *clast, v;
    if (is_error_pending())
        return;
    if (passed & 2) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    im = &img[i];
    if (im->pal) {
        error(5);
        return;
    } // does not work on paletted images!
    if (a < 0 || a > 255) {
        error(5);
        return;
    } // invalid range
    if (passed & 4) {
        // ranged
        if (c == c2)
            goto uniquerange;
        b_min = c & 0xFF;
        g_min = c >> 8 & 0xFF;
        r_min = c >> 16 & 0xFF;
        a_min = c >> 24 & 0xFF;
        b_max = c2 & 0xFF;
        g_max = c2 >> 8 & 0xFF;
        r_max = c2 >> 16 & 0xFF;
        a_max = c2 >> 24 & 0xFF;
        if (b_min > b_max)
            std::swap(b_min, b_max);

        if (g_min > g_max)
            std::swap(g_min, g_max);
        if (r_min > r_max)
            std::swap(r_min, r_max);
        if (a_min > a_max)
            std::swap(a_min, a_max);
        cp = im->offset;
        z = im->width * im->height;
    setalpha:
        if (z--) {
            v = *cp;
            if (v <= b_max && v >= b_min) {
                v = *(cp + 1);
                if (v <= g_max && v >= g_min) {
                    v = *(cp + 2);
                    if (v <= r_max && v >= r_min) {
                        v = *(cp + 3);
                        if (v <= a_max && v >= a_min) {
                            *(cp + 3) = a;
                        }
                    }
                }
            }
            cp += 4;
            goto setalpha;
        }
        return;
    }
    if (passed & 1) {
    uniquerange:
        // alpha of c=a
        c2 = a << 24;
        lp = im->offset32 - 1;
        last = im->offset32 + im->width * im->height - 1;
        while (lp < last) {
            if (*++lp == c) {
                *lp = (*lp & 0xFFFFFF) | c2;
            }
        }
        return;
    }
    // all alpha=a
    cp = im->offset - 1;
    clast = im->offset + im->width * im->height * 4 - 4;
    while (cp < clast) {
        *(cp += 4) = a;
    }
    return;
}

// Finding information about an image surface:

int32 func__width(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;

#ifdef QB64_WINDOWS
    if ((read_page->console && !passed) || i == console_image) {
        SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
        HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
        CONSOLE_SCREEN_BUFFER_INFO cl_bufinfo;
        GetConsoleScreenBufferInfo(cl_conout, &cl_bufinfo);
        return cl_bufinfo.srWindow.Right - cl_bufinfo.srWindow.Left + 1;
    }
#endif

    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            auto himg = get_hardware_img(i);
            if (himg) {
                return himg->w;
            }
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    return img[i].width;
}

int32 func__height(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;

#ifdef QB64_WINDOWS
    if ((read_page->console && !passed) || i == console_image) {
        SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
        HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
        CONSOLE_SCREEN_BUFFER_INFO cl_bufinfo;
        GetConsoleScreenBufferInfo(cl_conout, &cl_bufinfo);
        return cl_bufinfo.srWindow.Bottom - cl_bufinfo.srWindow.Top + 1;
        return cl_bufinfo.dwMaximumWindowSize.Y;
    }
#endif

    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            auto himg = get_hardware_img(i);
            if (himg) {
                return himg->h;
            }
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    return img[i].height;
}

int32 func__pixelsize(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    i = img[i].compatible_mode;
    if (i == 32)
        return 4;
    if (!i)
        return 0;
    return 1;
}

int32 func__clearcolor(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    if (img[i].text)
        return img[i].transparent_color; // -1 = none, 0-65535 = cell value
    if (img[i].compatible_mode == 32)
        return 0;
    return img[i].transparent_color;
}

int32 func__blend(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    if (img[i].compatible_mode == 32) {
        if (!img[i].alpha_disabled)
            return -1;
    }
    return 0;
}

uint32 func__defaultcolor(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    return img[i].color;
}

uint32 func__backgroundcolor(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    return img[i].background_color;
}

// Working with 256 color palettes:

uint32 func__palettecolor(int32 n, int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    if (!img[i].pal) {
        error(5);
        return 0;
    }
    if (n < 0 || n > 255) {
        error(5);
        return 0;
    } // out of range
    return img[i].pal[n] | 0xFF000000;
}

void sub__palettecolor(int32 n, uint32 c, int32 i, int32 passed) {
    if (is_error_pending())
        return;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    if (!img[i].pal) {
        error(5);
        return;
    }
    if (n < 0 || n > 255) {
        error(5);
        return;
    } // out of range
    img[i].pal[n] = c;
}

void sub__copypalette(int32 i, int32 i2, int32 passed) {
    if (is_error_pending())
        return;
    if (passed & 1) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = read_page_index;
    }
    if (!img[i].pal) {
        error(5);
        return;
    }
    std::swap(i, i2);
    if (passed & 2) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    if (!img[i].pal) {
        error(5);
        return;
    }
    std::swap(i, i2);
    memcpy(img[i2].pal, img[i].pal, 1024);
}

void sub__printstring(float x, float y, qbs *text, int32 i, int32 passed) {
    if (is_error_pending())
        return;

    int32 old_dest = func__dest();

    if (passed & 2) {
        sub__dest(i);
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                goto printstring_exit;
            }
            if (!img[i].valid) {
                error(258);
                goto printstring_exit;
            }
        }
    } else {
        i = write_page_index;
    }
    static img_struct *im;
    im = &img[i];
    if (!text->len)
        goto printstring_exit;
    if (im->text) {
        int oldx = func_pos(0), oldy = func_csrlin();
        qbg_sub_locate(y, x, 0, 0, 0, 3);
        qbs_print(text, 0);
        qbg_sub_locate(oldy, oldx, 0, 0, 0, 3);
        goto printstring_exit;
    }
    // graphics modes only
    if (!text->len)
        goto printstring_exit;
    // Step?
    if (passed & 1) {
        im->x += x;
        im->y += y;
    } else {
        im->x = x;
        im->y = y;
    }
    // Adjust co-ordinates for viewport?
    static int32 x2, y2;
    if (im->clipping_or_scaling) {
        if (im->clipping_or_scaling == 2) {
            x2 = qbr_float_to_long(im->x * im->scaling_x + im->scaling_offset_x) + im->view_offset_x;
            y2 = qbr_float_to_long(im->y * im->scaling_y + im->scaling_offset_y) + im->view_offset_y;
        } else {
            x2 = qbr_float_to_long(im->x) + im->view_offset_x;
            y2 = qbr_float_to_long(im->y) + im->view_offset_y;
        }
    } else {
        x2 = qbr_float_to_long(im->x);
        y2 = qbr_float_to_long(im->y);
    }

    if (!text->len)
        goto printstring_exit;

    static uint32 w, h, z, z2, z3, a, a2, a3, color, background_color, f;
    static uint32 *lp;
    static uint8 *cp;

    color = im->color;
    background_color = im->background_color;

    f = im->font;
    h = fontheight[f];

    if (f >= 32) { // custom font

        // 8-bit / alpha-disabled 32-bit / dont-blend(alpha may still be applied)
        if ((im->bytes_per_pixel == 1) || ((im->bytes_per_pixel == 4) && (im->alpha_disabled)) || (fontflags[f] & FONT_LOAD_DONTBLEND)) {

            // render character
            static int32 ok;
            static uint8 *rt_data;
            static int32 rt_w, rt_h;
            ok = FontRenderTextASCII(font[f], (uint8 *)text->chr, text->len, 1, &rt_data, &rt_w, &rt_h);
            if (!ok)
                goto printstring_exit;

            w = rt_w;

            switch (im->print_mode) {
            case 3:
                for (y2 = 0; y2 < h; y2++) {
                    cp = rt_data + y2 * w;
                    for (x2 = 0; x2 < w; x2++) {
                        if (*cp++)
                            pset_and_clip(x + x2, y + y2, color);
                        else
                            pset_and_clip(x + x2, y + y2, background_color);
                    }
                }
                break;
            case 1:
                for (y2 = 0; y2 < h; y2++) {
                    cp = rt_data + y2 * w;
                    for (x2 = 0; x2 < w; x2++) {
                        if (*cp++)
                            pset_and_clip(x + x2, y + y2, color);
                    }
                }
                break;
            case 2:
                for (y2 = 0; y2 < h; y2++) {
                    cp = rt_data + y2 * w;
                    for (x2 = 0; x2 < w; x2++) {
                        if (!(*cp++))
                            pset_and_clip(x + x2, y + y2, background_color);
                    }
                }
                break;
            default:
                break;
            }

            free(rt_data);
            goto printstring_exit;
        } // 1-8 bit
        // assume 32-bit blended

        a = (color >> 24) + 1;
        a2 = (background_color >> 24) + 1;
        z = color & 0xFFFFFF;
        z2 = background_color & 0xFFFFFF;

        // render character
        static int32 ok;
        static uint8 *rt_data;
        static int32 rt_w, rt_h;
        ok = FontRenderTextASCII(font[f], (uint8 *)text->chr, text->len, 0, &rt_data, &rt_w, &rt_h);

        if (!ok)
            goto printstring_exit;

        w = rt_w;

        switch (im->print_mode) {
        case 3:

            static float r1, g1, b1, alpha1, r2, g2, b2, alpha2;
            alpha1 = (color >> 24) & 255;
            r1 = (color >> 16) & 255;
            g1 = (color >> 8) & 255;
            b1 = color & 255;
            alpha2 = (background_color >> 24) & 255;
            r2 = (background_color >> 16) & 255;
            g2 = (background_color >> 8) & 255;
            b2 = background_color & 255;
            static float dr, dg, db, da;

            dr = r2 - r1;
            dg = g2 - g1;
            db = b2 - b1;
            da = alpha2 - alpha1;
            static float cw; // color weight multiplier, avoids seeing black when transitioning from RGBA(?,?,?,255) to RGBA(0,0,0,0)
            if (alpha1)
                cw = alpha2 / alpha1;
            else
                cw = 100000;
            static float d;

            for (y2 = 0; y2 < h; y2++) {
                cp = rt_data + y2 * w;
                for (x2 = 0; x2 < w; x2++) {

                    d = *cp++;
                    d = 255 - d;
                    d /= 255.0;
                    static float r3, g3, b3, alpha3;
                    alpha3 = alpha1 + da * d;
                    d *= cw;
                    if (d > 1.0)
                        d = 1.0;
                    r3 = r1 + dr * d;
                    g3 = g1 + dg * d;
                    b3 = b1 + db * d;
                    static int32 r4, g4, b4, alpha4;
                    r4 = qbr_float_to_long(r3);
                    g4 = qbr_float_to_long(g3);
                    b4 = qbr_float_to_long(b3);
                    alpha4 = qbr_float_to_long(alpha3);
                    pset_and_clip(x + x2, y + y2, b4 + (g4 << 8) + (r4 << 16) + (alpha4 << 24));
                }
            }
            break;
        case 1:
            for (y2 = 0; y2 < h; y2++) {
                cp = rt_data + y2 * w;
                for (x2 = 0; x2 < w; x2++) {
                    z3 = *cp++;
                    if (z3)
                        pset_and_clip(x + x2, y + y2, ((z3 * a) >> 8 << 24) + z);
                }
            }
            break;
        case 2:
            for (y2 = 0; y2 < h; y2++) {
                cp = rt_data + y2 * w;
                for (x2 = 0; x2 < w; x2++) {
                    z3 = *cp++;
                    if (z3 != 255)
                        pset_and_clip(x + x2, y + y2, (((255 - z3) * a2) >> 8 << 24) + z2);
                }
            }
            break;
        default:
            break;
        }
        free(rt_data);
        goto printstring_exit;
    } // custom font

    // default fonts
    static int32 character, character_c;
    for (character_c = 0; character_c < text->len; character_c++) {
        character = text->chr[character_c];
        if (im->font == 8)
            cp = &charset8x8[character][0][0];
        if (im->font == 14)
            cp = &charset8x16[character][1][0];
        if (im->font == 16)
            cp = &charset8x16[character][0][0];
        switch (im->print_mode) {
        case 3:
            for (y2 = 0; y2 < h; y2++) {
                for (x2 = 0; x2 < 8; x2++) {
                    if (*cp++)
                        pset_and_clip(x + x2, y + y2, color);
                    else
                        pset_and_clip(x + x2, y + y2, background_color);
                }
            }
            break;
        case 1:
            for (y2 = 0; y2 < h; y2++) {
                for (x2 = 0; x2 < 8; x2++) {
                    if (*cp++)
                        pset_and_clip(x + x2, y + y2, color);
                }
            }
            break;
        case 2:
            for (y2 = 0; y2 < h; y2++) {
                for (x2 = 0; x2 < 8; x2++) {
                    if (!(*cp++))
                        pset_and_clip(x + x2, y + y2, background_color);
                }
            }
            break;
        default:
            break;
        }
        x += 8;
    } // z

printstring_exit:
    if (passed & 2)
        sub__dest(old_dest);
    return;
}

int32 func__fontwidth(int32 f, int32 passed);
int32 func__fontheight(int32 f, int32 passed);
int32 func__font(int32 f, int32 passed);

int32 func__printwidth(qbs *text, int32 screenhandle, int32 passed) {
    // Validate screenhandle (taken from func__font)
    if (passed) {
        if (screenhandle >= 0) {
            validatepage(screenhandle);
            screenhandle = page[screenhandle];
        } else {
            screenhandle = -screenhandle;
            if (screenhandle >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[screenhandle].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        screenhandle = write_page_index;
    }

    if (text->len == 0)
        return 0; // No point calculating an empty string

    if (img[screenhandle].text) { // Return LEN(text) in non-graphic modes
        // error(5);
        return text->len;
    }

    auto fonthandle = img[screenhandle].font;     // Get the font used in screenhandle
    auto fwidth = func__fontwidth(fonthandle, 1); // Try to get the font width
    if (fwidth)
        return fwidth * text->len; // if it's not a variable width font, return the width * the letter count

    // a740g: For variable width fonts
    return FontPrintWidthASCII(font[fonthandle], text->chr, text->len);
}

/// @brief f = _LOADFONT(ttf_filename$, height[, "monospace,dontblend,unicode,memory"][, index])
/// @param qbsFileName The font file path name or a font memory buffer when loading from memory
/// @param size The font height in pixels
/// @param qbsRequirements This can be monospace, dontblend, unicode, memory
/// @param font_index The index of the font to load from a font collection
/// @param passed Which optional arguments were passed
/// @return Returns a valid handle on success or zero on failure
int32_t func__loadfont(const qbs *qbsFileName, int32_t size, const qbs *qbsRequirements, int32_t font_index, int32_t passed) {
    if (is_error_pending() || !qbsFileName->len)
        return INVALID_FONT_HANDLE; // return invalid handle for any garbage input

    // validate size
    if (size < 1) {
        error(5);
        return INVALID_FONT_HANDLE;
    }

    auto isLoadFromMemory = false; // should the font be loaded from memory?
    int32_t options = 0;           // font flags that we'll prepare and save to fontflags[]

    // image_log_trace("passed = %i", passed);

    // Check requirements
    // 8 dontblend (blending is the default in 32-bit alpha-enabled modes)
    // 16 monospace
    // 32 unicode
    if ((passed & 1) && qbsRequirements->len) {
        std::string requirements(reinterpret_cast<char *>(qbsRequirements->chr), qbsRequirements->len);
        std::transform(requirements.begin(), requirements.end(), requirements.begin(), [](unsigned char c) { return std::toupper(c); });

        image_log_trace("Parsing requirements string: %s", requirements.c_str());

        if (requirements.find("DONTBLEND") != std::string::npos) {
            options |= FONT_LOAD_DONTBLEND;
            image_log_trace("No alpha blending requested");
        }

        if (requirements.find("MONOSPACE") != std::string::npos) {
            options |= FONT_LOAD_MONOSPACE;
            image_log_trace("Monospaced font requested");
        }

        if (requirements.find("UNICODE") != std::string::npos) {
            options |= FONT_LOAD_UNICODE;
            image_log_trace("Unicode requested");
        }

        if (requirements.find("MEMORY") != std::string::npos) {
            isLoadFromMemory = true;
            image_log_trace("Loading from memory requested");
        }

        if (requirements.find("AUTOMONO") != std::string::npos) {
            options |= FONT_LOAD_AUTOMONO;
            image_log_trace("Automatic monospacing requested");
        }
    }

    // Check if a font index was requested
    if (passed & 2) {
        image_log_trace("Loading font index %i", font_index);
    } else {
        image_log_trace("Loading default font index (0)");
        font_index = 0;
    }

    uint8_t *content;
    int32_t bytes;

    if (isLoadFromMemory) {
        content = qbsFileName->chr; // we should not free this!!!
        bytes = qbsFileName->len;
        image_log_trace("Loading font from memory. Size = %i", bytes);
    } else {
        std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
        content = FontLoadFileToMemory(filepath_fix_directory(fileName), &bytes); // this we must free!!!
        image_log_trace("Loading font from file %s", fileName.c_str());
    }

    if (!content)
        return INVALID_FONT_HANDLE; // Return invalid handle if loading the image failed

    // open the font
    // get free font handle
    int32_t i;
    for (i = 32; i <= lastfont; i++) {
        if (!font[i])
            goto got_font_index;
    }
    // increase handle range
    // a740g: none of this stuff is safe and ought to be rewritten
    // We will need to look at how to do this better
    lastfont++;
    font = (int32 *)realloc(font, 4 * (lastfont + 1));
    font[lastfont] = NULL;
    fontheight = (int32 *)realloc(fontheight, 4 * (lastfont + 1));
    fontwidth = (int32 *)realloc(fontwidth, 4 * (lastfont + 1));
    fontflags = (int32 *)realloc(fontflags, 4 * (lastfont + 1));
    i = lastfont;

got_font_index:
    auto h = FontLoad(content, bytes, size, font_index, options);

    if (!isLoadFromMemory)
        free(content); // free font memory if it was loaded from a file

    if (!h)
        return INVALID_FONT_HANDLE;

    font[i] = h;
    fontflags[i] = options;
    fontheight[i] = size;
    fontwidth[i] = FontWidth(h);

    return i;
}

void sub__font(int32 f, int32 i, int32 passed) {
    //_FONT "?[,?]"
    int32 i2 = 0; // no need for a static variable here, when we just init it to zero before ever using it.
    static img_struct *im;
    if (is_error_pending())
        return;
    if (passed & 1) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    im = &img[i];
    // validate f
    if ((f == 8) || (f == 9) || ((f > 13) && (f < 18))) // built-in fonts 8, 9, 14, 15, 16, 17 should be valid
        i2 = 1;
    if (((f == 9) || (f == 15) || (f == 17)) && (!im->text)) { // but 9, 15, and 17 are *ONLY* valid for text screes
        error(5);
        return;
    }
    if (f >= 32 && f <= lastfont) {
        if (font[f])
            i2 = 1;
    }
    if (!i2) {
        error(258);
        return;
    }

    if (im->text && ((fontflags[f] & FONT_LOAD_MONOSPACE) ==
                     0)) { // fontflags[f] & 16 is the bit which we set for MONOSPACE fonts.  If it's a SCREEN 0 screen, and the font
        error(5);          // isn't monospaced, toss an error and return.
        return;
    }
    // note: font changes to text screen mode images requires:
    //      i) font change across all screen pages
    //      ii) locking of the display
    //      iii) update of the data being displayed
    if (im->text) {
        if (im->flags & IMG_SCREEN) {
            // lock display
            if (autodisplay) {
                if (lock_display == 0)
                    lock_display = 1; // request lock
                while (lock_display != 2)
                    Sleep(0);
            }
            // force update of data
            screen_last_valid = 0; // ignore cache used to update the screen on next update
            // apply change across all video pages
            for (i = 0; i < pages; i++) {
                if (page[i]) {
                    im = &img[page[i]];
                    im->font = f;
                    // note: moving the cursor is unnecessary
                }
            }
            // unlock
            if (autodisplay) {
                if (lock_display_required)
                    lock_display = 0; // release lock
            }
            return;
        }
    } // text

    im->font = f;
    im->cursor_x = 1;
    im->cursor_y = 1;
    im->top_row = 1;
    if (im->compatible_mode)
        im->bottom_row = im->height / fontheight[f];
    else
        im->bottom_row = im->height;
    im->bottom_row--;
    if (im->bottom_row <= 0)
        im->bottom_row = 1;
    return;
}

int32 func__fontwidth(int32 f, int32 passed) {
    static int32 i2;
    if (is_error_pending())
        return 0;
    if (passed) {
        // validate f
        i2 = 0;
        if (f == 8)
            i2 = 1;
        if (f == 14)
            i2 = 1;
        if (f == 16)
            i2 = 1;
        if (f >= 32 && f <= lastfont) {
            if (font[f])
                i2 = 1;
        }
        if (!i2) {
            error(258);
            return 0;
        }
    } else {
        f = write_page->font;
    }
    return fontwidth[f];
}

int32 func__fontheight(int32 f, int32 passed) {
    static int32 i2;
    if (is_error_pending())
        return 0;
    if (passed) {
        // validate f
        i2 = 0;
        if (f == 8)
            i2 = 1;
        if (f == 14)
            i2 = 1;
        if (f == 16)
            i2 = 1;
        if (f >= 32 && f <= lastfont) {
            if (font[f])
                i2 = 1;
        }
        if (!i2) {
            error(258);
            return 0;
        }
    } else {
        f = write_page->font;
    }
    return fontheight[f];
}

int32 func__font(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    return img[i].font;
}

void sub__freefont(int32 f) {
    if (is_error_pending())
        return;
    static int32 i, i2;
    // validate f (cannot remove QBASIC built in fonts!)
    i2 = 0;
    if (f >= 32 && f <= lastfont) {
        if (font[f])
            i2 = 1;
    }
    if (!i2) {
        error(258);
        return;
    }
    // check all surfaces, no surface can be using the font
    for (i = 1; i < nextimg; i++) {
        if (img[i].valid) {
            if (img[i].font == f) {
                error(5);
                return;
            }
        }
    }
    // remove font
    FontFree(font[f]);
    font[f] = NULL;
}

void sub__printmode(int32 mode, int32 i, int32 passed) {
    if (is_error_pending())
        return;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
    } else {
        i = write_page_index;
    }
    if (img[i].text) {
        if (mode != 1) {
            error(5);
            return;
        }
    }
    if (mode == 1)
        img[i].print_mode = 3; // fill
    if (mode == 2)
        img[i].print_mode = 1; // keep
    if (mode == 3)
        img[i].print_mode = 2; // only
}

int32 func__printmode(int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
    } else {
        i = write_page_index;
    }
    return img[i].print_mode;
}

uint32 matchcol(int32 r, int32 g, int32 b) {
    static int32 v, v2, n, n2, best, c;
    static int32 *p;
    p = (int32 *)write_page->pal;
    if (write_page->text)
        n2 = 16;
    else
        n2 = write_page->mask + 1;
    v = 1000;
    best = 0;
    for (n = 0; n < n2; n++) {
        c = *p++;
        v2 = abs(b - (c & 0xFF)) + abs(g - (c >> 8 & 0xFF)) + abs(r - (c >> 16 & 0xFF));
        if (v2 < v) {
            if (!v2)
                return n; // perfect match
            v = v2;
            best = n;
        }
    } // n
    return best;
}

uint32 matchcol(int32 r, int32 g, int32 b, int32 i) {
    static int32 v, v2, n, n2, best, c;
    static int32 *p;
    p = (int32 *)img[i].pal;
    if (img[i].text)
        n2 = 16;
    else
        n2 = img[i].mask + 1;
    v = 1000;
    best = 0;
    for (n = 0; n < n2; n++) {
        c = *p++;
        v2 = abs(b - (c & 0xFF)) + abs(g - (c >> 8 & 0xFF)) + abs(r - (c >> 16 & 0xFF));
        if (v2 < v) {
            if (!v2)
                return n; // perfect match
            v = v2;
            best = n;
        }
    } // n
    return best;
}

uint32 func__rgb(int32 r, int32 g, int32 b, int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (r < 0)
        r = 0;
    if (r > 255)
        r = 255;
    if (g < 0)
        g = 0;
    if (g > 255)
        g = 255;
    if (b < 0)
        b = 0;
    if (b > 255)
        b = 255;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
        if (img[i].bytes_per_pixel == 4) {
            return (r << 16) + (g << 8) + b | 0xFF000000;
        } else { //==4
            return matchcol(r, g, b, i);
        } //==4
    } else {
        if (write_page->bytes_per_pixel == 4) {
            return (r << 16) + (g << 8) + b | 0xFF000000;
        } else { //==4
            return matchcol(r, g, b);
        } //==4
    } // passed
} // rgb

uint32 func__rgba(int32 r, int32 g, int32 b, int32 a, int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (r < 0)
        r = 0;
    if (r > 255)
        r = 255;
    if (g < 0)
        g = 0;
    if (g > 255)
        g = 255;
    if (b < 0)
        b = 0;
    if (b > 255)
        b = 255;
    if (a < 0)
        a = 0;
    if (a > 255)
        a = 255;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
        if (img[i].bytes_per_pixel == 4) {
            return (a << 24) + (r << 16) + (g << 8) + b;
        } else { //==4
            // error(5); return 0;
            if ((!a) && (img[i].transparent_color != -1))
                return img[i].transparent_color;
            return matchcol(r, g, b, i);
        } //==4
    } else {
        if (write_page->bytes_per_pixel == 4) {
            return (a << 24) + (r << 16) + (g << 8) + b;
        } else { //==4
            // error(5); return 0;
            if ((!a) && (write_page->transparent_color != -1))
                return write_page->transparent_color;
            return matchcol(r, g, b);
        } //==4
    } // passed
} // rgba

int32 func__alpha(uint32 col, int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
        if (img[i].bytes_per_pixel == 4) {
            return col >> 24;
        } else { //==4
            // error(5); return 0;
            if ((col < 0) || (col > (img[i].mask))) {
                error(5);
                return 0;
            }
            if (img[i].transparent_color == col)
                return 0;
            return 255;
        } //==4
    } else {
        if (write_page->bytes_per_pixel == 4) {
            return col >> 24;
        } else { //==4
            // error(5); return 0;
            if ((col < 0) || (col > (write_page->mask))) {
                error(5);
                return 0;
            }
            if (write_page->transparent_color == col)
                return 0;
            return 255;
        } //==4
    } // passed
}

int32 func__red(uint32 col, int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
        if (img[i].bytes_per_pixel == 4) {
            return col >> 16 & 0xFF;
        } else { //==4
            if ((col < 0) || (col > (img[i].mask))) {
                error(5);
                return 0;
            }
            return img[i].pal[col] >> 16 & 0xFF;
        } //==4
    } else {
        if (write_page->bytes_per_pixel == 4) {
            return col >> 16 & 0xFF;
        } else { //==4
            if ((col < 0) || (col > (write_page->mask))) {
                error(5);
                return 0;
            }
            return write_page->pal[col] >> 16 & 0xFF;
        } //==4
    } // passed
}

int32 func__green(uint32 col, int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
        if (img[i].bytes_per_pixel == 4) {
            return col >> 8 & 0xFF;
        } else { //==4
            if ((col < 0) || (col > (img[i].mask))) {
                error(5);
                return 0;
            }
            return img[i].pal[col] >> 8 & 0xFF;
        } //==4
    } else {
        if (write_page->bytes_per_pixel == 4) {
            return col >> 8 & 0xFF;
        } else { //==4
            if ((col < 0) || (col > (write_page->mask))) {
                error(5);
                return 0;
            }
            return write_page->pal[col] >> 8 & 0xFF;
        } //==4
    } // passed
}

int32 func__blue(uint32 col, int32 i, int32 passed) {
    if (is_error_pending())
        return 0;
    if (passed) {
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return 0;
            }
            if (!img[i].valid) {
                error(258);
                return 0;
            }
        }
        if (img[i].bytes_per_pixel == 4) {
            return col & 0xFF;
        } else { //==4
            if ((col < 0) || (col > (img[i].mask))) {
                error(5);
                return 0;
            }
            return img[i].pal[col] & 0xFF;
        } //==4
    } else {
        if (write_page->bytes_per_pixel == 4) {
            return col & 0xFF;
        } else { //==4
            if ((col < 0) || (col > (write_page->mask))) {
                error(5);
                return 0;
            }
            return write_page->pal[col] & 0xFF;
        } //==4
    } // passed
}

void sub_end() {

    if (sub_gl_called)
        error(271);

    dont_call_sub_gl = 1;

    sub_close(NULL, 0);
    exit_blocked = 0; // allow exit via X-box or CTRL+BREAK

#ifdef DEPENDENCY_CONSOLE_ONLY
    screen_hide = 1;
#endif

    if (!screen_hide) {
        // 1. set the display page as the destination page
        sub__dest(func__display());
        // 2. VIEW PRINT bottomline,bottomline
        static int32 y;
        if (write_page->text) {
            y = write_page->height;
        } else {
            y = write_page->height / fontheight[write_page->font];
        }
        qbg_sub_view_print(y, y, 1 | 2);
        // 3. PRINT 'clears the line without having to worry about its contents/size
        qbs_print(nothingstring, 1);
        // 4. PRINT "Press any key to continue"
        qbs_print(qbs_new_txt("Press any key to continue"), 0);
        // 5. Clear any buffered keypresses
        static uint32 qbs_tmp_base;
        qbs_tmp_base = qbs_tmp_list_nexti;
        while (qbs_cleanup(qbs_tmp_base, qbs_notequal(qbs_inkey(), qbs_new_txt("")))) {
            Sleep(0);
        }
        // 6. Enable autodisplay
        autodisplay = 1;
        // 7. Wait for a new keypress
        do {
            Sleep(100);
            if (stop_program)
                end();
        } while (qbs_cleanup(qbs_tmp_base, qbs_equal(qbs_inkey(), qbs_new_txt(""))));

    } else {
        if (console) {
// screen is hidden, console is visible
#ifdef QB64_WINDOWS
            std::cout << "\nPress any key to continue";
            int32 junk;
            FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); // clear any stray buffer events before we run END.
            do {                                                     // ignore all console input
                junk = func__getconsoleinput();
            } while (junk != 1); // until we have a key down event
#else
            std::cout << "\nPress enter to continue";
            static int32 ignore;
            ignore = fgetc(stdin);
#endif
        }
    }
    close_program = 1;
    end();
    exit(0); //<-- should never happen
}

uint8 pu_dig[1024]; // digits (left justified)
int32 pu_ndig;      // number of digits
int32 pu_dp;        // decimal place modifier
// note: if dp=0, the number is an integer and can be read as is
//      if dp=1 the number is itself*10
//      if dp=-1 the number is itself/10
int32 pu_neg;
uint8 pu_buf[1024];     // a buffer for preprocessing
uint8 pu_exp_char = 69; //"E"

int32 print_using(qbs *f, int32 s2, qbs *dest, qbs *pu_str) {
    // type: 1=numeric, 2=string
    if (is_error_pending())
        return 0;

    static int32 x, z, z2, z3, z4, ii;
    // x  - current format string read position
    // z - used as a temp variable for various calculations and loops
    // z2  - used for various calculations involving exponent digits
    // z3 - used as a temp variable for various calculations and loops
    // z4 - number of 0s between . and digits after point
    // ii  - used as a counter for writing the output
    static uint8 c;
    static int32 stage, len, chrsleft, type, s;
    static int32 leading_plus, dollar_sign, asterisk_spaces, digits_before_point, commas;
    static int32 decimal_point, digits_after_point, trailing_plus, exponent_digits, trailing_minus;
    static int32 cant_fit, extra_sign_space, rounded, digits_and_commas_before_point, leading_zero;
    static qbs *qbs1 = NULL;

    if (qbs1 == NULL)
        qbs1 = qbs_new(1, 0);

    if (pu_str)
        type = 2;
    else
        type = 1;

    s = s2;
    len = f->len;

scan:
    rounded = 0;
rounded_repass:

    x = s - 1; // subtract one to counter pre-increment later

    leading_plus = 0;
    dollar_sign = 0;
    asterisk_spaces = 0;
    digits_before_point = 0;
    commas = 0;
    decimal_point = 0;
    digits_after_point = 0;
    trailing_plus = 0;
    exponent_digits = 0;
    trailing_minus = 0;
    digits_and_commas_before_point = 0;
    leading_zero = 0;
    stage = 0;

nextchar:
    x++;
    if (x < len) {
        c = f->chr[x];
        chrsleft = len - x;

        if ((stage >= 2) && (stage <= 4)) {

            if (c == 43) { //+
                trailing_plus = 1;
                x++;
                goto numeric_spacer;
            }

            if (c == 45) { //-
                trailing_minus = 1;
                x++;
                goto numeric_spacer;
            }

        } // stage>=2 & stage<=4

        if ((stage >= 2) && (stage <= 3)) {

            if (chrsleft >= 5) {
                if ((c == 94) && (f->chr[x + 1] == 94) && (f->chr[x + 2] == 94) && (f->chr[x + 3] == 94) && (f->chr[x + 4] == 94)) { //^^^^^
                    exponent_digits = 3;
                    stage = 4;
                    x += 4;
                    goto nextchar;
                }
            } // 5

            if (chrsleft >= 4) {
                if ((c == 94) && (f->chr[x + 1] == 94) && (f->chr[x + 2] == 94) && (f->chr[x + 3] == 94)) { //^^^^
                    exponent_digits = 2;
                    stage = 4;
                    x += 3;
                    goto nextchar;
                }
            } // 4

        } // stage>=2 & stage<=3

        if (stage == 3) {

            if (c == 35) { // #
                digits_after_point++;
                goto nextchar;
            }

        } // stage==3

        if (stage == 2) {

            if (c == 44) { //,
                commas = 1;
                digits_before_point++;
                goto nextchar;
            }

        } // stage==2

        if (stage <= 2) {

            if (c == 35) { // #
                digits_before_point++;
                stage = 2;
                goto nextchar;
            }

            if (c == 46) { //.
                decimal_point = 1;
                stage = 3;
                goto nextchar;
            }

        } // stage<=2

        if (stage <= 1) {

            if (chrsleft >= 3) {
                if ((c == 42) && (f->chr[x + 1] == 42) && (f->chr[x + 2] == 36)) { //**$
                    asterisk_spaces = 1;
                    digits_before_point = 2;
                    dollar_sign = 1;
                    stage = 2;
                    x += 2;
                    goto nextchar;
                }
            } // 3

            if (chrsleft >= 2) {
                if ((c == 42) && (f->chr[x + 1] == 42)) { //**
                    asterisk_spaces = 1;
                    digits_before_point = 2;
                    stage = 2;
                    x++;
                    goto nextchar;
                }
                if ((c == 36) && (f->chr[x + 1] == 36)) { //$$
                    dollar_sign = 1;
                    digits_before_point = 1;
                    stage = 2;
                    x++;
                    goto nextchar;
                }
            } // 2

        } // stage 1

        if (stage == 0) {

            if (c == 43) { //+
                leading_plus = 1;
                stage = 1;
                goto nextchar;
            }

        } // stage 0

        // spacer/end encountered
    } // x<len
numeric_spacer:

    // valid numeric format?
    if (stage <= 1)
        goto invalid_numeric_format;
    if ((digits_before_point == 0) && (digits_after_point == 0))
        goto invalid_numeric_format;

    if (type == 0)
        return s;    // s is the beginning of a new format but item has already been added to dest
    if (type == 2) { // expected string format, not numeric format
        error(13);   // type mismatch
        return 0;
    }

    // reduce digits before point appropriately
    extra_sign_space = 0;
    if (exponent_digits) {
        if ((leading_plus == 0) && (trailing_plus == 0) && (trailing_minus == 0)) {
            digits_before_point--;
            if (digits_before_point == -1) {
                digits_after_point--;
                digits_before_point = 0;
                if (digits_after_point == 0) {
                    decimal_point = 0;
                    digits_before_point++;
                }
            }
            extra_sign_space = 1;
        }
    } else {
        // the following doesn't occur if using an exponent
        if (pu_neg) {
            if ((leading_plus == 0) && (trailing_plus == 0) && (trailing_minus == 0)) {
                digits_before_point--;
                extra_sign_space = 1;
            }
        }
        if (commas) {
            digits_and_commas_before_point = digits_before_point;
            ii = digits_before_point / 4; // for every 4 digits, one digit will be used up by a comma
            digits_before_point -= ii;
        }
    }

    //'0'->'.0' exception (for when format doesn't allow for any digits_before_point)
    if (digits_before_point == 0) { // no digits allowed before decimal point
        // note: pu_ndig=256, pu_dp=-255
        if ((pu_ndig + pu_dp) == 1) { // 1 digit exists in front of the decimal point
            if (pu_dig[0] == 48) {    // is it 0?
                pu_dp--;              // moves decimal point left one position
            } // 0
        }
    }

    // will number fit? if it can't then adjustments will be made
    cant_fit = 0;
    if (exponent_digits) {
        // give back extra_sign_space?
        if (extra_sign_space) {
            if (!pu_neg) {
                if (digits_before_point <= 0) {
                    extra_sign_space = 0;
                    digits_before_point++; // will become 0 or 1
                    // force 0 in recovered digit?
                    if ((digits_before_point == 1) && (digits_after_point > 0)) {
                        digits_before_point--;
                        extra_sign_space = 2; // 2=put 0 instead of blank space
                    }
                }
            }
        }
        if ((digits_before_point == 0) && (digits_after_point == 0)) {
            cant_fit = 1;
            digits_before_point = 1; // give back removed (for extra sign space) digit
        }
        // but does the exponent fit?
        z2 = pu_ndig + pu_dp - 1; // calc exponent of most significant digit
        // 1.0  = 0
        // 10.0 = 1
        // 0.1  = -1
        // calc exponent of format's most significant position
        if (digits_before_point)
            z3 = digits_before_point - 1;
        else
            z3 = -1;
        z = z2 - z3; // combine to calculate actual exponent which will be "printed"
        z3 = abs(z);
        z2 = sprintf((char *)pu_buf, "%u", z3); // use pu_buf to convert exponent to a string
        if (z2 > exponent_digits) {
            cant_fit = 1;
            exponent_digits = z2;
        }
    } else {
        z2 = 0;
        z = pu_ndig + pu_dp; // calc number of digits required before decimal places
        if (digits_before_point < z) {
            digits_before_point = z;
            cant_fit = 1;
            if (commas)
                digits_and_commas_before_point = digits_before_point + (digits_before_point - 1) / 3;
        }
    }

    static int32 buf_size; // buf_size is an estimation of size required
    static uint8 *cp, *buf = NULL;
    static int32 count;
    if (buf)
        free(buf);
    buf_size = 256; // 256 bytes to account for calc overflow (such as exponent digits)
    buf_size += 9;  //%(1)+-(1)$(1)???.(1)???exponent(5)
    buf_size += digits_before_point;
    if (commas)
        buf_size += ((digits_before_point / 3) + 2);
    buf_size += digits_after_point;
    buf = (uint8 *)malloc(buf_size);
    cp = buf;
    count = 0; // char count
    ii = 0;

    if (asterisk_spaces)
        asterisk_spaces = 42;
    else
        asterisk_spaces = 32; // character to fill blanks with

    if (cant_fit) {
        *cp++ = 37;
        count++;
    } //%

    // leading +/-
    if (leading_plus) {
        if (pu_neg)
            *cp++ = 45;
        else
            *cp++ = 43;
        count++;
    }

    if (exponent_digits) {
        z4 = 0;
        // add $?
        if (dollar_sign) {
            *cp++ = 36;
            count++;
        } //$
        // add - sign? (as sign space was not specified)
        if (extra_sign_space) {
            if (pu_neg) {
                *cp++ = 45;
            } else {
                if (extra_sign_space == 2)
                    *cp++ = 48;
                else
                    *cp++ = 32;
            }
            count++;
        }
        // add digits left of decimal point
        for (z3 = 0; z3 < digits_before_point; z3++) {
            if (ii < pu_ndig)
                *cp++ = pu_dig[ii++];
            else
                *cp++ = 48;
            count++;
        }
        // add decimal point
        if (decimal_point) {
            *cp++ = 46;
            count++;
        }
        // add digits right of decimal point
        for (z3 = 0; z3 < digits_after_point; z3++) {
            if (ii < pu_ndig)
                *cp++ = pu_dig[ii++];
            else
                *cp++ = 48;
            count++;
        }
        // round last digit (requires a repass)
        if (!rounded) {
            if (ii < pu_ndig) {
                if (pu_dig[ii] >= 53) { //>="5" (round 5 up)
                    z = ii - 1;
                    // round up pu (by adding 1 from digit at character position z)
                    // note: pu_dig is rebuilt one character to the right so highest digit can flow over into next character
                    rounded = 1;
                    memmove(&pu_dig[1], &pu_dig[0], pu_ndig);
                    pu_dig[0] = 48;
                    z++;
                puround2:
                    pu_dig[z]++;
                    if (pu_dig[z] > 57) {
                        pu_dig[z] = 48;
                        z--;
                        goto puround2;
                    }
                    if (pu_dig[0] != 48) { // was extra character position necessary?
                        pu_ndig++;         // note: pu_dp does not require any changes
                    } else {
                        memmove(&pu_dig[0], &pu_dig[1], pu_ndig);
                    }
                    goto rounded_repass;
                }
            }
        }
        // add exponent...
        *cp++ = pu_exp_char;
        count++; // add exponent D/E/F (set and restored by calling function as necessary)
        if (z >= 0) {
            *cp++ = 43;
            count++;
        } else {
            *cp++ = 45;
            count++;
        } //+/- exponent's sign
        // add exponent's leading 0s (if any)
        for (z3 = 0; z3 < (exponent_digits - z2); z3++) {
            *cp++ = 48;
            count++;
        }
        // add exponent's value
        for (z3 = 0; z3 < z2; z3++) {
            *cp++ = pu_buf[z3];
            count++;
        }
    } else {
        //"print" everything before the point
        // calculate digit spaces before the point in number
        if (!commas)
            digits_and_commas_before_point = digits_before_point;
        z = pu_ndig + pu_dp; // num of character whole portion of number requires
        z4 = 0;
        if (z < 0)
            z4 = -z; // number of 0s between . and digits after point
        if (commas)
            z = z + (z - 1) / 3; // including appropriate amount of commas
        if (z < 0)
            z = 0;
        z2 = digits_and_commas_before_point - z;
        if ((z == 0) && (z2 > 0)) {
            leading_zero = 1;
            z2--;
        } // change .1 to 0.1 if possible
        for (z3 = 0; z3 < z2; z3++) {
            *cp++ = asterisk_spaces;
            count++;
        }
        // add - sign? (as sign space was not specified)
        if (extra_sign_space) {
            *cp++ = 45;
            count++;
        }
        // add $?
        if (dollar_sign) {
            *cp++ = 36;
            count++;
        } //$
        // leading 0?
        if (leading_zero) {
            *cp++ = 48;
            count++;
        } // 0
        // add digits left of decimal point
        for (z3 = 0; z3 < z; z3++) {
            if ((commas != 0) && (((z - z3) & 3) == 0)) {
                *cp++ = 44;
            } else {
                if (ii < pu_ndig)
                    *cp++ = pu_dig[ii++];
                else
                    *cp++ = 48;
            }
            count++;
        }
        // add decimal point
        if (decimal_point) {
            *cp++ = 46;
            count++;
        }
        // add digits right of decimal point
        for (z3 = 0; z3 < digits_after_point; z3++) {
            if (z4) {
                z4--;
                *cp++ = 48;
            } else {
                if (ii < pu_ndig)
                    *cp++ = pu_dig[ii++];
                else
                    *cp++ = 48;
            }
            count++;
        }
        // round last digit (requires a repass)
        if (!rounded) {
            if (ii < pu_ndig) {
                if (pu_dig[ii] >= 53) { //>="5" (round 5 up)
                    z = ii - 1;
                    // round up pu (by adding 1 from digit at character position z)
                    // note: pu_dig is rebuilt one character to the right so highest digit can flow over into next character
                    rounded = 1;
                    memmove(&pu_dig[1], &pu_dig[0], pu_ndig);
                    pu_dig[0] = 48;
                    z++;
                puround1:
                    pu_dig[z]++;
                    if (pu_dig[z] > 57) {
                        pu_dig[z] = 48;
                        z--;
                        goto puround1;
                    }
                    if (pu_dig[0] != 48) { // was extra character position necessary?
                        pu_ndig++;         // note: pu_dp does not require any changes
                    } else {
                        memmove(&pu_dig[0], &pu_dig[1], pu_ndig);
                    }
                    goto rounded_repass;
                }
            }
        }
    } // exponent_digits

    // add trailing sign?
    // trailing +/-
    if (trailing_plus) {
        if (pu_neg)
            *cp++ = 45;
        else
            *cp++ = 43;
        count++;
    }
    // trailing -
    if (trailing_minus) {
        if (pu_neg)
            *cp++ = 45;
        else
            *cp++ = 32;
        count++;
    }

    qbs_set(dest, qbs_add(dest, qbs_new_txt_len((char *)buf, count)));

    s = x;
    type = 0; // passed type added
    if (s >= len)
        return 0; // end of format line encountered and passed item added
    goto scan;

invalid_numeric_format:
    // string format
    static int32 string_size;

    x = s;
    if (x < len) {
        c = f->chr[x];
        string_size = 0; // invalid
        if (c == 38)
            string_size = -1; //"&" (all of string)
        if (c == 33)
            string_size = 1; //"!" (first character only)
        if (c == 92) {       //"\" first n characters
            z = 1;
            x++;
        get_str_fmt:
            if (x >= len)
                goto invalid_string_format;
            c = f->chr[x];
            z++;
            if (c == 32) {
                x++;
                goto get_str_fmt;
            }
            if (c != 92)
                goto invalid_string_format;
            string_size = z;
        } // c==47
        if (string_size) {
            if (type == 0)
                return s;    // s is the beginning of a new format but item has already been added to dest
            if (type == 1) { // expected numeric format, not string format
                error(13);   // type mismatch
                return 0;
            }
            if (string_size != -1) {
                s += string_size;
                for (z = 0; z < string_size; z++) {
                    if (z < pu_str->len)
                        qbs1->chr[0] = pu_str->chr[z];
                    else
                        qbs1->chr[0] = 32;
                    qbs_set(dest, qbs_add(dest, qbs1));
                } // z
            } else {
                qbs_set(dest, qbs_add(dest, pu_str));
                s++;
            }
            type = 0; // passed type added
            if (s >= len)
                return 0; // end of format line encountered and passed item added
            goto scan;
        } // string_size
    } // x<len
invalid_string_format:

    // add literal?
    if ((f->chr[s] == 95) && (s < (len - 1))) { // trailing single _ in format is treated as a literal _
        s++;
    }
    // add non-format character
    qbs1->chr[0] = f->chr[s];
    qbs_set(dest, qbs_add(dest, qbs1));

    s++;
    if (s >= len) {
        s = 0;
        if (type == 0)
            return s; // end of format line encountered and passed item added
        // illegal format? (format has been passed from start (s2=0) to end and has no numeric/string identifiers
        if (s2 == 0) {
            error(5); // illegal function call
            return 0;
        }
    }
    goto scan;

    return 0;
}

int32 print_using_integer64(qbs *format, int64 value, int32 start, qbs *output) {
    if (is_error_pending())
        return 0;
    pu_ndig = sprintf((char *)pu_buf, "% " PRId64, value);
    if (pu_buf[0] == 45)
        pu_neg = 1;
    else
        pu_neg = 0;
    pu_ndig--; // remove sign
    memcpy(pu_dig, &pu_buf[1], pu_ndig);
    pu_dp = 0;
    start = print_using(format, start, output, NULL);
    return start;
}

int32 print_using_uinteger64(qbs *format, uint64 value, int32 start, qbs *output) {
    if (is_error_pending())
        return 0;
    pu_ndig = sprintf((char *)pu_dig, "%" PRIu64, value);
    pu_neg = 0;
    pu_dp = 0;
    start = print_using(format, start, output, NULL);
    return start;
}

int32 print_using_single(qbs *format, float value, int32 start, qbs *output) {
    if (is_error_pending())
        return 0;
    static int32 i, len, neg_exp;
    static uint8 c;
    static int64 exp;
    len = sprintf((char *)&pu_buf, "% .255E", value); // 256 character limit ([1].[255])
    pu_dp = 0;
    pu_ndig = 0;
    // 1. sign
    if (pu_buf[0] == 45)
        pu_neg = 1;
    else
        pu_neg = 0;
    i = 1;
// 2. digits before decimal place
getdigits:
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if ((c >= 48) && (c <= 57)) {
        pu_dig[pu_ndig++] = c;
        i++;
        goto getdigits;
    }
    // 3. decimal place
    if (c != 46) {
        error(5);
        return 0;
    } // expected decimal point
    i++;
// 4. digits after decimal place
getdigits2:
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if ((c >= 48) && (c <= 57)) {
        pu_dig[pu_ndig++] = c;
        pu_dp--;
        i++;
        goto getdigits2;
    }
    // assume random character signifying an exponent
    i++;
    // optional exponent sign
    neg_exp = 0;
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if (c == 45) {
        neg_exp = 1;
        i++;
    } //-
    if (c == 43)
        i++; //+
    // assume remaining characters are an exponent
    exp = 0;
getdigits3:
    if (i < len) {
        c = pu_buf[i];
        if ((c < 48) || (c > 57)) {
            error(5);
            return 0;
        }
        exp = exp * 10;
        exp = exp + c - 48;
        i++;
        goto getdigits3;
    }
    if (neg_exp)
        exp = -exp;
    pu_dp += exp;
    start = print_using(format, start, output, NULL);
    return start;
}

int32 print_using_double(qbs *format, double value, int32 start, qbs *output) {
    if (is_error_pending())
        return 0;
    static int32 i, len, neg_exp;
    static uint8 c;
    static int64 exp;
    len = sprintf((char *)&pu_buf, "% .255E", value); // 256 character limit ([1].[255])
    pu_dp = 0;
    pu_ndig = 0;
    // 1. sign
    if (pu_buf[0] == 45)
        pu_neg = 1;
    else
        pu_neg = 0;
    i = 1;
// 2. digits before decimal place
getdigits:
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if ((c >= 48) && (c <= 57)) {
        pu_dig[pu_ndig++] = c;
        i++;
        goto getdigits;
    }
    // 3. decimal place
    if (c != 46) {
        error(5);
        return 0;
    } // expected decimal point
    i++;
// 4. digits after decimal place
getdigits2:
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if ((c >= 48) && (c <= 57)) {
        pu_dig[pu_ndig++] = c;
        pu_dp--;
        i++;
        goto getdigits2;
    }
    // assume random character signifying an exponent
    i++;
    // optional exponent sign
    neg_exp = 0;
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if (c == 45) {
        neg_exp = 1;
        i++;
    } //-
    if (c == 43)
        i++; //+
    // assume remaining characters are an exponent
    exp = 0;
getdigits3:
    if (i < len) {
        c = pu_buf[i];
        if ((c < 48) || (c > 57)) {
            error(5);
            return 0;
        }
        exp = exp * 10;
        exp = exp + c - 48;
        i++;
        goto getdigits3;
    }
    if (neg_exp)
        exp = -exp;
    pu_dp += exp;
    pu_exp_char = 68; //"D"
    start = print_using(format, start, output, NULL);
    pu_exp_char = 69; //"E"
    return start;
}

// WARNING: DUE TO MINGW NOT SUPPORTING PRINTF long double, VALUES ARE CONVERTED TO A DOUBLE
//         BUT PRINTED AS IF THEY WERE A long double
int32 print_using_float(qbs *format, long double value, int32 start, qbs *output) {
    if (is_error_pending())
        return 0;
    static int32 i, len, neg_exp;
    static uint8 c;
    static int64 exp;
// len=sprintf((char*)&pu_buf,"% .255E",value);//256 character limit ([1].[255])
#ifdef QB64_MINGW
    len = __mingw_sprintf((char *)&pu_buf, "% .255Lf", value); // 256 character limit ([1].[255])
#else
    len = sprintf((char *)&pu_buf, "% .255Lf", value); // 256 character limit ([1].[255])
#endif

    // qbs_print(qbs_new_txt((char*)&pu_buf),1);

    pu_dp = 0;
    pu_ndig = 0;
    // 1. sign
    if (pu_buf[0] == 45)
        pu_neg = 1;
    else
        pu_neg = 0;
    i = 1;
// 2. digits before decimal place
getdigits:
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if ((c >= 48) && (c <= 57)) {
        pu_dig[pu_ndig++] = c;
        i++;
        goto getdigits;
    }
    // 3. decimal place
    if (c != 46) {
        error(5);
        return 0;
    } // expected decimal point
    i++;
// 4. digits after decimal place
getdigits2:
    if (i >= len) {
        // no exponent information has been provided
        neg_exp = 0;
        exp = 0;
        goto no_exponent_provided;
        // error(5); return 0;
    }
    c = pu_buf[i];
    if ((c >= 48) && (c <= 57)) {
        pu_dig[pu_ndig++] = c;
        pu_dp--;
        i++;
        goto getdigits2;
    }
    // assume random character signifying an exponent
    i++;
    // optional exponent sign
    neg_exp = 0;
    if (i >= len) {
        error(5);
        return 0;
    }
    c = pu_buf[i];
    if (c == 45) {
        neg_exp = 1;
        i++;
    } //-
    if (c == 43)
        i++; //+
    // assume remaining characters are an exponent
    exp = 0;
getdigits3:
    if (i < len) {
        c = pu_buf[i];
        if ((c < 48) || (c > 57)) {
            error(5);
            return 0;
        }
        exp = exp * 10;
        exp = exp + c - 48;
        i++;
        goto getdigits3;
    }
    if (neg_exp)
        exp = -exp;
    pu_dp += exp;
no_exponent_provided:
    pu_exp_char = 70; //"F"
    start = print_using(format, start, output, NULL);
    pu_exp_char = 69; //"E"
    return start;
}

void sub_run_init() {
    // Reset ON KEY trapping
    // note: KEY bar F-key bindings are not affected
    static int32 i;
    for (i = 1; i <= 31; i++) {
        onkey[i].id = 0;
        onkey[i].active = 0;
        onkey[i].state = 0;
    }
    onkey_inprogress = 0;
    // note: if already in screen 0:80x25, screen pages are left intact
    // set screen mode to 0 (80x25)
    qbg_screen(0, NULL, 0, 0, NULL, 1 | 4 | 8);
    // make sure WIDTH is 80x25
    qbsub_width(NULL, 80, 25, 0, 0, 1 | 2);
    // restore view print
    qbg_sub_view_print(NULL, NULL, 0);
    // restore palette
    restorepalette(write_page);
    // restore default colors
    write_page->background_color = 0;
    write_page->color = 7;
    // note: cursor state does not appear to be reset by the RUN command
    // im->cursor_show=0; im->cursor_firstvalue=4; im->cursor_lastvalue=4;
    // Reset RND & RANDOMIZE state
    rnd_seed = 327680;
    rnd_seed_first = 327680; // Note: must contain the same value as rnd_seed
    // clear keyboard buffers
    sub__keyclear(NULL, 0);
}

void sub_run(qbs *f) {
    if (is_error_pending())
        return;

    // run program
    static qbs *str = NULL;
    if (str == NULL)
        str = qbs_new(0, 0);
    static qbs *strz = NULL;
    if (!strz)
        strz = qbs_new(0, 0);

    qbs_set(str, f);
    filepath_fix_directory(str);

#ifdef QB64_WINDOWS

    qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
    if (WinExec((char *)strz->chr, SW_SHOWDEFAULT) > 31) {
        goto run_exit;
    } else {
        // 0-out of resources/memory
        // ERROR_BAD_FORMAT
        // ERROR_FILE_NOT_FOUND
        // ERROR_PATH_NOT_FOUND
        error(53);
        return; // file not found
    }

#else
    qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
    system((char *)strz->chr);
    // success?
    goto run_exit;

#endif

// exit this program
run_exit:
    close_program = 1;
    end();
    exit(99); //<--this line should never actually be executed
}

#ifdef DEPENDENCY_ICON
void sub__icon(int32 handle_icon, int32 handle_window_icon, int32 passed) {

    if (is_error_pending())
        return;
#    ifndef DEPENDENCY_CONSOLE_ONLY
    if (!(passed & 2))
        handle_window_icon = handle_icon;
    if (!(passed & 1)) {
        if (!image_qbicon16_handle) {
            image_qbicon16_handle = func__newimage(image_qbicon16_w, image_qbicon16_h, 32, 1);
            memcpy(img[-image_qbicon16_handle].offset32, &image_qbicon16[0], image_qbicon16_w * image_qbicon16_h * 4);
        }

        if (!image_qbicon32_handle) {
            image_qbicon32_handle = func__newimage(image_qbicon32_w, image_qbicon32_h, 32, 1);
            memcpy(img[-image_qbicon32_handle].offset32, &image_qbicon32[0], image_qbicon32_w * image_qbicon32_h * 4);
        }

        handle_icon = image_qbicon32_handle;
        handle_window_icon = image_qbicon16_handle;
    }

    static int32 i, i2, ii, w, h;
    static uint32 *o, *o2;
    static int32 x, y, n, c, i3, c2;

    // validation
    for (ii = 1; ii <= 2; ii++) {
        if (ii == 1)
            i = handle_icon;
        if (ii == 2)
            i = handle_window_icon;
        if (i >= 0) { // validate i
            validatepage(i);
            i = page[i];
        } else {
            i = -i;
            if (i >= nextimg) {
                error(258);
                return;
            }
            if (!img[i].valid) {
                error(258);
                return;
            }
        }
        if (img[i].text) {
            error(5);
            return;
        }
        if (ii == 1)
            handle_icon = i;
        if (ii == 2)
            handle_window_icon = i;
    }

#        ifdef QB64_WINDOWS
    HWND win = (HWND)func__windowhandle();
    if (!win) {
        return;
    }

    static HANDLE ExeIcon;
    static HANDLE ExeIcon16;

    // Attempt to load the first icon embedded in the .exe
    if (!ExeIcon)
        ExeIcon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(0), IMAGE_ICON, 32, 32, 0);
    if (!ExeIcon16)
        ExeIcon16 = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(0), IMAGE_ICON, 16, 16, 0);

    // If we have an embedded icon, we'll use it instead of QB64-PE's default
    if (!(passed & 1) && (ExeIcon)) {
        SendMessage(win, WM_SETICON, ICON_BIG, (LPARAM)ExeIcon);

        if (ExeIcon16) {
            SendMessage(win, WM_SETICON, ICON_SMALL, (LPARAM)ExeIcon16);
        } else {
            SendMessage(win, WM_SETICON, ICON_SMALL, (LPARAM)ExeIcon);
        }
        return;
    }
    for (ii = 1; ii <= 2; ii++) {

        if (ii == 1) {
            i = handle_icon;
            w = GetSystemMetrics(SM_CXICON);
            h = GetSystemMetrics(SM_CYICON);
        }
        if (ii == 2) {
            i = handle_window_icon;
            w = GetSystemMetrics(SM_CXSMICON);
            h = GetSystemMetrics(SM_CYSMICON);
        }

        // source[http://support.microsoft.com/kb/318876]
        ICONINFO iinfo;
        HDC hdc;
        BITMAPV5HEADER bi;
        HBITMAP hBitmap, hOldBitmap;
        void *lpBits;
        HCURSOR hAlphaCursor = NULL;
        ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
        bi.bV5Size = sizeof(BITMAPV5HEADER);
        bi.bV5Width = w;
        bi.bV5Height = h;
        bi.bV5Planes = 1;
        bi.bV5BitCount = 32;
        bi.bV5Compression = BI_BITFIELDS;
        // The following mask specification specifies a supported 32 BPP
        // alpha format for Windows XP.
        bi.bV5RedMask = 0x00FF0000;
        bi.bV5GreenMask = 0x0000FF00;
        bi.bV5BlueMask = 0x000000FF;
        bi.bV5AlphaMask = 0xFF000000;
        hdc = GetDC(NULL);
        // Create the DIB section with an alpha channel.
        hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&lpBits, NULL, (DWORD)0);
        ReleaseDC(NULL, hdc);

        i2 = func__newimage(w, h, 32, 1);
        sub__dontblend(i2, 1);
        sub__putimage(NULL, NULL, NULL, NULL, -i, i2, NULL, NULL, NULL, NULL, 8 + 32);

        o = img[-i2].offset32;
        o2 = (uint32 *)lpBits;
        for (y = 0; y < h; y++) {
            for (x = 0; x < w; x++) {
                c = o[(h - 1 - y) * w + x];
                o2[y * w + x] = c;
            }
        }

        sub__freeimage(i2, 1);

        // Create an empty mask bitmap.
        HBITMAP hMonoBitmap = CreateBitmap(w, h, 1, 1, NULL);

        iinfo.fIcon = TRUE; // Change fIcon to TRUE to create an alpha icon
        iinfo.xHotspot = 0;
        iinfo.yHotspot = 0;
        iinfo.hbmMask = hMonoBitmap;
        iinfo.hbmColor = hBitmap;
        // Create the alpha cursor with the alpha DIB section.
        hAlphaCursor = CreateIconIndirect(&iinfo);

        DeleteObject(hBitmap);
        DeleteObject(hMonoBitmap);

        if (ii == 1)
            SendMessage(win, WM_SETICON, ICON_BIG, (LPARAM)hAlphaCursor);
        if (ii == 2)
            SendMessage(win, WM_SETICON, ICON_SMALL, (LPARAM)hAlphaCursor);

    } // ii
#        endif // QB64_WINDOWS
#    endif     // DEPENDENCY_CONSOLE_ONLY
} // sub__icon
#endif // DEPENDENCY_ICON

int32_t func__desktopwidth() {
#ifdef QB64_WINDOWS
    return GetSystemMetrics(SM_CXSCREEN);
#else
#    ifdef QB64_GLUT
    OPTIONAL_GLUT(0);
    return libqb_glut_get(GLUT_SCREEN_WIDTH);
#    else
    return 0;
#    endif
#endif
}

int32_t func__desktopheight() {
#ifdef QB64_WINDOWS
    return GetSystemMetrics(SM_CYSCREEN);
#else
#    ifdef QB64_GLUT
    OPTIONAL_GLUT(0);
    return libqb_glut_get(GLUT_SCREEN_HEIGHT);
#    else
    return 0;
#    endif
#endif
}

void sub_screenicon() {
#ifdef QB64_GLUT
    NEEDS_GLUT();
    libqb_glut_iconify_window();
#endif
}

int32 func_windowexists() {
#ifdef QB64_GLUT
    return libqb_is_glut_up();
#else
    return -1;
#endif
}

int32 func_screenicon() {
#ifdef QB64_GLUT
#    ifdef QB64_WINDOWS
    HWND win = (HWND)func__windowhandle();
    if (!win) {
        return 0;
    }
    return -IsIconic(win);
#    else
    /*
        Linux code not compiling for now
        #include <X11/X.h>
        #include <X11/Xlib.h>
        extern Display *X11_display;
        extern Window X11_window;
        extern int32 screen_hide;
        XWindowAttributes attribs;
        while (!(X11_display && X11_window));
        XGetWindowAttributes(X11_display, X11_window, &attribs);
        if (attribs.map_state == IsUnmapped) return -1;
        return 0;
    #endif */
    return 0; // if we get here and haven't exited already, we failed somewhere along the way.
#    endif
#else
    return 0;
#endif
}

int32 func__autodisplay() {
    if (autodisplay) {
        return -1;
    }
    return 0;
}

void sub__autodisplay() {
    autodisplay = 1;
}

void sub__display() {
    if (screen_hide)
        return;

    // disable autodisplay (if enabled)
    if (autodisplay) {
        autodisplay = -1; // toggle request
        while (autodisplay)
            Sleep(1);
        return; // note: autodisplay is set to 0 after display() has been called so a second call to display() is unnecessary
    }
    display();
}

int32 sub_draw_i;
uint8 *sub_draw_cp;
int32 sub_draw_len;

int32 draw_num_invalid;
int32 draw_num_undefined;

double draw_num() {
    static int32 c, dp, vptr, x, offset;
    static double d, dp_mult, sgn;

    draw_num_invalid = 0;
    draw_num_undefined = 1;
    d = 0;
    dp = 0;
    sgn = 1;
    vptr = 0;

nextchar:
    if (sub_draw_i >= sub_draw_len)
        return d * sgn;
    c = sub_draw_cp[sub_draw_i];

    if (vptr) {
        if ((sub_draw_i + 2) >= sub_draw_len) {
            draw_num_invalid = 1;
            return 0;
        } // not enough data!
        offset = sub_draw_cp[sub_draw_i + 2] * 256 + sub_draw_cp[sub_draw_i + 1];
        sub_draw_i += 3;
        vptr = 0;
        /*
            'BYTE=1
            'INTEGER=2
            'STRING=3 (unsupported)
            'SINGLE=4
            'INT64=5
            'FLOAT=6
            'DOUBLE=8
            'LONG=20
            'BIT=64+n (unsupported)
        */
        if (c == 1) {
            d = *((int8 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == (1 + 128)) {
            d = *((uint8 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == 2) {
            d = *((int16 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == (2 + 128)) {
            d = *((uint16 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == 4) {
            d = *((float *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == 5) {
            d = *((int64 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == (5 + 128)) {
            d = *((uint64 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == 6) {
            d = *((long double *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == 8) {
            d = *((double *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == 20) {
            d = *((int32 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        if (c == (20 + 128)) {
            d = *((uint32 *)(&cmem[1280 + offset]));
            goto nextcharv;
        }
        // unknown/unsupported types(bit/string) return an error
        draw_num_invalid = 1;
        return 0;
    nextcharv:
        draw_num_invalid = 0;
        draw_num_undefined = 0;
        return d;
    }

    if ((c == 32) || (c == 9)) {
        sub_draw_i++;
        goto nextchar;
    } // skip whitespace

    if ((c >= 48) && (c <= 57)) {
        c -= 48;
        if (dp) {
            d += (((double)c) * dp_mult);
            dp_mult /= 10.0;
        } else {
            d = (d * 10) + c;
        }
        draw_num_undefined = 0;
        draw_num_invalid = 0;
        sub_draw_i++;
        goto nextchar;
    }

    if (c == 45) { //-
        if (dp || (!draw_num_undefined))
            return d * sgn;
        sgn = -sgn;
        draw_num_invalid = 1;
        sub_draw_i++;
        goto nextchar;
    }

    if (c == 43) { //+
        if (dp || (!draw_num_undefined))
            return d * sgn;
        draw_num_invalid = 1;
        sub_draw_i++;
        goto nextchar;
    }

    if (c == 46) { //.
        if (dp)
            return d * sgn;
        dp = 1;
        dp_mult = 0.1;
        if (!draw_num_undefined)
            draw_num_invalid = 1;
        sub_draw_i++;
        goto nextchar;
    }

    if (c == 61) { //=
        if (draw_num_invalid || dp || (!draw_num_undefined)) {
            draw_num_invalid = 1;
            return 0;
        } // leading data invalid!
        vptr = 1;
        sub_draw_i++;
        goto nextchar;
    }

    return d * sgn;
}

void sub_draw(qbs *s) {
    if (is_error_pending())
        return;

    /*

        Aspect ratio determination:
        32/256 modes always assume 1:1 ratio
        All other modes (1-13) determine their aspect ratio from the destination surface's dimensions (presuming it is stretched onto a 4:3 ratio monitor)

        Reference:
        Line-drawing and cursor-movement commands:
        D[n%]            Moves cursor down n% units.
        E[n%]            Moves cursor up and right n% units.
        F[n%]            Moves cursor down and right n% units.
        G[n%]            Moves cursor down and left n% units.
        H[n%]            Moves cursor up and left n% units.
        L[n%]            Moves cursor left n% units.
        M[{+|-}]x%,y%    Moves cursor to point x%,y%. If x% is preceded
        by + or -, moves relative to the current point.
        -+/- relative ONLY if after the M, after comma doesn't affect method
        -nothing to do with VIEW/WINDOW coordinates (but still clipped)
        R[n%]            Moves cursor right n% units.
        U[n%]            Moves cursor up n% units.
        [B]              Optional prefix that moves cursor without drawing.
        [N]              Optional prefix that draws and returns cursor to
        its original position.
        *Prefixes B&N can be used anywhere. They set (not toggle) their respective states. They are only cleared if they are used in a statement. They are
       forgotten when a new DRAW statement is called. Color, rotation, and scale commands: An%              Rotates an object n% * 90 degrees (n% can be 0, 1,
        2, or 3).
        Cn%              Sets the drawing color (n% is a color attribute).

        Pn1%,n2%         Sets the paint fill and border colors of an object
        (n1% is the fill-color attribute, n2% is the
        border-color attribute).
        Sn%              Determines the drawing scale by setting the length
        of a unit of cursor movement. The default n% is 4,
        which is equivalent to 1 pixel.
        TAn%             Turns an angle n% degrees (-360 through 360).

        -If you omit n% from line-drawing and cursor-movement commands, the
        cursor moves 1 unit.
        -To execute a DRAW command substring from a DRAW command string, use
        the "X" command:
        DRAW "X"+ VARPTR$(commandstring$)
    */

    static double r, ir, vx, vy, hx, hy, ex, ey, fx, fy, xx, yy, px, py, px2, py2, d, d2, sin_ta, cos_ta;
    static int64 c64, c64b, c64c;
    static uint32 col;
    static int32 x, c, prefix_b, prefix_n, offset;
    static uint8 *stack_s[8192];
    static uint16 stack_len[8192];
    static uint16 stack_i[8192];
    static int32 stacksize;
    static double draw_ta;
    static double draw_scale;

    if (write_page->text) {
        error(5);
        return;
    }

    draw_ta = write_page->draw_ta;
    draw_scale = write_page->draw_scale;

    if (write_page->compatible_mode <= 13) {
        if (write_page->compatible_mode == 1)
            r = 4.0 / ((3.0 / 200.0) * 320.0);
        if (write_page->compatible_mode == 2)
            r = 4.0 / ((3.0 / 200.0) * 640.0);
        if (write_page->compatible_mode == 7)
            r = 4.0 / ((3.0 / 200.0) * 320.0);
        if (write_page->compatible_mode == 8)
            r = 4.0 / ((3.0 / 200.0) * 640.0);
        if (write_page->compatible_mode == 9)
            r = 4.0 / ((3.0 / 350.0) * 640.0);
        if (write_page->compatible_mode == 10)
            r = 4.0 / ((3.0 / 350.0) * 640.0);
        if (write_page->compatible_mode == 11)
            r = 4.0 / ((3.0 / 480.0) * 640.0);
        if (write_page->compatible_mode == 12)
            r = 4.0 / ((3.0 / 480.0) * 640.0);
        if (write_page->compatible_mode == 13)
            r = 4.0 / ((3.0 / 200.0) * 320.0);
        // Old method: r=4.0 /( (3.0/((double)write_page->height)) * ((double)write_page->width) ); //calculate aspect ratio of image
        ir = 1 / r; // note: all drawing must multiply the x offset by ir (inverse ratio)
    } else {
        r = 1;
        ir = 1;
    }

    vx = 0;
    vy = -1;
    ex = r;
    ey = -1;
    hx = r;
    hy = 0;
    fx = r;
    fy = 1; // reset vectors
    // rotate vectors by ta?
    if (draw_ta) {
        d = draw_ta * 0.0174532925199433;
        sin_ta = std::sin(d);
        cos_ta = std::cos(d);
        px2 = vx;
        py2 = vy;
        vx = px2 * cos_ta + py2 * sin_ta;
        vy = py2 * cos_ta - px2 * sin_ta;
        px2 = hx;
        py2 = hy;
        hx = px2 * cos_ta + py2 * sin_ta;
        hy = py2 * cos_ta - px2 * sin_ta;
        px2 = ex;
        py2 = ey;
        ex = px2 * cos_ta + py2 * sin_ta;
        ey = py2 * cos_ta - px2 * sin_ta;
        px2 = fx;
        py2 = fy;
        fx = px2 * cos_ta + py2 * sin_ta;
        fy = py2 * cos_ta - px2 * sin_ta;
    }

    // convert x,y image position into a pixel coordinate
    if (write_page->clipping_or_scaling) {
        if (write_page->clipping_or_scaling == 2) {
            px = write_page->x * write_page->scaling_x + write_page->scaling_offset_x + write_page->view_offset_x;
            py = write_page->y * write_page->scaling_y + write_page->scaling_offset_y + write_page->view_offset_y;
        } else {
            px = write_page->x + write_page->view_offset_x;
            py = write_page->y + write_page->view_offset_y;
        }
    } else {
        px = write_page->x;
        py = write_page->y;
    }

    col = write_page->draw_color;
    prefix_b = 0;
    prefix_n = 0;

    stacksize = 0;

    sub_draw_cp = s->chr;
    sub_draw_len = s->len;
    sub_draw_i = 0;

nextchar:
    if (sub_draw_i >= sub_draw_len) {

        // revert from X-stack
        if (stacksize) {
            stacksize--;
            sub_draw_cp = stack_s[stacksize];
            sub_draw_len = stack_len[stacksize];
            sub_draw_i = stack_i[stacksize]; // restore state
            // continue
            goto nextchar;
        }

        // revert px,py to image->x,y offsets
        if (write_page->clipping_or_scaling) {
            if (write_page->clipping_or_scaling == 2) {
                px = (px - write_page->view_offset_x - write_page->scaling_offset_x) / write_page->scaling_x;
                py = (py - write_page->view_offset_y - write_page->scaling_offset_y) / write_page->scaling_y;
            } else {
                px = px - write_page->view_offset_x;
                py = py - write_page->view_offset_y;
            }
        }
        write_page->x = px;
        write_page->y = py;
        return;
    }
    c = sub_draw_cp[sub_draw_i];

    if ((c >= 97) && (c <= 122))
        c -= 32; // ucase c

    if (c == 77) { // M
    m_nextchar:
        sub_draw_i++;
        if (sub_draw_i >= sub_draw_len) {
            error(5);
            return;
        }
        c = sub_draw_cp[sub_draw_i];
        if ((c == 32) || (c == 9))
            goto m_nextchar; // skip whitespace
        // check for absolute/relative positioning
        if ((c == 43) || (c == 45))
            x = 1;
        else
            x = 0;
        px2 = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        c = sub_draw_cp[sub_draw_i];
        if (c != 44) {
            error(5);
            return;
        } // expected ,
        sub_draw_i++;
        py2 = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        if (x) { // relative positioning
            xx = (px2 * ir) * hx - (py2 * ir) * vx;
            yy = px2 * hy - py2 * vy;
            px2 = px + xx * draw_scale;
            py2 = py + yy * draw_scale;
        }
        if (!prefix_b)
            fast_line(qbr(px), qbr(py), qbr(px2), qbr(py2), col);
        if (!prefix_n) {
            px = px2;
            py = py2;
        } // update position
        prefix_b = 0;
        prefix_n = 0;
        goto nextchar;
    }

    if (c == 84) { // T(A)
    ta_nextchar:
        sub_draw_i++;
        if (sub_draw_i >= sub_draw_len) {
            error(5);
            return;
        }
        c = sub_draw_cp[sub_draw_i];
        if ((c == 32) || (c == 9))
            goto ta_nextchar; // skip whitespace
        if ((c != 65) && (c != 97)) {
            error(5);
            return;
        } // not TA
        sub_draw_i++;
        d = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        draw_ta = d;
        write_page->draw_ta = draw_ta;
    ta_entry:
        // note: ta rotation is not relative to previous angle
        vx = 0;
        vy = -1;
        ex = r;
        ey = -1;
        hx = r;
        hy = 0;
        fx = r;
        fy = 1; // reset vectors
        // rotate vectors by ta
        d = draw_ta * 0.0174532925199433;
        sin_ta = std::sin(d);
        cos_ta = std::cos(d);
        px2 = vx;
        py2 = vy;
        vx = px2 * cos_ta + py2 * sin_ta;
        vy = py2 * cos_ta - px2 * sin_ta;
        px2 = hx;
        py2 = hy;
        hx = px2 * cos_ta + py2 * sin_ta;
        hy = py2 * cos_ta - px2 * sin_ta;
        px2 = ex;
        py2 = ey;
        ex = px2 * cos_ta + py2 * sin_ta;
        ey = py2 * cos_ta - px2 * sin_ta;
        px2 = fx;
        py2 = fy;
        fx = px2 * cos_ta + py2 * sin_ta;
        fy = py2 * cos_ta - px2 * sin_ta;
        goto nextchar;
    }

    if (c == 85) {
        xx = vx;
        yy = vy;
        goto udlr;
    } // U
    if (c == 68) {
        xx = -vx;
        yy = -vy;
        goto udlr;
    } // D
    if (c == 76) {
        xx = -hx;
        yy = -hy;
        goto udlr;
    } // L
    if (c == 82) {
        xx = hx;
        yy = hy;
        goto udlr;
    } // R

    if (c == 69) {
        xx = ex;
        yy = ey;
        goto udlr;
    } // E
    if (c == 70) {
        xx = fx;
        yy = fy;
        goto udlr;
    } // F
    if (c == 71) {
        xx = -ex;
        yy = -ey;
        goto udlr;
    } // G
    if (c == 72) {
        xx = -fx;
        yy = -fy;
        goto udlr;
    } // H

    if (c == 67) { // C
        sub_draw_i++;
        d = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        c64 = d;
        xx = c64;
        if (xx != d) {
            error(5);
            return;
        } // non-integer
        // if (c64<0){error(5); return;}
        // c64b=1; c64b<<=write_page->bits_per_pixel; c64b--;
        // if (c64>c64b){error(5); return;}
        col = c64;
        write_page->draw_color = col;
        goto nextchar;
    }

    if (c == 66) { // B (move without drawing prefix)
        prefix_b = 1;
        sub_draw_i++;
        goto nextchar;
    }

    if (c == 78) { // N (draw without moving)
        prefix_n = 1;
        sub_draw_i++;
        goto nextchar;
    }

    if (c == 83) { // S
        sub_draw_i++;
        d = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        if (d < 0) {
            error(5);
            return;
        }
        draw_scale = d / 4.0;
        write_page->draw_scale = draw_scale;
        goto nextchar;
    }

    if (c == 80) { // P
        sub_draw_i++;
        d = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        c64 = d;
        xx = c64;
        if (xx != d) {
            error(5);
            return;
        } // non-integer
        // if (c64<0){error(5); return;}
        // c64b=1; c64b<<=write_page->bits_per_pixel; c64b--;
        // if (c64>c64b){error(5); return;}
        c64c = c64;
        c = sub_draw_cp[sub_draw_i];
        if (c != 44) {
            error(5);
            return;
        } // expected ,
        sub_draw_i++;
        d = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        c64 = d;
        xx = c64;
        if (xx != d) {
            error(5);
            return;
        } // non-integer
        // if (c64<0){error(5); return;}
        // c64b=1; c64b<<=write_page->bits_per_pixel; c64b--;
        // if (c64>c64b){error(5); return;}
        // revert px,py to x,y offsets
        if (write_page->clipping_or_scaling) {
            if (write_page->clipping_or_scaling == 2) {
                xx = (px - write_page->view_offset_x - write_page->scaling_offset_x) / write_page->scaling_x;
                yy = (py - write_page->view_offset_y - write_page->scaling_offset_y) / write_page->scaling_y;
            } else {
                xx = px - write_page->view_offset_x;
                yy = py - write_page->view_offset_y;
            }
        } else {
            xx = px;
            yy = py;
        }
        sub_paint(xx, yy, c64c, c64, NULL, 2 + 4);
        col = c64c;
        goto nextchar;
    }

    if (c == 65) { // A
        sub_draw_i++;
        d = draw_num();
        if (draw_num_invalid || draw_num_undefined) {
            error(5);
            return;
        }
        if (d == 0) {
            draw_ta = 0;
            write_page->draw_ta = draw_ta;
            goto ta_entry;
        }
        if (d == 1) {
            draw_ta = 90;
            write_page->draw_ta = draw_ta;
            goto ta_entry;
        }
        if (d == 2) {
            draw_ta = 180;
            write_page->draw_ta = draw_ta;
            goto ta_entry;
        }
        if (d == 3) {
            draw_ta = 270;
            write_page->draw_ta = draw_ta;
            goto ta_entry;
        }
        error(5);
        return; // invalid value
    }

    if (c == 88) { // X
        sub_draw_i++;
        if ((sub_draw_i + 2) >= sub_draw_len) {
            error(5);
            return;
        }
        if (sub_draw_cp[sub_draw_i] != 3) {
            error(5);
            return;
        }
        offset = sub_draw_cp[sub_draw_i + 2] * 256 + sub_draw_cp[sub_draw_i + 1]; // offset of string descriptor in DBLOCK
        sub_draw_i += 3;
        if (stacksize == 8192) {
            error(6);
            return;
        } // X-stack "OVERFLOW" (should never occur because DBLOCK will overflow first)
        stack_s[stacksize] = sub_draw_cp;
        stack_len[stacksize] = sub_draw_len;
        stack_i[stacksize] = sub_draw_i;
        stacksize++; // backup state
        // set new state
        sub_draw_i = 0;
        x = cmem[1280 + offset + 3] * 256 + cmem[1280 + offset + 2];
        sub_draw_cp = &cmem[1280] + x;
        sub_draw_len = cmem[1280 + offset + 1] * 256 + cmem[1280 + offset + 0];
        // continue processing
        goto nextchar;
    }

    if ((c == 32) || (c == 9) || (c == 59)) {
        sub_draw_i++;
        goto nextchar;
    } // skip whitespace/semicolons

    error(5);
    return; // unknown command encountered!

udlr:
    sub_draw_i++;
    d = draw_num();
    if (draw_num_invalid) {
        error(5);
        return;
    }
    if (draw_num_undefined)
        d = 1;
    xx *= d;
    yy *= d;
    //***apply scaling here***
    xx = xx * ir;
    px2 = px + xx * draw_scale;
    py2 = py + yy * draw_scale;
    if (!prefix_b)
        fast_line(qbr(px), qbr(py), qbr(px2), qbr(py2), col);
    if (!prefix_n) {
        px = px2;
        py = py2;
    } // update position
    prefix_b = 0;
    prefix_n = 0;
    goto nextchar;
}

// Referenced: http://johnnie.jerrata.com/winsocktutorial/
// Much of the unix sockets code based on http://beej.us/guide/bgnet/
#ifdef QB64_WINDOWS
#    include <winsock2.h>
WSADATA wsaData;
WORD sockVersion;
#else
#    include <netdb.h>
#    include <sys/socket.h>
#    include <sys/types.h>
#endif

#define NETWORK_ERROR -1
#define NETWORK_OK 0

void tcp_init() {
    static int32 init = 0;
    if (!init) {
        init = 1;
#if !defined(DEPENDENCY_SOCKETS)
#elif defined(QB64_WINDOWS)
        sockVersion = MAKEWORD(1, 1);
        WSAStartup(sockVersion, &wsaData);
#endif
    }
}

void tcp_done() {
#if !defined(DEPENDENCY_SOCKETS)
#elif defined(QB64_WINDOWS)
    WSACleanup();
#endif
}

struct tcp_connection {
#if !defined(DEPENDENCY_SOCKETS)
#elif defined(QB64_WINDOWS)
    SOCKET socket;
#elif defined(QB64_UNIX)
    int socket;
#else
#endif
    int32 port;      // connection to host & clients only
    uint8 ip4[4];    // connection to host only
    uint8 *hostname; // clients only
    int connected;
};

void *tcp_host_open(int64 port) {
    tcp_init();
    if ((port < 0) || (port > 65535))
        return NULL;
#if !defined(DEPENDENCY_SOCKETS)
    return NULL;
#elif defined(QB64_WINDOWS)
    // Ref. from 'winsock.h': typedef u_int SOCKET;
    static SOCKET listeningSocket;
    listeningSocket = socket(AF_INET,      // Go over TCP/IP
                             SOCK_STREAM,  // This is a stream-oriented socket
                             IPPROTO_TCP); // Use TCP rather than UDP
    if (listeningSocket == INVALID_SOCKET)
        return NULL;
    static SOCKADDR_IN serverInfo;
    serverInfo.sin_family = AF_INET;
    serverInfo.sin_addr.s_addr = INADDR_ANY; // Since this socket is listening for connections,
    // any local address will do
    serverInfo.sin_port = htons(port); // Convert integer port to network-byte order
    // and insert into the port field
    // Bind the socket to our local server address
    static int nret;
    nret = ::bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));
    if (nret == SOCKET_ERROR) {
        closesocket(listeningSocket);
        return NULL;
    }
    nret = listen(listeningSocket, SOMAXCONN); // Up to x connections may wait at any
    // one time to be accept()'ed
    if (nret == SOCKET_ERROR) {
        closesocket(listeningSocket);
        return NULL;
    }
    static u_long iMode;
    iMode = 1;
    ioctlsocket(listeningSocket, FIONBIO, &iMode);

    static tcp_connection *connection;
    connection = (tcp_connection *)calloc(sizeof(tcp_connection), 1);
    connection->socket = listeningSocket;
    connection->connected = -1;
    return (void *)connection;
#elif defined(QB64_UNIX)
    struct addrinfo hints, *servinfo, *p;
    int sockfd;
    char str_port[6];
    int yes = 1;
    snprintf(str_port, 6, "%" PRId64, port);
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    if (getaddrinfo(NULL, str_port, &hints, &servinfo) != 0)
        return NULL;
    for (p = servinfo; p != NULL; p = p->ai_next) {
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd == -1)
            continue;
        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
        if (::bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            continue;
        }
        break; // if we get here, all is good
    }
    freeaddrinfo(servinfo);
    if (p == NULL)
        return NULL;                    // indicates none of the entries succeeded
    fcntl(sockfd, F_SETFL, O_NONBLOCK); // make socket non-blocking

    if (listen(sockfd, SOMAXCONN) == -1) {
        close(sockfd);
        return NULL;
    }

    tcp_connection *connection;
    connection = (tcp_connection *)calloc(sizeof(tcp_connection), 1);
    connection->socket = sockfd;
    connection->connected = -1;
    return (void *)connection;
#else
    return NULL;
#endif
}

void *tcp_client_open(uint8 *host, int64 port) {

    tcp_init();

    if ((port < 0) || (port > 65535))
        return NULL;
#if !defined(DEPENDENCY_SOCKETS)
    return NULL;
#elif defined(QB64_WINDOWS)
    static LPHOSTENT hostEntry;
    hostEntry = gethostbyname((char *)host);
    if (!hostEntry)
        return NULL;
    // Ref. from 'winsock.h': typedef u_int SOCKET;
    static SOCKET theSocket;
    theSocket = socket(AF_INET,      // Go over TCP/IP
                       SOCK_STREAM,  // This is a stream-oriented socket
                       IPPROTO_TCP); // Use TCP rather than UDP
    if (theSocket == INVALID_SOCKET)
        return NULL;
    static SOCKADDR_IN serverInfo;
    serverInfo.sin_family = AF_INET;
    serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
    serverInfo.sin_port = htons(port);
    static int nret;
    nret = connect(theSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));
    if (nret == SOCKET_ERROR) {
        closesocket(theSocket);
        return NULL;
    }
    // Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms738573%28v=vs.85%29.aspx
    // Set the socket I/O mode: In this case FIONBIO
    // enables or disables the blocking mode for the
    // socket based on the numerical value of iMode.
    // If iMode = 0, blocking is enabled;
    // If iMode != 0, non-blocking mode is enabled.
    static u_long iMode;
    iMode = 1;
    ioctlsocket(theSocket, FIONBIO, &iMode);

    static tcp_connection *connection;
    connection = (tcp_connection *)calloc(sizeof(tcp_connection), 1);
    connection->socket = theSocket;
    connection->port = port;
    connection->connected = -1;
    connection->hostname = (uint8 *)malloc(strlen((char *)host) + 1);
    memcpy(connection->hostname, host, strlen((char *)host) + 1);
    return (void *)connection;
#elif defined(QB64_UNIX)
    struct addrinfo hints, *servinfo, *p;
    int sockfd;
    char str_port[6];
    snprintf(str_port, 6, "%" PRId64, port);
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    if (getaddrinfo((char *)host, str_port, &hints, &servinfo) != 0)
        return NULL;
    for (p = servinfo; p != NULL; p = p->ai_next) {
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd == -1)
            continue;
        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            continue;
        }
        break; // if we get here, all is good
    }
    freeaddrinfo(servinfo);
    if (p == NULL)
        return NULL;                    // indicates none of the entries succeeded
    fcntl(sockfd, F_SETFL, O_NONBLOCK); // make socket non-blocking

    // now we just need to create a struct tcp_connection to return
    tcp_connection *connection;
    connection = (tcp_connection *)calloc(sizeof(tcp_connection), 1);
    connection->socket = sockfd;
    connection->port = port;
    connection->connected = -1;
    connection->hostname = (uint8 *)malloc(strlen((char *)host) + 1);
    memcpy(connection->hostname, host, strlen((char *)host) + 1);
    return (void *)connection;
#else
    return NULL;
#endif
}

void *tcp_connection_open(void *host_tcp) {
#if !defined(DEPENDENCY_SOCKETS)
    return NULL;
#elif defined(QB64_WINDOWS)
    static tcp_connection *host;
    host = (tcp_connection *)host_tcp;
    static sockaddr sa;
    static int sa_size;
    sa_size = sizeof(sa);
    static SOCKET new_socket;
    new_socket = accept(host->socket,
                        &sa,       // Optionally, address of a SOCKADDR_IN struct
                        &sa_size); //             sizeof ( struct SOCKADDR_IN )
    if (new_socket == INVALID_SOCKET)
        return NULL;
    static u_long iMode;
    iMode = 1;
    ioctlsocket(new_socket, FIONBIO, &iMode);
    static tcp_connection *connection;
    connection = (tcp_connection *)calloc(sizeof(tcp_connection), 1);
    connection->socket = new_socket;
    // IPv4: port,port,ip,ip,ip,ip
    connection->port = *((uint16 *)sa.sa_data);
    connection->connected = -1;
    *((uint32 *)(connection->ip4)) = *((uint32 *)(sa.sa_data + 2));
    return (void *)connection;
#elif defined(QB64_UNIX)
    tcp_connection *host;
    host = (tcp_connection *)host_tcp;
    struct sockaddr remote_addr;
    socklen_t addr_size;
    int fd;

    addr_size = sizeof(remote_addr);
    fd = accept(host->socket, &remote_addr, &addr_size);
    if (fd == -1)
        return NULL;
    fcntl(fd, F_SETFL, O_NONBLOCK); // make socket non-blocking

    tcp_connection *connection;
    connection = (tcp_connection *)calloc(sizeof(tcp_connection), 1);
    connection->socket = fd;
    connection->connected = -1;
    // IPv4: port,port,ip,ip,ip,ip
    connection->port = *((uint16 *)remote_addr.sa_data);
    *((uint32 *)(connection->ip4)) = *((uint32 *)(remote_addr.sa_data + 2));
    return (void *)connection;
#else
    return NULL:
#endif
}

void tcp_close(void *connection) {
    tcp_connection *tcp = (tcp_connection *)connection;
#if !defined(DEPENDENCY_SOCKETS)
#elif defined(QB64_WINDOWS)
    if (tcp->socket) {
        shutdown(tcp->socket, SD_BOTH);
        closesocket(tcp->socket);
    }
#elif defined(QB64_UNIX)
    if (tcp->socket) {
        shutdown(tcp->socket, SHUT_RDWR);
        close(tcp->socket);
    }
#endif
    if (tcp->hostname)
        free(tcp->hostname);
    free(tcp);
}

void tcp_out(void *connection, void *offset, ptrszint bytes) {
#if !defined(DEPENDENCY_SOCKETS)
#elif defined(QB64_WINDOWS) || defined(QB64_UNIX)
// Handle Windows which might not have this flag (it would be a no-op anyway)
#    if !defined(MSG_NOSIGNAL)
#        define MSG_NOSIGNAL 0
#    endif
    tcp_connection *tcp;
    tcp = (tcp_connection *)connection;
    int total = 0;         // how many bytes we've sent
    int bytesleft = bytes; // how many we have left to send
    int n;

    while (total < bytes) {
        n = send(tcp->socket, (char *)((char *)offset + total), bytesleft, MSG_NOSIGNAL);
        if (n < 0) {
            tcp->connected = 0;
            return;
        }
        total += n;
        bytesleft -= n;
    }
#else
#endif
}

struct connection_struct {
    int8 in_use;   // 0=not being used, 1=in use
    int8 protocol; // 1=TCP/IP
    int8 type;     // 1=client, 2=host(listening), 3=host's connection from a client
    ptrszint stream;
    ptrszint handle;
    void *connection;
    //---------------------------------
    int32 port;
};

list *connection_handles = NULL;

void stream_out(stream_struct *st, void *offset, ptrszint bytes) {
    if (st->type == stream_type::Tcp) { // Network
        static connection_struct *co;
        co = (connection_struct *)st->index;
        if ((co->type == 1) || (co->type == 3)) { // client or host's connection from a client

            if (co->protocol == 1) { // TCP/IP
                tcp_out((void *)co->connection, offset, bytes);
            }
        } // client or host's connection from a client
    } // Network
} // stream_out

void stream_update(stream_struct *stream) {
#ifdef DEPENDENCY_SOCKETS
    // assume tcp

    static connection_struct *connection;
    connection = (connection_struct *)(stream->index);
    static tcp_connection *tcp;
    tcp = (tcp_connection *)(connection->connection);
    static ptrszint bytes;

    if (!stream->in_limit) {
        stream->in = (uint8 *)malloc(1024);
        stream->in_size = 0;
        stream->in_limit = 1024;
    }

expand_and_retry:

    // expand buffer if 'in' stream is full
    // also guarantees that bytes requested from recv() is not 0
    if (stream->in_size == stream->in_limit) {
        stream->in_limit *= 2;
        stream->in = (uint8 *)realloc(stream->in, stream->in_limit);
    }

    bytes = recv(tcp->socket, (char *)(stream->in + stream->in_size), stream->in_limit - stream->in_size, 0);
    if (bytes < 0) { // some kind of error
#    ifdef QB64_WINDOWS
        if (WSAGetLastError() != WSAEWOULDBLOCK)
            tcp->connected = 0; // fatal error
#    else
        if (errno != EAGAIN && errno != EWOULDBLOCK)
            tcp->connected = 0;
#    endif
    } else if (bytes == 0) { // graceful shutdown occurred
        tcp->connected = 0;
    } else {
        stream->in_size += bytes;
        if (stream->in_size == stream->in_limit)
            goto expand_and_retry;
    }
#endif
}

void connection_close(ptrszint i) {
    // Note: 'i' is a positive integer 1 or greater
    //      'i' must be a valid handle
    static connection_struct *cs;
    static stream_struct *ss;
    static special_handle_struct *sh;
    sh = (special_handle_struct *)list_get(special_handles, i);

    switch (sh->type) {
    case special_handle_type::Stream:
        ss = (stream_struct *)sh->index;

        // FIXME: What if it's not Tcp??
        if (ss->type == stream_type::Tcp) { // network
            cs = (connection_struct *)ss->index;
            if (cs->protocol == 1)
                tcp_close(cs->connection);
            list_remove(connection_handles, list_get_index(connection_handles, cs));
            stream_free(ss);
            list_remove(special_handles, list_get_index(special_handles, sh));
            return;
        } // network
        break;

    case special_handle_type::Host:
        cs = (connection_struct *)sh->index;
        if (cs->protocol == 1)
            tcp_close(cs->connection);
        list_remove(connection_handles, list_get_index(connection_handles, cs));
        list_remove(special_handles, list_get_index(special_handles, sh));
        break;

    case special_handle_type::Http:
        libqb_http_close(i);
        list_remove(special_handles, list_get_index(special_handles, sh));
        break;

    case special_handle_type::Invalid:
        // TODO: Check if anything needs to be done here
        break;
    }
}

int32 connection_new(int32 method, qbs *info_in, int32 value) {
    // method: 0=_OPENCLIENT [info=~"TCP/IP:12345:23.96.32.123], value=NULL"
    //        1=_OPENHOST [info=~"TCP/IP:12345", value=NULL]
    //        2=_OPENCONNECTION [info=NULL, value=host's handle]
    // returns: -1=invalid arguments passed
    //          0=failed to open
    //         >0=handle of successfully opened connection

    static int32 i, x;

    // generic division of parts
    static qbs *info_part[10 + 1];
    static qbs *str;
    static qbs *strz;
    static qbs *info;

    static int32 first_call = 1;
    if (first_call) {
        first_call = 0;
        for (i = 1; i <= 10; i++) {
            info_part[i] = qbs_new(0, 0);
        }
        str = qbs_new(0, 0);
        strz = qbs_new(1, 0);
        strz->chr[0] = 0;
        info = qbs_new(0, 0);
    } // first call

    // split info string
    static int32 parts;
    parts = 0;
    if ((method == 0) || (method == 1)) {
        qbs_set(info, info_in);
        qbs_set(str, qbs_new_txt(":"));
        i = 1;

        // Split string on each ':'
        //
        // Make sure we don't create more splits than can fix in the array
        for (int k = 0; k < sizeof(info_part) / sizeof(*info_part) - 1; k++) {
            x = func_instr(i, info, str, 1);
            if (!x)
                break;

            parts++;
            qbs_set(info_part[parts], func_mid(info, i, x - i, 1));
            i = x + 1;
        }

        // Add the extra part after the last ':'
        parts++;
        qbs_set(info_part[parts], func_mid(info, i, NULL, NULL));
    } // split info string

    static double d;
    static int32 port;

    if ((method == 0) || (method == 1)) {
        if (method == 0 && parts >= 1 &&
            (qbs_equal(qbs_ucase(info_part[1]), qbs_new_txt("HTTP")) || qbs_equal(qbs_ucase(info_part[1]), qbs_new_txt("HTTPS")))) {

            // Strings given to curl have to be null terminated
            qbs_set(str, qbs_add(info, strz));

            const char *url = (const char *)str->chr;
            for (; *url && *url != ':'; url++)
                ;

            // Shouldn't happen, since parts >= 1
            if (!*url)
                return -1;

            // Skip the ':'
            url++;

            // Special case, if the next character is '/' then someone may have
            // just passed an http:// URL verbatim with no HTTP: prefix before
            // it.
            //
            // In this situation we'll just use the whole thing and hope that's
            // what they want.
            if (*url == '/')
                url = (const char *)str->chr;

            // Get the handle for this connection, we do this first because the
            // libqb_http logic uses the handle
            int32 my_handle = list_add(special_handles);

            int err = libqb_http_open(url, my_handle);

            if (err) {
                list_remove(special_handles, my_handle);
                // There's no good way to report the HTTP error here
                return 0;
            }

            special_handle_struct *my_handle_struct;
            my_handle_struct = (special_handle_struct *)list_get(special_handles, my_handle);

            my_handle_struct->type = special_handle_type::Http;
            my_handle_struct->index = 0; // Not used by Http clients

            return my_handle;
        }

        if (parts < 2)
            return -1;

        if (qbs_equal(qbs_ucase(info_part[1]), qbs_new_txt("TCP/IP")) == 0) {
            if (qbs_equal(qbs_ucase(info_part[1]), qbs_new_txt("QB64IDE")) == 0 || vwatch != -1) {
                return -1;
            }
        }

        d = qbs_val<long double>(info_part[2]);
        port = qbr_double_to_long(d); //***assume*** port number is within valid range

        if (method == 0) { //_OPENCLIENT
            if (parts != 3)
                return -1;

            static void *connection;
            qbs_set(str, qbs_add(info_part[3], strz));
            connection = tcp_client_open(str->chr, port);
            if (!connection)
                return 0;

            static int32 my_handle;
            my_handle = list_add(special_handles);
            static special_handle_struct *my_handle_struct;
            my_handle_struct = (special_handle_struct *)list_get(special_handles, my_handle);
            static int32 my_stream;
            my_stream = list_add(stream_handles);
            static stream_struct *my_stream_struct;
            my_stream_struct = (stream_struct *)list_get(stream_handles, my_stream);
            static int32 my_connection;
            my_connection = list_add(connection_handles);
            static connection_struct *my_connection_struct;
            my_connection_struct = (connection_struct *)list_get(connection_handles, my_connection);
            my_handle_struct->type = special_handle_type::Stream;
            my_handle_struct->index = (ptrszint)my_stream_struct;
            my_stream_struct->type = stream_type::Tcp; // network
            my_stream_struct->index = (ptrszint)my_connection_struct;
            my_connection_struct->protocol = 1; // tcp/ip
            my_connection_struct->type = 1;     // client
            my_connection_struct->connection = connection;
            my_connection_struct->port = port;

            // init stream
            my_stream_struct->in = NULL;
            my_stream_struct->in_size = 0;
            my_stream_struct->in_limit = 0;

            if (vwatch == -1)
                vwatch = my_handle;
            return my_handle;
        } // client

        if (method == 1) { //_OPENHOST
            if (parts != 2)
                return -1;

            static void *connection;
            connection = tcp_host_open(port);
            if (!connection)
                return 0;

            static int32 my_handle;
            my_handle = list_add(special_handles);
            static special_handle_struct *my_handle_struct;
            my_handle_struct = (special_handle_struct *)list_get(special_handles, my_handle);
            static int32 my_connection;
            my_connection = list_add(connection_handles);
            static connection_struct *my_connection_struct;
            my_connection_struct = (connection_struct *)list_get(connection_handles, my_connection);
            my_handle_struct->type = special_handle_type::Host;
            my_handle_struct->index = (ptrszint)my_connection_struct;
            my_connection_struct->protocol = 1; // tcp/ip
            my_connection_struct->type = 2;     // host(listening)
            my_connection_struct->connection = connection;
            my_connection_struct->port = port;
            return my_handle;
        }
    } // 0 or 1

    if (method == 2) { //_OPENCONNECTION
        static special_handle_struct *sh;
        sh = (special_handle_struct *)list_get(special_handles, value);
        if (!sh)
            return -1;
        if (sh->type != special_handle_type::Host)
            return -1; // listening host?
        static connection_struct *co;
        co = (connection_struct *)sh->index;
        static void *connection;
        connection = tcp_connection_open(co->connection);
        if (!connection)
            return 0;

        static int32 my_handle;
        my_handle = list_add(special_handles);
        static special_handle_struct *my_handle_struct;
        my_handle_struct = (special_handle_struct *)list_get(special_handles, my_handle);
        static int32 my_stream;
        my_stream = list_add(stream_handles);
        static stream_struct *my_stream_struct;
        my_stream_struct = (stream_struct *)list_get(stream_handles, my_stream);
        static int32 my_connection;
        my_connection = list_add(connection_handles);
        static connection_struct *my_connection_struct;
        my_connection_struct = (connection_struct *)list_get(connection_handles, my_connection);
        my_handle_struct->type = special_handle_type::Stream;
        my_handle_struct->index = (ptrszint)my_stream_struct;
        my_stream_struct->type = stream_type::Tcp; // network
        my_stream_struct->index = (ptrszint)my_connection_struct;
        my_connection_struct->protocol = 1; // tcp/ip
        my_connection_struct->type = 3;     // host's client connection
        my_connection_struct->connection = connection;
        my_connection_struct->port = port;

        // init stream
        my_stream_struct->in = NULL;
        my_stream_struct->in_size = 0;
        my_stream_struct->in_limit = 0;

        return my_handle;

    } //_OPENCONNECTION

    return -1;
} // connection_new

// network prototype:

int32 func__openclient(qbs *info) {
    if (is_error_pending())
        return 0;
    static int32 i;
    i = connection_new(0, info, NULL);
    if (i == -1) {
        error(5);
        return 0;
    }
    if (i == 0)
        return 0;
    return -1 - i;
}

int32 func__openhost(qbs *info) {
    if (is_error_pending())
        return 0;
    static int32 i;
    i = connection_new(1, info, NULL);
    if (i == -1) {
        error(5);
        return 0;
    }
    if (i == 0)
        return 0;
    return -1 - i;
}

int32 func__openconnection(int32 i) {

    if (is_error_pending())
        return 0;
    i = -(i + 1);
    i = connection_new(2, NULL, i);
    if (i == -1) {
        error(258);
        return 0;
    } // invalid handle
    if (i == 0)
        return 0; // no new connections
    return -1 - i;
}

qbs *func__connectionaddress(int32 i) {
    static qbs *tqbs, *tqbs2, *str = NULL, *str2 = NULL;
    static int32 x;
    if (is_error_pending())
        goto error;

#ifdef DEPENDENCY_SOCKETS
    if (!str)
        str = qbs_new(0, 0);
    if (!str2)
        str2 = qbs_new(0, 0);

    if (i < 0) {
        x = -(i + 1);
        static connection_struct *cs;
        static stream_struct *ss;
        static special_handle_struct *sh;
        const char *url;
        sh = (special_handle_struct *)list_get(special_handles, x);
        if (!sh) {
            error(52);
            goto error;
        }

        switch (sh->type) {
        case special_handle_type::Stream:
            ss = (stream_struct *)sh->index;
            if (ss->type == stream_type::Tcp) { // network
                static connection_struct *cs;
                cs = (connection_struct *)ss->index;
                if (cs->protocol == 1) {                  // TCP/IP
                    if (cs->type == 1 || cs->type == 3) { // 1=client, 2=host(listening), 3=host's connection from a client

                        static tcp_connection *tcp;
                        tcp = (tcp_connection *)cs->connection;
                        qbs_set(str, qbs_new_txt("TCP/IP:"));                      // network type
                        qbs_set(str, qbs_add(str, qbs_ltrim(qbs_str(tcp->port)))); // port
                        qbs_set(str, qbs_add(str, qbs_new_txt(":")));
                        if (cs->type == 3) {
                            qbs_set(str, qbs_add(str, qbs_ltrim(qbs_str(tcp->ip4[0])))); // ip
                            qbs_set(str, qbs_add(str, qbs_new_txt(".")));
                            qbs_set(str, qbs_add(str, qbs_ltrim(qbs_str(tcp->ip4[1])))); // ip
                            qbs_set(str, qbs_add(str, qbs_new_txt(".")));
                            qbs_set(str, qbs_add(str, qbs_ltrim(qbs_str(tcp->ip4[2])))); // ip
                            qbs_set(str, qbs_add(str, qbs_new_txt(".")));
                            qbs_set(str, qbs_add(str, qbs_ltrim(qbs_str(tcp->ip4[3])))); // ip
                        } else {
                            qbs_set(str, qbs_add(str, qbs_new_txt((char *)tcp->hostname)));
                        }
                        return str;
                    }
                } // TCP/IP
            } // network
            break;

        case special_handle_type::Host:
            cs = (connection_struct *)sh->index;
            if (cs->protocol == 1) {                                      // TCP/IP
                qbs_set(str, qbs_new_txt("TCP/IP:"));                     // network type
                qbs_set(str, qbs_add(str, qbs_ltrim(qbs_str(cs->port)))); // port
                qbs_set(str, qbs_add(str, qbs_new_txt(":")));
                tqbs2 = FUNC__WHATISMYIP();
                if (tqbs2->len) {
                    qbs_set(str, qbs_add(str, tqbs2));
                } else {
                    qbs_set(str, qbs_add(str, qbs_new_txt("127.0.0.1"))); // localhost
                }
                return str;
            } // TCP/IP
            break;

        case special_handle_type::Http:
            url = libqb_http_get_url(x);

            qbs_set(str, qbs_new_txt("HTTP:"));

            if (url)
                qbs_set(str, qbs_add(str, qbs_new_txt(url)));
            else
                qbs_set(str, qbs_add(str, qbs_new_txt("UNKNOWN")));

            return FUNC__DECODEURL(str);

            break;

        case special_handle_type::Invalid:
            // TODO: Check if anything needs to be done here
            break;
        }
    } // i<0
#endif
    error(52);

error:
    tqbs = qbs_new(0, 1);
    return tqbs;
}

int32 tcp_connected(void *connection) {
    tcp_connection *tcp = (tcp_connection *)connection;
#ifndef DEPENDENCY_SOCKETS
    return 0;
#else
    return tcp->connected;
#endif
}

int32 func__connected(int32 i) {
    if (is_error_pending())
        return 0;
    if (i < 0) {
        static int32 x;
        x = -(i + 1);
        static stream_struct *ss;
        static connection_struct *cs;
        static special_handle_struct *sh;
        sh = (special_handle_struct *)list_get(special_handles, x);
        if (!sh)
            goto error;

        switch (sh->type) {
        case special_handle_type::Stream:
            ss = (stream_struct *)sh->index;
            if (ss->type == stream_type::Tcp) { // network
                cs = (connection_struct *)ss->index;
                if (cs->protocol == 1) { // TCP/IP
                    return tcp_connected(cs->connection);
                } // TCP/IP
            } // network
            break;

        case special_handle_type::Host:
            cs = (connection_struct *)sh->index;
            if (cs->protocol == 1) { // TCP/IP
                return -1;
            } // TCP/IP
            break;

        case special_handle_type::Http:
            return libqb_http_connected(x) ? -1 : 0;
            break;

        case special_handle_type::Invalid:
            // TODO: Check if anything needs to be done here
            break;
        }

    } // i<0
error:
    error(52);
    return 0;
}

int32 func__exit() {
    exit_blocked = 1;
    static int32 x;
    x = exit_value;
    if (x)
        exit_value = 0;
    return x;
}

int32 display_called = 0;

void display_now() {
    if (autodisplay) {
        display_called = 0;
        while (!display_called)
            Sleep(1);
    } else {
        display();
    }
}

void sub__fullscreen(int32 method, int32 passed) {
    // ref: "[{_OFF|_STRETCH|_SQUAREPIXELS|OFF}][, _SMOOTH]"
    //          1      2           3        4         1
    int32 x;
    if (method == 0)
        x = 1;
    if (method == 1 || method == 4)
        x = 0;
    if (method == 2)
        x = 1;
    if (method == 3)
        x = 2;
    if (passed & 1)
        fullscreen_smooth = 1;
    else
        fullscreen_smooth = 0;
    if (full_screen != x)
        full_screen_set = x;
    force_display_update = 1;
}

void sub__allowfullscreen(int32 method, int32 smooth) {
    // ref: "[{_STRETCH|_SQUAREPIXELS|_OFF|_ALL|OFF}][, _SMOOTH|_OFF|_ALL|OFF]"
    //            1          2         3    4   5         1      2    3   4

    fullscreen_allowedmode = method;
    if (method == 3 || method == 5)
        fullscreen_allowedmode = -1;
    if (method == 4 || method == 0)
        fullscreen_allowedmode = 0;

    fullscreen_allowedsmooth = smooth;
    if (smooth == 2 || smooth == 4)
        fullscreen_allowedsmooth = -1;
    if (smooth == 3 || smooth == 0)
        fullscreen_allowedsmooth = 0;
}

int32 func__fullscreen() {
    static int32 x;
    x = full_screen_set;
    if (x != -1)
        return x;
    return full_screen;
}

int32 func__fullscreensmooth() {
    return -fullscreen_smooth;
}

void chain_restorescreenstate(int32 i) {
    static int32 i32, i32b, i32c, x, x2;
    generic_get(i, -1, (uint8 *)&i32, 4);

    if (i32 == 256) {
        generic_get(i, -1, (uint8 *)&i32, 4);
        if (i32 != 0)
            qbg_screen(i32, 0, 0, 0, 0, 1);
        generic_get(i, -1, (uint8 *)&i32, 4);
        if (i32 == 258) {
            generic_get(i, -1, (uint8 *)&i32, 4);
            i32b = i32;
            generic_get(i, -1, (uint8 *)&i32, 4);
            qbsub_width(0, i32b, i32, 0, 0, 1 + 2);
            generic_get(i, -1, (uint8 *)&i32, 4);
        }
    }

    if (i32 == 257) {
        generic_get(i, -1, (uint8 *)&i32, 4);
        i32c = i32;
        generic_get(i, -1, (uint8 *)&i32, 4);
        i32b = i32;
        generic_get(i, -1, (uint8 *)&i32, 4);
        qbg_screen(func__newimage(i32b, i32, i32c, 1), 0, 0, 0, 0, 1);
        generic_get(i, -1, (uint8 *)&i32, 4);
    }

    if (i32 == 259) {
        generic_get(i, -1, (uint8 *)&i32, 4);
        sub__font(i32, 0, 0);
        generic_get(i, -1, (uint8 *)&i32, 4);
    }

    static img_struct *ix;
    static img_struct imgs;

    while (i32 == 260) {
        generic_get(i, -1, (uint8 *)&i32, 4);
        x = i32;
        qbg_screen(0, 0, x, 0, 0, 4 + 8); // switch to page (allocates the page)
        ix = &img[page[x]];
        generic_get(i, -1, ix->offset, ix->width * ix->height * ix->bytes_per_pixel);
        imgs = *ix;
        generic_get(i, -1, (uint8 *)ix, sizeof(img_struct));
        // revert specific data
        if (ix->font >= 32)
            ix->font = imgs.font;
        ix->offset = imgs.offset;
        ix->pal = imgs.pal;
        generic_get(i, -1, (uint8 *)&i32, 4);
    }

    if (i32 == 261) {
        generic_get(i, -1, (uint8 *)&i32, 4);
        i32b = i32;
        generic_get(i, -1, (uint8 *)&i32, 4);
        qbg_screen(0, 0, i32b, i32, 0, 4 + 8); // switch to correct active & visual pages
        generic_get(i, -1, (uint8 *)&i32, 4);
    }

    if (i32 == 262) {
        for (x = 0; x <= 255; x++) {
            generic_get(i, -1, (uint8 *)&i32, 4);
            sub__palettecolor(x, i32, 0, 1);
        }
        generic_get(i, -1, (uint8 *)&i32, 4);
    }

    // assume command #511("finished screen state") in i32
}

void chain_savescreenstate(int32 i) { // adds the screen state to file #i
    static int32 i32, x, x2;
    static img_struct *i0, *ix;
    i0 = &img[page[0]];

    if ((i0->offset > cmem) && (i0->offset < (cmem + 1114099))) { // cmem?[need to maintain cmem state]
        //[256][mode]
        i32 = 256;
        generic_put(i, -1, (uint8 *)&i32, 4);
        i32 = i0->compatible_mode;
        generic_put(i, -1, (uint8 *)&i32, 4);
        if (i0->text) {
            //[258][WIDTH:X][Y]
            i32 = 258;
            generic_put(i, -1, (uint8 *)&i32, 4);
            i32 = i0->width;
            generic_put(i, -1, (uint8 *)&i32, 4);
            i32 = i0->height;
            generic_put(i, -1, (uint8 *)&i32, 4);
        }
    } else {
        //[257][mode][X][Y]
        i32 = 257;
        generic_put(i, -1, (uint8 *)&i32, 4);
        i32 = i0->compatible_mode;
        generic_put(i, -1, (uint8 *)&i32, 4);
        i32 = i0->width;
        generic_put(i, -1, (uint8 *)&i32, 4);
        i32 = i0->height;
        generic_put(i, -1, (uint8 *)&i32, 4);
    }
    //[259][font] (standard fonts only)
    if (i0->font < 32) {
        i32 = 259;
        generic_put(i, -1, (uint8 *)&i32, 4);
        i32 = i0->font;
        generic_put(i, -1, (uint8 *)&i32, 4);
    }

    //[260][page][rawdata]
    // note: write page is done last to avoid having its values undone by the later page switch
    x2 = -1;
    for (x = 0; x < pages; x++) {
        if (page[x]) {
            if (page[x] != write_page_index) {
            save_write_page:
                ix = &img[page[x]];
                i32 = 260;
                generic_put(i, -1, (uint8 *)&i32, 4);
                i32 = x;
                generic_put(i, -1, (uint8 *)&i32, 4);
                generic_put(i, -1, ix->offset, ix->width * ix->height * ix->bytes_per_pixel);
                // save structure (specific parts will be reincorporated)
                generic_put(i, -1, (uint8 *)ix, sizeof(img_struct));
                if (x == x2)
                    break;
            } else
                x2 = x;
        }
    }
    if ((x2 != -1) && (x != x2)) {
        x = x2;
        goto save_write_page;
    }

    //[261][activepage][visualpage]
    i32 = 261;
    generic_put(i, -1, (uint8 *)&i32, 4);
    i32 = 0; // note: activepage could be a non-screenpage
    for (x = 0; x < pages; x++) {
        if (page[x] == write_page_index) {
            i32 = x;
            break;
        }
    }
    generic_put(i, -1, (uint8 *)&i32, 4);
    i32 = 0;
    for (x = 0; x < pages; x++) {
        if (page[x] == display_page_index) {
            i32 = x;
            break;
        }
    }
    generic_put(i, -1, (uint8 *)&i32, 4);

    //[262][256x32-bit color palette]
    if (i0->bytes_per_pixel != 4) {
        i32 = 262;
        generic_put(i, -1, (uint8 *)&i32, 4);
        for (x = 0; x <= 255; x++) {
            i32 = func__palettecolor(x, 0, 1);
            generic_put(i, -1, (uint8 *)&i32, 4);
        }
    }

    //[511](screen state finished)
    i32 = 511;
    generic_put(i, -1, (uint8 *)&i32, 4);

} // chain_savescreenstate

void sub_lock(int32 i, int64 start, int64 end, int32 passed) {
    if (is_error_pending())
        return;
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);

    // If the file has been opened for sequential input or output, LOCK and UNLOCK affect the entire file, regardless of the range specified by start& and end&.
    if (gfs->type > 2)
        passed = 0;

    if (passed & 1) {
        start--;
        if (start < 0) {
            error(5);
            return;
        }
        if (gfs->type == 1)
            start *= gfs->record_length;
    } else {
        start = -1;
    }

    if (passed & 2) {
        end--;
        if (end < 0) {
            error(5);
            return;
        }
        if (gfs->type == 1)
            end = end * gfs->record_length + gfs->record_length - 1;
    } else {
        end = start;
        if (gfs->type == 1)
            end = start + gfs->record_length - 1;
        if (!(passed & 1))
            end = -1;
    }

    int32 e;
    e = gfs_lock(i, start, end);
    if (e) {
        if (e == -2) {
            error(258);
            return;
        } // invalid handle
        if (e == -4) {
            error(5);
            return;
        } // illegal function call
        if (e == -7) {
            error(70);
            return;
        } // permission denied
        error(75);
        return; // assume[-9]: path/file access error
    }
}

void sub_unlock(int32 i, int64 start, int64 end, int32 passed) {
    if (is_error_pending())
        return;
    if (gfs_fileno_valid(i) != 1) {
        error(52);
        return;
    } // Bad file name or number
    i = gfs_get_fileno(i); // convert fileno to gfs index
    static gfs_file_struct *gfs;
    gfs = gfs_get_file_struct(i);

    // If the file has been opened for sequential input or output, LOCK and UNLOCK affect the entire file, regardless of the range specified by start& and end&.
    if (gfs->type > 2)
        passed = 0;

    if (passed & 1) {
        start--;
        if (start < 0) {
            error(5);
            return;
        }
        if (gfs->type == 1)
            start *= gfs->record_length;
    } else {
        start = -1;
    }

    if (passed & 2) {
        end--;
        if (end < 0) {
            error(5);
            return;
        }
        if (gfs->type == 1)
            end = end * gfs->record_length + gfs->record_length - 1;
    } else {
        end = start;
        if (gfs->type == 1)
            end = start + gfs->record_length - 1;
        if (!(passed & 1))
            end = -1;
    }

    int32 e;
    e = gfs_unlock(i, start, end);
    if (e) {
        if (e == -2) {
            error(258);
            return;
        } // invalid handle
        if (e == -4) {
            error(5);
            return;
        } // illegal function call
        if (e == -7) {
            error(70);
            return;
        } // permission denied
        error(75);
        return; // assume[-9]: path/file access error
    }
}

#ifdef DEPENDENCY_SCREENIMAGE
int32 func__screenimage(int32 x1, int32 y1, int32 x2, int32 y2, int32 passed) {

#    ifdef QB64_WINDOWS

    static int32 init = 0;
    static int32 x, y, w, h;

    static HWND hwnd;
    static RECT rect;
    static HDC hdc;

    if (!init) {
        hwnd = GetDesktopWindow();
        GetWindowRect(hwnd, &rect);
        x = rect.right - rect.left;
        y = rect.bottom - rect.top;
    }

    hdc = GetDC(NULL);

    static HDC hdc2 = NULL;
    static HBITMAP bitmap;
    static int32 bx, by;

    if (passed) {
        if (x1 < 0)
            x1 = 0;
        if (y1 < 0)
            y1 = 0;
        if (x2 > x - 1)
            x2 = x - 1;
        if (y2 > y - 1)
            y2 = y - 1;
        w = x2 - x1 + 1;
        h = y2 - y1 + 1;
    } else {
        x1 = 0;
        x2 = x - 1;
        y1 = 0;
        y2 = y - 1;
        w = x;
        h = y;
    }

    if (hdc2) {
        if ((w != bx) || (h != by)) {
            DeleteObject(bitmap);
            ReleaseDC(NULL, hdc2);
            hdc2 = CreateCompatibleDC(hdc);
            bitmap = CreateCompatibleBitmap(hdc, w, h);
            bx = w;
            by = h;
            SelectObject(hdc2, bitmap);
        }
    } else {
        hdc2 = CreateCompatibleDC(hdc);
        bitmap = CreateCompatibleBitmap(hdc, w, h);
        bx = w;
        by = h;
        SelectObject(hdc2, bitmap);
    }

    init = 1;

    BitBlt(hdc2, 0, 0, w, h, hdc, x1, y1, SRCCOPY);

    static BITMAPFILEHEADER bmfHeader;
    static BITMAPINFOHEADER bi;
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = w;
    bi.biHeight = -h; // A bottom-up DIB is specified by setting the height to a positive number, while a top-down DIB is specified by setting the height to a
                      // negative number. The bitmap color table will be appended to the BITMAPINFO structure.
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    static int32 i, i2;
    i2 = func__dest();
    i = func__newimage(w, h, 32, 1);
    sub__dest(i);

    GetDIBits(hdc2, bitmap, 0, h, write_page->offset, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
    sub__setalpha(255, NULL, NULL, NULL, 0);
    sub__dest(i2);

    ReleaseDC(NULL, hdc);
    return i;
#    else
    return func__newimage(func__desktopwidth(), func__desktopheight(), 32, 1);
#    endif
}
#endif // DEPENDENCY_SCREENIMAGE

void sub__screenclick(int32 x, int32 y, int32 button, int32 passed) {

#ifdef QB64_WINDOWS

    static INPUT input;

    ZeroMemory(&input, sizeof(INPUT));
    input.type = INPUT_MOUSE;
    input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
    static HWND hwnd;
    hwnd = GetDesktopWindow();
    static RECT rect;
    GetWindowRect(hwnd, &rect);
    static double x2, y2, fx, fy;
    x2 = rect.right - rect.left;
    y2 = rect.bottom - rect.top;
    fx = x * (65535.0f / x2);
    fy = y * (65535.0f / y2);
    input.mi.dx = fx;
    input.mi.dy = fy;
    SendInput(1, &input, sizeof(INPUT));

    ZeroMemory(&input, sizeof(INPUT));
    input.type = INPUT_MOUSE;
    input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    SendInput(1, &input, sizeof(INPUT));

    ZeroMemory(&input, sizeof(INPUT));
    input.type = INPUT_MOUSE;

    if (passed) {
        if (button == 1) {
            input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
        }
        if (button == 2) {
            input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
        }
        if (button == 3) {
            input.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
        }
        SendInput(1, &input, sizeof(INPUT));

        ZeroMemory(&input, sizeof(INPUT));
        input.type = INPUT_MOUSE;

        if (button == 1) {
            input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
        }
        if (button == 2) {
            input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
        }
        if (button == 3) {
            input.mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
        }
    } else {
        input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;

        SendInput(1, &input, sizeof(INPUT));

        ZeroMemory(&input, sizeof(INPUT));
        input.type = INPUT_MOUSE;

        input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    }
    SendInput(1, &input, sizeof(INPUT));

#endif

#ifdef QB64_MACOSX
    CGEventRef click1_down = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, CGPointMake(x, y), kCGMouseButtonLeft);
    CGEventRef click1_up = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, CGPointMake(x, y), kCGMouseButtonLeft);
    CGEventPost(kCGHIDEventTap, click1_down);
    CGEventPost(kCGHIDEventTap, click1_up);
    CFRelease(click1_up);
    CFRelease(click1_down);
#endif
}

#ifdef QB64_MACOSX
// 128 indicates SHIFT must be held to achieve the indexed ASCII character
static uint16 ASCII_TO_MACVK[] = {0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0x3300 + 8,
                                  0x3000 + 9,
                                  0,
                                  0,
                                  0,
                                  0x2400 + 13,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0x3500 + 27,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0x3100 + 32,
                                  0x1200 + 33 + 128,
                                  0x2700 + 34 + 128,
                                  0x1400 + 35 + 128,
                                  0x1500 + 36 + 128,
                                  0x1700 + 37 + 128,
                                  0x1A00 + 38 + 128,
                                  0x2700 + 39,
                                  0x1900 + 40 + 128,
                                  0x1D00 + 41 + 128,
                                  0x1C00 + 42 + 128,
                                  0x1800 + 43 + 128,
                                  0x2B00 + 44,
                                  0x1B00 + 45,
                                  0x2F00 + 46,
                                  0x2C00 + 47,
                                  0x1D00 + 48,
                                  0x1200 + 49,
                                  0x1300 + 50,
                                  0x1400 + 51,
                                  0x1500 + 52,
                                  0x1700 + 53,
                                  0x1600 + 54,
                                  0x1A00 + 55,
                                  0x1C00 + 56,
                                  0x1900 + 57,
                                  0x2900 + 58 + 128,
                                  0x2900 + 59,
                                  0x2B00 + 60 + 128,
                                  0x1800 + 61,
                                  0x2F00 + 62 + 128,
                                  0x2C00 + 63 + 128,
                                  0x1300 + 64 + 128,
                                  0x0000 + 65 + 128,
                                  0x0B00 + 66 + 128,
                                  0x0800 + 67 + 128,
                                  0x0200 + 68 + 128,
                                  0x0E00 + 69 + 128,
                                  0x0300 + 70 + 128,
                                  0x0500 + 71 + 128,
                                  0x0400 + 72 + 128,
                                  0x2200 + 73 + 128,
                                  0x2600 + 74 + 128,
                                  0x2800 + 75 + 128,
                                  0x2500 + 76 + 128,
                                  0x2E00 + 77 + 128,
                                  0x2D00 + 78 + 128,
                                  0x1F00 + 79 + 128,
                                  0x2300 + 80 + 128,
                                  0x0C00 + 81 + 128,
                                  0x0F00 + 82 + 128,
                                  0x0100 + 83 + 128,
                                  0x1100 + 84 + 128,
                                  0x2000 + 85 + 128,
                                  0x0900 + 86 + 128,
                                  0x0D00 + 87 + 128,
                                  0x0700 + 88 + 128,
                                  0x1000 + 89 + 128,
                                  0x0600 + 90 + 128,
                                  0x2100 + 91,
                                  0x2A00 + 92,
                                  0x1E00 + 93,
                                  0x1600 + 94 + 128,
                                  0x1B00 + 95 + 128,
                                  0x3200 + 96,
                                  0x0000 + 65 + 32,
                                  0x0B00 + 66 + 32,
                                  0x0800 + 67 + 32,
                                  0x0200 + 68 + 32,
                                  0x0E00 + 69 + 32,
                                  0x0300 + 70 + 32,
                                  0x0500 + 71 + 32,
                                  0x0400 + 72 + 32,
                                  0x2200 + 73 + 32,
                                  0x2600 + 74 + 32,
                                  0x2800 + 75 + 32,
                                  0x2500 + 76 + 32,
                                  0x2E00 + 77 + 32,
                                  0x2D00 + 78 + 32,
                                  0x1F00 + 79 + 32,
                                  0x2300 + 80 + 32,
                                  0x0C00 + 81 + 32,
                                  0x0F00 + 82 + 32,
                                  0x0100 + 83 + 32,
                                  0x1100 + 84 + 32,
                                  0x2000 + 85 + 32,
                                  0x0900 + 86 + 32,
                                  0x0D00 + 87 + 32,
                                  0x0700 + 88 + 32,
                                  0x1000 + 89 + 32,
                                  0x0600 + 90 + 32,
                                  0x2100 + 123 + 128,
                                  0x2A00 + 124 + 128,
                                  0x1E00 + 125 + 128,
                                  0x3200 + 126 + 128,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0,
                                  0};
#endif

int32 MACVK_ANSI_A = 0x00;
int32 MACVK_ANSI_S = 0x01;
int32 MACVK_ANSI_D = 0x02;
int32 MACVK_ANSI_F = 0x03;
int32 MACVK_ANSI_H = 0x04;
int32 MACVK_ANSI_G = 0x05;
int32 MACVK_ANSI_Z = 0x06;
int32 MACVK_ANSI_X = 0x07;
int32 MACVK_ANSI_C = 0x08;
int32 MACVK_ANSI_V = 0x09;
int32 MACVK_ANSI_B = 0x0B;
int32 MACVK_ANSI_Q = 0x0C;
int32 MACVK_ANSI_W = 0x0D;
int32 MACVK_ANSI_E = 0x0E;
int32 MACVK_ANSI_R = 0x0F;
int32 MACVK_ANSI_Y = 0x10;
int32 MACVK_ANSI_T = 0x11;
int32 MACVK_ANSI_1 = 0x12;
int32 MACVK_ANSI_2 = 0x13;
int32 MACVK_ANSI_3 = 0x14;
int32 MACVK_ANSI_4 = 0x15;
int32 MACVK_ANSI_6 = 0x16;
int32 MACVK_ANSI_5 = 0x17;
int32 MACVK_ANSI_Equal = 0x18;
int32 MACVK_ANSI_9 = 0x19;
int32 MACVK_ANSI_7 = 0x1A;
int32 MACVK_ANSI_Minus = 0x1B;
int32 MACVK_ANSI_8 = 0x1C;
int32 MACVK_ANSI_0 = 0x1D;
int32 MACVK_ANSI_RightBracket = 0x1E;
int32 MACVK_ANSI_O = 0x1F;
int32 MACVK_ANSI_U = 0x20;
int32 MACVK_ANSI_LeftBracket = 0x21;
int32 MACVK_ANSI_I = 0x22;
int32 MACVK_ANSI_P = 0x23;
int32 MACVK_ANSI_L = 0x25;
int32 MACVK_ANSI_J = 0x26;
int32 MACVK_ANSI_Quote = 0x27;
int32 MACVK_ANSI_K = 0x28;
int32 MACVK_ANSI_Semicolon = 0x29;
int32 MACVK_ANSI_Backslash = 0x2A;
int32 MACVK_ANSI_Comma = 0x2B;
int32 MACVK_ANSI_Slash = 0x2C;
int32 MACVK_ANSI_N = 0x2D;
int32 MACVK_ANSI_M = 0x2E;
int32 MACVK_ANSI_Period = 0x2F;
int32 MACVK_ANSI_Grave = 0x32;
int32 MACVK_ANSI_KeypadDecimal = 0x41;
int32 MACVK_ANSI_KeypadMultiply = 0x43;
int32 MACVK_ANSI_KeypadPlus = 0x45;
int32 MACVK_ANSI_KeypadClear = 0x47;
int32 MACVK_ANSI_KeypadDivide = 0x4B;
int32 MACVK_ANSI_KeypadEnter = 0x4C;
int32 MACVK_ANSI_KeypadMinus = 0x4E;
int32 MACVK_ANSI_KeypadEquals = 0x51;
int32 MACVK_ANSI_Keypad0 = 0x52;
int32 MACVK_ANSI_Keypad1 = 0x53;
int32 MACVK_ANSI_Keypad2 = 0x54;
int32 MACVK_ANSI_Keypad3 = 0x55;
int32 MACVK_ANSI_Keypad4 = 0x56;
int32 MACVK_ANSI_Keypad5 = 0x57;
int32 MACVK_ANSI_Keypad6 = 0x58;
int32 MACVK_ANSI_Keypad7 = 0x59;
int32 MACVK_ANSI_Keypad8 = 0x5B;
int32 MACVK_ANSI_Keypad9 = 0x5C;
int32 MACVK_Return = 0x24;
int32 MACVK_Tab = 0x30;
int32 MACVK_Space = 0x31;
int32 MACVK_Delete = 0x33;
int32 MACVK_Escape = 0x35;
int32 MACVK_Command = 0x37;
int32 MACVK_Shift = 0x38;
int32 MACVK_CapsLock = 0x39;
int32 MACVK_Option = 0x3A;
int32 MACVK_Control = 0x3B;
int32 MACVK_RightShift = 0x3C;
int32 MACVK_RightOption = 0x3D;
int32 MACVK_RightControl = 0x3E;
int32 MACVK_Function = 0x3F;
int32 MACVK_F17 = 0x40;
int32 MACVK_VolumeUp = 0x48;
int32 MACVK_VolumeDown = 0x49;
int32 MACVK_Mute = 0x4A;
int32 MACVK_F18 = 0x4F;
int32 MACVK_F19 = 0x50;
int32 MACVK_F20 = 0x5A;
int32 MACVK_F5 = 0x60;
int32 MACVK_F6 = 0x61;
int32 MACVK_F7 = 0x62;
int32 MACVK_F3 = 0x63;
int32 MACVK_F8 = 0x64;
int32 MACVK_F9 = 0x65;
int32 MACVK_F11 = 0x67;
int32 MACVK_F13 = 0x69;
int32 MACVK_F16 = 0x6A;
int32 MACVK_F14 = 0x6B;
int32 MACVK_F10 = 0x6D;
int32 MACVK_F12 = 0x6F;
int32 MACVK_F15 = 0x71;
int32 MACVK_Help = 0x72;
int32 MACVK_Home = 0x73;
int32 MACVK_PageUp = 0x74;
int32 MACVK_ForwardDelete = 0x75;
int32 MACVK_F4 = 0x76;
int32 MACVK_End = 0x77;
int32 MACVK_F2 = 0x78;
int32 MACVK_PageDown = 0x79;
int32 MACVK_F1 = 0x7A;
int32 MACVK_LeftArrow = 0x7B;
int32 MACVK_RightArrow = 0x7C;
int32 MACVK_DownArrow = 0x7D;
int32 MACVK_UpArrow = 0x7E;
int32 MACVK_ISO_Section = 0x0A;
int32 MACVK_JIS_Yen = 0x5D;
int32 MACVK_JIS_Underscore = 0x5E;
int32 MACVK_JIS_KeypadComma = 0x5F;
int32 MACVK_JIS_Eisu = 0x66;
int32 MACVK_JIS_Kana = 0x68;

void sub__screenprint(qbs *txt) {

    static int32 i, s, x, vk, c;

#ifdef QB64_MACOSX
    /* MACOSX virtual key code reference (with ASCII value & shift state):
        static int32 MACVK_ANSI_A                    = 0x0000+65+32;
        static int32 MACVK_ANSI_S                    = 0x0100+83+32;
        static int32 MACVK_ANSI_D                    = 0x0200+68+32;
        static int32 MACVK_ANSI_F                    = 0x0300+70+32;
        static int32 MACVK_ANSI_H                    = 0x0400+72+32;
        static int32 MACVK_ANSI_G                    = 0x0500+71+32;
        static int32 MACVK_ANSI_Z                    = 0x0600+90+32;
        static int32 MACVK_ANSI_X                    = 0x0700+88+32;
        static int32 MACVK_ANSI_C                    = 0x0800+67+32;
        static int32 MACVK_ANSI_V                    = 0x0900+86+32;
        static int32 MACVK_ANSI_B                    = 0x0B00+66+32;
        static int32 MACVK_ANSI_Q                    = 0x0C00+81+32;
        static int32 MACVK_ANSI_W                    = 0x0D00+87+32;
        static int32 MACVK_ANSI_E                    = 0x0E00+69+32;
        static int32 MACVK_ANSI_R                    = 0x0F00+82+32;
        static int32 MACVK_ANSI_Y                    = 0x1000+89+32;
        static int32 MACVK_ANSI_T                    = 0x1100+84+32;
        static int32 MACVK_ANSI_1                    = 0x1200+49;
        static int32 MACVK_ANSI_2                    = 0x1300+50;
        static int32 MACVK_ANSI_3                    = 0x1400+51;
        static int32 MACVK_ANSI_4                    = 0x1500+52;
        static int32 MACVK_ANSI_6                    = 0x1600+54;
        static int32 MACVK_ANSI_5                    = 0x1700+53;
        static int32 MACVK_ANSI_Equal                = 0x1800+61;
        static int32 MACVK_ANSI_9                    = 0x1900+57;
        static int32 MACVK_ANSI_7                    = 0x1A00+55;
        static int32 MACVK_ANSI_Minus                = 0x1B00+45;
        static int32 MACVK_ANSI_8                    = 0x1C00+56;
        static int32 MACVK_ANSI_0                    = 0x1D00+48;
        static int32 MACVK_ANSI_RightBracket         = 0x1E00+93;
        static int32 MACVK_ANSI_O                    = 0x1F00+79+32;
        static int32 MACVK_ANSI_U                    = 0x2000+85+32;
        static int32 MACVK_ANSI_LeftBracket          = 0x2100+91;
        static int32 MACVK_ANSI_I                    = 0x2200+73+32;
        static int32 MACVK_ANSI_P                    = 0x2300+80+32;
        static int32 MACVK_ANSI_L                    = 0x2500+76+32;
        static int32 MACVK_ANSI_J                    = 0x2600+74+32;
        static int32 MACVK_ANSI_Quote                = 0x2700+39;
        static int32 MACVK_ANSI_K                    = 0x2800+75+32;
        static int32 MACVK_ANSI_Semicolon            = 0x2900+59;
        static int32 MACVK_ANSI_Backslash            = 0x2A00+92;
        static int32 MACVK_ANSI_Comma                = 0x2B00+44;
        static int32 MACVK_ANSI_Slash                = 0x2C00+47;
        static int32 MACVK_ANSI_N                    = 0x2D00+78+32;
        static int32 MACVK_ANSI_M                    = 0x2E00+77+32;
        static int32 MACVK_ANSI_Period               = 0x2F00+46;
        static int32 MACVK_ANSI_Grave                = 0x3200+96;
        static int32 MACVK_ANSI_KeypadDecimal        = 0x4100;
        static int32 MACVK_ANSI_KeypadMultiply       = 0x4300;
        static int32 MACVK_ANSI_KeypadPlus           = 0x4500;
        static int32 MACVK_ANSI_KeypadClear          = 0x4700;
        static int32 MACVK_ANSI_KeypadDivide         = 0x4B00;
        static int32 MACVK_ANSI_KeypadEnter          = 0x4C00;
        static int32 MACVK_ANSI_KeypadMinus          = 0x4E00;
        static int32 MACVK_ANSI_KeypadEquals         = 0x5100;
        static int32 MACVK_ANSI_Keypad0              = 0x5200;
        static int32 MACVK_ANSI_Keypad1              = 0x5300;
        static int32 MACVK_ANSI_Keypad2              = 0x5400;
        static int32 MACVK_ANSI_Keypad3              = 0x5500;
        static int32 MACVK_ANSI_Keypad4              = 0x5600;
        static int32 MACVK_ANSI_Keypad5              = 0x5700;
        static int32 MACVK_ANSI_Keypad6              = 0x5800;
        static int32 MACVK_ANSI_Keypad7              = 0x5900;
        static int32 MACVK_ANSI_Keypad8              = 0x5B00;
        static int32 MACVK_ANSI_Keypad9              = 0x5C00;
        static int32 MACVK_Return                    = 0x2400+13;
        static int32 MACVK_Tab                       = 0x3000+9;
        static int32 MACVK_Space                     = 0x3100+32;
        static int32 MACVK_Delete                    = 0x3300+8;
        static int32 MACVK_Escape                    = 0x3500+27;
        static int32 MACVK_Command                   = 0x3700;
        static int32 MACVK_Shift                     = 0x3800;
        static int32 MACVK_CapsLock                  = 0x3900;
        static int32 MACVK_Option                    = 0x3A00;
        static int32 MACVK_Control                   = 0x3B00;
        static int32 MACVK_RightShift                = 0x3C00;
        static int32 MACVK_RightOption               = 0x3D00;
        static int32 MACVK_RightControl              = 0x3E00;
        static int32 MACVK_Function                  = 0x3F00;
        static int32 MACVK_F17                       = 0x4000;
        static int32 MACVK_VolumeUp                  = 0x4800;
        static int32 MACVK_VolumeDown                = 0x4900;
        static int32 MACVK_Mute                      = 0x4A00;
        static int32 MACVK_F18                       = 0x4F00;
        static int32 MACVK_F19                       = 0x5000;
        static int32 MACVK_F20                       = 0x5A00;
        static int32 MACVK_F5                        = 0x6000;
        static int32 MACVK_F6                        = 0x6100;
        static int32 MACVK_F7                        = 0x6200;
        static int32 MACVK_F3                        = 0x6300;
        static int32 MACVK_F8                        = 0x6400;
        static int32 MACVK_F9                        = 0x6500;
        static int32 MACVK_F11                       = 0x6700;
        static int32 MACVK_F13                       = 0x6900;
        static int32 MACVK_F16                       = 0x6A00;
        static int32 MACVK_F14                       = 0x6B00;
        static int32 MACVK_F10                       = 0x6D00;
        static int32 MACVK_F12                       = 0x6F00;
        static int32 MACVK_F15                       = 0x7100;
        static int32 MACVK_Help                      = 0x7200;
        static int32 MACVK_Home                      = 0x7300;
        static int32 MACVK_PageUp                    = 0x7400;
        static int32 MACVK_ForwardDelete             = 0x7500;
        static int32 MACVK_F4                        = 0x7600;
        static int32 MACVK_End                       = 0x7700;
        static int32 MACVK_F2                        = 0x7800;
        static int32 MACVK_PageDown                  = 0x7900;
        static int32 MACVK_F1                        = 0x7A00;
        static int32 MACVK_LeftArrow                 = 0x7B00;
        static int32 MACVK_RightArrow                = 0x7C00;
        static int32 MACVK_DownArrow                 = 0x7D00;
        static int32 MACVK_UpArrow                   = 0x7E00;
        static int32 MACVK_ISO_Section               = 0x0A00;
        static int32 MACVK_JIS_Yen                   = 0x5D00;
        static int32 MACVK_JIS_Underscore            = 0x5E00;
        static int32 MACVK_JIS_KeypadComma           = 0x5F00;
        static int32 MACVK_JIS_Eisu                  = 0x6600;
        static int32 MACVK_JIS_Kana                  = 0x6800;
        static int32 MACVKS_ANSI_A                    = 0x0000+65+128;
        static int32 MACVKS_ANSI_S                    = 0x0100+83+128;
        static int32 MACVKS_ANSI_D                    = 0x0200+68+128;
        static int32 MACVKS_ANSI_F                    = 0x0300+70+128;
        static int32 MACVKS_ANSI_H                    = 0x0400+72+128;
        static int32 MACVKS_ANSI_G                    = 0x0500+71+128;
        static int32 MACVKS_ANSI_Z                    = 0x0600+90+128;
        static int32 MACVKS_ANSI_X                    = 0x0700+88+128;
        static int32 MACVKS_ANSI_C                    = 0x0800+67+128;
        static int32 MACVKS_ANSI_V                    = 0x0900+86+128;
        static int32 MACVKS_ANSI_B                    = 0x0B00+66+128;
        static int32 MACVKS_ANSI_Q                    = 0x0C00+81+128;

        static int32 MACVKS_ANSI_W                    = 0x0D00+87+128;
        static int32 MACVKS_ANSI_E                    = 0x0E00+69+128;
        static int32 MACVKS_ANSI_R                    = 0x0F00+82+128;
        static int32 MACVKS_ANSI_Y                    = 0x1000+89+128;
        static int32 MACVKS_ANSI_T                    = 0x1100+84+128;
        static int32 MACVKS_ANSI_1                    = 0x1200+33+128;
        static int32 MACVKS_ANSI_2                    = 0x1300+64+128;
        static int32 MACVKS_ANSI_3                    = 0x1400+35+128;
        static int32 MACVKS_ANSI_4                    = 0x1500+36+128;
        static int32 MACVKS_ANSI_6                    = 0x1600+94+128;
        static int32 MACVKS_ANSI_5                    = 0x1700+37+128;
        static int32 MACVKS_ANSI_Equal                = 0x1800+43+128;
        static int32 MACVKS_ANSI_9                    = 0x1900+40+128;
        static int32 MACVKS_ANSI_7                    = 0x1A00+38+128;
        static int32 MACVKS_ANSI_Minus                = 0x1B00+95+128;
        static int32 MACVKS_ANSI_8                    = 0x1C00+42+128;
        static int32 MACVKS_ANSI_0                    = 0x1D00+41+128;
        static int32 MACVKS_ANSI_RightBracket         = 0x1E00+125+128;
        static int32 MACVKS_ANSI_O                    = 0x1F00+79+128;
        static int32 MACVKS_ANSI_U                    = 0x2000+85+128;
        static int32 MACVKS_ANSI_LeftBracket          = 0x2100+123+128;
        static int32 MACVKS_ANSI_I                    = 0x2200+73+128;
        static int32 MACVKS_ANSI_P                    = 0x2300+80+128;
        static int32 MACVKS_ANSI_L                    = 0x2500+76+128;
        static int32 MACVKS_ANSI_J                    = 0x2600+74+128;
        static int32 MACVKS_ANSI_Quote                = 0x2700+34+128;
        static int32 MACVKS_ANSI_K                    = 0x2800+75+128;
        static int32 MACVKS_ANSI_Semicolon            = 0x2900+58+128;
        static int32 MACVKS_ANSI_Backslash            = 0x2A00+124+128;
        static int32 MACVKS_ANSI_Comma                = 0x2B00+60+128;
        static int32 MACVKS_ANSI_Slash                = 0x2C00+63+128;
        static int32 MACVKS_ANSI_N                    = 0x2D00+78+128;
        static int32 MACVKS_ANSI_M                    = 0x2E00+77+128;
        static int32 MACVKS_ANSI_Period               = 0x2F00+62+128;
        static int32 MACVKS_ANSI_Grave                = 0x3200+126+128;
    */

    static CGEventSourceRef es;
    static CGEventRef e;

    for (i = 0; i < txt->len; i++) {
        c = txt->chr[i];

        // static int32 i,s,x,vk,c;

        /*
            CONTROL+{A-Z}
            The following 'x' letters cannot be simulated this way because they map to implemented control code (8,9,13) functionality:
            ABCDEFGHIJKLMNOPQRSTUVWXYZ
            .......xx...x.............
            Common/standard CTRL+? combinations for copying, pasting, undoing, cutting, etc. are available
        */

        if ((c >= 1) && (c <= 26)) {
            if ((c != 8) && (c != 9) && (c != 13)) {
                // Note: Under MacOSX, COMMAND is used instead of control for general tasks
                vk = ASCII_TO_MACVK[c + 96] >> 8;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)MACVK_Command, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventSetFlags(e, kCGEventFlagMaskCommand);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)MACVK_Command, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                goto special_character;
            }
        }

        // custom extended characters
        if (c == 0) {
            if (i == (txt->len - 1))
                goto special_character;
            i++;
            c = txt->chr[i];
            if (c == 15) { // SHIFT+TAB
                vk = MACVK_Tab;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)MACVK_Shift, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventSetFlags(e, kCGEventFlagMaskShift);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)MACVK_Shift, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            // 4 arrows
            if (c == 75) {
                vk = MACVK_LeftArrow;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 77) {
                vk = MACVK_RightArrow;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 72) {
                vk = MACVK_UpArrow;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 80) {
                vk = MACVK_DownArrow;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            // 6 control keys
            if (c == 82) {
                vk = MACVK_Help;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 71) {
                vk = MACVK_Home;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 83) {
                vk = MACVK_ForwardDelete;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 79) {
                vk = MACVK_End;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 81) {
                vk = MACVK_PageDown;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            if (c == 73) {
                vk = MACVK_PageUp;
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
            //...
            // todo: F1-F12, shift/control/alt+above
            goto special_character;
        }

        // standard ASCII character output
        x = ASCII_TO_MACVK[c];
        if (x & 127) { // available character
            vk = x >> 8;
            if (x & 128) {
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)MACVK_Shift, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventSetFlags(e, kCGEventFlagMaskShift);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)MACVK_Shift, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            } else {
                es = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 1);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                e = CGEventCreateKeyboardEvent(es, (CGKeyCode)vk, 0);
                CGEventPost(kCGAnnotatedSessionEventTap, e);
                CFRelease(e);
                CFRelease(es);
            }
        } // available character

    special_character:;

    } // i

#endif // QB64_MACOSX

#ifdef QB64_WINDOWS

    static INPUT input;

    /*VK reference:
        http://msdn.microsoft.com/en-us/library/ms927178.aspx
    */

    for (i = 0; i < txt->len; i++) {
        c = txt->chr[i];
        // custom characters
        if (c == 9) {
            ZeroMemory(&input, sizeof(INPUT));
            input.ki.wVk = VK_TAB;
            input.type = INPUT_KEYBOARD;
            SendInput(1, &input, sizeof(INPUT));
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
            goto special_character;
        }
        if (c == 8) {
            ZeroMemory(&input, sizeof(INPUT));
            input.ki.wVk = VK_BACK;
            input.type = INPUT_KEYBOARD;
            SendInput(1, &input, sizeof(INPUT));
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
            goto special_character;
        }
        if (c == 13) {
            ZeroMemory(&input, sizeof(INPUT));
            input.ki.wVk = VK_RETURN;
            input.type = INPUT_KEYBOARD;
            SendInput(1, &input, sizeof(INPUT));
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
            goto special_character;
        }
        //...

        /*
            CONTROL+{A-Z}
            The following 'x' letters cannot be simulated this way because they map to above functionality:
            ABCDEFGHIJKLMNOPQRSTUVWXYZ
            .......xx...x.............
            Common/standard CTRL+? combinations for copying, pasting, undoing, cutting, etc. are available
        */
        if ((c >= 1) && (c <= 26)) {
            ZeroMemory(&input, sizeof(INPUT));
            input.type = INPUT_KEYBOARD;
            input.ki.wVk = VK_CONTROL;
            SendInput(1, &input, sizeof(INPUT));
            ZeroMemory(&input, sizeof(INPUT));
            input.ki.wVk = VkKeyScan(64 + c) & 255;
            input.type = INPUT_KEYBOARD;
            SendInput(1, &input, sizeof(INPUT));
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
            ZeroMemory(&input, sizeof(INPUT));
            input.type = INPUT_KEYBOARD;
            input.ki.wVk = VK_CONTROL;
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
            goto special_character;
        }

        // custom extended characters
        if (c == 0) {
            if (i == (txt->len - 1))
                goto special_character;
            i++;
            c = txt->chr[i];
            if (c == 15) { // SHIFT+TAB
                ZeroMemory(&input, sizeof(INPUT));
                input.type = INPUT_KEYBOARD;
                input.ki.wVk = VK_SHIFT;
                SendInput(1, &input, sizeof(INPUT));
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_TAB;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
                ZeroMemory(&input, sizeof(INPUT));
                input.type = INPUT_KEYBOARD;
                input.ki.wVk = VK_SHIFT;
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 75) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_LEFT;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 77) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_RIGHT;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 72) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_UP;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 80) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_DOWN;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 82) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_INSERT;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 71) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_HOME;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 83) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_DELETE;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 79) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_END;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 81) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_NEXT;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            if (c == 73) {
                ZeroMemory(&input, sizeof(INPUT));
                input.ki.wVk = VK_PRIOR;
                input.type = INPUT_KEYBOARD;
                SendInput(1, &input, sizeof(INPUT));
                input.ki.dwFlags = KEYEVENTF_KEYUP;
                SendInput(1, &input, sizeof(INPUT));
            }
            //...
            // todo: F1-F12, shift/control/alt+above

            goto special_character;
        }

        if ((c > 126) || (c < 32))
            goto special_character;

        x = VkKeyScan(txt->chr[i]);
        vk = x & 255;

        s = (x >> 8) & 255;
        // 1 Either shift key is pressed.
        // 2 Either CTRL key is pressed.
        // 4 Either ALT key is pressed.
        if (s & 1) {
            ZeroMemory(&input, sizeof(INPUT));
            input.type = INPUT_KEYBOARD;
            input.ki.wVk = VK_SHIFT;
            SendInput(1, &input, sizeof(INPUT));
        }

        ZeroMemory(&input, sizeof(INPUT));
        input.type = INPUT_KEYBOARD;
        input.ki.wVk = vk;
        SendInput(1, &input, sizeof(INPUT));

        ZeroMemory(&input, sizeof(INPUT));
        input.type = INPUT_KEYBOARD;
        input.ki.wVk = vk;
        input.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(1, &input, sizeof(INPUT));

        if (s & 1) {
            ZeroMemory(&input, sizeof(INPUT));
            input.type = INPUT_KEYBOARD;
            input.ki.wVk = VK_SHIFT;
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
        }

    special_character:;

    } // i

#endif
}

#ifndef DEPENDENCY_PRINTER

// stubs
void sub__printimage(int32 i) {
    return;
}

#else

void sub__printimage(int32 i) {

#    ifdef QB64_WINDOWS

    static LPSTR szPrinterName = NULL;
    DWORD dwNameLen;
    HDC dc;
    DOCINFOA di;
    uint32 w, h;
    int32 x, y;
    int32 i2;
    BITMAPFILEHEADER bmfHeader;
    BITMAPINFOHEADER bi;
    img_struct *s, *s2;

    if (i >= 0) {
        validatepage(i);
        s = &img[page[i]];
    } else {
        x = -i;
        if (x >= nextimg) {
            error(258);
            return;
        }
        s = &img[x];
        if (!s->valid) {
            error(258);
            return;
        }
    }

    if (!szPrinterName)
        szPrinterName = (LPSTR)malloc(65536);
    dwNameLen = 65536;
    GetDefaultPrinterA(szPrinterName, &dwNameLen);
    if ((dc = CreateDCA("WINSPOOL", szPrinterName, NULL, NULL)) == NULL)
        goto failed;
    ZeroMemory(&di, sizeof(DOCINFO));
    di.cbSize = sizeof(DOCINFO);
    di.lpszDocName = "Document";
    if (StartDocA(dc, &di) <= 0) {
        DeleteDC(dc);
        goto failed;
    }
    if (StartPage(dc) <= 0) {
        EndDoc(dc);
        DeleteDC(dc);
        goto failed;
    }

    w = GetDeviceCaps(dc, HORZRES);
    h = GetDeviceCaps(dc, VERTRES);

    i2 = func__newimage(w, h, 32, 1);
    if (i2 == -1) {
        EndDoc(dc);
        DeleteDC(dc);
        goto failed;
    }
    s2 = &img[-i2];
    sub__dontblend(i2, 1);
    sub__putimage(NULL, NULL, NULL, NULL, i, i2, NULL, NULL, NULL, NULL, 8 + 32);

    ZeroMemory(&bi, sizeof(BITMAPINFOHEADER));

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = w;
    bi.biHeight = h;
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    for (y = 0; y < h; y++) {
        SetDIBitsToDevice(dc, 0, y, w, 1, 0, 0, 0, 1, s2->offset32 + (y * w), (BITMAPINFO *)&bi, DIB_RGB_COLORS);
    }

    sub__freeimage(i2, 1);

    if (EndPage(dc) <= 0) {
        EndDoc(dc);
        DeleteDC(dc);
        goto failed;
    }
    if (EndDoc(dc) <= 0) {
        DeleteDC(dc);
        goto failed;
    }
    DeleteDC(dc);
failed:;
#    endif
}

#endif

int32 func__keyhit() {
    /*
        //keyhit cyclic buffer
        int64 keyhit[8192];
        //    keyhit specific internal flags: (stored in high 32-bits)
        //    &4294967296->numpad was used
        int32 keyhit_nextfree=0;
        int32 keyhit_next=0;
        //note: if full, the oldest message is discarded to make way for the new message
    */
    if (keyhit_next != keyhit_nextfree) {
        static int32 x;
        x = *(int32 *)&keyhit[keyhit_next];
        keyhit_next = (keyhit_next + 1) & 0x1FFF;
        return x;
    }
    return 0;
}

int32 func__keydown(int32 x) {
    if (x <= 0) {
        error(5);
        return 0;
    }
    if (keyheld(x))
        return -1;
    return 0;
}

void sub__mapunicode(int32 unicode_code, int32 ascii_code) {
    if (is_error_pending())
        return;
    if ((unicode_code < 0) || (unicode_code > 65535)) {
        error(5);
        return;
    }
    if ((ascii_code < 0) || (ascii_code > 255)) {
        error(5);
        return;
    }
    codepage437_to_unicode16[ascii_code] = unicode_code;
}

int32 func__mapunicode(int32 ascii_code) {
    if (is_error_pending())
        return NULL;
    if ((ascii_code < 0) || (ascii_code > 255)) {
        error(5);
        return NULL;
    }
    return (codepage437_to_unicode16[ascii_code]);
}

int32 addone(int32 x) {
    return x + 1;
} // for testing purposes only

qbs *func__os() {
#ifdef QB64_WINDOWS
#    define QB64_OS_SYSTEM_STR "[WINDOWS]"
#elif defined(QB64_LINUX)
#    define QB64_OS_SYSTEM_STR "[LINUX]"
#elif defined(QB64_MACOSX)
#    define QB64_OS_SYSTEM_STR "[MACOSX]"
#else
#    define QB64_OS_SYSTEM_STR ""
#endif

#ifdef QB64_MACOSX
#    define QB64_OS_SYSTEM_EXTRA_STR "[LINUX]"
#else
#    define QB64_OS_SYSTEM_EXTRA_STR ""
#endif

#ifdef QB64_32
#    define QB64_OS_BITS_STR "[32BIT]"
#else
#    define QB64_OS_BITS_STR "[64BIT]"
#endif

#ifdef QB64_ARM
#    define QB64_OS_ARCH_STR "[ARM]"
#else
#    define QB64_OS_ARCH_STR ""
#endif

    // Have the compiler combine all our selections into one string
    return qbs_new_txt(QB64_OS_SYSTEM_STR QB64_OS_SYSTEM_EXTRA_STR QB64_OS_BITS_STR QB64_OS_ARCH_STR);
}

int32 func__screenx() {
#if defined(QB64_GUI) && defined(QB64_WINDOWS) && defined(QB64_GLUT)
    NEEDS_GLUT(0);
    return libqb_glut_get(GLUT_WINDOW_X) - libqb_glut_get(GLUT_WINDOW_BORDER_WIDTH);
#elif defined(QB64_GUI) && defined(QB64_MACOSX) && defined(QB64_GLUT)
    NEEDS_GLUT(0);
    return libqb_glut_get(GLUT_WINDOW_X);
#endif
    return 0; // if not windows then return 0
}

int32 func__screeny() {
#if defined(QB64_GUI) && defined(QB64_WINDOWS) && defined(QB64_GLUT)
    NEEDS_GLUT(0);
    return libqb_glut_get(GLUT_WINDOW_Y) - libqb_glut_get(GLUT_WINDOW_BORDER_WIDTH) - libqb_glut_get(GLUT_WINDOW_HEADER_HEIGHT);
#elif defined(QB64_GUI) && defined(QB64_MACOSX) && defined(QB64_GLUT)
    NEEDS_GLUT(0);
    return libqb_glut_get(GLUT_WINDOW_Y);
#endif
    return 0; // if not windows then return 0
}

void sub__screenmove(int32 x, int32 y, int32 passed) {
    if (is_error_pending())
        return;
    if (!passed)
        goto error;
    if (passed == 3)
        goto error;
    if (full_screen)
        return;

#if defined(QB64_GUI) && defined(QB64_GLUT)
    NEEDS_GLUT();

    if (passed == 2) {
        libqb_glut_position_window(x, y);
    } else {
        int32 SW = -1, SH, WW, WH;
        while (SW == -1) {
            SW = libqb_glut_get(GLUT_SCREEN_WIDTH);
        }
        SH = libqb_glut_get(GLUT_SCREEN_HEIGHT);
        WW = libqb_glut_get(GLUT_WINDOW_WIDTH);
        WH = libqb_glut_get(GLUT_WINDOW_HEIGHT);
        x = (SW - WW) / 2;
        y = (SH - WH) / 2;
        libqb_glut_position_window(x, y);
    }
#endif

    return;

error:
    error(5);
}

void key_update() {

    if (key_display_redraw) {
        key_display_redraw = 0;
        if (!key_display)
            return;
    } else {
        if (key_display == key_display_state)
            return;
        key_display_state = key_display;
    }

    // use display page 0
    static int32 olddest;
    olddest = func__dest();
    sub__dest(0);
    static img_struct *i;
    i = write_page;

    static int32 f, z, c, x2;

    // locate bottom-left
    // get current status
    static int32 cx, cy, holding, col1, col2;
    cx = i->cursor_x;
    cy = i->cursor_y;
    holding = i->holding_cursor;
    col1 = i->color;
    col2 = i->background_color;
    static int32 h, w;
    // calculate height & width in characters
    if (i->compatible_mode) {
        h = i->height / fontheight[i->font];
        if (fontwidth[i->font]) {
            w = i->width / fontwidth[i->font];
        } else {
            w = write_page->width;
        }
    } else {
        h = i->height;
        w = i->width;
    }
    i->cursor_x = 1;
    i->cursor_y = h;
    i->holding_cursor = 0;

    static qbs *str = NULL;
    if (!str)
        str = qbs_new(0, 0);
    static qbs *str2 = NULL;
    if (!str2)
        str2 = qbs_new(1, 0);

    // clear bottom row using background color
    if (i->text) {
        for (x2 = 1; x2 <= i->width; x2++) {
            str2->chr[0] = 32;
            qbs_print(str2, 0);
        }
        i->cursor_x = 1;
        i->cursor_y = h;
        i->holding_cursor = 0;
    } else {
        fast_boxfill(0, (i->cursor_y - 1) * fontheight[i->font], i->width - 1, i->cursor_y * fontheight[i->font] - 1, col2 | 0xFF000000);
    }

    if (!key_display)
        goto no_key;

    static int32 item_x, limit_x, row_limit, leeway_x;
    leeway_x = 0;
    if (i->compatible_mode) {
        if (fontwidth[i->font]) {
            item_x = w / 12;
            row_limit = item_x * 12;
            if (item_x < 8) { // cannot fit min. width
                item_x = 8;
                row_limit = (w / 8) * 8;
                if (item_x > w) {
                    item_x = w;
                    row_limit = w;
                } // can't even fit 1!
            }
        } else {
            leeway_x = fontheight[i->font];
            item_x = w / 12;
            row_limit = item_x * 12 - leeway_x;
            x2 = ((float)fontheight[i->font]) * 0.5; // estimate the average character width (it's OK for this to be wrong)
            if (item_x < (x2 * 8 + leeway_x)) {      // cannot fit min. width
                item_x = (x2 * 8 + leeway_x);
                row_limit = (w / (x2 * 8 + leeway_x)) * (x2 * 8 + leeway_x) - leeway_x;
                if (item_x > w) {
                    item_x = w;
                    row_limit = w - leeway_x;
                } // can't even fit 1!
            }
        }
    } else {
        item_x = w / 12;
        row_limit = item_x * 12;
        if (item_x < 8) { // cannot fit min. width
            item_x = 8;
            row_limit = (w / 8) * 8;
            if (item_x > w) {
                item_x = w;
                row_limit = w;
            } // can't even fit 1!
        }
    }

    static int32 final_chr, row_final_chr;

    row_final_chr = 0;
    for (f = 1; f <= 12; f++) {
        final_chr = 0;
        limit_x = f * item_x - leeway_x; // set new limit

        // relocate
        x2 = ((f - 1) * item_x) + 1;
        if (x2 >= row_limit) {
            row_final_chr = 1;
            goto done_f;
        }
        i->cursor_x = x2;

        // number string
        if (fontwidth[i->font]) {
            qbs_set(str, qbs_ltrim(qbs_str(f)));
        } else {
            qbs_set(str, qbs_add(qbs_ltrim(qbs_str(f)), qbs_new_txt(")")));
        }
        for (z = 0; z < str->len; z++) {
            if (i->cursor_x >= row_limit)
                row_final_chr = 1;
            if (i->cursor_x > limit_x)
                goto done_f;
            if (i->cursor_x >= limit_x)
                final_chr = 1;
            str2->chr[0] = str->chr[z];
            qbs_print(str2, 0);
            if (final_chr)
                goto done_f;
        }

        // text
        static int32 fi;
        fi = f;
        if (f > 10)
            fi = f - 11 + 30;
        if (onkey[fi].text) {
            qbs_set(str, onkey[fi].text);
            if (i->text) {
                if (i->background_color) {
                    i->color = 7;
                    i->background_color = 0;
                } else {
                    i->color = 0;
                    i->background_color = 7;
                }
            }
        } else {
            str->len = 0;
        }
        z = 0;
        while (i->cursor_x < limit_x) {
            static int32 c;

            if (z >= str->len) {
                if (!onkey[fi].text)
                    goto done_f;
                c = 32;
            } else {
                c = str->chr[z++];
            }

            if (i->cursor_x >= row_limit)
                row_final_chr = 1;
            if (i->cursor_x > limit_x)
                goto done_f;
            if (i->cursor_x >= limit_x)
                final_chr = 1;
            /*
                7->14
                8->254
                9->26
                10->60
                11->127
                12->22
                13->27
                28->16
                29->17
                30->24
                31->25
                KEY LIST puts spaces instead of non-printables
                QBASIC's KEY LIST differs from QBX in this regard
                CHR$(13) is also turned into a space in KEY LIST, even if it is at the end
            */
            if (c == 7)
                c = 14;
            if (c == 8)
                c = 254;
            if (c == 9)
                c = 26;
            if (c == 10)
                c = 60;
            if (c == 11)
                c = 127;
            if (c == 12)
                c = 22;
            if (c == 13)
                c = 27;
            if (c == 28)
                c = 16;
            if (c == 29)
                c = 17;
            if (c == 30)
                c = 24;
            if (c == 31)
                c = 25;
            str2->chr[0] = c;
            no_control_characters = 1;
            qbs_print(str2, 0);
            no_control_characters = 0;
            if (final_chr)
                goto done_f;
        }

    done_f:;
        i->color = col1;
        i->background_color = col2;
        if (row_final_chr)
            goto done_row;
    }
done_row:;

// revert status
no_key:
    i->cursor_x = cx;
    i->cursor_y = cy;
    i->holding_cursor = holding;
    i->color = col1;
    i->background_color = col2;
    sub__dest(olddest);
}

void key_on() {
    key_display = 1;
    key_update();
}

void key_off() {
    key_display = 0;
    key_update();
}

void key_list() {
    static img_struct *i;
    i = write_page;
    static int32 mono;
    mono = 1;
    if (!fontwidth[i->font])
        if (func__printwidth(qbs_new_txt(" "), NULL, NULL) != func__printwidth(qbs_new_txt(")"), NULL, NULL))
            mono = 0;
    static int32 f, fi;
    static qbs *str = NULL;
    if (!str)
        str = qbs_new(0, 0);
    for (f = 1; f <= 12; f++) {

        // F-number & spacer
        if (fontwidth[i->font]) {
            if (f < 10) {
                qbs_set(str, qbs_add(qbs_ltrim(qbs_str(f)), qbs_new_txt("  ")));
            } else {
                qbs_set(str, qbs_add(qbs_ltrim(qbs_str(f)), qbs_new_txt(" ")));
            }
        } else {
            if ((f < 10) && (mono == 1)) {
                qbs_set(str, qbs_add(qbs_ltrim(qbs_str(f)), qbs_new_txt(")  ")));
            } else {
                qbs_set(str, qbs_add(qbs_ltrim(qbs_str(f)), qbs_new_txt(") ")));
            }
        }
        qbs_set(str, qbs_add(qbs_new_txt("F"), str));

        // text
        fi = f;
        if (f > 10)
            fi = f - 11 + 30;
        if (onkey[fi].text) {
            qbs_print(str, 0);
            /*
                7->14
                8->254
                9->26
                10->60
                11->127
                12->22
                13->27
                28->16
                29->17
                30->24
                31->25
                KEY LIST puts spaces instead of non-printables
                QBASIC's KEY LIST differs from QBX in this regard
                CHR$(13) is also turned into a space in KEY LIST, even if it is at the end
            */
            str->len = 1;
            static int32 x, c;
            for (x = 0; x < onkey[fi].text->len; x++) {
                c = onkey[fi].text->chr[x];
                if ((c >= 7) && (c <= 13))
                    c = 32;
                if ((c >= 28) && (c <= 31))
                    c = 32;
                str->chr[0] = c;
                qbs_print(str, 0);
            }
            str->len = 0;
            qbs_print(str, 1);
        } else {
            qbs_print(str, 1);
        }

    } // f
}

void key_assign(int32 i, qbs *str) {
    if (is_error_pending())
        return;
    static int32 x, x2, i2;

    if (((i >= 1) && (i <= 10)) || (i == 30) || (i == 31)) { // F1-F10,F11,F12
        if (str->len > 15) {
            error(5);
            return;
        }
        if (!onkey[i].text)
            onkey[i].text = qbs_new(0, 0);
        qbs_set(onkey[i].text, str);
        key_display_redraw = 1;
        key_update();
        return;
    } // F1-F10,F11,F12

    if ((i >= 15) && (i <= 29)) { // user defined key
        if (str->len == 0) {
            onkey[i].key_scancode = 0;
        } else {
            x = str->chr[str->len - 1];
            x2 = 0;
            for (i2 = 0; i2 < str->len - 1; i2++)
                x2 |= str->chr[i2];
            onkey[i].key_scancode = x;
            onkey[i].key_flags = x2;
        }
        return;
    } // user defined key

    error(5);
    return;
}

void sub_paletteusing(void *element, int32 bits) {
    // note: bits is either 16(INTEGER) or 32(LONG)
    if (is_error_pending())
        return;
    static byte_element_struct *ele;
    ele = (byte_element_struct *)element;
    static int16 *i16;
    i16 = (int16 *)ele->offset;
    static int32 *i32;
    i32 = (int32 *)ele->offset;
    if (write_page->bits_per_pixel == 32)
        goto error;
    static int32 last_color, i, c;
    last_color = write_page->mask;
    if (ele->length < ((bits / 8) * (last_color + 1)))
        goto error;
    if ((write_page->compatible_mode == 11) || (write_page->compatible_mode == 12) || (write_page->compatible_mode == 13) ||
        (write_page->compatible_mode == 256)) {
        if (bits == 16)
            goto error; // must be an array of type LONG in these modes
    }
    for (i = 0; i <= last_color; i++) {
        if (bits == 16) {
            c = *i16;
            i16++;
        } else {
            c = *i32;
            i32++;
        }
        if (c < -1)
            goto error;
        if (c != -1) {
            qbg_palette(i, c, 1);
            if (is_error_pending())
                return;
        }
    }
    return;
error:

    error(5);
}

extern int32 func__devices();

int32 func_stick(int32 i, int32 axis_group, int32 passed) {
    // note: range: 1-254 (127=neutral), top-left to bottom-right positive
    //             128 returned for unattached devices
    // QB64 extension: 'i' allows for joystick selection 0,1->JoyA, 2,3->JoyB, 4,5->JoyC, etc
    //                'axis_group' selects the pair of axes to read from, 1 is the default
    if (device_last == 0)
        func__devices(); // init device interface (if not already setup)
    if (passed) {
        if (axis_group < 1 || axis_group > 65535) {
            error(5);
            return 0;
        }
    } else {
        axis_group = 1;
    }
    if (i < 0 || i > 65535) {
        error(5);
        return 0;
    }
    static int32 di, axis, i2, v;
    static device_struct *d;
    static float f;
    axis = (i & 1) + (axis_group - 1) * 2;
    i = i >> 1;
    i2 = 0;
    for (di = 1; di <= device_last; di++) {
        d = &devices[di];
        if (d->type == 1) {
            if (i == i2) {
                if (axis < d->lastaxis) {
                    f = getDeviceEventAxisValue(d, d->queued_events - 1, axis);
                    if (f > -0.01 && f <= 0.01)
                        f = 0;
                    v = qbr_float_to_long(f * 127.0) + 127;
                    if (v > 254)
                        v = 254;
                    if (v < 1)
                        v = 1;
                    return v;
                } // axis valid
            }
            i2++;
        } // type==1
    } // di
    return 128;
}

int32 func_strig(int32 i, int32 controller, int32 passed) {
    // note: returns 0 or -1(true)
    // QB64 extension: 'i' refers to a button (b1,b1,b1,b1,b2,b2,b2,b2,b3,b3,b3,b3,b4,...)
    //                'controller' overrides the controller implied by 'i', 1 is the default
    if (device_last == 0)
        func__devices(); // init device interface (if not already setup)
    if (i < 0 || i > 65535) {
        error(5);
        return 0;
    }
    if (passed) {
        if (controller < 1 || controller > 65535) {
            error(5);
            return 0;
        }
    } else {
        controller = 1;
        if (i & 2) {
            controller = 2;
            i -= 2;
        }
    }
    static int32 di, button, method, c, v;
    static device_struct *d;
    button = (i >> 2) + 1;
    method = (i & 1) + 1; // 1=if pressed since last call, 2=currently down
    c = 1;
    for (di = 1; di <= device_last; di++) {
        d = &devices[di];
        if (d->type == 1) {
            if (c == controller) {
                if (button <= d->lastbutton) { // button exists
                    if (method == 1) {
                        // method 1: pressed since last call
                        if (button > 0 && button <= 256) {
                            if (d->STRIG_button_pressed[button - 1]) {
                                d->STRIG_button_pressed[button - 1] = 0;
                                return -1;
                            }
                        }
                        return 0;
                    } else {
                        // method 2: currently down
                        v = getDeviceEventButtonValue(d, d->queued_events - 1, button - 1);
                        if (v)
                            return -1;
                        else
                            return 0;
                    }
                } // button exists
            } // c==controller
            c++;
        } // type==1
    } // di
    return 0;
}

int32 func__console() {
    if (is_error_pending())
        return -1;
    return console_image;
}

void sub__console(int32 onoff) { // on=1 off=2
    if (!console)
        return; // command does nothing if console unavailable
    if (onoff == 1) {
        // turn on
        if (!console_active) {
#ifdef QB64_WINDOWS
            if (console_child) {
                ShowWindow(GetConsoleWindow(), SW_SHOWNOACTIVATE);
            }
#endif
            console_active = 1;
        }
    } else {
        // turn off
        if (console_active) {
#ifdef QB64_WINDOWS
            if (console_child) {
                ShowWindow(GetConsoleWindow(), SW_HIDE);
            }
#endif
            console_active = 0;
        }
    }
}

void sub__screenshow() {
#ifdef QB64_GLUT
    screen_hide = 0;
    // $SCREENHIDE programs will not have the window running
    libqb_start_glut_thread();
    libqb_glut_show_window();
#endif
}

void sub__screenhide() {
    if (screen_hide)
        return;

#ifdef QB64_GLUT
    // This is probably unnecessary, no conditions allow for screen_hide==0
    // without GLUT running, but it doesn't hurt anything.
    libqb_start_glut_thread();
    libqb_glut_hide_window();
#endif

    screen_hide = 1;
}

int32 func__screenhide() {
    return -screen_hide;
}

void sub__consoletitle(qbs *s) {
#ifdef QB64_WINDOWS
    char *title;
    title = (char *)malloc(s->len + 1);
    title[s->len] = '\0'; // add NULL terminator
    memcpy(title, s->chr, s->len);
    if (console) {
        if (console_active) {
            SetConsoleTitleA(title);
            Sleep(40);
        }
    }
#endif
}

mem_block func__memimage(int32 i, int32 passed) {

    static mem_block b;

    if (is_error_pending())
        goto error;

    static int image_handle;

    static img_struct *im;
    if (passed) {
        if (i >= 0) {
            validatepage(i);
            im = &img[image_handle = page[i]];
            image_handle = -image_handle;
        } else {
            image_handle = i;
            i = -i;
            if (i >= nextimg) {
                error(258);
                goto error;
            }
            im = &img[i];
            if (!im->valid) {
                error(258);
                goto error;
            }
        }
    } else {
        im = write_page;
    }

    if (im->lock_id) {
        b.lock_offset = (ptrszint)im->lock_offset;
        b.lock_id = im->lock_id; // get existing tag
    } else {
        new_mem_lock();
        mem_lock_tmp->type = 2; // image
        b.lock_offset = (ptrszint)mem_lock_tmp;
        b.lock_id = mem_lock_id;
        im->lock_offset = (void *)mem_lock_tmp;
        im->lock_id = mem_lock_id; // create tag
    }

    b.offset = (ptrszint)im->offset;
    b.size = im->bytes_per_pixel * im->width * im->height;
    b.type = im->bytes_per_pixel + 128 + 1024 + 2048; // integer+unsigned+pixeltype
    b.elementsize = im->bytes_per_pixel;
    b.image = image_handle;

    return b;
error:
    b.offset = 0;
    b.size = 0;
    b.lock_offset = (ptrszint)mem_lock_base;
    b.lock_id = 1073741821; // set invalid lock
    b.type = 0;
    b.elementsize = 0;
    b.image = -1;
    return b;
}

void GLUT_key_ascii(int32 key, int32 down) {
#ifdef QB64_GLUT
    static int32 v;

    static int32 mod;
    mod = glutGetModifiers(); // shift=1, control=2, alt=4

#    ifndef CORE_FREEGLUT
    /*
        if (mod&GLUT_ACTIVE_SHIFT){
        keydown_vk(VK+QBVK_LSHIFT);
        }else{
        keyup_vk(VK+QBVK_LSHIFT);
        }

        if (mod&GLUT_ACTIVE_CTRL){
        keydown_vk(VK+QBVK_LCTRL);
        }else{
        keyup_vk(VK+QBVK_LCTRL);
        }

        if (mod&GLUT_ACTIVE_ALT){
        keydown_vk(VK+QBVK_LALT);
        }else{
        keyup_vk(VK+QBVK_LALT);
        }
    */
#    endif

    // Note: The following is required regardless of whether FREEGLUT is/isn't being used
    // #ifdef CORE_FREEGLUT
    // Is CTRL key down? If so, unencode character (applying shift as required)
    if (mod & 2) {
        // if (key==127){ //Removed: Might clash with CTRL+DELETE
        // key=8;
        // goto ctrl_mod;
        //}//127
        // if (key==3){//CTRL+(BREAK|SCROLL-LOCK)
        // if (down) keydown_vk(VK+QBVK_BREAK); else keyup_vk(VK+QBVK_BREAK);
        // return;
        //}
        if (key == 10) {
            key = 13;
            goto ctrl_mod;
        } // 10
        if ((key >= 1) && (key <= 26)) {
            if (mod & 1)
                key = key - 1 + 65;
            else
                key = key - 1 + 97; // assume caps lock off
            goto ctrl_mod;
        } // 1-26
    }
ctrl_mod:
    // #endif

#    ifdef QB64_MACOSX

    // swap DEL and backspace keys

    if (key == 8) {
        key = 127;
    } else {
        if (key == 127) {
            key = 8;
        }
    }

#    endif

    if (key == 127) { // delete
        if (down)
            keydown_vk(0x5300);
        else
            keyup_vk(0x5300);
        return;
    }
    if (down)
        keydown_ascii(key);
    else
        keyup_ascii(key);
#endif
}

void GLUT_KEYBOARD_FUNC(unsigned char key, int x, int y) {

    // glutPostRedisplay();

    // qbs_print(qbs_str(key),0);
    // qbs_print(qbs_str((int32)glutGetModifiers()),1);

    GLUT_key_ascii(key, 1);
}

void GLUT_KEYBOARDUP_FUNC(unsigned char key, int x, int y) {
    GLUT_key_ascii(key, 0);
}

void GLUT_key_special(int32 key, int32 down) {
#ifdef QB64_GLUT
#    ifndef CORE_FREEGLUT
    /*
        static int32 mod;
        mod=glutGetModifiers();//shift=1, control=2, alt=4
        if (mod&GLUT_ACTIVE_SHIFT){
        keydown_vk(VK+QBVK_LSHIFT);
        }else{
        keyup_vk(VK+QBVK_LSHIFT);
        }

        if (mod&GLUT_ACTIVE_CTRL){
        keydown_vk(VK+QBVK_LCTRL);
        }else{
        keyup_vk(VK+QBVK_LCTRL);
        }

        if (mod&GLUT_ACTIVE_ALT){
        keydown_vk(VK+QBVK_LALT);
        }else{
        keyup_vk(VK+QBVK_LALT);
        }
    */
#    endif

    static int32 vk;
    vk = -1;
    if (key == GLUT_KEY_F1) {
        vk = 0x3B00;
    }
    if (key == GLUT_KEY_F2) {
        vk = 0x3C00;
    }
    if (key == GLUT_KEY_F3) {
        vk = 0x3D00;
    }
    if (key == GLUT_KEY_F4) {
        vk = 0x3E00;
    }
    if (key == GLUT_KEY_F5) {
        vk = 0x3F00;
    }
    if (key == GLUT_KEY_F6) {
        vk = 0x4000;
    }
    if (key == GLUT_KEY_F7) {
        vk = 0x4100;
    }
    if (key == GLUT_KEY_F8) {
        vk = 0x4200;
    }
    if (key == GLUT_KEY_F9) {
        vk = 0x4300;
    }
    if (key == GLUT_KEY_F10) {
        vk = 0x4400;
    }
    if (key == GLUT_KEY_F11) {
        vk = 0x8500;
    }
    if (key == GLUT_KEY_F12) {
        vk = 0x8600;
    }
    if (key == GLUT_KEY_LEFT) {
        vk = 0x4B00;
    }
    if (key == GLUT_KEY_UP) {
        vk = 0x4800;
    }
    if (key == GLUT_KEY_RIGHT) {
        vk = 0x4D00;
    }
    if (key == GLUT_KEY_DOWN) {
        vk = 0x5000;
    }
    if (key == GLUT_KEY_PAGE_UP) {
        vk = 0x4900;
    }
    if (key == GLUT_KEY_PAGE_DOWN) {
        vk = 0x5100;
    }
    if (key == GLUT_KEY_HOME) {
        vk = 0x4700;
    }
    if (key == GLUT_KEY_END) {
        vk = 0x4F00;
    }
    if (key == GLUT_KEY_INSERT) {
        vk = 0x5200;
    }

#    ifdef CORE_FREEGLUT
    if (key == 112) {
        vk = VK + QBVK_LSHIFT;
    }
    if (key == 113) {
        vk = VK + QBVK_RSHIFT;
    }
    if (key == 114) {
        vk = VK + QBVK_LCTRL;
    }
    if (key == 115) {
        vk = VK + QBVK_RCTRL;
    }
    if (key == 116) {
        vk = VK + QBVK_LALT;
    }
    if (key == 117) {
        vk = VK + QBVK_RALT;
    }
#    endif

    if (vk != -1) {
#    ifdef QB64_WINDOWS
        if (!func__hasfocus() && !(keyheld(vk) && !down))
            return;
#    endif

        if (down)
            keydown_vk(vk);
        else
            keyup_vk(vk);
    }

#endif
}

void GLUT_SPECIAL_FUNC(int key, int x, int y) {

    // qbs_print(qbs_str((int32)glutGetModifiers()),1);

    GLUT_key_special(key, 1);
}

void GLUT_SPECIALUP_FUNC(int key, int x, int y) {
    GLUT_key_special(key, 0);
}

static int64_t lastTick = 0;
static double deltaTick = 0;

void GLUT_IDLEFUNC() {
    libqb_process_glut_queue();

#ifdef QB64_MACOSX
#    ifdef DEPENDENCY_DEVICEINPUT
    // must be in same thread as GLUT for OSX
    QB64_GAMEPAD_POLL();
    //[[[[NSApplication sharedApplication] mainWindow] standardWindowButton:NSWindowCloseButton] setEnabled:YES];
#    endif
#endif

#ifdef QB64_GLUT

#    ifdef QB64_LINUX
    if (x11_lock_request) {
        x11_locked = 1;
        x11_lock_request = 0;
        while (x11_locked)
            Sleep(1);
    }
#    endif
    int64_t curTime = GetTicks();

    // This is how long the frame took to render
    int64_t elapsed = curTime - lastTick;

    // Calculate out the error between how long the frame was 'supposed' to take vs. how long it actually took.
    deltaTick += ((double)1000 / max_fps) - (double)elapsed;

    lastTick = curTime;

    // If the error is positive, we sleep for that period of time.
    if (deltaTick > 0) {
        int32 msdelay = deltaTick;
        Sleep(msdelay);

        int64_t sleepTime = GetTicks();

        // Subtract off the time we spent sleeping. This should leave deltaTick at zero or slightly negative.
        // If it ends up negative, then we'll sleep less next frame to compensate
        deltaTick -= sleepTime - lastTick;
        lastTick = sleepTime;
    } else {
        // If we fall behind by a full frame or more, then skip to the next one
        while (deltaTick < -((double)1000 / max_fps))
            deltaTick += ((double)1000 / max_fps);
    }

    glutPostRedisplay();
#endif
}

#ifdef QB64_MACOSX
#    include <sys/sysctl.h>
#endif

// trigger recompilation = 3

int32 displayorder_screen = 1;
int32 displayorder_hardware = 2;
int32 displayorder_glrender = 3;
int32 displayorder_hardware1 = 4;

// sub__displayorder( 1 , 2 , 4 , 3 );
// id.specialformat =
// "[{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}[,{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}[,{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}[,{_SCREEN|_HARDWARE|_HARDWARE1|_GLRENDER}]]]]"
void sub__displayorder(int32 method1, int32 method2, int32 method3, int32 method4) {

    // check no value has been used twice
    if (method1 != 0)
        if (method1 == method2 || method1 == method3 || method1 == method4) {
            error(5);
            return;
        }
    if (method2 != 0)
        if (method2 == method1 || method2 == method3 || method2 == method4) {
            error(5);
            return;
        }
    if (method3 != 0)
        if (method3 == method1 || method3 == method2 || method3 == method4) {
            error(5);
            return;
        }
    if (method4 != 0)
        if (method4 == method1 || method4 == method2 || method4 == method3) {
            error(5);
            return;
        }
    displayorder_screen = 0;
    displayorder_hardware = 0;
    displayorder_hardware1 = 0;
    displayorder_glrender = 0;
    static int32 i, method;
    for (i = 1; i <= 4; i++) {
        if (i == 1)
            method = method1;
        if (i == 2)
            method = method2;
        if (i == 3)
            method = method3;
        if (i == 4)
            method = method4;
        if (method == 1)
            displayorder_screen = i;
        if (method == 2)
            displayorder_hardware = i;
        if (method == 3)
            displayorder_hardware1 = i;
        if (method == 4)
            displayorder_glrender = i;
    }
}

// int32 gl_render_method=2; //1=behind, 2=ontop[default], 3=only
void sub__glrender(int32 method) {
    // gl_render_method=method;
    if (method == 1)
        sub__displayorder(4, 1, 2, 3);
    if (method == 2)
        sub__displayorder(1, 2, 4, 3);
    if (method == 3)
        sub__displayorder(4, 0, 0, 0);
}

#ifndef QB64_GUI // begin stubs

#else // end stubs

void GLUT_RESHAPE_FUNC(int width, int height) {
    resize_event_x = width;
    resize_event_y = height;
    resize_event = -1;
    display_x_prev = display_x, display_y_prev = display_y;
    display_x = width;
    display_y = height;
    resize_pending = 0;
    os_resize_event = 1;
    set_view(VIEW_MODE__UNKNOWN);
    //***glutReshapeWindow(...) has no effect if called
    //   within GLUT_RESHAPE_FUNC***
}

// displaycall is the window of time to update our display

#    ifdef DEPENDENCY_GL
extern void SUB__GL();
#    endif

#    define GL_BGR 0x80E0
#    define GL_BGRA 0x80E1

/* reference
    struct hardware_img_struct{
    int32 w;
    int32 h;
    int32 texture_handle;
    int32 dest_context_handle;//used when rendering other images onto this image
    int32 temp;//if =1, delete immediately after use
    }
    list *hardware_img_handles=NULL;
*/

void free_hardware_img(int32 handle, int32 caller_id) {

    hardware_img_struct *hardware_img;
    hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle);

    if (hardware_img == NULL) {
        gui_alert("free_hardware_img: image does not exist");
    }

    if (hardware_img->dest_context_handle) {
        GLuint context = (GLuint)hardware_img->dest_context_handle;
        glDeleteFramebuffersEXT(1, &context);
    }
    if (hardware_img->depthbuffer_handle) {
        GLuint depthbuffer_handle = (GLuint)hardware_img->depthbuffer_handle;
        glDeleteFramebuffersEXT(1, &depthbuffer_handle);
    }
    GLuint texture = (GLuint)hardware_img->texture_handle;
    glDeleteTextures(1, &texture);

    // test reasset of hardware+img
    // hardware_img=(hardware_img_struct*)list_get(hardware_img_handles,handle);
    // if (hardware_img==NULL) alert("free_hardware_img: image does not exist");

    // if image has not been used, it may still have buffered pixel content

    if (hardware_img->software_pixel_buffer != NULL) {
        free(hardware_img->software_pixel_buffer);
    }

    list_remove(hardware_img_handles, handle);
}

/*
    int32 new_hardware_frame(int32 x, int32 y){
    int32 handle=new_hardware_frame_handle();
    glBindTexture (GL_TEXTURE_2D, handle);
    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_BGRA, GL_UNSIGNED_BYTE,
   NULL); return handle;
    }

    void free_hardware_frame(int32 handle){
    GLuint texture=(GLuint)handle;
    glDeleteTextures(1, &texture);
    }
*/

void prepare_environment_2d() { // called prior to rendering 2D content

    // precalculate critical dimensions, offsets & ratios

    static int32 can_scale; // can the screen be scaled on the window
    can_scale = 0;
    static int32 need_square_pixels; // do we need square_pixels? if not we can
                                     // stretch the screen
    need_square_pixels = 0;

    environment_2d__screen_smooth = 0;

    environment_2d__letterbox = 0;

    if (full_screen > 0) { // in full screen
        // reference: ---int32
        // full_screen_set=-1;//0(windowed),1(stretched/closest),2(1:1)---
        can_scale = 1;
        if (full_screen == 2)
            need_square_pixels = 1;
        // note: 'letter-boxing' is only required where the size of the window
        // cannot be controlled, and the only place where this is the
        //      case is full screen mode _SQUAREPIXELS
        environment_2d__screen_smooth = fullscreen_smooth;
    } else {                   // windowed
        if (resize_auto > 0) { // 1=STRETCH,2=SMOOTH
            can_scale = 1;
            if (resize_auto == 2)
                environment_2d__screen_smooth = 1;
            // note: screen will fix its aspect ratio automatically, so there is
            // no need to enforce squarepixels
        }
    }

    if (environment_2d__screen_width == environment__window_width && environment_2d__screen_height == environment__window_height) {
        // screen size matches window
        environment_2d__screen_x1 = 0;
        environment_2d__screen_y1 = 0;
        environment_2d__screen_x2 = environment_2d__screen_width - 1;
        environment_2d__screen_y2 = environment_2d__screen_height - 1;
        environment_2d__screen_x_scale = 1.0f;
        environment_2d__screen_y_scale = 1.0f;
        environment_2d__screen_scaled_width = environment_2d__screen_width;
        environment_2d__screen_scaled_height = environment_2d__screen_height;
        environment_2d__screen_smooth = 0; // no smoothing required
    } else {
        // screen size does not match
        // calculate ratios
        static float window_ratio;
        static float screen_ratio;
        window_ratio = (float)environment__window_width / (float)environment__window_height;
        screen_ratio = (float)environment_2d__screen_width / (float)environment_2d__screen_height;
        if (can_scale == 0) {
            // screen will appear in the top-left of the window with blank space
            // on the bottom & right
            environment_2d__screen_x1 = 0;
            environment_2d__screen_y1 = 0;
            environment_2d__screen_x2 = environment_2d__screen_width - 1;
            environment_2d__screen_y2 = environment_2d__screen_height - 1;
            goto cant_scale;
        }
        if (need_square_pixels == 0 || (window_ratio == screen_ratio)) {
            // can stretch, no 'letter-box' required
            environment_2d__screen_x1 = 0;
            environment_2d__screen_y1 = 0;
            environment_2d__screen_x2 = environment__window_width - 1;
            environment_2d__screen_y2 = environment__window_height - 1;
        } else {
            //'letter-box' required
            // this section needs revision
            static float x_scale, y_scale;
            static int32 x1, y1, x2, y2, z, x_limit, y_limit, x_offset, y_offset;
            // x_scale=(float)environment_2d__screen_width/(float)environment__window_width;
            // y_scale=(float)environment_2d__screen_height/(float)environment__window_height;
            // x_offset=0; y_offset=0;

            x1 = 0;
            y1 = 0;
            x2 = environment__window_width - 1;
            y2 = environment__window_height - 1;
            // x_limit=x2; y_limit=y2;
            if (window_ratio > screen_ratio) {
                // pad sides
                z = (float)environment__window_height * screen_ratio; // new width
                x1 = environment__window_width / 2 - z / 2;
                x2 = x1 + z - 1;
                environment_2d__letterbox = 1; // vertical black stripes
                                               // required
                // x_offset=-x1;
                // x_scale=(float)environment_2d__screen_width/(float)z;
                // x_limit=z-1;
            } else {
                // pad top/bottom
                z = (float)environment__window_width / screen_ratio; // new height
                y1 = environment__window_height / 2 - z / 2;
                y2 = y1 + z - 1;
                environment_2d__letterbox = 2; // horizontal black stripes required
                // y_offset=-y1;
                // y_scale=(float)environment_2d__screen_height/(float)z;
                // y_limit=z-1;
            }
            environment_2d__screen_x1 = x1;
            environment_2d__screen_y1 = y1;
            environment_2d__screen_x2 = x2;
            environment_2d__screen_y2 = y2;
        }
    cant_scale:
        environment_2d__screen_scaled_width = environment_2d__screen_x2 - environment_2d__screen_x1 + 1;
        environment_2d__screen_scaled_height = environment_2d__screen_y2 - environment_2d__screen_y1 + 1;
        environment_2d__screen_x_scale = (float)environment_2d__screen_scaled_width / (float)environment_2d__screen_width;
        environment_2d__screen_y_scale = (float)environment_2d__screen_scaled_height / (float)environment_2d__screen_height;
    }

} // prepare_environment_2d

int32 environment_2d__get_window_x1_coord(int32 x) {
    return qbr_float_to_long(((float)x) * environment_2d__screen_x_scale) + environment_2d__screen_x1;
}

int32 environment_2d__get_window_y1_coord(int32 y) {
    return qbr_float_to_long((float)y * environment_2d__screen_y_scale) + environment_2d__screen_y1;
}

int32 environment_2d__get_window_x2_coord(int32 x) {
    return qbr_float_to_long(((float)x + 1.0f) * environment_2d__screen_x_scale - 1.0f) + environment_2d__screen_x1;
}

int32 environment_2d__get_window_y2_coord(int32 y) {
    return qbr_float_to_long(((float)y + 1.0f) * environment_2d__screen_y_scale - 1.0f) + environment_2d__screen_y1;
}

struct environment_2d__window_rect_struct {
    int32 x1;
    int32 y1;
    int32 x2;
    int32 y2;
};

// this functions returns a constant rect dimensions to stop warping of image
environment_2d__window_rect_struct tmp_rect;

environment_2d__window_rect_struct *environment_2d__screen_to_window_rect(int32 x1, int32 y1, int32 x2, int32 y2) {
    tmp_rect.x1 = qbr_float_to_long(((float)x1) * environment_2d__screen_x_scale) + environment_2d__screen_x1;
    tmp_rect.y1 = qbr_float_to_long(((float)y1) * environment_2d__screen_y_scale) + environment_2d__screen_y1;
    static int32 w, h;
    w = abs(x2 - x1) + 1;
    h = abs(y2 - y1) + 1;
    // force round upwards to correct gaps when tiling
    w = ((float)w) * environment_2d__screen_x_scale + 0.99f;
    h = ((float)h) * environment_2d__screen_y_scale + 0.99f;
    tmp_rect.x2 = w - 1 + tmp_rect.x1;
    tmp_rect.y2 = h - 1 + tmp_rect.y1;
    //(code which doesn't support tiling)
    // tmp_rect.x2=qbr_float_to_long(((float)w)*environment_2d__screen_x_scale-1.0f)+tmp_rect.x1;
    // tmp_rect.y2=qbr_float_to_long(((float)h)*environment_2d__screen_y_scale-1.0f)+tmp_rect.y1;
    return &tmp_rect;
}

float *hardware_buffer_vertices = (float *)malloc(sizeof(float) * 1);
int32 hardware_buffer_vertices_max = 1;
int32 hardware_buffer_vertices_count = 0;

float *hardware_buffer_texcoords = (float *)malloc(sizeof(float) * 1);
int32 hardware_buffer_texcoords_max = 1;
int32 hardware_buffer_texcoords_count = 0;

void hardware_buffer_flush() {
    if (hardware_buffer_vertices_count) {
        // ref:
        // http://stackoverflow.com/questions/5009014/draw-square-with-opengl-es-for-ios
        if (hardware_buffer_vertices_count == hardware_buffer_texcoords_count) {
            glVertexPointer(2, GL_FLOAT, 2 * sizeof(GL_FLOAT),
                            hardware_buffer_vertices); // http://www.opengl.org/sdk/docs/man2/xhtml/glVertexPointer.xml
            glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GL_FLOAT),
                              hardware_buffer_texcoords); // http://www.opengl.org/sdk/docs/man2/xhtml/glTexCoordPointer.xml
            glDrawArrays(GL_TRIANGLES, 0,
                         hardware_buffer_vertices_count / 2); // start index, number of indexes
        } else {
            glVertexPointer(3, GL_FLOAT, 3 * sizeof(GL_FLOAT),
                            hardware_buffer_vertices); // http://www.opengl.org/sdk/docs/man2/xhtml/glVertexPointer.xml
            glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GL_FLOAT),
                              hardware_buffer_texcoords); // http://www.opengl.org/sdk/docs/man2/xhtml/glTexCoordPointer.xml
            glDrawArrays(GL_TRIANGLES, 0,
                         hardware_buffer_vertices_count / 3); // start index, number of indexes
        }
        hardware_buffer_vertices_count = 0;
        hardware_buffer_texcoords_count = 0;
    }
}

void set_smooth(int32 new_mode_shrunk, int32 new_mode_stretched) {
    static int32 current_mode_shrunk;
    current_mode_shrunk = render_state.source->smooth_shrunk;
    static int32 current_mode_stretched;
    current_mode_stretched = render_state.source->smooth_stretched;
    if (new_mode_shrunk == current_mode_shrunk && new_mode_stretched == current_mode_stretched)
        return;
    hardware_buffer_flush();
    if (new_mode_shrunk == SMOOTH_MODE__DONT_SMOOTH) {
        if (render_state.source->PO2_fix == PO2_FIX__MIPMAPPED) {
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        } else {
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                            GL_NEAREST); // Use _MAPTRIANGLE's _SMOOTHSHRUNK to
                                         // apply linear filtering here
        }
    }
    if (new_mode_shrunk == SMOOTH_MODE__SMOOTH) {
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    }
    if (new_mode_stretched == SMOOTH_MODE__DONT_SMOOTH) {
        if (render_state.source->PO2_fix == PO2_FIX__MIPMAPPED) {
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        } else {
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        }
    }
    if (new_mode_stretched == SMOOTH_MODE__SMOOTH) {
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
    render_state.source->smooth_shrunk = new_mode_shrunk;
    render_state.source->smooth_stretched = new_mode_stretched;
}

void set_texture_wrap(int32 new_mode) {
    static int32 current_mode;
    current_mode = render_state.source->texture_wrap;
    if (new_mode == current_mode)
        return;
    hardware_buffer_flush();
    if (new_mode == TEXTURE_WRAP_MODE__DONT_WRAP) {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }
    if (new_mode == TEXTURE_WRAP_MODE__WRAP) {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    }
    render_state.source->texture_wrap = new_mode;
}

void set_alpha(int32 new_mode) {
    static int32 current_mode;
    current_mode = render_state.use_alpha;
    if (new_mode == current_mode)
        return;
    hardware_buffer_flush();
    if (new_mode == ALPHA_MODE__DONT_BLEND) {
        glDisable(GL_BLEND);
    }
    if (new_mode == ALPHA_MODE__BLEND) {
        glEnable(GL_BLEND);
        if (framebufferobjects_supported) {
            // glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
            // GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
            glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
        } else {
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        }
    }
    render_state.use_alpha = new_mode;
}

void set_depthbuffer(int32 new_mode) {

    static int32 current_mode;
    current_mode = render_state.depthbuffer_mode;
    if (new_mode == current_mode)
        return;
    hardware_buffer_flush();
    if (new_mode == DEPTHBUFFER_MODE__OFF) {
        glDisable(GL_DEPTH_TEST);
        glAlphaFunc(GL_ALWAYS, 0);
    }
    if (new_mode == DEPTHBUFFER_MODE__ON) {
        glEnable(GL_DEPTH_TEST);
        glDepthMask(GL_TRUE);
        glAlphaFunc(GL_GREATER, 0.001);
        glEnable(GL_ALPHA_TEST);
    }
    if (new_mode == DEPTHBUFFER_MODE__LOCKED) {
        glEnable(GL_DEPTH_TEST);
        glDepthMask(GL_FALSE);
        glAlphaFunc(GL_ALWAYS, 0);
    }
    render_state.depthbuffer_mode = new_mode;
}

void set_cull_mode(int32 new_mode) {
    static int32 current_mode;
    current_mode = render_state.cull_mode;
    if (new_mode == current_mode)
        return;
    hardware_buffer_flush();
    if (new_mode == CULL_MODE__NONE) {
        glDisable(GL_CULL_FACE);
    }
    if (new_mode == CULL_MODE__CLOCKWISE_ONLY) {
        glFrontFace(GL_CW);
        if (current_mode != CULL_MODE__ANTICLOCKWISE_ONLY)
            glEnable(GL_CULL_FACE);
    }
    if (new_mode == CULL_MODE__ANTICLOCKWISE_ONLY) {
        glFrontFace(GL_CCW);
        if (current_mode != CULL_MODE__CLOCKWISE_ONLY)
            glEnable(GL_CULL_FACE);
    }
    render_state.cull_mode = new_mode;
}

void set_view(int32 new_mode) { // set view can only be called after the correct
                                // destination is chosen
    static int32 current_mode;
    current_mode = render_state.view_mode;
    if (new_mode == current_mode)
        return;
    hardware_buffer_flush();
    if (new_mode == VIEW_MODE__RESET) {
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_ALPHA_TEST);
        glDisable(GL_BLEND);
        glDisable(GL_COLOR_MATERIAL);
        glDisable(GL_DEPTH_TEST);
        glDepthMask(GL_TRUE);
        glDisable(GL_LIGHTING);
        glFrontFace(GL_CCW);
        glCullFace(GL_BACK);
        glDisable(GL_CULL_FACE);
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glAlphaFunc(GL_ALWAYS, 0);
        if (framebufferobjects_supported)
            glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
        glClear(GL_DEPTH_BUFFER_BIT);
        glColor4f(1.f, 1.f, 1.f, 1.f);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        // invalidate current states
        set_alpha(ALPHA_MODE__UNKNOWN);
        set_depthbuffer(DEPTHBUFFER_MODE__UNKNOWN);
        set_cull_mode(CULL_MODE__UNKNOWN);
        set_render_source(INVALID_HARDWARE_HANDLE);
        set_render_dest(INVALID_HARDWARE_HANDLE);
        new_mode = VIEW_MODE__UNKNOWN; // resets are performed before unknown
                                       // operations are executed
    }
    if (new_mode == VIEW_MODE__2D) {
        if (current_mode != VIEW_MODE__3D) {
            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
            glDisable(GL_COLOR_MATERIAL);
            glDisable(GL_LIGHTING);
            set_alpha(ALPHA_MODE__BLEND);
            glEnable(GL_TEXTURE_2D);
            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glCullFace(GL_BACK);
        }

        if (render_state.dest_handle == 0) {
            static int32 dst_w, dst_h;
            static int32 scale_factor = 0;

#    ifdef QB64_MACOSX
            if (scale_factor == 0) {
                // by default scale_factor should be 1, but in macOS Catalina
                // (10.15.*) scale_factor must be setted in 2
                // * in cases where the app is executed on system with Retina
                // Display
                scale_factor = 1; // by default

                // lookup for retina/5k output from system_profiler (storing all
                // outpun in stream)
                bool b_isRetina, b_is5k;
                FILE *consoleStream = popen("system_profiler SPDisplaysDataType 2>/dev/null", "r");
                if (consoleStream) {
                    char buffer[128];
                    while (!feof(consoleStream)) {
                        if (fgets(buffer, 128, consoleStream) != NULL) {
                            std::string szBuffer(buffer);

                            if (!b_isRetina)
                                b_isRetina = (szBuffer.rfind("Retina") != ULONG_MAX);
                            if (!b_is5k)
                                b_is5k = (szBuffer.rfind("5K") != ULONG_MAX);
                        }
                    }
                }
                pclose(consoleStream);

                if (b_isRetina || b_is5k) {
                    // apply only factor = 2 if macOS is Catalina (11.15.* //
                    // kern.osrelease 19.*)
                    char str[256];
                    size_t size = sizeof(str);
                    int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);

                    std::string sz_osrelease(str);
                    if (sz_osrelease.rfind("19.") == 0)
                        scale_factor = 2;
                }
            }
#    else
            scale_factor = 1;
#    endif

            dst_w = environment__window_width;
            dst_h = environment__window_height;

            // alert(dst_w);
            // alert(dst_h);

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(0.0, dst_w, 0.0, dst_h, -1.0, 1.0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glScalef(1, -1, 1);         // flip vertically
            glTranslatef(0, -dst_h, 0); // move to new vertical position
            glViewport(0, 0, dst_w * scale_factor, dst_h * scale_factor);

        } else {
            static hardware_img_struct *hardware_img;
            hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, render_state.dest_handle);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluOrtho2D(0, hardware_img->w, 0, hardware_img->h);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glViewport(0, 0, hardware_img->w, hardware_img->h);
        }
    }
    if (new_mode == VIEW_MODE__3D) {
        if (current_mode != VIEW_MODE__2D) {
            glColor4f(1.f, 1.f, 1.f, 1.f);
            glDisable(GL_COLOR_MATERIAL);
            glDisable(GL_LIGHTING);
            set_alpha(ALPHA_MODE__BLEND);
            glEnable(GL_TEXTURE_2D);
            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glCullFace(GL_BACK);
        }
        if (render_state.dest_handle == 0) {
            static int32 dst_w, dst_h;
            dst_w = environment__window_width;
            dst_h = environment__window_height;
            glViewport(0, 0, (GLsizei)dst_w, (GLsizei)dst_h);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();

            // note: the max FOV is 90-degrees (this maximum applies to the
            // longest screen dimension)
            float fov;
            if (environment_2d__screen_scaled_width > environment_2d__screen_scaled_height) {
                fov = 90.0f * ((float)environment__window_width / (float)environment_2d__screen_scaled_width);
                // convert fov from horizontal to vertical
                fov = fov * ((float)dst_h / (float)dst_w);
            } else {
                fov = 90.0f * ((float)environment__window_height / (float)environment_2d__screen_scaled_height);
            }
            gluPerspective(fov, (GLfloat)dst_w / (GLfloat)dst_h, 0.1,
                           10000.0); // Set the Field of view angle (in
                                     // degrees), the aspect ratio of our
                                     // window, and the new and far planes
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
        } else {

            static hardware_img_struct *hardware_img;
            hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, render_state.dest_handle);

            static int32 dst_w, dst_h;
            dst_w = hardware_img->w;
            dst_h = hardware_img->h;
            glViewport(0, 0, (GLsizei)dst_w, (GLsizei)dst_h);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glScalef(1.0, -1.0, 1.0);
            // note: the max FOV is 90-degrees (this maximum applies to the
            // longest screen dimension)
            float fov;
            if (dst_w > dst_h) {
                fov = 90.0f;
                // convert fov from horizontal to vertical
                fov = fov * ((float)dst_h / (float)dst_w);
            } else {
                fov = 90.0f;
            }
            gluPerspective(fov, (GLfloat)dst_w / (GLfloat)dst_h, 0.1,
                           10000.0); // Set the Field of view angle (in
                                     // degrees), the aspect ratio of our
                                     // window, and the new and far planes
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            // alert("3D rendering onto FBO not supported yet");
        }
    }
    render_state.view_mode = new_mode;
} // change_render_state

void set_render_source(int32 new_handle) {
    if (new_handle == INVALID_HARDWARE_HANDLE) {
        hardware_buffer_flush();
        render_state.source_handle = INVALID_HARDWARE_HANDLE;
        return;
    }
    int32 current_handle;
    current_handle = render_state.source_handle;

    if (current_handle == new_handle)
        return;

    hardware_buffer_flush();

    hardware_img_struct *hardware_img;
    hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, new_handle);
    if (hardware_img->texture_handle == 0)
        hardware_img_buffer_to_texture(new_handle);
    glBindTexture(GL_TEXTURE_2D, hardware_img->texture_handle);
    render_state.source_handle = new_handle;
    render_state.source = &hardware_img->source_state;

    // note: some older systems require calling glTexParameterf after textures
    // are rebound
    if (framebufferobjects_supported == 0) {
        render_state.source->smooth_shrunk = SMOOTH_MODE__UNKNOWN;
        render_state.source->smooth_stretched = SMOOTH_MODE__UNKNOWN;
    }
}

void set_render_dest(int32 new_handle) {
    if (new_handle == INVALID_HARDWARE_HANDLE) {
        hardware_buffer_flush();
        render_state.dest_handle = INVALID_HARDWARE_HANDLE;
        set_view(VIEW_MODE__UNKNOWN);
        return;
    }
    // 0=primary surface
    static int32 current_handle;
    current_handle = render_state.dest_handle;
    if (new_handle == current_handle)
        return;
    hardware_buffer_flush();
    set_view(VIEW_MODE__UNKNOWN);
    if (new_handle == 0) {
        if (framebufferobjects_supported)
            glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
        render_state.dest = &dest_render_state0;
    } else {
        static hardware_img_struct *hardware_img;
        hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, new_handle);
        // convert to regular texture first if necessary
        if (hardware_img->texture_handle == 0)
            hardware_img_buffer_to_texture(new_handle);
        // does it have a dest context/FBO? if not create one
        if (hardware_img->dest_context_handle == 0) {

            static GLuint framebuffer_handle;
            framebuffer_handle = 0;
            glGenFramebuffersEXT(1, &framebuffer_handle);
            glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_handle);
            hardware_img->dest_context_handle = framebuffer_handle;
            glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hardware_img->texture_handle, 0);

            // glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            // glClear(GL_COLOR_BUFFER_BIT);
            glColor4f(1.f, 1.f, 1.f, 1.f);

            set_render_source(INVALID_HARDWARE_HANDLE);

        } else {
            glBindFramebufferEXT(GL_FRAMEBUFFER, hardware_img->dest_context_handle);
        }
        render_state.dest = &hardware_img->dest_state;
    }
    render_state.dest_handle = new_handle;
}

void hardware_img_put(int32 dst_x1, int32 dst_y1, int32 dst_x2, int32 dst_y2, int32 src_img, int32 dst_img, int32 src_x1, int32 src_y1, int32 src_x2,
                      int32 src_y2, int32 use_alpha, int32 smooth) {

    if (dst_img < 0)
        dst_img = 0; // both layers render to the primary context

    // ensure dst_x1/y1 represent top-left co-ordinate of destination
    static int32 swap_tmp;
    if (dst_x2 < dst_x1) {
        swap_tmp = dst_x2;
        dst_x2 = dst_x1;
        dst_x1 = swap_tmp;
        swap_tmp = src_x2;
        src_x2 = src_x1;
        src_x1 = swap_tmp;
    }
    if (dst_y2 < dst_y1) {
        swap_tmp = dst_y2;
        dst_y2 = dst_y1;
        dst_y1 = swap_tmp;
        swap_tmp = src_y2;
        src_y2 = src_y1;
        src_y1 = swap_tmp;
    }

    set_render_dest(dst_img);

    set_view(VIEW_MODE__2D);

    if (dst_img) {
        // static hardware_img_struct* dst_hardware_img;
        // dst_hardware_img=(hardware_img_struct*)list_get(hardware_img_handles,dst_img);
        //(no specific action required here --area reserved for future use)
    } else { // dest is 0
        environment_2d__window_rect_struct *rect;
        rect = environment_2d__screen_to_window_rect(dst_x1, dst_y1, dst_x2, dst_y2);
        dst_x1 = rect->x1;
        dst_y1 = rect->y1;
        dst_x2 = rect->x2;
        dst_y2 = rect->y2;
    }

    set_render_source(src_img);

    static hardware_img_struct *src_hardware_img;
    static int32 src_h, src_w;
    src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src_img);
    src_h = src_hardware_img->h;
    src_w = src_hardware_img->w;

    if (smooth) {
        set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__SMOOTH);
    } else {
        set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__DONT_SMOOTH);
    }

    if (use_alpha) {
        set_alpha(ALPHA_MODE__BLEND);
    } else {
        set_alpha(ALPHA_MODE__DONT_BLEND);
    }

    set_depthbuffer(DEPTHBUFFER_MODE__OFF);
    set_cull_mode(CULL_MODE__NONE);

    set_texture_wrap(TEXTURE_WRAP_MODE__DONT_WRAP);

    // adjust for render (x2 & y2 need to be one greater than the destination
    // offset)
    dst_x2++;
    dst_y2++;

    if (src_hardware_img->source_state.PO2_fix) {
        src_w = src_hardware_img->PO2_w;
        src_h = src_hardware_img->PO2_h;
    }

    // calc source texture co-ordinates
    static float x1f, y1f, x2f, y2f;
    if (src_x1 <= src_x2) {
        x1f = ((float)src_x1 + 0.01f) / (float)src_w;
        x2f = ((float)src_x2 + 0.99f) / (float)src_w;
    } else {
        x2f = ((float)src_x2 + 0.01f) / (float)src_w;
        x1f = ((float)src_x1 + 0.99f) / (float)src_w;
    }
    if (src_y1 <= src_y2) {
        y1f = ((float)src_y1 + 0.01f) / (float)src_h;
        y2f = ((float)src_y2 + 0.99f) / (float)src_h;
    } else {
        y2f = ((float)src_y2 + 0.01f) / (float)src_h;
        y1f = ((float)src_y1 + 0.99f) / (float)src_h;
    }

    // expand buffers if necessary
    if ((hardware_buffer_vertices_count + 18) > hardware_buffer_vertices_max) {
        hardware_buffer_vertices_max = hardware_buffer_vertices_max * 2 + 18;
        hardware_buffer_vertices = (float *)realloc(hardware_buffer_vertices, hardware_buffer_vertices_max * sizeof(float));
    }
    if ((hardware_buffer_texcoords_count + 12) > hardware_buffer_texcoords_max) {
        hardware_buffer_texcoords_max = hardware_buffer_texcoords_max * 2 + 12;
        hardware_buffer_texcoords = (float *)realloc(hardware_buffer_texcoords, hardware_buffer_texcoords_max * sizeof(float));
    }

    // clockwise
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f;

    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f;

    // hardware_buffer_flush(); //uncomment for debugging only
}

void hardware_img_tri2d(float dst_x1, float dst_y1, float dst_x2, float dst_y2, float dst_x3, float dst_y3, int32 src_img, int32 dst_img, float src_x1,
                        float src_y1, float src_x2, float src_y2, float src_x3, float src_y3, int32 use_alpha, int32 smooth) {

    if (dst_img < 0)
        dst_img = 0; // both layers render to the primary context

    set_render_dest(dst_img);
    set_view(VIEW_MODE__2D);

    if (dst_img) {
        static hardware_img_struct *dst_hardware_img;
        dst_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, dst_img);

        static int32 dst_w, dst_h;
        dst_w = dst_hardware_img->w;
        dst_h = dst_hardware_img->h;
        // SEAMLESS adjustments:
        // reduce texture co-ordinates (maintaining top-left)
        //(todo)
        // NON-SEAMLESS adjustments:
        // Extend rhs/bottom row to fill extra pixel space
        // calculate extents
        int32 rx1;
        int32 rx2;
        rx1 = dst_x1;
        if (dst_x2 < rx1) {
            rx1 = dst_x2;
        }
        if (dst_x3 < rx1) {
            rx1 = dst_x3;
        }
        rx2 = dst_x1;
        if (dst_x2 > rx2) {
            rx2 = dst_x2;
        }
        if (dst_x3 > rx2) {
            rx2 = dst_x3;
        }
        float xr; // the multiplier for where we should be (1=no change)
        if (rx1 == rx2) {
            xr = 1.0f;
        } else {
            xr = ((float)rx2 - (float)rx1 + 1.0) / ((float)rx2 - (float)rx1);
        }
        int32 ry1;
        int32 ry2;
        ry1 = dst_y1;
        if (dst_y2 < ry1) {
            ry1 = dst_y2;
        }
        if (dst_y3 < ry1) {
            ry1 = dst_y3;
        }
        ry2 = dst_y1;
        if (dst_y2 > ry2) {
            ry2 = dst_y2;
        }
        if (dst_y3 > ry2) {
            ry2 = dst_y3;
        }
        float yr; // the multiplier for where we should be (1=no change)
        if (ry1 == ry2) {
            yr = 1.0f;
        } else {
            yr = ((float)ry2 - (float)ry1 + 1.0f) / ((float)ry2 - (float)ry1);
        }
        // apply multipliers so right-most and bottom-most rows will be filled
        static int32 basex;
        basex = rx1;
        dst_x1 = qbr_float_to_long(((float)(dst_x1 - rx1)) * xr + (float)basex);
        dst_x2 = qbr_float_to_long(((float)(dst_x2 - rx1)) * xr + (float)basex);
        dst_x3 = qbr_float_to_long(((float)(dst_x3 - rx1)) * xr + (float)basex);
        static int32 basey;
        basey = ry1;
        dst_y1 = qbr_float_to_long(((float)(dst_y1 - ry1)) * yr + (float)basey);
        dst_y2 = qbr_float_to_long(((float)(dst_y2 - ry1)) * yr + (float)basey);
        dst_y3 = qbr_float_to_long(((float)(dst_y3 - ry1)) * yr + (float)basey);

    } else { // dest is 0

        static int32 dst_w, dst_h;
        dst_w = environment__window_width;
        dst_h = environment__window_height;
        // SEAMLESS adjustments:
        // reduce texture co-ordinates (maintaining top-left)
        //(todo)
        // NON-SEAMLESS adjustments:
        // Extend rhs/bottom row to fill extra pixel space
        // calculate extents
        int32 rx1;
        int32 rx2;
        rx1 = dst_x1;
        if (dst_x2 < rx1) {
            rx1 = dst_x2;
        }
        if (dst_x3 < rx1) {
            rx1 = dst_x3;
        }
        rx2 = dst_x1;
        if (dst_x2 > rx2) {
            rx2 = dst_x2;
        }
        if (dst_x3 > rx2) {
            rx2 = dst_x3;
        }
        float xr; // the multiplier for where we should be (1=no change)
        if (rx1 == rx2) {
            xr = 1.0f;
        } else {
            xr = ((float)rx2 - (float)rx1 + 1.0) / ((float)rx2 - (float)rx1);
        }
        int32 ry1;
        int32 ry2;
        ry1 = dst_y1;
        if (dst_y2 < ry1) {
            ry1 = dst_y2;
        }
        if (dst_y3 < ry1) {
            ry1 = dst_y3;
        }
        ry2 = dst_y1;
        if (dst_y2 > ry2) {
            ry2 = dst_y2;
        }
        if (dst_y3 > ry2) {
            ry2 = dst_y3;
        }
        float yr; // the multiplier for where we should be (1=no change)
        if (ry1 == ry2) {
            yr = 1.0f;
        } else {
            yr = ((float)ry2 - (float)ry1 + 1.0f) / ((float)ry2 - (float)ry1);
        }
        // apply multipliers so right-most and bottom-most rows will be filled
        static int32 basex;
        basex = qbr_float_to_long(((float)(rx1)) * environment_2d__screen_x_scale + (float)environment_2d__screen_x1);
        dst_x1 = basex + qbr_float_to_long(((float)(dst_x1 - rx1)) * environment_2d__screen_x_scale * xr);
        dst_x2 = basex + qbr_float_to_long(((float)(dst_x2 - rx1)) * environment_2d__screen_x_scale * xr);
        dst_x3 = basex + qbr_float_to_long(((float)(dst_x3 - rx1)) * environment_2d__screen_x_scale * xr);
        static int32 basey;
        basey = qbr_float_to_long(((float)(ry1)) * environment_2d__screen_y_scale + (float)environment_2d__screen_y1);
        dst_y1 = basey + qbr_float_to_long(((float)(dst_y1 - ry1)) * environment_2d__screen_y_scale * yr);
        dst_y2 = basey + qbr_float_to_long(((float)(dst_y2 - ry1)) * environment_2d__screen_y_scale * yr);
        dst_y3 = basey + qbr_float_to_long(((float)(dst_y3 - ry1)) * environment_2d__screen_y_scale * yr);
    }

    set_render_source(src_img);

    static hardware_img_struct *src_hardware_img;
    static int32 src_h, src_w;
    src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src_img);
    src_h = src_hardware_img->h;
    src_w = src_hardware_img->w;

    if (smooth == 0) {
        set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__DONT_SMOOTH);
    }
    if (smooth == 1) {
        set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__SMOOTH);
    }
    if (smooth == 2) {
        set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__DONT_SMOOTH);
    }
    if (smooth == 3) {
        set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__SMOOTH);
    }

    set_texture_wrap(TEXTURE_WRAP_MODE__WRAP);

    if (use_alpha) {
        set_alpha(ALPHA_MODE__BLEND);
    } else {
        set_alpha(ALPHA_MODE__DONT_BLEND);
    }

    set_depthbuffer(DEPTHBUFFER_MODE__OFF);
    set_cull_mode(CULL_MODE__NONE);

    if (src_hardware_img->source_state.PO2_fix) {
        src_w = src_hardware_img->PO2_w;
        src_h = src_hardware_img->PO2_h;
    }

    // calc source texture co-ordinates
    static float x1f, y1f, x2f, y2f, x3f, y3f;
    x1f = ((float)src_x1 + 0.5f) / (float)src_w;
    x2f = ((float)src_x2 + 0.5f) / (float)src_w;
    x3f = ((float)src_x3 + 0.5f) / (float)src_w;
    y1f = ((float)src_y1 + 0.5f) / (float)src_h;
    y2f = ((float)src_y2 + 0.5f) / (float)src_h;
    y3f = ((float)src_y3 + 0.5f) / (float)src_h;

    // expand buffers if necessary
    if ((hardware_buffer_vertices_count + 9) > hardware_buffer_vertices_max) {
        hardware_buffer_vertices_max = hardware_buffer_vertices_max * 2 + 9;
        hardware_buffer_vertices = (float *)realloc(hardware_buffer_vertices, hardware_buffer_vertices_max * sizeof(float));
    }
    if ((hardware_buffer_texcoords_count + 6) > hardware_buffer_texcoords_max) {
        hardware_buffer_texcoords_max = hardware_buffer_texcoords_max * 2 + 6;
        hardware_buffer_texcoords = (float *)realloc(hardware_buffer_texcoords, hardware_buffer_texcoords_max * sizeof(float));
    }

    // clockwise
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x3;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y3;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x3f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y3f;

    // hardware_buffer_flush(); //uncomment for debugging only
}

void clear_depthbuffer(int32 dst_img) {
    hardware_buffer_flush();
    if (dst_img < 0)
        dst_img = 0; // both layers render to the primary context
    set_render_dest(dst_img);
    if (dst_img > 0) {
        hardware_img_requires_depthbuffer((hardware_img_struct *)list_get(hardware_img_handles, dst_img));
    }
    glClear(GL_DEPTH_BUFFER_BIT);
}

void hardware_img_tri3d(float dst_x1, float dst_y1, float dst_z1, float dst_x2, float dst_y2, float dst_z2, float dst_x3, float dst_y3, float dst_z3,
                        int32 src_img, int32 dst_img, float src_x1, float src_y1, float src_x2, float src_y2, float src_x3, float src_y3, int32 use_alpha,
                        int32 smooth, int32 cull_mode, int32 depthbuffer_mode) {

    if (dst_img < 0)
        dst_img = 0; // both layers render to the primary context

    set_render_dest(dst_img);
    set_view(VIEW_MODE__3D);

    if (dst_img) {
        static hardware_img_struct *dst_hardware_img;
        dst_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, dst_img);
        hardware_img_requires_depthbuffer(dst_hardware_img);
    } else { // dest is 0
    }

    set_render_source(src_img);

    static hardware_img_struct *src_hardware_img;
    static int32 src_h, src_w;
    src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src_img);
    src_h = src_hardware_img->h;
    src_w = src_hardware_img->w;

    if (smooth == 0) {
        set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__DONT_SMOOTH);
    }
    if (smooth == 1) {
        set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__SMOOTH);
    }
    if (smooth == 2) {
        set_smooth(SMOOTH_MODE__SMOOTH, SMOOTH_MODE__DONT_SMOOTH);
    }
    if (smooth == 3) {
        set_smooth(SMOOTH_MODE__DONT_SMOOTH, SMOOTH_MODE__SMOOTH);
    }

    set_texture_wrap(TEXTURE_WRAP_MODE__WRAP);

    if (use_alpha) {
        set_alpha(ALPHA_MODE__BLEND);
    } else {
        set_alpha(ALPHA_MODE__DONT_BLEND);
    }

    set_depthbuffer(depthbuffer_mode);

    // on frame buffers the 3D perspective is flipped vertically reversing the
    // cull direction
    if (dst_img > 0) {
        if (cull_mode == CULL_MODE__CLOCKWISE_ONLY) {
            cull_mode = CULL_MODE__ANTICLOCKWISE_ONLY;
        } else {
            if (cull_mode == CULL_MODE__ANTICLOCKWISE_ONLY)
                cull_mode = CULL_MODE__CLOCKWISE_ONLY;
        }
    }

    set_cull_mode(cull_mode);

    if (src_hardware_img->source_state.PO2_fix) {
        src_w = src_hardware_img->PO2_w;
        src_h = src_hardware_img->PO2_h;
    }

    // calc source texture co-ordinates
    static float x1f, y1f, x2f, y2f, x3f, y3f;
    x1f = ((float)src_x1 + 0.5f) / (float)src_w;
    x2f = ((float)src_x2 + 0.5f) / (float)src_w;
    x3f = ((float)src_x3 + 0.5f) / (float)src_w;
    y1f = ((float)src_y1 + 0.5f) / (float)src_h;
    y2f = ((float)src_y2 + 0.5f) / (float)src_h;
    y3f = ((float)src_y3 + 0.5f) / (float)src_h;

    // expand buffers if necessary
    if ((hardware_buffer_vertices_count + 9) > hardware_buffer_vertices_max) {
        hardware_buffer_vertices_max = hardware_buffer_vertices_max * 2 + 9;
        hardware_buffer_vertices = (float *)realloc(hardware_buffer_vertices, hardware_buffer_vertices_max * sizeof(float));
    }
    if ((hardware_buffer_texcoords_count + 6) > hardware_buffer_texcoords_max) {
        hardware_buffer_texcoords_max = hardware_buffer_texcoords_max * 2 + 6;
        hardware_buffer_texcoords = (float *)realloc(hardware_buffer_texcoords, hardware_buffer_texcoords_max * sizeof(float));
    }

    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_z1;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_z2;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_x3;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_y3;
    hardware_buffer_vertices[hardware_buffer_vertices_count++] = dst_z3;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y1f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y2f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = x3f;
    hardware_buffer_texcoords[hardware_buffer_texcoords_count++] = y3f;
    // hardware_buffer_flush(); //uncomment for debugging only
}

static int32 software_screen_hardware_frame = 0;

static int32 in_GLUT_DISPLAY_REQUEST = 0;

void GLUT_DISPLAY_REQUEST() {

    if (in_GLUT_DISPLAY_REQUEST) {
        return;
    }
    in_GLUT_DISPLAY_REQUEST = 1;

    // general use variables
    static int32 i, i2, i3;
    static int32 x, y, x2, y2;

    // determine which software frame to display
    static int32 last_i; // the last software frame displayed
    last_i = -1;
    for (i2 = 0; i2 <= 2; i2++) {
        if (display_frame[i2].state == DISPLAY_FRAME_STATE__DISPLAYING) {
            last_i = i2;
        }
    }
    i = -1;
    static int64 highest_order;
    highest_order = 0;
    if (last_i != -1)
        highest_order = display_frame[last_i].order; // avoid any frames below the current one
    for (i2 = 0; i2 <= 2; i2++) {
        if (display_frame[i2].state == DISPLAY_FRAME_STATE__READY && display_frame[i2].order > highest_order) {
            highest_order = display_frame[i2].order;
            i = i2;
        }
    }
    if (i == -1)
        i = last_i;
    if (i == -1) {
        in_GLUT_DISPLAY_REQUEST = 0;
        return; // no frames exist yet, so screen size cannot be determined,
                // therefore no action possible
    }
    if (i != last_i) {
        for (i2 = 0; i2 <= 2; i2++) {
            if (display_frame[i2].order < display_frame[i].order &&
                (display_frame[i2].state == DISPLAY_FRAME_STATE__DISPLAYING || display_frame[i2].state == DISPLAY_FRAME_STATE__READY))
                display_frame[i2].state = DISPLAY_FRAME_STATE__EMPTY;
        }
        display_frame[i].state = DISPLAY_FRAME_STATE__DISPLAYING;
    }

    static int64 order;
    order = last_hardware_display_frame_order;

    static int32 first_command_prev_order = 0;
    static int32 rerender_prev_hardware_frame = 0;
    rerender_prev_hardware_frame = 0;

    // if no new software frame, only proceed if there is _GL content to render
    if (last_rendered_hardware_display_frame_order == last_hardware_display_frame_order) {
        if (i == last_i) {
            if (full_screen_set == -1) {    // no pending full-screen changes
                if (os_resize_event == 0) { // no resize events
#    ifndef DEPENDENCY_GL // we aren't using SUB _GL
                    in_GLUT_DISPLAY_REQUEST = 0;
                    return;
#    endif
                    if (displayorder_glrender == 0) {
                        in_GLUT_DISPLAY_REQUEST = 0;
                        return;
                    }
                    if (first_command_prev_order) {
                        rerender_prev_hardware_frame = 1;
                        // reset next command to prev hardware frame's handle
                        // (if any)
                        last_hardware_command_rendered = first_command_prev_order;
                    }
                }
            }
        }
    }

    first_command_prev_order = 0;

    // set environment variables
    environment_2d__screen_width = display_frame[i].w;
    environment_2d__screen_height = display_frame[i].h;

    os_resize_event = 0; // turn off flag which forces a render to take place
                         // even if no content has changed

    if ((full_screen == 0) && (full_screen_set == -1)) { // not in (or attempting to enter) full screen

        display_required_x = display_frame[i].w;
        display_required_y = display_frame[i].h;
        static int32 framesize_changed;
        framesize_changed = 0;
        if ((display_required_x != resize_snapback_x) || (display_required_y != resize_snapback_y))
            framesize_changed = 1;

        resize_auto_ideal_aspect = (float)display_frame[i].w / (float)display_frame[i].h;
        resize_snapback_x = display_required_x;
        resize_snapback_y = display_required_y;

        if (resize_auto) {
            // maintain aspect ratio
            static float ar;
            ar = (float)display_x / (float)display_y;
            if ((ar != resize_auto_accept_aspect) && (ar != resize_auto_ideal_aspect)) {
                // set new size
                static int32 x, y;
                if (display_x_prev == display_x) {
                    y = display_y;
                    x = (float)y * resize_auto_ideal_aspect;
                }
                if (display_y_prev == display_y) {
                    x = display_x;
                    y = (float)x / resize_auto_ideal_aspect;
                }
                if ((display_y_prev != display_y) && (display_x_prev != display_x)) {
                    if (abs(display_y_prev - display_y) < abs(display_x_prev - display_x)) {
                        x = display_x;
                        y = (float)x / resize_auto_ideal_aspect;
                    } else {
                        y = display_y;
                        x = (float)y * resize_auto_ideal_aspect;
                    }
                }
                resize_auto_accept_aspect = (float)x / (float)y;
                resize_pending = 1;
                glutReshapeWindow(x, y);
                glutPostRedisplay();

                goto auto_resized;
            }
        } // resize_auto

        if ((display_required_x != display_x) || (display_required_y != display_y)) {
            if (resize_snapback || framesize_changed) {
                glutReshapeWindow(display_required_x, display_required_y);
                glutPostRedisplay();
                resize_pending = 1;
            }
        }

    auto_resized:;

    } // not in (or attempting to enter) full screen

    // Pseudo-Fullscreen
    if (!resize_pending) {           // avoid switching to fullscreen before resize
                                     // operations take effect
        if (full_screen_set != -1) { // full screen mode change requested
            if (full_screen_set == 0) {
                if (full_screen != 0) {
                    // exit full screen
                    resize_pending = 1;
                    glutReshapeWindow(display_frame[i].w, display_frame[i].h);
                    glutPostRedisplay();
                }
                full_screen = 0;
                full_screen_set = -1;
            } else {
                if (full_screen == 0) {
                    glutFullScreen();
                }
                full_screen = full_screen_set;
                full_screen_set = -1;
            } // enter full screen
        } // full_screen_set check
    } // size pending check

    // This code is deprecated but kept for reference purposes
    // 1) It was found to be unstable
    // 2) Switching modes means a high chance of losing pre-loaded OpenGL
    // hardware textures/surfaces
    /*
        static int32 glut_window;
        //fullscreen
        if (!resize_pending){//avoid switching to fullscreen before resize
       operations take effect if (full_screen_set!=-1){//full screen mode change
       requested if (full_screen_set==0){
        //exit full screen
        glutLeaveGameMode();
        glutSetWindow(glut_window);
        reinit_glut_callbacks();
        full_screen=0;
        full_screen_set=-1;
        return;
        }else{
        static char game_mode_string[1000];
        static int32 game_mode_string_i;
        game_mode_string_i=0;
        game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d",
       display_frame[i].w); game_mode_string[game_mode_string_i++]=120;//"x"
        game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d",
       display_frame[i].h); game_mode_string[game_mode_string_i++]=58;//":"
        game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d",
       32); glutGameModeString(game_mode_string);
        if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)){
        //full screen using native dimensions which match the frame size
        if (full_screen==0) glut_window=glutGetWindow();
        glutEnterGameMode();
        fullscreen_width=display_frame[i].w;
       fullscreen_height=display_frame[i].h; reinit_glut_callbacks();
        full_screen=full_screen_set;//it's currently irrelevant if it is
       stretched or 1:1 full_screen_set=-1; return; }else{ //native dimensions
       not possible
        //attempt full screen using desktop dimensions
        static int32 w; w=glutGet(GLUT_SCREEN_WIDTH);
        static int32 h; h=glutGet(GLUT_SCREEN_HEIGHT);
        game_mode_string_i=0;
        game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d",
       w); game_mode_string[game_mode_string_i++]=120;//"x"
        game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d",
       h); game_mode_string[game_mode_string_i++]=58;//":"
        game_mode_string_i+=sprintf(&game_mode_string[game_mode_string_i], "%d",
       32); glutGameModeString(game_mode_string);
        if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)){
        //full screen using desktop dimensions
        if (full_screen==0) glut_window=glutGetWindow();
        glutEnterGameMode();
        fullscreen_width=w; fullscreen_height=h;
        reinit_glut_callbacks();
        screen_scale=full_screen_set;
        full_screen=full_screen_set;
        full_screen_set=-1;
        return;
        }else{
        //cannot enter full screen
        full_screen=0;
        full_screen_set=-1;
        }
        }
        }//enter full screen
        }//full_screen_set check
        }//size pending check
    */

    // set window environment variables
    environment__window_width = display_x;
    environment__window_height = display_y;

    prepare_environment_2d();

    // need a few variables here

    static int32 first_hardware_layer_rendered;
    static int32 first_hardware_layer_command;
    first_hardware_layer_rendered = 0;
    first_hardware_layer_command = 0;

    static int32 level;
    for (level = 0; level <= 5; level++) {

        static int32 x1, y1, x2, y2;

        if (level == 0) {
            set_render_dest(0);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        } else {

            if (level == displayorder_glrender) {
#    ifdef DEPENDENCY_GL

                set_view(VIEW_MODE__RESET);

                if (close_program || dont_call_sub_gl || suspend_program || stop_program)
                    goto abort_gl;
                display_lock_request++;
                while (display_lock_confirmed < display_lock_request) {
                    if (close_program || dont_call_sub_gl || suspend_program || stop_program)
                        goto abort_gl;
                    if (display_lock_modal_count.load(std::memory_order_acquire) > 0) {
                        display_lock_confirmed = display_lock_request;
                        break;
                    }
                    qbevent = 1;
                    Sleep(0);
                }
                sub_gl_called = 1;
                SUB__GL();
                sub_gl_called = 0;
            abort_gl:;
                display_lock_released = display_lock_confirmed;

#    endif // DEPENDENCY_GL
            } // level==displayorder_glrender

            if (level == displayorder_screen) { // defaults to 1

                if (software_screen_hardware_frame != 0 && i != last_i) {
                    free_hardware_img(software_screen_hardware_frame, 847001);
                }
                if (i != last_i || software_screen_hardware_frame == 0) {
                    software_screen_hardware_frame = new_hardware_img(display_frame[i].w, display_frame[i].h, display_frame[i].bgra, NULL);
                }

                static hardware_img_struct *f1;
                f1 = (hardware_img_struct *)list_get(hardware_img_handles, software_screen_hardware_frame);
                if (software_screen_hardware_frame == 0) {
                    gui_alert("Invalid software_screen_hardware_frame!!");
                }
                if (f1 == NULL)
                    gui_alert("Invalid software_screen_hardware_frame!");

                static int32 use_alpha;
                use_alpha = 0;
                if (level > 1)
                    use_alpha = 1;

                // put the software screen
                hardware_img_put(0, 0, environment_2d__screen_width - 1, environment_2d__screen_height - 1, software_screen_hardware_frame, 0, 0, 0, f1->w - 1,
                                 f1->h - 1, use_alpha, environment_2d__screen_smooth);
                hardware_buffer_flush();

            } // level==displayorder_screen

            if (level == displayorder_hardware || level == displayorder_hardware1) {

                static int32 dst;
                dst = 0;
                if (level == displayorder_hardware1)
                    dst = -1;

                static int32 command;
                command = 0;

                static int32 caller_flag;
                caller_flag = 0;

                if (first_hardware_layer_rendered == 0) {

                    if (first_hardware_command) {

                        if (last_hardware_command_rendered) {

                            if (rerender_prev_hardware_frame) {
                                command = last_hardware_command_rendered;
                                caller_flag = 100;
                            } else {
                                hardware_graphics_command_struct *last_hgc =
                                    (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, last_hardware_command_rendered);
                                if (last_hgc == NULL)
                                    gui_alert("Rendering: Last HGC is NULL!");
                                command = last_hgc->next_command;
                                caller_flag = 200;
                            }

                        } else {

                            command = first_hardware_command;
                            caller_flag = 300;
                        }

                        // process/skip pending hardware puts before this
                        // frame's order value
                        while (command) {
                            hardware_graphics_command_struct *hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, command);
                            if (hgc->order < order) {

                                if (hgc->command == HARDWARE_GRAPHICS_COMMAND__FREEIMAGE) {
                                    free_hardware_img(hgc->src_img, 847002 + caller_flag);
                                }

                                if (hgc->command == HARDWARE_GRAPHICS_COMMAND__PUTIMAGE) {
                                    if (hgc->dst_img > 0) { // note: rendering to the old
                                                            // default surface is pointless, but
                                                            // renders onto maintained hardware
                                                            // images are still required
                                        hardware_img_put(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->src_img, hgc->dst_img, hgc->src_x1,
                                                         hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->use_alpha, hgc->smooth);
                                    }
                                }

                                if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE) {
                                    if (hgc->dst_img > 0) { // note: rendering to the old
                                                            // default surface is pointless, but
                                                            // renders onto maintained hardware
                                                            // images are still required
                                        hardware_img_tri2d(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->dst_x3, hgc->dst_y3, hgc->src_img,
                                                           hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->src_x3, hgc->src_y3,
                                                           hgc->use_alpha, hgc->smooth);
                                    }
                                }

                                if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE3D) {
                                    if (hgc->dst_img > 0) { // note: rendering to the old
                                                            // default surface is pointless, but
                                                            // renders onto maintained hardware
                                                            // images are still required
                                        hardware_img_tri3d(hgc->dst_x1, hgc->dst_y1, hgc->dst_z1, hgc->dst_x2, hgc->dst_y2, hgc->dst_z2, hgc->dst_x3,
                                                           hgc->dst_y3, hgc->dst_z3, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2,
                                                           hgc->src_y2, hgc->src_x3, hgc->src_y3, hgc->use_alpha, hgc->smooth, hgc->cull_mode,
                                                           hgc->depthbuffer_mode);
                                    }
                                }

                                if (hgc->command == HARDWARE_GRAPHICS_COMMAND__CLEAR_DEPTHBUFFER) {
                                    if (hgc->dst_img > 0) { // note: rendering to the old
                                                            // default surface is pointless, but
                                                            // renders onto maintained hardware
                                                            // images are still required
                                        clear_depthbuffer(hgc->dst_img);
                                    }
                                }

                                last_hardware_command_rendered = command;
                                if (next_hardware_command_to_remove == 0)
                                    next_hardware_command_to_remove = command;
                                command = hgc->next_command;
                                hgc->remove = 1;
                            } else {
                                goto found_command_from_current_order;
                            }
                        }
                    found_command_from_current_order:;

                    } // first_hardware_command

                    first_hardware_layer_command = command;
                } else {
                    command = first_hardware_layer_command;
                }

                // process pending hardware puts for this frame's order value
                while (command) {
                    hardware_graphics_command_struct *hgc = (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, command);
                    if (hgc == NULL) {

                        hardware_graphics_command_struct *hgcx =
                            (hardware_graphics_command_struct *)list_get(hardware_graphics_command_handles, next_hardware_command_to_remove);
                        gui_alert("Renderer: Command does not exist: command = %i, hgcx->order = %lld, order = %lld", command, hgcx->order, order);
                    }
                    if (hgc->order == order) {
                        if (first_command_prev_order == 0)
                            first_command_prev_order = command;

                        if (hgc->command == HARDWARE_GRAPHICS_COMMAND__FREEIMAGE && rerender_prev_hardware_frame == 0 && first_hardware_layer_rendered == 0) {
                            free_hardware_img(hgc->src_img, 847003);
                        }

                        if (hgc->command == HARDWARE_GRAPHICS_COMMAND__PUTIMAGE) {
                            if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) {
                                if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) {
                                    hardware_img_put(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1,
                                                     hgc->src_x2, hgc->src_y2, hgc->use_alpha, hgc->smooth);
                                }
                            }
                        }

                        if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE) {
                            if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) {
                                if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) {
                                    hardware_img_tri2d(hgc->dst_x1, hgc->dst_y1, hgc->dst_x2, hgc->dst_y2, hgc->dst_x3, hgc->dst_y3, hgc->src_img, hgc->dst_img,
                                                       hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->src_x3, hgc->src_y3, hgc->use_alpha,
                                                       hgc->smooth);
                                }
                            }
                        }

                        if (hgc->command == HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE3D) {
                            if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) {
                                if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) {
                                    hardware_img_tri3d(hgc->dst_x1, hgc->dst_y1, hgc->dst_z1, hgc->dst_x2, hgc->dst_y2, hgc->dst_z2, hgc->dst_x3, hgc->dst_y3,
                                                       hgc->dst_z3, hgc->src_img, hgc->dst_img, hgc->src_x1, hgc->src_y1, hgc->src_x2, hgc->src_y2, hgc->src_x3,
                                                       hgc->src_y3, hgc->use_alpha, hgc->smooth, hgc->cull_mode, hgc->depthbuffer_mode);
                                }
                            }
                        }

                        if (hgc->command == HARDWARE_GRAPHICS_COMMAND__CLEAR_DEPTHBUFFER) {
                            if (rerender_prev_hardware_frame == 0 || hgc->dst_img <= 0) {
                                if ((hgc->dst_img > 0 && first_hardware_layer_rendered == 0) || hgc->dst_img == dst) {
                                    clear_depthbuffer(hgc->dst_img);
                                }
                            }
                        }

                        last_hardware_command_rendered = command;
                        if (next_hardware_command_to_remove == 0)
                            next_hardware_command_to_remove = command; //!!!! should be prev to this command
                        command = hgc->next_command;
                        hgc->remove = 1;
                    } else {
                        goto finished_all_commands_for_current_frame;
                    }
                }
            finished_all_commands_for_current_frame:;

                first_hardware_layer_rendered = 1;

                hardware_buffer_flush();
            } // level==displayorder_hardware||level==displayorder_hardware1

            if (level == 5) {

                if (environment_2d__letterbox) {

                    // create a black texture (if not yet created)
                    static uint32 black_pixel = 0x00000000;
                    static int32 black_texture = 0;
                    if (black_texture == 0) {
                        black_texture = new_hardware_img(1, 1, &black_pixel, NULL);
                    }

                    if (environment_2d__letterbox == 1) {
                        // vertical stripes
                        hardware_img_put(((float)-environment_2d__screen_x1) / environment_2d__screen_x_scale - 1.0f, 0, -1, environment_2d__screen_height - 1,
                                         black_texture, 0, 0, 0, 0, 0, 0, 0);
                        hardware_img_put(environment_2d__screen_width, 0,
                                         (((float)-environment_2d__screen_x1) + (float)environment__window_width - 1.0f) / environment_2d__screen_x_scale +
                                             1.0f,
                                         environment_2d__screen_height - 1, black_texture, 0, 0, 0, 0, 0, 0, 0);
                    } else {
                        // horizontal stripes
                        hardware_img_put(0, ((float)-environment_2d__screen_y1) / environment_2d__screen_y_scale - 1.0f, environment_2d__screen_width - 1, -1,
                                         black_texture, 0, 0, 0, 0, 0, 0, 0);
                        hardware_img_put(0, environment_2d__screen_height, environment_2d__screen_width - 1,
                                         (((float)-environment_2d__screen_y1) + (float)environment__window_height - 1.0f) / environment_2d__screen_y_scale +
                                             1.0f,
                                         black_texture, 0, 0, 0, 0, 0, 0, 0);
                    }
                    hardware_buffer_flush();
                } // letterbox

            } // level==5

        } // level!=0
    } // level loop

    if (requestedKeyboardOverlayImage) {
        int32 src = requestedKeyboardOverlayImage - HARDWARE_IMG_HANDLE_OFFSET;
        hardware_img_struct *src_hardware_img;
        src_hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, src);
        /*
            hardware_img_put(0,0,src_hardware_img->w-1,src_hardware_img->h-1,
            src, 0,
            0,0,src_hardware_img->w-1,src_hardware_img->h-1,
            1,0);
        */
        hardware_img_put(0, 0, environment_2d__screen_width - 1, environment_2d__screen_height - 1, src, 0, 0, 0, src_hardware_img->w - 1,
                         src_hardware_img->h - 1, 1, 0);
        hardware_buffer_flush();
    }

    last_rendered_hardware_display_frame_order = last_hardware_display_frame_order;

    if (suspend_program) { // Otherwise skipped SUB__GL content becomes
                           // "invisible"
        //...
    } else {
        glutSwapBuffers();
    }

    in_GLUT_DISPLAY_REQUEST = 0;

} // GLUT_DISPLAY_REQUEST

void GLUT_MouseButton_Up(int button, int x, int y) {
#    ifdef QB64_GLUT
    int32 i;
    mouse_message_queue_struct *queue = &mouse_message_queue;

    i = queue->last + 1;
    if (i > queue->lastIndex)
        i = 0;
    if (i == queue->current) {
        int32 nextIndex = queue->last + 1;
        if (nextIndex > queue->lastIndex)
            nextIndex = 0;
        queue->current = nextIndex;
    }
    queue->queue[i].x = x;
    queue->queue[i].y = y;
    queue->queue[i].movementx = 0;
    queue->queue[i].movementy = 0;
    queue->queue[i].buttons = queue->queue[queue->last].buttons;
    if (queue->queue[i].buttons & (1 << (button - 1)))
        queue->queue[i].buttons ^= (1 << (button - 1));
    queue->last = i;

    if (device_last) { // core devices required?
        if ((button >= 1) && (button <= 3)) {
            button--;
            static device_struct *d;
            d = &devices[2]; // mouse

            int32 eventIndex = createDeviceEvent(d);
            setDeviceEventButtonValue(d, eventIndex, button, 0);
            commitDeviceEvent(d);

        } // valid range
    } // core devices required

#    endif
}

void GLUT_MouseButton_Down(int button, int x, int y) {
#    ifdef QB64_GLUT

    int32 i;
    mouse_message_queue_struct *queue = &mouse_message_queue;

    i = queue->last + 1;
    if (i > queue->lastIndex)
        i = 0;
    if (i == queue->current) {
        int32 nextIndex = queue->last + 1;
        if (nextIndex > queue->lastIndex)
            nextIndex = 0;
        queue->current = nextIndex;
    }
    queue->queue[i].x = x;
    queue->queue[i].y = y;
    queue->queue[i].movementx = 0;
    queue->queue[i].movementy = 0;
    queue->queue[i].buttons = queue->queue[queue->last].buttons;
    queue->queue[i].buttons |= (1 << (button - 1));
    queue->last = i;

    if (device_last) { // core devices required?
        if ((button >= 1) && (button <= 3)) {
            button--;
            static device_struct *d;
            d = &devices[2]; // mouse

            int32 eventIndex = createDeviceEvent(d);
            setDeviceEventButtonValue(d, eventIndex, button, 1);
            commitDeviceEvent(d);

            // 1-3
        } else {
            // not 1-3
            // mouse wheel?
            if ((button >= 4) && (button <= 5)) {
                static float f;
                if (button == 4)
                    f = -1;
                else
                    f = 1;
                static device_struct *d;
                d = &devices[2]; // mouse

                int32 eventIndex = createDeviceEvent(d);
                setDeviceEventWheelValue(d, eventIndex, 2, f);
                commitDeviceEvent(d);

                eventIndex = createDeviceEvent(d);
                setDeviceEventWheelValue(d, eventIndex, 2, 0);
                commitDeviceEvent(d);

            } // 4-5
        } // not 1-3
    } // core devices required
#    endif
}

void GLUT_MOUSE_FUNC(int glut_button, int state, int x, int y) {
#    ifdef QB64_GLUT
    if (state == GLUT_DOWN)
        GLUT_MouseButton_Down(glut_button + 1, x, y);
    if (state == GLUT_UP)
        GLUT_MouseButton_Up(glut_button + 1, x, y);
#    endif
}

void GLUT_MOTION_FUNC(int x, int y) {

    int32 i, last_i;
    int32 xrel, yrel;

    // This is used to save the last mouse position which is then paired with the mouse wheel event on macOS
    MacMouse_UpdatePosition(x, y);

    mouse_message_queue_struct *queue = &mouse_message_queue;

    // message #1
    last_i = queue->last;
    i = queue->last + 1;
    if (i > queue->lastIndex)
        i = 0; // wrap around
    if (i == queue->current) {
        int32 nextIndex = queue->last + 1;
        if (nextIndex > queue->lastIndex)
            nextIndex = 0;
        queue->current = nextIndex;
    }

#    if defined(QB64_WINDOWS) || defined(QB64_MACOSX)
    // Windows calculates relative movement by intercepting WM_INPUT events
    // macOS uses the Quartz Event Services to get relative movements
    xrel = 0;
    yrel = 0;
#    else
    // TODO: This needs to be correctly implemented on Linux
    xrel = x - queue->queue[queue->last].x;
    yrel = y - queue->queue[queue->last].y;
#    endif

    queue->queue[i].x = x;
    queue->queue[i].y = y;
    queue->queue[i].movementx = xrel;
    queue->queue[i].movementy = yrel;
    queue->queue[i].buttons = queue->queue[last_i].buttons;
    queue->last = i;

    // message #2 (clears movement values to avoid confusion)
    last_i = queue->last;
    i = queue->last + 1;
    if (i > queue->lastIndex)
        i = 0;
    if (i == queue->current) {
        int32 nextIndex = queue->last + 1;
        if (nextIndex > queue->lastIndex)
            nextIndex = 0;
        queue->current = nextIndex;
    }
    queue->queue[i].x = x;
    queue->queue[i].y = y;
    queue->queue[i].movementx = 0;
    queue->queue[i].movementy = 0;
    queue->queue[i].buttons = queue->queue[last_i].buttons;
    queue->last = i;

    if (device_last) { // core devices required?
        if (!device_mouse_relative) {
            static device_struct *d;
            d = &devices[2]; // mouse

            int32 eventIndex = createDeviceEvent(d);
            static float fx, fy;
            static int32 z;
            fx = x;
            fx -= x_offset;
            z = x_monitor - x_offset * 2;
            if (fx < 0)
                fx = 0;
            if (fx >= z)
                fx = z - 1;
            fx = fx / (float)(z - 1); // 0 to 1
            fx *= 2.0;                // 0 to 2
            fx -= 1.0;                //-1 to 1
            fy = y;
            fy -= y_offset;
            z = y_monitor - y_offset * 2;
            if (fy < 0)
                fy = 0;
            if (fy >= z)
                fy = z - 1;
            fy = fy / (float)(z - 1); // 0 to 1
            fy *= 2.0;                // 0 to 2
            fy -= 1.0;                //-1 to 1
            setDeviceEventAxisValue(d, eventIndex, 0, fx);
            setDeviceEventAxisValue(d, eventIndex, 1, fy);
            commitDeviceEvent(d);

        } else {
            static device_struct *d;
            d = &devices[2]; // mouse

            int32 eventIndex = createDeviceEvent(d);
            static float fx, fy;
            static int32 z;
            fx = xrel;
            fy = yrel;
            setDeviceEventWheelValue(d, eventIndex, 0, fx);
            setDeviceEventWheelValue(d, eventIndex, 1, fy);
            commitDeviceEvent(d);

            eventIndex = createDeviceEvent(d);
            fx = 0;
            fy = 0;
            setDeviceEventWheelValue(d, eventIndex, 0, fx);
            setDeviceEventWheelValue(d, eventIndex, 1, fy);
            commitDeviceEvent(d);
        }
    } // core devices required
}

void GLUT_PASSIVEMOTION_FUNC(int x, int y) {
    GLUT_MOTION_FUNC(x, y);
}

void GLUT_MOUSEWHEEL_FUNC(int wheel, int direction, int x, int y) {
#    ifdef QB64_GLUT
    // Note: freeglut specific, limited documentation existed so the following
    // research was done:
    //  qbs_print(qbs_str(wheel),NULL); <-- was always 0 [could 1 indicate
    //  horizontal wheel?] qbs_print(qbs_str(direction),NULL); <-- 1(up) or
    //  -1(down) qbs_print(qbs_str(x),NULL); <--mouse x,y co-ordinates
    //  qbs_print(qbs_str(y),1);    <
    if (direction > 0) {
        GLUT_MouseButton_Down(4, x, y);
        GLUT_MouseButton_Up(4, x, y);
    }
    if (direction < 0) {
        GLUT_MouseButton_Down(5, x, y);
        GLUT_MouseButton_Up(5, x, y);
    }
#    endif
}

#endif

void sub__title(qbs *title) {
    if (is_error_pending())
        return;
    static qbs *cz = NULL;
    if (!cz) {
        cz = qbs_new(1, 0);
        cz->chr[0] = 0;
    }
    static qbs *str = NULL;
    if (!str)
        str = qbs_new(0, 0);
    qbs_set(str, qbs_add(title, cz));

    uint8 *buf, *old_buf;
    buf = (uint8 *)malloc(str->len);
    memcpy(buf, str->chr, str->len);
    old_buf = window_title;
    window_title = buf;
    if (old_buf)
        free(old_buf);

    OPTIONAL_GLUT();

    libqb_glut_set_window_title((char *)window_title);
} // title

void sub__echo(qbs *message) {
    if (is_error_pending())
        return;

    int32 prevDest = func__dest();
    sub__dest(func__console());

    makefit(message);
    qbs_print(message, 0);
    qbs_print(nothingstring, 1);

    sub__dest(prevDest);

} // echo

qbs *func__readfile(qbs *filespec) {
    FILE *file;
    int len; // file handle; file length;
    qbs *namez;
    qbs *cont; // 0-term file name; file contents;

    namez = qbs_add(filespec, func_chr(0)); // add terminator
    filepath_fix_directory(namez);          // fix separators
    file = fopen((const char *)namez->chr, "rb");

    if (file) {
        fseek(file, 0, SEEK_END); // end pos
        len = ftell(file);        //   = file length
        rewind(file);             // rewind to start
        cont = qbs_new(len, 1);   // get new string for file contents
        fread(cont->chr, 1, len, file);
        if (ferror(file)) {
            error(QB_ERROR_PATH_FILE_ACCESS_ERROR); // something went wrong
            cont = qbs_new_txt("");                 // return empty on error
        }
        fclose(file);
        return cont;
    } else {
        error(QB_ERROR_FILE_NOT_FOUND); // most common when trying to read
        return qbs_new_txt("");         // return empty on error
    }
}

void sub__writefile(qbs *filespec, qbs *contents) {
    FILE *file; // file handle;
    qbs *namez; // 0-term file name;

    namez = qbs_add(filespec, func_chr(0)); // add terminator
    filepath_fix_directory(namez);          // fix separators
    file = fopen((const char *)namez->chr, "wb");

    if (file) {
        fwrite(contents->chr, 1, contents->len, file);
        if (ferror(file))
            error(QB_ERROR_PATH_FILE_ACCESS_ERROR); // something went wrong
        fclose(file);
    } else {
        error(QB_ERROR_PATH_NOT_FOUND); // most common when making a new file
    }
}

void sub__filedrop(int32 on_off = NULL) {
#ifdef QB64_WINDOWS
    HWND win = (HWND)func__windowhandle();
    if (!win)
        return;

    if ((on_off == NULL) || (on_off == 1)) {
        DragAcceptFiles(win, TRUE);
        acceptFileDrop = -1;
    }
    if (on_off == 2) {
        DragAcceptFiles(win, FALSE);
        acceptFileDrop = 0;
    }
#endif
}

int32 func__filedrop() {
    return acceptFileDrop;
}

void sub__finishdrop() {
#ifdef QB64_WINDOWS
    DragFinish(hdrop);
    totalDroppedFiles = 0;
#endif
}

int32 func__totaldroppedfiles() {
#ifdef QB64_WINDOWS
    return totalDroppedFiles;
#endif
    return 0;
}

qbs *func__droppedfile(int32 fileIndex, int32 passed) {
#ifdef QB64_WINDOWS
    static int32 index = -1;
    static char szNextFile[MAX_PATH];

    if (totalDroppedFiles > 0) {
        index++;
        if (passed)
            index = fileIndex - 1;
        if ((index > totalDroppedFiles - 1) || (index < 0)) {
            // out of bounds;
            // if reading _DROPPEDFILE$ sequentially (without an
            // index), hdrop is reset and the list is cleared.
            if (!passed)
                sub__finishdrop();
            index = -1;
            return qbs_new_txt("");
        }
        // fetch file[index] from hdrop:
        if (DragQueryFileA(hdrop, index, szNextFile, MAX_PATH) > 0) {
            if ((!passed) && (index == totalDroppedFiles - 1)) {
                // last file read sequentially
                sub__finishdrop();
                index = -1;
            }
            return qbs_new_txt(szNextFile);
        } else {
            // error fetching file from hdrop;
            sub__finishdrop();
            index = -1;
        }
    } else {
        index = -1;
    }
#endif
    return qbs_new_txt("");
}

//                     0 1  2        0 1       2
void sub__resize(int32 on_off, int32 stretch_smooth) {

    if (on_off == 1)
        resize_snapback = 0;
    if (on_off == 2)
        resize_snapback = 1;
    // no change if omitted

    if (stretch_smooth) {
        resize_auto = stretch_smooth;
    } else {
        resize_auto = 0; // revert if omitted
    }
}

int32 func__resize() {
    if (resize_snapback)
        return 0; // resize must be enabled
    if (resize_event) {
        resize_event = 0;
        return -1;
    }
    return 0;
}

int32 func__resizewidth() {
    return resize_event_x;
}

int32 func__resizeheight() {
    return resize_event_y;
}

int32 func__scaledwidth() {
    return environment_2d__screen_scaled_width;
}

int32 func__scaledheight() {
    return environment_2d__screen_scaled_height;
}

extern void set_dynamic_info();

#ifdef QB64_WINDOWS
static bool isValidCygwinPipe(int fd) {
    HANDLE h = (HANDLE)_get_osfhandle(fd);
    if (h == INVALID_HANDLE_VALUE) {
        return false;
    }

    if (GetFileType(h) != FILE_TYPE_PIPE) {
        return false;
    }

    size_t size = 4096;
    std::vector<char> nameinfoBuf(sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * size);
    FILE_NAME_INFO *nameinfo = reinterpret_cast<FILE_NAME_INFO *>(nameinfoBuf.data());

    if (GetFileInformationByHandleEx(h, FileNameInfo, nameinfo, size)) {
        nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0';

        // When a valid pipe is found, disable buffering so that results are seen immediately
        if (wcsncmp(nameinfo->FileName, L"\\msys-", 6) == 0) {
            setbuf(stdout, NULL);
            setbuf(stderr, NULL);
            return true;
        } else if (wcsncmp(nameinfo->FileName, L"\\cygwin-", 8) == 0) {
            setbuf(stdout, NULL);
            setbuf(stderr, NULL);
            return true;
        }
    }

    return false;
}
#endif

int main(int argc, char *argv[]) {
    clock_init();

#ifdef QB64_WINDOWS
    // `isValidCygwinPipe()` checks for Cygwin-based stdout, which is good
    // enough to use directly. Otherwise we try to connect to the console we
    // were started from (if there is one).
    //
    // If we're a console program and `AttachConsole()` did not work then we
    // will end up spawning our own console.
    if (!isValidCygwinPipe(STDOUT_FILENO) && AttachConsole(ATTACH_PARENT_PROCESS)) {
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);
    }
#endif

    libqb_log_init();
    libqb_log_info("Program starting.");

#if defined(QB64_LINUX) && defined(X11)
    XInitThreads();
#endif

    static int32 i, i2, i3, i4;
    static uint8 c, c2, c3, c4;
    static int32 x, x2, x3, x4;
    static int32 y, y2, y3, y4;
    static int32 z, z2, z3, z4;
    static float f, f2, f3, f4;
    static uint8 *cp, *cp2, *cp3, *cp4;

    /********** Render State **********/
    render_state.dest = NULL;
    render_state.source = NULL;
    render_state.dest_handle = INVALID_HARDWARE_HANDLE;
    render_state.source_handle = INVALID_HARDWARE_HANDLE;
    render_state.view_mode = VIEW_MODE__UNKNOWN;
    render_state.use_alpha = ALPHA_MODE__UNKNOWN;
    render_state.depthbuffer_mode = DEPTHBUFFER_MODE__UNKNOWN;
    render_state.cull_mode = CULL_MODE__UNKNOWN;
    /********** Render State **********/

    for (i = 0; i <= 2; i++) {
        display_frame[i].state = DISPLAY_FRAME_STATE__EMPTY;
        display_frame[i].order = 0;
        display_frame[i].bgra = NULL;
        display_frame[i].w = 0;
        display_frame[i].h = 0;
        display_frame[i].bytes = 0;
    }

    set_dynamic_info();
    if (ScreenResize) {
        resize_snapback = 0;
    }
    if (ScreenResizeScale) {
        resize_auto = ScreenResizeScale;
    }

    // setup lists
    special_handles = list_new(sizeof(special_handle_struct));
    stream_handles = list_new(sizeof(stream_struct));
    connection_handles = list_new(sizeof(connection_struct));

    hardware_img_handles = list_new_threadsafe(sizeof(hardware_img_struct));
    hardware_graphics_command_handles = list_new(sizeof(hardware_graphics_command_struct));

    // setup default mouse message queue
    mouse_message_queue_struct *queue = &mouse_message_queue;
    queue->lastIndex = 65535;
    queue->queue = (mouse_message *)calloc(1, sizeof(mouse_message) * (queue->lastIndex + 1));

    if (screen_hide_startup)
        screen_hide = 1;

#ifdef QB64_WINDOWS
    if (console) {
        LPDWORD plist = (LPDWORD)malloc(1000);
        if (GetConsoleProcessList(plist, 256) == 1) {
            console_child = 1; // only this program is using the console
        }
    }
#endif

    onkey[1].keycode = 59 << 8; // F1-F10
    onkey[2].keycode = 60 << 8;
    onkey[3].keycode = 61 << 8;
    onkey[4].keycode = 62 << 8;
    onkey[5].keycode = 63 << 8;
    onkey[6].keycode = 64 << 8;
    onkey[7].keycode = 65 << 8;
    onkey[8].keycode = 66 << 8;
    onkey[9].keycode = 67 << 8;
    onkey[10].keycode = 68 << 8;
    onkey[11].keycode = 72 << 8; // up,left,right,down
    onkey[11].keycode_alternate = VK + QBVK_KP8;
    onkey[12].keycode = 75 << 8;
    onkey[12].keycode_alternate = VK + QBVK_KP4;
    onkey[13].keycode = 77 << 8;
    onkey[13].keycode_alternate = VK + QBVK_KP6;
    onkey[14].keycode = 80 << 8;
    onkey[14].keycode_alternate = VK + QBVK_KP2;
    onkey[30].keycode = 133 << 8; // F11,F12
    onkey[31].keycode = 134 << 8;

    ontimer[0].allocated = 1;
    ontimer[0].id = 0;
    ontimer[0].state = 0;
    ontimer[0].active = 0;

    fontwidth[8] = 8;
    fontwidth[14] = 8;
    fontwidth[16] = 8;
    fontheight[8] = 8;
    fontheight[14] = 14;
    fontheight[16] = 16;
    fontflags[8] = FONT_LOAD_MONOSPACE;
    fontflags[14] = FONT_LOAD_MONOSPACE;
    fontflags[16] = FONT_LOAD_MONOSPACE; // monospace flag
    fontwidth[8 + 1] = 8 * 2;
    fontwidth[14 + 1] = 8 * 2;
    fontwidth[16 + 1] = 8 * 2;
    fontheight[8 + 1] = 8;
    fontheight[14 + 1] = 14;
    fontheight[16 + 1] = 16;
    fontflags[8 + 1] = FONT_LOAD_MONOSPACE;
    fontflags[14 + 1] = FONT_LOAD_MONOSPACE;
    fontflags[16 + 1] = FONT_LOAD_MONOSPACE; // monospace flag

    memset(img, 0, IMG_BUFFERSIZE * sizeof(img_struct));
    x = newimg(); // reserve index 0
    img[x].valid = 0;
    x = newimg(); // reserve index 1
    img[x].valid = 0;

    memset(&cpu, 0, sizeof(cpu_struct));

    // uint8 *asmcodep=(uint8*)&asmcode[0];
    // memcpy(&cmem[0],asmcodep,sizeof(asmcode));
    reg8[0] = &cpu.al;
    reg8[1] = &cpu.cl;
    reg8[2] = &cpu.dl;
    reg8[3] = &cpu.bl;
    reg8[4] = &cpu.ah;
    reg8[5] = &cpu.ch;
    reg8[6] = &cpu.dh;
    reg8[7] = &cpu.bh;

    reg16[0] = &cpu.ax;
    reg16[1] = &cpu.cx;
    reg16[2] = &cpu.dx;
    reg16[3] = &cpu.bx;
    reg16[4] = &cpu.sp;
    reg16[5] = &cpu.bp;
    reg16[6] = &cpu.si;
    reg16[7] = &cpu.di;

    reg32[0] = &cpu.eax;
    reg32[1] = &cpu.ecx;
    reg32[2] = &cpu.edx;
    reg32[3] = &cpu.ebx;
    reg32[4] = &cpu.esp;
    reg32[5] = &cpu.ebp;
    reg32[6] = &cpu.esi;
    reg32[7] = &cpu.edi;

    segreg[0] = &cpu.es;
    segreg[1] = &cpu.cs;
    segreg[2] = &cpu.ss;
    segreg[3] = &cpu.ds;
    segreg[4] = &cpu.fs;
    segreg[5] = &cpu.gs;

    for (i = 0; i < 32; i++)
        sub_file_print_spaces[i] = 32;

    port60h_event[0] = 128 + 1; // simulate release of ESC

    mem_static_size = 1048576; // 1MEG
    mem_static = (uint8 *)malloc(mem_static_size);
    mem_static_pointer = mem_static;
    mem_static_limit = mem_static + mem_static_size;

    memset(&cmem[0], 0, sizeof(cmem));

    memset(&keyon[0], 0, sizeof(keyon));

    dblock = (ptrszint)&cmem + 1280; // 0:500h

    // define "nothing"
    cmem_sp -= 8;
    nothingvalue = (uint64 *)(dblock + cmem_sp);
    *nothingvalue = 0;
    nothingstring = qbs_new_cmem(0, 0);
    singlespace = qbs_new_cmem(1, 0);
    singlespace->chr[0] = 32;

    // Store _CWD$ for recall using _STARTDIR$
    FS_SaveStartDirectory();

// switch to directory of this EXE file
// http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
#if defined(QB64_WINDOWS) && !defined(QB64_MICROSOFT)
    static char *exepath = (char *)malloc(65536);
    GetModuleFileNameA(NULL, exepath, 65536);
    i = strlen(exepath);
    for (i2 = i - 1; i2 >= 0; i2--) {
        x = exepath[i2];
        if ((x == 92) || (x == 47) || (x == 58)) {
            if (x == 58)
                exepath[i2 + 1] = 0;
            else
                exepath[i2] = 0;
            break;
        }
    }
    chdir(exepath);
#elif defined(QB64_LINUX)
    {
        char pathbuf[65536];
        memset(pathbuf, 0, sizeof(pathbuf));
        readlink("/proc/self/exe", pathbuf, 65535);
        chdir(dirname(pathbuf));
    }
#elif defined(QB64_MACOSX)
    {
        char pathbuf[65536];
        uint32_t pathbufsize = sizeof(pathbuf);
        _NSGetExecutablePath(pathbuf, &pathbufsize);
        chdir(dirname(pathbuf));
    }
#endif

    unknown_opcode_mess = qbs_new(0, 0);
    qbs_set(unknown_opcode_mess, qbs_new_txt_len("Unknown Opcode (  )\0", 20));

    command_initialize(argc, argv);

#ifdef QB64_WINDOWS
    // for caps lock, use the state of the lock (=1)
    // for held keys check against (=-127)
    if (GetKeyState(VK_SCROLL) & 1)
        keyheld_add(QBK + QBK_SCROLL_LOCK_MODE);
    if (GetKeyState(VK_SCROLL) < 0) {
        bindkey = QBVK_SCROLLOCK;
        keydown_vk(VK + QBVK_SCROLLOCK);
    }
    if (GetKeyState(VK_LSHIFT) < 0) {
        bindkey = QBVK_LSHIFT;
        keydown_vk(VK + QBVK_LSHIFT);
    }
    if (GetKeyState(VK_RSHIFT) < 0) {
        bindkey = QBVK_RSHIFT;
        keydown_vk(VK + QBVK_RSHIFT);
    }
    if (GetKeyState(VK_LCONTROL) < 0) {
        bindkey = QBVK_LCTRL;
        keydown_vk(VK + QBVK_LCTRL);
    }
    if (GetKeyState(VK_RCONTROL) < 0) {
        bindkey = QBVK_RCTRL;
        keydown_vk(VK + QBVK_RCTRL);
    }
    if (GetKeyState(VK_LMENU) < 0) {
        bindkey = QBVK_LALT;
        keydown_vk(VK + QBVK_LALT);
    }
    if (GetKeyState(VK_RMENU) < 0) {
        bindkey = QBVK_RALT;
        keydown_vk(VK + QBVK_RALT);
    }
    if (GetKeyState(VK_CAPITAL) & 1) {
        bindkey = QBVK_CAPSLOCK;
        keydown_vk(VK + QBVK_CAPSLOCK);
    }
    if (GetKeyState(VK_NUMLOCK) & 1) {
        bindkey = QBVK_NUMLOCK;
        keydown_vk(VK + QBVK_NUMLOCK);
    }
    update_shift_state();
    keyhit_next = keyhit_nextfree; // skip hitkey events generated by above code
#endif

    // init fake keyb. cyclic buffer
    cmem[0x41a] = 30;
    cmem[0x41b] = 0; // head
    cmem[0x41c] = 30;
    cmem[0x41d] = 0; // tail

    std::ifstream fh;

    // default 256 color palette
    memcpy(&palette_256, &file_qb64_pal[0], file_qb64_pal_len);
    for (i = 0; i < 256; i++)
        palette_256[i] |= 0xFF000000;

    // default EGA(64) color palette
    memcpy(&palette_64, &file_qb64ega_pal[0], file_qb64ega_pal_len);
    for (i = 0; i < 64; i++)
        palette_64[i] |= 0xFF000000;

    // manually set screen 10 palette
    pal_mode10[0][0] = 0;
    pal_mode10[0][1] = 0;
    pal_mode10[0][2] = 0;
    pal_mode10[0][3] = 0x808080;
    pal_mode10[0][4] = 0x808080;
    pal_mode10[0][5] = 0x808080;
    pal_mode10[0][6] = 0xFFFFFF;
    pal_mode10[0][7] = 0xFFFFFF;
    pal_mode10[0][8] = 0xFFFFFF;
    pal_mode10[1][0] = 0;
    pal_mode10[1][1] = 0x808080;
    pal_mode10[1][2] = 0xFFFFFF;

    pal_mode10[1][3] = 0;
    pal_mode10[1][4] = 0x808080;
    pal_mode10[1][5] = 0xFFFFFF;
    pal_mode10[1][6] = 0;
    pal_mode10[1][7] = 0x808080;
    pal_mode10[1][8] = 0xFFFFFF;

    // 8x8 character set
    memcpy(&charset8x8, &file_charset8_raw[0], file_charset8_raw_len);

    // 8x16 character set
    memcpy(&charset8x16, &file_chrset16_raw[0], file_chrset16_raw_len);

    qbg_screen(0, NULL, NULL, NULL, NULL, 1);
    width8050switch = 1; // reaffirm switch reset by above command

    if (console) {
        console_image = func__newimage(80, 25, 0, 0);
        i = -console_image;
        img[i].console = 1;
    }

    // setup default _DEVICE(s)
    i = 0;

    // keyboard
    i++;
    devices[i].type = DEVICETYPE_KEYBOARD;
    devices[i].name = strdup("[KEYBOARD][BUTTON]"); // TODO: when re-writing game_controller.cpp use std::string
    devices[i].lastbutton = 512;
    devices[i].description = "Keyboard";
    setupDevice(&devices[i]);

    // mouse
    i++;
    devices[i].type = DEVICETYPE_MOUSE;
    devices[i].name = strdup("[MOUSE][BUTTON][AXIS][WHEEL]"); // TODO: when re-writing game_controller.cpp use std::string
    devices[i].lastbutton = 3;
    devices[i].lastaxis = 2;
    devices[i].lastwheel = 3;
    devices[i].description = "Mouse";
    setupDevice(&devices[i]);

    device_last = i;

#ifdef DEPENDENCY_DEVICEINPUT
    QB64_GAMEPAD_INIT();
#endif

    libqb_http_init();

    libqb_glut_presetup(argc, argv);

    struct libqb_thread *qbmain = libqb_thread_new();
    libqb_thread_start(qbmain, QBMAIN, NULL);

    struct libqb_thread *timer = libqb_thread_new();
    libqb_thread_start(timer, TIMERTHREAD, NULL);

    lock_display_required = 1;

    libqb_start_main_thread(argc, argv);

    return 0; // Should never get here
}

// ###################### Main Loop ####################
void MAIN_LOOP(void *unused) {

    int32 update = 0; // 0=update input,1=update display

main_loop:

#ifdef DEPENDENCY_DEVICEINPUT
#    ifndef QB64_MACOSX
    QB64_GAMEPAD_POLL();
#    endif
#endif

    if (lock_mainloop == 1) {
        lock_mainloop = 2;
        while (lock_mainloop == 2)
            Sleep(1);
    }

    if (exit_value) {
        if (!exit_blocked)
            goto end_program;
    }

    // update timer bytes in cmem
    uint64_t cmem_ticks;

    cmem_ticks = (uint64_t)(func_timer(0.001, 1) * 1000);

    // make timer value loop after midnight
    // note: there are 86400000 milliseconds in 24hrs(1 day)
    cmem_ticks %= 86400000;
    cmem_ticks = ((double)cmem_ticks) * 0.0182;
    cmem[0x46c] = cmem_ticks & 255;
    cmem[0x46d] = (cmem_ticks >> 8) & 255;
    cmem[0x46e] = (cmem_ticks >> 16) & 255;
    // note: a discrepancy exists of unknown cause

    if (shell_call_in_progress) {
        if (shell_call_in_progress != -1) {
            shell_call_in_progress = -1;
            goto update_display_only;
        }
        Sleep(64);
        goto main_loop;
    }

    Sleep(15);
    vertical_retrace_happened = 1;
    vertical_retrace_in_progress = 1;
    Sleep(1);

    if (close_program) {
        lock_mainloop = 2; // report mainloop as locked so that any process waiting for a successful lock can continue
        goto end_program;
    }

    snd_update();

    update ^= 1; // toggle update

    if (!lprint) { // not currently performing an LPRINT operation
        lprint_locked = 1;
        if (lprint_buffered) {
            if (std::fabs(func_timer(0.001, 1) - lprint_last) >= 10.0) { // 10 seconds elapsed since last LPRINT operation
                sub__printimage(lprint_image);
                lprint_buffered = 0;
                static int32 old_dest;
                old_dest = func__dest();
                sub__dest(lprint_image);
                sub_cls(NULL, 15, 2);
                sub__dest(old_dest);
            }
        }
        lprint_locked = 0;
    }

    // note: this mainloop loops with breaks of 16ms, display is toggled every 2nd loop
    // update display?
    if (update == 1) {
    update_display_only:
        if (autodisplay)
            display(); // occurs every 32ms or 31.25 times per second
        frame++;       //~32 fps
    } // update==1

    vertical_retrace_in_progress = 0;

    if (shell_call_in_progress)
        goto main_loop;

    if (update == 0) {

        static int32 scancode;
        static const uint8 QBVK_2_scancode[] = {
            0,  0,  0,  0,  0,   0,   0,  0,  14, 15, 0,  0,  0,  28, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  0,   0,  57, 0,  0,  0,
            0,  0,  0,  40, 0,   0,   0,  0,  51, 12, 52, 53, 11, 2,  3,  4,  5,  6,  7,  8,  9,  10, 0,  39, 0,  13, 0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  26, 43, 27, 0,  0,  41, 30, 48, 46, 32, 18, 33,  34, 35, 23, 36, 37,
            38, 50, 49, 24, 25,  16,  19, 31, 20, 22, 47, 17, 45, 21, 44, 0,  0,  0,  0,  83, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  82,  79,  80, 81, 75, 76, 77, 71, 72, 73, 83, 53, 55, 74, 78, 28, 0,  72, 80, 77, 75, 82, 71, 79, 73, 81, 59,  60, 61, 62, 63, 64,
            65, 66, 67, 68, 133, 134, 0,  0,  0,  0,  0,  0,  69, 58, 70, 54, 42, 29, 29, 56, 56, 0,  0,  91, 92, 0,  0,  0,  0,  55, 197, 93, 0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0};

#ifdef QB64_WINDOWS
        // manage important external keyboard lock/state changes
        if ((GetKeyState(VK_SCROLL) & 1) != keyheld(QBK + QBK_SCROLL_LOCK_MODE)) {
            if (keyheld(QBK + QBK_SCROLL_LOCK_MODE)) {
                keyheld_remove(QBK + QBK_SCROLL_LOCK_MODE);
            } else {
                keyheld_add(QBK + QBK_SCROLL_LOCK_MODE);
            }
            update_shift_state();
        }
        if ((GetKeyState(VK_CAPITAL) & 1) != keyheld(VK + QBVK_CAPSLOCK)) {
            if (keyheld(VK + QBVK_CAPSLOCK)) {
                bindkey = QBVK_CAPSLOCK;
                keyup_vk(VK + QBVK_CAPSLOCK);
            } else {
                bindkey = QBVK_CAPSLOCK;
                keydown_vk(VK + QBVK_CAPSLOCK);
            }
            update_shift_state();
        }
        if ((GetKeyState(VK_NUMLOCK) & 1) != keyheld(VK + QBVK_NUMLOCK)) {
            if (keyheld(VK + QBVK_NUMLOCK)) {
                bindkey = QBVK_NUMLOCK;
                keyup_vk(VK + QBVK_NUMLOCK);
            } else {
                bindkey = QBVK_NUMLOCK;
                keydown_vk(VK + QBVK_NUMLOCK);
            }
            update_shift_state();
        }
#endif

        if (shell_call_in_progress)
            goto main_loop;

    } // update==0

    goto main_loop;

end_program:
    stop_program = 1;
    qbevent = 1;
    while (exit_ok != 3)
        Sleep(16);

    if (lprint_buffered) {
        sub__printimage(lprint_image); // print any pending content
    }

    // close all open files
    sub_close(NULL, 0);

    libqb_http_stop();

// shutdown device interface
#ifdef DEPENDENCY_DEVICEINPUT
    QB64_GAMEPAD_SHUTDOWN();
#endif

    libqb_exit(exit_code);
}

// used to preserve the previous frame's content for comparison/reuse purposes
uint8 *pixeldata = (uint8 *)malloc(1);
int32 pixeldatasize = 1;
uint32 paldata[256];

// note: temporarily swapping a source palette is far more efficient than converting the resulting image pixels
void swap_paldata_BGRA_with_RGBA() {
    static uint32 col;
    static uint32 *pos;
    pos = (uint32 *)&paldata[0];
    static int32 pixels;
    pixels = 256;
    while (pixels--) {
        col = *pos;
        *pos++ = (col & 0xFF00FF00) | ((col & 0xFF0000) >> 16) | ((col & 0x0000FF) << 16);
    }
}

// display updates the visual page onto the visible window/monitor
void display() {

    // general variables
    static int32 i, i2, i3, i4;
    static uint8 c, c2, c3, c4;
    static int32 x, x2, x3, x4;
    static int32 y, y2, y3, y4;
    static int32 z, z2, z3, z4;
    static float f, f2, f3, f4;
    static uint8 *cp, *cp2, *cp3, *cp4;
    uint32 *pixel;

    static uint8 BGRA_to_RGBA; // set to 1 to invert the output to RGBA
    BGRA_to_RGBA = 0;          // default is 0 but 1 is fun

    if (lock_display == 1) {
        lock_display = 2;
        Sleep(0);
    }

    if (screen_hide) {
        display_called = 1;
        return;
    }

    if (lock_display == 0) {

        // Identify which display_frame to build
        static int32 frame_i;
        frame_i = -1;
        // use LOWEST order ready frame
        static int64 lowest_order;
        lowest_order = display_frame_order_next;
        for (i = 0; i <= 2; i++) {
            if (display_frame[i].order < lowest_order && display_frame[i].state == DISPLAY_FRAME_STATE__READY) {
                lowest_order = display_frame[i].order;
                frame_i = i;
            }
        }
        // or preferably an unused frame if possible (note: this check happens 2nd for threading reasons)
        for (i = 0; i <= 2; i++) {
            if (display_frame[i].state == DISPLAY_FRAME_STATE__EMPTY) {
                frame_i = i;
            }
        }
        if (frame_i == -1) {
            gui_alert("Software frame buffer: Failed to find available frame");
            return;
        }
        display_frame[frame_i].state = DISPLAY_FRAME_STATE__BUILDING;
        display_frame[frame_i].order = display_frame_order_next++;

        // validate display_page
        if (!display_page)
            goto display_page_invalid;

        // check what is possible in full screen
        x = display_page->width;
        y = display_page->height;

        if (display_page->compatible_mode == 0) {
            x = display_page->width * fontwidth[display_page->font];
            y = display_page->height * fontheight[display_page->font];
        }

        // check for y-stretch flag?
        if (x <= 512 && y <= 384) {
            x *= 2;
            y *= 2;
        }

        static int32 mode_square, mode_stretch;

        // find best fullscreen mode(s) (eg. square/"1:1", stretched)
        mode_square = -1;
        mode_stretch = -1;

        x = display_page->width;
        y = display_page->height;
        if (display_page->compatible_mode == 0) {
            x = display_page->width * fontwidth[display_page->font];
            y = display_page->height * fontheight[display_page->font];
        }
        x_monitor = x;
        y_monitor = y;

        z = 0; //?

        conversion_required = 0;
        pixel = display_surface_offset; //<-will be made obsolete

        if (!display_page->compatible_mode) { // text

            static int32 show_flashing_last = 0;
            static int32 show_cursor_last = 0;
            static int32 check_last;
            static uint8 *cp, *cp2, *cp_last;
            static uint32 *lp;
            static int32 cx, cy;
            static int32 cx_last = -1, cy_last = -1;
            static int32 show_cursor;
            static int32 show_flashing;
            static uint8 chr, col, chr_last, col_last;
            static int32 qbg_y_offset;

            static int32 f, f_pitch, f_width, f_height; // font info
            f = display_page->font;
            f_width = fontwidth[f];
            f_height = fontheight[f];

            static int32 content_changed;

            check_last = screen_last_valid; // If set, modify the previous pixelbuffer's contents
            content_changed = 0;

            // Realloc pixel-buffer if necessary
            i = display_page->width * display_page->height * 2;
            if (screen_last_size != i) {
                free(screen_last);
                screen_last = (uint8 *)malloc(i);
                screen_last_size = i;
                check_last = 0;
            }

            if (displayorder_screen == 0 && check_last == 1) {
                // a valid frame of the correct dimensions exists and we are not required to display software content
                goto no_new_frame;
            }

            // Check/Prepare palette-buffer
            if (!check_last) {
                // set pal_last (no prev pal was available to compare to)
                memcpy(&paldata, display_page->pal, 256 * 4);
            } else {
                // if palette has changed, update paldata and draw all characters
                if (memcmp(&paldata[0], display_page->pal, 256 * 4)) {
                    // Different palette
                    memcpy(&paldata[0], display_page->pal, 256 * 4);
                    check_last = 0;
                }
            }

            // Check/Prepare content
            if (check_last) {
                // i=display_frame_end;
                if (memcmp(screen_last, display_page->offset, screen_last_size)) {
                    // Different content
                    content_changed = 1;
                }
            }

            // Note: frame is a global variable incremented ~32 times per second [2013]
            if (frame & 8)
                show_cursor = 1;
            else
                show_cursor = 0; //[2013]halved cursor blink rate from 8 changes p/sec -> 4 changes p/sec
            if (frame & 8)
                show_flashing = 1;
            else
                show_flashing = 0;

            // calculate cursor position (base 0)
            cx = display_page->cursor_x - 1;
            cy = display_page->cursor_y - 1;
            if (display_page->holding_cursor == 2) { // special case
                if (cy < (display_page->height - 1)) {
                    cy++;
                    cx = 0;
                }
            }

            if (check_last) {
                if (show_flashing != show_flashing_last)
                    content_changed = 1;
                if (show_cursor != show_cursor_last)
                    content_changed = 1;
                if ((cx != cx_last) || (cy != cy_last))
                    content_changed = 1;
            }

            if (!check_last)
                content_changed = 1;

            if (!content_changed) {
                // No content has changed, so skip the generation & display of this frame
                goto no_new_frame;
            }

            static int64 last_frame_i = 0;

            // ################################ Setup new frame ################################
            {
                static int32 new_size_bytes;
                new_size_bytes = x_monitor * y_monitor * 4;
                if (new_size_bytes > display_frame[frame_i].bytes) {
                    free(display_frame[frame_i].bgra);
                    display_frame[frame_i].bgra = (uint32 *)malloc(new_size_bytes);
                    display_frame[frame_i].bytes = new_size_bytes;
                }
                display_frame[frame_i].w = x_monitor;
                display_frame[frame_i].h = y_monitor;
            }

            display_surface_offset = display_frame[frame_i].bgra;

            // If a compare & update changes method will be used copy the previous content to the new buffer

            if (check_last) {
                // find the most recently published page to compare with
                //(the most recent READY or DISPLAYING page)
                static int64 highest_order;
                highest_order = 0;
                i2 = -1;
                for (i3 = 0; i3 <= 2; i3++) {
                    if ((display_frame[i3].state == DISPLAY_FRAME_STATE__DISPLAYING || display_frame[i3].state == DISPLAY_FRAME_STATE__READY) &&
                        display_frame[i3].order > highest_order) {
                        highest_order = display_frame[i3].order;
                        i2 = i3;
                    }
                }
                if (i2 != -1) {
                    memcpy(display_frame[frame_i].bgra, display_frame[i2].bgra, display_frame[frame_i].w * display_frame[frame_i].h * 4);
                } else {
                    gui_alert("Text Screen Update: Failed to locate previous frame's data for comparison");
                    check_last = 0; // never occurs, safe-guard only
                }
            }

            qbg_y_offset = 0;          // the screen base offset
            cp = display_page->offset; // read from
            cp_last = screen_last;     // written to for future comparisons

            if (BGRA_to_RGBA)
                swap_paldata_BGRA_with_RGBA();

            // outer loop
            y2 = 0;
            for (y = 0; y < display_page->height; y++) {
                x2 = 0;
                for (x = 0; x < display_page->width; x++) {

                    chr = *cp;
                    cp++;
                    col = *cp;
                    cp++;

                    // can be skipped?
                    chr_last = *cp_last;
                    cp_last++;
                    col_last = *cp_last;
                    cp_last++;

                    if (check_last) {
                        if (chr == chr_last) {     // same character
                            if (col == col_last) { // same colours
                                if (col & 128)
                                    if (show_flashing != show_flashing_last)
                                        goto cantskip; // same flash
                                if (x == cx)
                                    if (y == cy)
                                        if (show_cursor != show_cursor_last)
                                            goto cantskip; // same cursor
                                if (x == cx_last) {
                                    if (y == cy_last) {
                                        if ((cx != cx_last) || (cy != cy_last))
                                            goto cantskip; // fixup old cursor's location
                                    }
                                }
                                goto skip;
                            }
                        }
                    }
                cantskip:
                    cp_last -= 2;
                    *cp_last = chr;
                    cp_last++;
                    *cp_last = col;
                    cp_last++;

                    // set cp2 to the character's data
                    z2 = 0; // double-width if set

                    if (f >= 32) { // custom font

                        static uint32 chr_utf32;
                        chr_utf32 = codepage437_to_unicode16[chr];

                        static uint8 *rt_data_last = NULL;
                        static int32 render_option;
                        static int32 ok;
                        static uint8 *rt_data;
                        static int32 rt_w, rt_h;
                        render_option = 1;
                        if (rt_data_last)
                            free(rt_data_last);
                        ok = FontRenderTextUTF32(font[f], (char32_t *)&chr_utf32, 1, render_option, &rt_data, &rt_w, &rt_h);
                        rt_data_last = rt_data;
                        cp2 = rt_data;
                        f_pitch = 0;

                    } else { // default font
                        f_pitch = 0;
                        if (f == 8)
                            cp2 = &charset8x8[chr][0][0];
                        if (f == 14)
                            cp2 = &charset8x16[chr][1][0];
                        if (f == 16)
                            cp2 = &charset8x16[chr][0][0];
                        if (f == (8 + 1)) {
                            cp2 = &charset8x8[chr][0][0];
                            z2 = 1;
                        }
                        if (f == (14 + 1)) {
                            cp2 = &charset8x16[chr][1][0];
                            z2 = 1;
                        }
                        if (f == (16 + 1)) {
                            cp2 = &charset8x16[chr][0][0];
                            z2 = 1;
                        }
                    }
                    c = col & 0xF; // foreground col
                    if (H3C0_blink_enable) {
                        c2 = (col >> 4) & 7; // background col
                        c3 = col >> 7;       // flashing?
                    } else {
                        c2 = (col >> 4); // background col
                    }
                    if (c3 && show_flashing && H3C0_blink_enable)
                        c = c2;
                    i2 = paldata[c];
                    i3 = paldata[c2];
                    lp = display_surface_offset + qbg_y_offset + y2 * x_monitor + x2;
                    z = x_monitor - fontwidth[display_page->font];

                    // inner loop
                    for (y3 = 0; y3 < f_height; y3++) {
                        for (x3 = 0; x3 < f_width; x3++) {
                            if (*cp2)
                                *lp = i2;
                            else
                                *lp = i3;
                            if (z2) {
                                if (x3 & z2)
                                    cp2++;
                            } else {
                                cp2++;
                            }
                            lp++;
                        }
                        lp += z;
                        cp2 += f_pitch;
                    } // y3,x3

                    // draw cursor
                    if (display_page->cursor_show && show_cursor && (cx == x) && (cy == y)) {
                        static int32 v1, v2;
                        static uint8 from_bottom; // bottom is the 2nd bottom scanline in width ?x25
                        static uint8 half_cursor; // if set, overrides all following values
                        static uint8 size;        // if 0, no cursor is drawn, if 255, from begin to bottom
                        static uint8 begin;       // only relevant if from_bottom was not specified
                        v1 = display_page->cursor_firstvalue;
                        v2 = display_page->cursor_lastvalue;
                        from_bottom = 0;
                        half_cursor = 0;
                        size = 0;
                        begin = 0;
                        // RULE: IF V2=0, NOTHING (UNLESS V1=0)
                        if (v2 == 0) {
                            if (v1 == 0) {
                                size = 1;
                                goto cursor_created;
                            }
                            goto nocursor; // no cursor!
                        }
                        // RULE: IF V2<V1, FROM V2 TO BOTTOM
                        if (v2 < v1) {
                            begin = v2;
                            size = 255;
                            goto cursor_created;
                        }
                        // RULE: IF V1=V2, SINGLE SCANLINE AT V1 (OR BOTTOM IF V1>=4)
                        if (v1 == v2) {
                            if (v1 <= 3) {
                                begin = v1;
                                size = 1;
                                goto cursor_created;
                            }
                            from_bottom = 1;
                            size = 1;
                            goto cursor_created;
                        }
                        // NOTE: V2 MUST BE LARGER THAN V1!
                        // RULE: IF V1>=3, CALC. DIFFERENCE BETWEEN V1 & V2
                        //                IF DIFF=1, 2 SCANLINES AT BOTTOM
                        //                IF DIFF=2, 3 SCANLINES AT BOTTOM
                        //                OTHERWISE HALF CURSOR
                        if (v1 >= 3) {
                            if ((v2 - v1) == 1) {
                                from_bottom = 1;
                                size = 2;
                                goto cursor_created;
                            }
                            if ((v2 - v1) == 2) {
                                from_bottom = 1;
                                size = 3;
                                goto cursor_created;
                            }
                            half_cursor = 1;
                            goto cursor_created;
                        }
                        // RULE: IF V1<=1, IF V2<=3 FROM V1 TO V3 ELSE FROM V1 TO BOTTOM
                        if (v1 <= 1) {
                            if (v2 <= 3) {
                                begin = v1;
                                size = v2 - v1 + 1;
                                goto cursor_created;
                            }
                            begin = v1;
                            size = 255;
                            goto cursor_created;
                        }
                        // RULE: IF V1=2, IF V2=3, 2 TO 3
                        //               IF V2=4, 3 SCANLINES AT BOTTOM
                        //               IF V2>=5, FROM 2 TO BOTTOM
                        //(assume V1=2)
                        if (v2 == 3) {
                            begin = 2;
                            size = 2;
                            goto cursor_created;
                        }
                        if (v2 == 4) {
                            from_bottom = 1;
                            size = 3;
                            goto cursor_created;
                        }
                        begin = 2;
                        size = 255;
                    cursor_created:
                        static int32 cw, ch;
                        cw = fontwidth[display_page->font];
                        ch = fontheight[display_page->font];
                        if (half_cursor) {
                            // half cursor
                            y3 = ch - 1;
                            size = ch / 2;
                            c = col & 0xF; // foreground col
                            i2 = paldata[c];
                        draw_half_curs:
                            lp = display_surface_offset + qbg_y_offset + (y2 + y3) * x_monitor + x2;
                            for (x3 = 0; x3 < cw; x3++) {
                                *lp = i2;
                                lp++;
                            }
                            y3--;
                            size--;
                            if (size)
                                goto draw_half_curs;
                        } else {
                            if (from_bottom) {
                                // cursor from bottom
                                y3 = ch - 1;
                                if (y3 == 15)
                                    y3 = 14;   // leave bottom line blank in 8x16 char set
                                c = col & 0xF; // foreground col
                                i2 = paldata[c];
                            draw_curs_from_bottom:
                                lp = display_surface_offset + qbg_y_offset + (y2 + y3) * x_monitor + x2;
                                for (x3 = 0; x3 < cw; x3++) {
                                    *lp = i2;
                                    lp++;
                                }
                                y3--;
                                size--;
                                if (size)
                                    goto draw_curs_from_bottom;
                            } else {
                                // cursor from begin using size
                                if (begin < ch) {
                                    y3 = begin;
                                    c = col & 0xF; // foreground col
                                    i2 = paldata[c];
                                    if (size == 255)
                                        size = ch - begin;
                                draw_curs_from_begin:
                                    lp = display_surface_offset + qbg_y_offset + (y2 + y3) * x_monitor + x2;
                                    for (x3 = 0; x3 < cw; x3++) {
                                        *lp = i2;
                                        lp++;
                                    }
                                    y3++;
                                    size--;
                                    if (size)
                                        goto draw_curs_from_begin;
                                }
                            }
                        }
                    } // draw cursor?
                nocursor:

                // outer loop
                skip:
                    x2 = x2 + fontwidth[display_page->font];
                }
                y2 = y2 + fontheight[display_page->font];
            }

            show_flashing_last = show_flashing;
            show_cursor_last = show_cursor;
            cx_last = cx;
            cy_last = cy;
            screen_last_valid = 1;

            if (BGRA_to_RGBA)
                swap_paldata_BGRA_with_RGBA();

            /*
                //backup for reuse in next frame
                i=display_frame[frame_i].w*display_frame[frame_i].h*4;
                if (i!=pixeldatasize){
                free(pixeldata);
                pixeldata=(uint8*)malloc(i);
                pixeldatasize=i;
                }
                memcpy(pixeldata,display_frame[frame_i].bgra,i);
            */

            last_frame_i = frame_i;

            goto screen_refreshed;

        } // text

        if (display_page->bits_per_pixel == 32) {

            // note: as software->hardware should be avoided at all costs, pixeldata is
            //      still backed up for comparison purposes because in the very likely
            //      event the data has not changed there is no point generating a
            //      new hardware surface from the software frame when the old hardware surface
            //      can be reused. It also saves on BGRA->RGBA conversion on some platforms.

            if (!BGRA_to_RGBA) {
                // find the most recently published page to compare with
                //(the most recent READY or DISPLAYING page)
                static int64 highest_order;
                highest_order = 0;
                i2 = -1;
                for (i3 = 0; i3 <= 2; i3++) {
                    if ((display_frame[i3].state == DISPLAY_FRAME_STATE__DISPLAYING || display_frame[i3].state == DISPLAY_FRAME_STATE__READY) &&
                        display_frame[i3].order > highest_order) {
                        highest_order = display_frame[i3].order;
                        i2 = i3;
                    }
                }
                if (force_display_update)
                    goto update_display32b; // force update
                if (i2 != -1) {
                    if (!screen_last_valid)
                        goto update_display32b; // force update because of mode change?
                    i = display_page->width * display_page->height * 4;
                    if (i != (display_frame[i2].w * display_frame[i2].h * 4))
                        goto update_display32b;

                    if (displayorder_screen == 0) {
                        // a valid frame of the correct dimensions exists and we are not required to display software content
                        goto no_new_frame;
                    }

                    if (memcmp(display_frame[i2].bgra, display_page->offset, i))
                        goto update_display32b;
                    goto no_new_frame; // no need to update display
                }
            update_display32b:;
            } else {

                // BGRA_to_RGBA
                i = display_page->width * display_page->height * 4;
                if (i != pixeldatasize) {
                    free(pixeldata);
                    pixeldata = (uint8 *)malloc(i);
                    pixeldatasize = i;
                    goto update_display32;
                }
                if (force_display_update)
                    goto update_display32; // force update

                if (displayorder_screen == 0) {
                    // a valid frame of the correct dimensions exists and we are not required to display software content
                    goto no_new_frame;
                }

                if (memcmp(pixeldata, display_page->offset, i))
                    goto update_display32;
                if (!screen_last_valid)
                    goto update_display32; // force update because of mode change?
                goto no_new_frame;         // no need to update display
            update_display32:
                memcpy(pixeldata, display_page->offset, i);
            }

            // ################################ Setup new frame ################################
            {
                static int32 new_size_bytes;
                new_size_bytes = x_monitor * y_monitor * 4;
                if (new_size_bytes > display_frame[frame_i].bytes) {
                    free(display_frame[frame_i].bgra);
                    display_frame[frame_i].bgra = (uint32 *)malloc(new_size_bytes);
                    display_frame[frame_i].bytes = new_size_bytes;
                }
                display_frame[frame_i].w = x_monitor;
                display_frame[frame_i].h = y_monitor;
            }

            if (!BGRA_to_RGBA) {
                memcpy(display_frame[frame_i].bgra, display_page->offset, display_frame[frame_i].w * display_frame[frame_i].h * 4);
            } else {
                static uint32 col;
                static uint32 *src_pos;
                static uint32 *dst_pos;
                src_pos = (uint32 *)pixeldata;
                dst_pos = display_frame[frame_i].bgra;
                static int32 pixels;
                pixels = display_frame[frame_i].w * display_frame[frame_i].h;
                if (pixels > 0) {
                    while (pixels--) {
                        col = *src_pos++;
                        *dst_pos++ = (col & 0xFF00FF00) | ((col & 0xFF0000) >> 16) | ((col & 0x0000FF) << 16);
                    }
                }
            }

            goto screen_refreshed;
        } // 32

        // assume <=256 colors using palette

        if (display_page->compatible_mode == 10) { // update SCREEN 10 palette
            i2 = GetTicks() & 512;
            if (i2)
                i2 = 1;
            for (i = 0; i <= 3; i++) {
                display_page->pal[i] = pal_mode10[i2][display_page->pal[i + 4]]; // pal_mode10[0-1][0-8]
            }
        }

        i = display_page->width * display_page->height;
        i2 = 1 << display_page->bits_per_pixel; // unique colors

        // data changed?
        if (i != pixeldatasize) {
            free(pixeldata);
            pixeldata = (uint8 *)malloc(i);
            pixeldatasize = i;
            goto update_display;
        }

        if (force_display_update)
            goto update_display; // force update

        if (displayorder_screen == 0) {
            // a valid frame of the correct dimensions exists and we are not required to display software content
            goto no_new_frame;
        }

        if (memcmp(pixeldata, display_page->offset, i))
            goto update_display;
        // palette changed?
        if (memcmp(paldata, display_page->pal, i2 * 4))
            goto update_display;
        // force update because of mode change?
        if (!screen_last_valid)
            goto update_display;

        goto no_new_frame; // no need to update display

    update_display:

        // ################################ Setup new frame ################################
        {
            static int32 new_size_bytes;
            new_size_bytes = x_monitor * y_monitor * 4;
            if (new_size_bytes > display_frame[frame_i].bytes) {
                free(display_frame[frame_i].bgra);
                display_frame[frame_i].bgra = (uint32 *)malloc(new_size_bytes);
                display_frame[frame_i].bytes = new_size_bytes;
            }
            display_frame[frame_i].w = x_monitor;
            display_frame[frame_i].h = y_monitor;
        }

        display_surface_offset = display_frame[frame_i].bgra;

        memcpy(pixeldata, display_page->offset, i);
        memcpy(paldata, display_page->pal, i2 * 4);

        if (BGRA_to_RGBA)
            swap_paldata_BGRA_with_RGBA();
        static uint8 *cp;
        static uint32 *lp2;
        static uint32 c;
        cp = pixeldata;
        lp2 = display_surface_offset;
        x2 = display_page->width;
        y2 = display_page->height;
        for (y = 0; y < y2; y++) {
            for (x = 0; x < x2; x++) {
                *lp2++ = paldata[*cp++];
            } // x
        } // y
        if (BGRA_to_RGBA)
            swap_paldata_BGRA_with_RGBA();

        goto screen_refreshed;

    screen_refreshed:

        force_display_update = 0;

        screen_last_valid = 1;

        // Set new display frame as ready
        // display_frame_end=frame_i;
        // if (!display_frame_begin) display_frame_begin=frame_i;

        display_frame[frame_i].state = DISPLAY_FRAME_STATE__READY;
        last_hardware_display_frame_order = display_frame[frame_i].order;

    no_new_frame:;
    display_page_invalid:;

        // cancel frame if not built
        if (display_frame[frame_i].state == DISPLAY_FRAME_STATE__BUILDING) {
            last_hardware_display_frame_order = display_frame[frame_i].order;
            display_frame[frame_i].state = DISPLAY_FRAME_STATE__EMPTY;
        }

    } // lock_display==0
    if (lock_display == 1) {
        lock_display = 2;
        Sleep(0);
    }
    if (autodisplay == -1)
        autodisplay = 0;
    display_called = 1;
    return;
}

/*
    int message_loop(
    SDL_Event event,
    SDL_Surface *screen,
    SDL_Surface *back,
    int *inputedWidth,
    Uint16 *inputedString,
    TTF_Font *font)
    {
    SDL_Color fg = {0x66, 0x66, 0xFF};
    SDL_Color bg = {0x00, 0x00, 0x00};
    SDL_Rect rect;
    SDL_Surface *surface;
    SDL_Event eventExpose;

    switch(event.type){
    case SDL_QUIT:
    return 1;
    case SDL_VIDEOEXPOSE:
    SDL_BlitSurface(back, NULL, screen, NULL);
    SDL_UpdateRect(screen, 0, 0, 0, 0);
    break;
    case SDL_KEYDOWN:





    if (event.key.keysym.sym == QBVK_F1) {
    InputMethod_Reset();
    }
    if (event.key.keysym.sym == QBVK_F2) {
    *inputedWidth = 0;
    inputedString[0] = 0x0000;
    InputMethod_Reset();
    }
    if (event.key.keysym.sym == QBVK_F3) {
    InputMethod_Validate();
    rect.x = 0;
    rect.y = 200;
    rect.w = 640;
    rect.h = 100;
    SDL_FillRect(back, &rect, 0x00000000);
    surface = TTF_RenderUTF8_Shaded(
    font, "Valid", fg, bg);
    SDL_BlitSurface(surface, NULL, back, &rect);
    SDL_FreeSurface(surface);
    eventExpose.type = SDL_VIDEOEXPOSE;
    SDL_PushEvent(&eventExpose);
    }
    if (event.key.keysym.sym == QBVK_F4) {
    InputMethod_Invalidate();
    rect.x = 0;
    rect.y = 200;
    rect.w = 640;
    rect.h = 100;
    SDL_FillRect(back, &rect, 0x00000000);
    surface = TTF_RenderUTF8_Shaded(
    font, "Invalid", fg, bg);
    SDL_BlitSurface(surface, NULL, back, &rect);
    SDL_FreeSurface(surface);
    eventExpose.type = SDL_VIDEOEXPOSE;
    SDL_PushEvent(&eventExpose);
    }
    break;
    default:
    break;
    }
    return 0;
    }
*/

void update_shift_state() {
    int32 x;
    /*
        0:417h                   Shift Status
        7 6 5 4 3 2 1 0
        x . . . . . . .      Insert locked
        . x . . . . . .      Caps Lock locked
        . . x . . . . .      Num Lock locked
        . . . x . . . .      Scroll Lock locked
        . . . . x . . .      Alt key is pressed
        . . . . . x . .      Ctrl key is pressed
        . . . . . . x .      Left Shift key is pressed
        . . . . . . . x      Right Shift key is pressed
    */
    x = 0;
    if (keyheld(VK + QBVK_RSHIFT))
        x |= 1;
    if (keyheld(VK + QBVK_LSHIFT))
        x |= 2;
    if (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL))
        x |= 4;
    if (keyheld(VK + QBVK_LALT) || keyheld(VK + QBVK_RALT))
        x |= 8;
    if (keyheld(QBK + QBK_SCROLL_LOCK_MODE))
        x |= 16;
    if (keyheld(VK + QBVK_NUMLOCK))
        x |= 32;
    if (keyheld(VK + QBVK_CAPSLOCK))
        x |= 64;
    // note: insert state is emulated (off by default)
    if (keyheld(QBK + QBK_INSERT_MODE))
        x |= 128;
    cmem[0x417] = x;
    /*
        0:418h                   Extended Shift Status
        (interpret the word 'pressed' as "being held down")
        7 6 5 4 3 2 1 0
        x . . . . . . .      Ins key is pressed
        . x . . . . . .      Caps Lock key is pressed (detection not possible, return 0)
        . . x . . . . .      Num Lock key is pressed (detection not possible, return 0)
        . . . x . . . .      Scroll Lock key is pressed
        . . . . x . . .      Pause key locked
        . . . . . x . .      SysReq key is pressed
        . . . . . . x .      Left Alt key is pressed
        . . . . . . . x      Left Ctrl key is pressed
    */
    x = 0;
    if (keyheld(VK + QBVK_LCTRL))
        x |= 1;
    if (keyheld(VK + QBVK_LALT))
        x |= 2;
    if (keyheld(VK + QBVK_SYSREQ))
        x |= 4;
    if (keyheld(VK + QBVK_PAUSE))
        x |= 8;
    if (keyheld(VK + QBVK_SCROLLOCK))
        x |= 16;
    // if (keyheld(VK+QBVK_NUMLOCK)) x|=32;
    // if (keyheld(VK+QBVK_CAPSLOCK)) x|=64;
    if (keyheld(0x5200))
        x |= 128;
    cmem[0x418] = x;
    /*
        0:496h                   Keyboard Status and Type Flags
        This byte holds keyboard status information.
        Keyboard Status Information
        7 6 5 4 3 2 1 0
        x . . . . . . .       Read ID in progress (always 0)
        . x . . . . . .       Last character was first ID character (always 0)
        . . x . . . . .       Force Num Lock if read ID and KBX (always 0)
        . . . x . . . .       101/102-key keyboard installed (always 1)
        . . . . x . . .       Right Alt key is pressed
        . . . . . x . .       Right Ctrl key is pressed
        . . . . . . x .       Last code was E0 Hidden Code (always 0)
        . . . . . . . x       last code was E1 Hidden Code (always 0)
    */
    x = 0;
    if (keyheld(VK + QBVK_RCTRL))
        x |= 1;
    if (keyheld(VK + QBVK_RALT))
        x |= 2;
    x |= 16;
    cmem[0x496] = x;
}

int32 keyup_mask_last = -1;
uint32 keyup_mask[256]; // NULL values indicate removed masks

void keyup(uint32 x) {

    if (!x)
        x = QBK + QBK_CHR0;

    keyheld_remove(x);

    if (asciicode_reading != 2) { // hide numpad presses related to ALT+1+2+3 type entries
        // identify and revert numpad specific key codes to non-numpad codes
        static uint32 x2;
        static int64 numpadkey;
        numpadkey = 0;
        x2 = x;
        // check multimapped NUMPAD keys
        if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP_ENTER))) {
            numpadkey = 4294967296ll;
            if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP9))) {
                x2 = x - (VK + QBVK_KP0) + 48;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_PERIOD)) {
                x2 = 46;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_DIVIDE)) {
                x2 = 47;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_MULTIPLY)) {
                x2 = 42;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_MINUS)) {
                x2 = 45;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_PLUS)) {
                x2 = 43;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_ENTER)) {
                x2 = 13;
                goto onnumpad;
            }
        }
        if ((x >= (QBK + 0)) && (x <= (QBK + 10))) {
            numpadkey = 4294967296ll;
            x2 = x - QBK;
            if (x2 == 0) {
                x2 = 82 << 8;
                goto onnumpad;
            }
            if (x2 == 1) {
                x2 = 79 << 8;
                goto onnumpad;
            }
            if (x2 == 2) {
                x2 = 80 << 8;
                goto onnumpad;
            }
            if (x2 == 3) {
                x2 = 81 << 8;
                goto onnumpad;
            }
            if (x2 == 4) {
                x2 = 75 << 8;
                goto onnumpad;
            }
            if (x2 == 5) {
                x2 = 76 << 8;
                goto onnumpad;
            }
            if (x2 == 6) {
                x2 = 77 << 8;
                goto onnumpad;
            }
            if (x2 == 7) {
                x2 = 71 << 8;
                goto onnumpad;
            }
            if (x2 == 8) {
                x2 = 72 << 8;
                goto onnumpad;
            }
            if (x2 == 9) {
                x2 = 73 << 8;
                goto onnumpad;
            }
            if (x2 == 10) {
                x2 = 83 << 8;
                goto onnumpad;
            }
        }
    onnumpad:;

        static int32 i;
        for (i = 0; i <= keyup_mask_last; i++) {
            if (x == keyup_mask[i]) {
                keyup_mask[i] = 0;
                goto key_handled;
            }
        }

        // add x2 to keyhit buffer
        static int32 z;
        z = (keyhit_nextfree + 1) & 0x1FFF;
        if (z == keyhit_next) { // remove oldest message when cyclic buffer is full
            keyhit_next = (keyhit_next + 1) & 0x1FFF;
        }
        static int32 sx;
        sx = x2;
        sx = -sx;
        x2 = sx; // negate x2
        keyhit[keyhit_nextfree] = x2 | numpadkey;
        keyhit_nextfree = z;
    } // asciicode_reading!=2

    static int32 shift, alt, ctrl, capslock, numlock;
    numlock = 0;
    capslock = 0;

    if (x <= 255) {
        if (scancode_lookup[x * 10 + 2])
            scancodeup(scancode_lookup[x * 10 + 1]);
        goto key_handled;
    } // x<=255

    // NUMPAD?
    if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP_ENTER))) {
        if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP_PERIOD)))
            numlock = 1;
        x = (x - (VK + QBVK_KP0) + 256) * 256;
        goto numpadkey;
    }
    if ((x >= (QBK + 0)) && (x <= (QBK + 0 + (QBVK_KP_PERIOD - QBVK_KP0)))) {
        x = (x - (QBK + 0) + 256) * 256;
        goto numpadkey;
    }

    if (x <= 65535) {
        static int32 r;
    numpadkey:
        r = (x >> 8) + 256;
        if (scancode_lookup[r * 10 + 2])
            scancodeup(scancode_lookup[r * 10 + 1]);

        if (x == 0x5200) { // INSERT lock emulation
            update_shift_state();
        }

        goto key_handled;
    } // x<=65536

    if (x == (VK + QBVK_LSHIFT)) {
        scancodeup(42);
        update_shift_state();
    }
    if (x == (VK + QBVK_RSHIFT)) {
        scancodeup(54);
        update_shift_state();
    }
    if (x == (VK + QBVK_LALT)) {
        scancodeup(56);
        update_shift_state();
    }
    if (x == (VK + QBVK_RALT)) {
        scancodeup(56);
        update_shift_state();
    }
    if (x == (VK + QBVK_LCTRL)) {
        scancodeup(29);
        update_shift_state();
    }
    if (x == (VK + QBVK_RCTRL)) {
        scancodeup(29);
        update_shift_state();
    }
    if (x == (VK + QBVK_NUMLOCK)) {
        scancodeup(69);
        update_shift_state();
    }
    if (x == (VK + QBVK_CAPSLOCK)) {
        scancodeup(58);
        update_shift_state();
    }
    if (x == (VK + QBVK_SCROLLOCK)) {
        scancodeup(70);
        update_shift_state();
    }

key_handled:;
}

void keydown(uint32 x) {

    if (!x)
        x = QBK + QBK_CHR0;

    static int32 glyph;
    glyph = keydown_glyph;
    keydown_glyph = 0;

    // INSERT lock emulation
    static int32 insert_held;
    if (x == 0x5200)
        insert_held = keyheld(0x5200);

    // SCROLL lock tracking
    static int32 scroll_lock_held;
    if (x == (VK + QBVK_SCROLLOCK))
        scroll_lock_held = keyheld(VK + QBVK_SCROLLOCK);

    keyheld_add(x);

    // note: On early keyboards without a Pause key (before the introduction of 101-key keyboards) the Pause function was assigned to Ctrl+NumLock, and the
    // Break function to Ctrl+ScrLock; these key-combinations still work with most programs, even on modern PCs with modern keyboards. CTRL+BREAK handling
    if ((x == (VK + QBVK_BREAK)) || ((x == (VK + QBVK_SCROLLOCK)) && (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL))) ||
        ((x == (VK + QBVK_F15)) && (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL)))) {
        if (exit_blocked) {
            exit_value |= 2;
            goto key_handled;
        }
        close_program = 1;
        goto key_handled;
    }

#ifdef QB64_WINDOWS
    // note: Alt+F4 is supposed to close the window, but glut windows don't seem to be affected;
    // this addresses the issue:
    if ((x == (0x3E00)) && (keyheld(VK + QBVK_RALT) || keyheld(VK + QBVK_LALT))) {
        if (exit_blocked) {
            exit_value |= 1;
            goto key_handled;
        }
        close_program = 1;
        goto key_handled;
    }
#endif

    // note: On early keyboards without a Pause key (before the introduction of 101-key keyboards) the Pause function was assigned to Ctrl+NumLock, and the
    // Break function to Ctrl+ScrLock; these key-combinations still work with most programs, even on modern PCs with modern keyboards. PAUSE handling
    if ((x == (VK + QBVK_PAUSE)) || ((x == (VK + QBVK_NUMLOCK)) && (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL)))) {
        suspend_program |= 1;
        qbevent = 1;
        goto key_handled;
    } else {
        if (suspend_program & 1) {
            suspend_program ^= 1;
            goto key_handled;
        }
    }

    // ALT+ENTER
    if (keyheld(VK + QBVK_RALT) || keyheld(VK + QBVK_LALT)) {
        if (x == 13) {
            if (fullscreen_allowedmode >= 0) { // fullscreen_allowedmode==-1 bypasses alt+enter allowing it to be user-trappable
                static int32 fs_mode, fs_smooth;
                fs_mode = full_screen_set;
                if (fs_mode == -1)
                    fs_mode = full_screen;
                fs_smooth = fullscreen_smooth;

                int32 fs_combo;
                if (fs_mode == 0)
                    fs_combo = 0;
                if ((fs_mode == 1) && (fs_smooth == 0))
                    fs_combo = 1;
                if ((fs_mode == 1) && (fs_smooth == 1))
                    fs_combo = 2;
                if ((fs_mode == 2) && (fs_smooth == 0))
                    fs_combo = 3;
                if ((fs_mode == 2) && (fs_smooth == 1))
                    fs_combo = 4;

                int32 fs_validmode = 0;
                while (fs_validmode == 0) {
                    fs_combo++;
                    if (fs_combo > 4)
                        fs_combo = 0;

                    switch (fs_combo) {
                    case 0:
                        fs_mode = 0;
                        fullscreen_smooth = 0;
                        break;
                    case 1:
                        fs_mode = 1;
                        fullscreen_smooth = 0;
                        break;
                    case 2:
                        fs_mode = 1;
                        fullscreen_smooth = 1;
                        break;
                    case 3:
                        fs_mode = 2;
                        fullscreen_smooth = 0;
                        break;
                    case 4:
                        fs_mode = 2;
                        fullscreen_smooth = 1;
                        break;
                    }

                    if (fs_combo == 0)
                        break; // 0 is always valid (= _OFF)

                    fs_validmode = 1;
                    // check _ALLOWFULLSCREEN's settings
                    if ((fullscreen_allowedmode > 0) && (fs_mode != fullscreen_allowedmode))
                        fs_validmode = 0;
                    if ((fullscreen_allowedsmooth == 1) && (fullscreen_smooth != 1))
                        fs_validmode = 0;
                    if ((fullscreen_allowedsmooth == -1) && (fullscreen_smooth != 0))
                        fs_validmode = 0;
                }

                // apply
                if (full_screen != fs_mode)
                    full_screen_set = fs_mode;
                force_display_update = 1;
                goto key_handled;
            }
        }
    }

    if (asciicode_reading != 2) { // hide numpad presses related to ALT+1+2+3 type entries

        // identify and revert numpad specific key codes to non-numpad codes
        static uint32 x2;
        static int64 numpadkey;
        numpadkey = 0;
        x2 = x;
        // check multimapped NUMPAD keys
        if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP_ENTER))) {
            numpadkey = 4294967296ll;
            if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP9))) {
                x2 = x - (VK + QBVK_KP0) + 48;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_PERIOD)) {
                x2 = 46;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_DIVIDE)) {
                x2 = 47;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_MULTIPLY)) {
                x2 = 42;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_MINUS)) {
                x2 = 45;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_PLUS)) {
                x2 = 43;
                goto onnumpad;
            }
            if (x == (VK + QBVK_KP_ENTER)) {
                x2 = 13;
                goto onnumpad;
            }
        }
        if ((x >= (QBK + 0)) && (x <= (QBK + 10))) {
            numpadkey = 4294967296ll;
            x2 = x - QBK;
            if (x2 == 0) {
                x2 = 82 << 8;
                goto onnumpad;
            }
            if (x2 == 1) {
                x2 = 79 << 8;
                goto onnumpad;
            }
            if (x2 == 2) {
                x2 = 80 << 8;
                goto onnumpad;
            }
            if (x2 == 3) {
                x2 = 81 << 8;
                goto onnumpad;
            }
            if (x2 == 4) {
                x2 = 75 << 8;
                goto onnumpad;
            }
            if (x2 == 5) {
                x2 = 76 << 8;
                goto onnumpad;
            }
            if (x2 == 6) {
                x2 = 77 << 8;
                goto onnumpad;
            }
            if (x2 == 7) {
                x2 = 71 << 8;
                goto onnumpad;
            }
            if (x2 == 8) {
                x2 = 72 << 8;
                goto onnumpad;
            }
            if (x2 == 9) {
                x2 = 73 << 8;
                goto onnumpad;
            }
            if (x2 == 10) {
                x2 = 83 << 8;
                goto onnumpad;
            }
        }
    onnumpad:;

        // ON KEY trapping
        { // new scope
            static int32 block_onkey = 0;
            static int32 f, x3, scancode, extended, c, flags_mask;
            int32 i, i2; // must not be static!

            // establish scancode (if any)
            scancode = 0;
            if (x <= 255) {
                scancode = scancode_lookup[x * 10 + 1];
                goto onkey_gotscancode;
            }
            //*check for 2 byte scancodes here
            x3 = x;
            if ((x3 >= (VK + QBVK_KP0)) && (x3 <= (VK + QBVK_KP_ENTER))) {
                x3 = (x3 - (VK + QBVK_KP0) + 256) * 256;
                goto onkey_numpadkey;
            }
            if ((x3 >= (QBK + 0)) && (x3 <= (QBK + 0 + (QBVK_KP_PERIOD - QBVK_KP0)))) {
                x3 = (x3 - (QBK + 0) + 256) * 256;
                goto onkey_numpadkey;
            }
            if (x3 <= 65535) {
            onkey_numpadkey:
                i = (x3 >> 8) + 256;
                if (scancode_lookup[i * 10 + 2])
                    scancode = scancode_lookup[i * 10 + 1];
            }
        onkey_gotscancode:

            // check modifier keys
            if (x == (VK + QBVK_LSHIFT)) {
                scancode = 42;
                flags_mask = 3;
            }
            if (x == (VK + QBVK_RSHIFT)) {
                scancode = 54;
                flags_mask = 3;
            }
            if (x == (VK + QBVK_LALT)) {
                scancode = 56;
                flags_mask = 8;
            }
            if (x == (VK + QBVK_RALT)) {
                scancode = 56;
                flags_mask = 8;
            }
            if (x == (VK + QBVK_LCTRL)) {
                scancode = 29;
                flags_mask = 4;
            }
            if (x == (VK + QBVK_RCTRL)) {
                scancode = 29;
                flags_mask = 4;
            }
            if (x == (VK + QBVK_NUMLOCK)) {
                scancode = 69;
                flags_mask = 32;
            }
            if (x == (VK + QBVK_CAPSLOCK)) {
                scancode = 58;
                flags_mask = 64;
            }
            if (x == (VK + QBVK_SCROLLOCK)) {
                scancode = 70;
                // note: no mask required
            }

            // establish if key is an extended key
            extended = 0;
            // arrow-pad (note: num-pad is ignored because x is a QB64 pure key value and only refers to the arrow-pad)
            if (x == 0x4B00)
                extended = 1;
            if (x == 0x4800)
                extended = 1;
            if (x == 0x4D00)
                extended = 1;
            if (x == 0x5000)
                extended = 1;
            // num-pad extended keys
            if (x == VK + QBVK_KP_DIVIDE)
                extended = 1;
            if (x == VK + QBVK_KP_ENTER)
                extended = 1;
            // ins/del/hom/end/pgu/pgd pad
            if (x == 0x5200)
                extended = 1;
            if (x == 0x4700)
                extended = 1;
            if (x == 0x4900)
                extended = 1;
            if (x == 0x5300)
                extended = 1;
            if (x == 0x4F00)
                extended = 1;
            if (x == 0x5100)
                extended = 1;
            // right alt/right control
            if (x == VK + QBVK_RCTRL)
                extended = 1;
            if (x == VK + QBVK_RALT)
                extended = 1;

            if (!block_onkey) {

                // priority #1: user defined keys
                if (scancode) {
                    for (i = 0; i <= 31; i++) {
                        if (onkey[i].key_scancode == scancode) {
                            if (onkey[i].active) {
                                if (onkey[i].id) {
                                    // check keyboard flags
                                    f = onkey[i].key_flags;
                                    // 0 No keyboard flag, 1-3 Either Shift key, 4 Ctrl key, 8 Alt key,32 NumLock key,64 Caps Lock key, 128 Extended keys on a
                                    // 101-key keyboard To specify multiple shift states, add the values together. For example, a value of 12 specifies that the
                                    // user-defined key is used in combination with the Ctrl and Alt keys.
                                    if ((flags_mask & 3) == 0) {
                                        if (f & 3) {
                                            if (keyheld(VK + QBVK_LSHIFT) == 0 && keyheld(VK + QBVK_RSHIFT) == 0)
                                                goto wrong_flags;
                                        } else {
                                            if (keyheld(VK + QBVK_LSHIFT) || keyheld(VK + QBVK_RSHIFT))
                                                goto wrong_flags;
                                        }
                                    }
                                    if ((flags_mask & 4) == 0) {
                                        if (f & 4) {
                                            if (keyheld(VK + QBVK_LCTRL) == 0 && keyheld(VK + QBVK_RCTRL) == 0)
                                                goto wrong_flags;
                                        } else {
                                            if (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL))
                                                goto wrong_flags;
                                        }
                                    }
                                    if ((flags_mask & 8) == 0) {
                                        if (f & 8) {
                                            if (keyheld(VK + QBVK_LALT) == 0 && keyheld(VK + QBVK_RALT) == 0)
                                                goto wrong_flags;
                                        } else {
                                            if (keyheld(VK + QBVK_LALT) || keyheld(VK + QBVK_RALT))
                                                goto wrong_flags;
                                        }
                                    }
                                    if ((flags_mask & 32) == 0) {
                                        if (f & 32) {
                                            if (keyheld(VK + QBVK_NUMLOCK) == 0)
                                                goto wrong_flags;
                                            //*revise
                                        }
                                    }
                                    if ((flags_mask & 64) == 0) {
                                        if (f & 64) {
                                            if (keyheld(VK + QBVK_CAPSLOCK) == 0)
                                                goto wrong_flags;
                                            //*revise
                                        }
                                    }
                                    if ((flags_mask & 128) == 0) {
                                        if (((f & 128) / 128) != extended)
                                            goto wrong_flags;
                                    }
                                    if (onkey[i].active == 1) { //(1)ON
                                        onkey[i].state++;
                                    } else { //(2)STOP
                                        onkey[i].state = 1;
                                    }

                                    qbevent = 1;

                                    // mask trigger key
                                    for (i = 0; i <= keyup_mask_last; i++) {
                                        if (!keyup_mask[i]) {
                                            keyup_mask[i] = x;
                                            break;
                                        }
                                    }
                                    if (i == keyup_mask_last + 1) {
                                        if (keyup_mask_last < 255) {
                                            keyup_mask[i] = x;
                                            keyup_mask_last++;
                                        }
                                    }

                                    goto key_handled;

                                } // id
                            } // active
                        } // scancode==
                    wrong_flags:;
                    } // i
                } // scancode

                // priority #2: fixed index F1-F12, arrows
                for (i = 0; i <= 31; i++) {
                    if (onkey[i].active) {
                        if (onkey[i].id) {
                            if ((x2 == onkey[i].keycode) || x == onkey[i].keycode_alternate) {
                                if (onkey[i].active == 1) { //(1)ON
                                    onkey[i].state++;
                                } else { //(2)STOP
                                    onkey[i].state = 1;
                                }
                                qbevent = 1;

                                // mask trigger key
                                for (i = 0; i <= keyup_mask_last; i++) {
                                    if (!keyup_mask[i]) {
                                        keyup_mask[i] = x;
                                        break;
                                    }
                                }
                                if (i == keyup_mask_last + 1) {
                                    if (keyup_mask_last < 255) {
                                        keyup_mask[i] = x;
                                        keyup_mask_last++;
                                    }
                                }

                                goto key_handled;
                            } // keycode
                        } // id
                    } // active
                } // i

            } // block_onkey

            // priority #3: string insertion
            for (i = 0; i <= 31; i++) {
                if (onkey[i].text) {
                    if (onkey[i].text->len) {
                        if ((x2 == onkey[i].keycode) || x == onkey[i].keycode_alternate) {

                            // mask trigger key
                            { // scope
                                static int32 i;
                                for (i = 0; i <= keyup_mask_last; i++) {
                                    if (!keyup_mask[i]) {
                                        keyup_mask[i] = x;
                                        break;
                                    }
                                }
                                if (i == keyup_mask_last + 1) {
                                    if (keyup_mask_last < 255) {
                                        keyup_mask[i] = x;
                                        keyup_mask_last++;
                                    }
                                }
                            } // descope

                            for (i2 = 0; i2 < onkey[i].text->len; i2++) {
                                block_onkey = 1;
                                keydown_ascii(onkey[i].text->chr[i2]);
                                keyup_ascii(onkey[i].text->chr[i2]);
                                block_onkey = 0;
                            } // i2
                            goto key_handled;
                        } // keycode
                    }
                } // text
            } // i

        } // descope

        /*
            //keyhit cyclic buffer
            int64 keyhit[8192];
            //    keyhit specific internal flags: (stored in high 32-bits)
            //    &4294967296->numpad was used
            int32 keyhit_nextfree=0;
            int32 keyhit_next=0;
            //note: if full, the oldest message is discarded to make way for the new message
        */
        // add x2 to keyhit buffer
        static int32 z;
        z = (keyhit_nextfree + 1) & 0x1FFF;
        if (z == keyhit_next) { // remove oldest message when cyclic buffer is full
            keyhit_next = (keyhit_next + 1) & 0x1FFF;
        }
        keyhit[keyhit_nextfree] = x2 | numpadkey;
        keyhit_nextfree = z;
    } // asciicode_reading!=2

    static int32 shift, alt, ctrl, capslock, numlock;
    numlock = 0;
    capslock = 0;

    if (x == QBK + QBK_CHR0)
        x = 0;

    if (x <= 255) {
        static int32 b1, b2, z, o;
        b1 = x;
        if ((b2 = scancode_lookup[x * 10 + 1])) { // table entry exists
            scancodedown(b2);

            // check for relevant table modifiers
            shift = 0;
            if (keyheld(VK + QBVK_LSHIFT) || keyheld(VK + QBVK_RSHIFT))
                shift = 1;
            ctrl = 0;
            if (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL))
                ctrl = 1;
            alt = 0;
            if (keyheld(VK + QBVK_LALT) || keyheld(VK + QBVK_RALT))
                alt = 1;
            o = 0;
            if (shift)
                o = 1;
            if (ctrl)
                o = 2;
            if (alt)
                o = 3;
            if (glyph) {
                if ((keyheld(VK + QBVK_LALT) == 0) && (keyheld(VK + QBVK_RCTRL) == 0) && keyheld(VK + QBVK_LCTRL) && keyheld(VK + QBVK_RALT))
                    o = 0; // assume alt-gr combo-key
            }
            z = scancode_lookup[x * 10 + 2 + o];
            if (!z)
                goto key_handled; // not possible
            if (z & 0xFF00) {
                b1 = 0;
                b2 = z >> 8;
            } else {
                b1 = z;
            }
        } // b2
        static int32 i, i2, i3;
        i = cmem[0x41a];
        i2 = cmem[0x41c];
        i3 = i2 + 2;
        if (i3 == 62)
            i3 = 30;
        if (i != i3) { // fits in buffer
            cmem[0x400 + i2] = b1;
            cmem[0x400 + i2 + 1] = b2; //(scancode)
            cmem[0x41c] = i3;          // fix tail location
        }
        goto key_handled;
    } // x<=255

    // NUMPAD?
    if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP_ENTER))) {
        if ((x >= (VK + QBVK_KP0)) && (x <= (VK + QBVK_KP_PERIOD)))
            numlock = 1;
        x = (x - (VK + QBVK_KP0) + 256) * 256;
        goto numpadkey;
    }
    if ((x >= (QBK + 0)) && (x <= (QBK + 0 + (QBVK_KP_PERIOD - QBVK_KP0)))) {
        x = (x - (QBK + 0) + 256) * 256;
        goto numpadkey;
    }

    if (x <= 65535) {
        static int32 b1, b2, z, o, r;
    numpadkey:
        b1 = 0;
        b2 = x >> 8;
        r = (x >> 8) + 256;
        if (scancode_lookup[r * 10 + 2]) {
            scancodedown(scancode_lookup[r * 10 + 1]);
            // check relevant modifiers
            shift = 0;
            if (keyheld(VK + QBVK_LSHIFT) || keyheld(VK + QBVK_RSHIFT))
                shift = 1;
            ctrl = 0;
            if (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL))
                ctrl = 1;
            alt = 0;
            if (keyheld(VK + QBVK_LALT) || keyheld(VK + QBVK_RALT))
                alt = 1;

            if (x == 0x5200) {          // INSERT lock emulation
                if (insert_held == 0) { // nullify effects of key repeats
                    if ((alt == 0) && (shift == 0) && (ctrl == 0)) {
                        // toggle insert mode
                        if (keyheld(QBK + QBK_INSERT_MODE)) {
                            keyheld_remove(QBK + QBK_INSERT_MODE);
                        } else {
                            keyheld_add(QBK + QBK_INSERT_MODE);
                        }
                        update_shift_state();
                    }
                }
            }

            o = 0;
            if (shift)
                o = 1;
            if (numlock)
                o = 4;
            if (numlock && shift)
                o = 7;
            if (ctrl)
                o = 2;
            if (alt)
                o = 3;
            z = scancode_lookup[r * 10 + 2 + o];
            if (!z)
                goto key_handled; // invalid combination
            if (z & 0xFF00) {
                b1 = 0;
                b2 = z >> 8;
            } else {
                b1 = z;
                b2 = scancode_lookup[r * 10 + 1];
            }
        } // z
        static int32 i, i2, i3;
        i = cmem[0x41a];
        i2 = cmem[0x41c];
        i3 = i2 + 2;
        if (i3 == 62)
            i3 = 30;
        if (i != i3) { // fits in buffer
            cmem[0x400 + i2] = b1;
            cmem[0x400 + i2 + 1] = b2; //(scancode)
            cmem[0x41c] = i3;          // fix tail location
        }
        goto key_handled;
    } // x<=65536

    if (x == (VK + QBVK_LSHIFT)) {
        scancodedown(42);
        update_shift_state();
    }
    if (x == (VK + QBVK_RSHIFT)) {
        scancodedown(54);
        update_shift_state();
    }
    if (x == (VK + QBVK_LALT)) {
        scancodedown(56);
        update_shift_state();
    }
    if (x == (VK + QBVK_RALT)) {
        scancodedown(56);
        update_shift_state();
    }
    if (x == (VK + QBVK_LCTRL)) {
        scancodedown(29);
        update_shift_state();
    }
    if (x == (VK + QBVK_RCTRL)) {
        scancodedown(29);
        update_shift_state();
    }
    if (x == (VK + QBVK_NUMLOCK)) {
        scancodedown(69);
        update_shift_state();
    }
    if (x == (VK + QBVK_CAPSLOCK)) {
        scancodedown(58);
        update_shift_state();
    }
    if (x == (VK + QBVK_SCROLLOCK)) {
        scancodedown(70);

        if (scroll_lock_held == 0) { // nullify effects of key repeats
            ctrl = 0;
            if (keyheld(VK + QBVK_LCTRL) || keyheld(VK + QBVK_RCTRL))
                ctrl = 1;
            if (ctrl == 0) {
                // toggle insert mode
                if (keyheld(QBK + QBK_SCROLL_LOCK_MODE)) {
                    keyheld_remove(QBK + QBK_SCROLL_LOCK_MODE);
                } else {
                    keyheld_add(QBK + QBK_SCROLL_LOCK_MODE);
                }
            }
        }

        update_shift_state();
    }

key_handled:
    sleep_break = 1;
}

void scancodedown(uint8 scancode) {
    if (port60h_events) {
        if (port60h_event[port60h_events - 1] == scancode)
            return; // avoid duplicate entries in buffer (eg. from key-repeats)
    }
    if (port60h_events == 256) {
        memmove(port60h_event, port60h_event + 1, 255);
        port60h_events = 255;
    }
    port60h_event[port60h_events] = scancode;
    port60h_events++;
}

void scancodeup(uint8 scancode) {
    if (port60h_events) {
        if (port60h_event[port60h_events - 1] == (scancode + 128))
            return; // avoid duplicate entries in buffer
    }
    if (port60h_events == 256) {
        memmove(port60h_event, port60h_event + 1, 255);
        port60h_events = 255;
    }
    port60h_event[port60h_events] = scancode + 128;
    port60h_events++;
}

#define OS_EVENT_PRE_PROCESSING 1
#define OS_EVENT_POST_PROCESSING 2
#define OS_EVENT_RETURN_IMMEDIATELY 3

static void reportKeyState(int code, int down) {
    static device_struct *d;
    d = &devices[1]; // keyboard

    // don't add message if state already matches what we're reporting
    if (getDeviceEventButtonValue(d, d->queued_events - 1, code) != down) {
        int32 eventIndex = createDeviceEvent(d);
        setDeviceEventButtonValue(d, eventIndex, code, down);
        commitDeviceEvent(d);
    }
}

#ifdef QB64_WINDOWS

// Windows only reports one WM_KEYUP event when both shift keys are held. To
// fix that somewhat we're manually check on each window message and reporting
// the keyup ourselves.
static void checkShiftKeys() {
    static int rightShift = 0, leftShift = 0;

    // GetAsyncKeyState() indicates the key state in the most significant bit
    if (rightShift != !!(GetAsyncKeyState(VK_RSHIFT) & 0x8000)) {
        rightShift = !rightShift;
        reportKeyState(0x36, rightShift);
    }

    if (leftShift != !!(GetAsyncKeyState(VK_LSHIFT) & 0x8000)) {
        leftShift = !leftShift;
        reportKeyState(0x2A, leftShift);
    }
}

extern "C" LRESULT qb64_os_event_windows(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, int *qb64_os_event_info) {
    if (*qb64_os_event_info == OS_EVENT_PRE_PROCESSING) {
        if (func__hasfocus())
            checkShiftKeys();

        if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) {
            if (device_last) { // core devices required?
                /*
                    16-23        The scan code. The value depends on the OEM.
                    24        Indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key
                   keyboard. The value is 1 if it is an extended key; otherwise, it is 0.
                */
                uint32_t code = (lParam >> 16) & 0x1FF;

                reportKeyState(code, 1);
            }
        }

        if (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) {
            if (device_last) { // core devices required?
                uint32_t code = (lParam >> 16) & 0x1FF;

                reportKeyState(code, 0);
            }
        }
    }
    if (*qb64_os_event_info == OS_EVENT_POST_PROCESSING) {
    }
    return 0;
}
#endif

#if defined(QB64_LINUX) && defined(QB64_GUI)
extern "C" void qb64_os_event_linux(XEvent *event, Display *display, int *qb64_os_event_info) {
    if (*qb64_os_event_info == OS_EVENT_PRE_PROCESSING) {

        if (X11_display == NULL) {
            X11_display = display;
            X11_window = event->xexpose.window;
        }
    }

    if (*qb64_os_event_info == OS_EVENT_POST_PROCESSING) {
        switch (event->type) {
        case FocusIn:
            window_focused = -1;
            break;

        case FocusOut:
            window_focused = 0;
            // Iterate over all modifiers
            for (uint32 key = VK + QBVK_RSHIFT; key <= VK + QBVK_MODE; key++) {
                if (keyheld(key))
                    keyup(key);
            }
            break;
        }
    }
}
#endif

void qb64_custom_event_relative_mouse_movement(int deltaX, int deltaY) {
    mouse_message_queue_struct *queue = &mouse_message_queue;
    // message #1
    int32_t i = queue->last + 1;
    if (i > queue->lastIndex)
        i = 0;
    if (i == queue->current) {
        int32_t nextIndex = queue->last + 1;
        if (nextIndex > queue->lastIndex)
            nextIndex = 0;
        queue->current = nextIndex;
    }
    queue->queue[i].x = queue->queue[queue->last].x;
    queue->queue[i].y = queue->queue[queue->last].y;
    queue->queue[i].movementx = deltaX;
    queue->queue[i].movementy = deltaY;
    queue->queue[i].buttons = queue->queue[queue->last].buttons;
    queue->last = i;
    // message #2 (clears movement values to avoid confusion)
    i = queue->last + 1;
    if (i > queue->lastIndex)
        i = 0;
    if (i == queue->current) {
        int32_t nextIndex = queue->last + 1;
        if (nextIndex > queue->lastIndex)
            nextIndex = 0;
        queue->current = nextIndex;
    }
    queue->queue[i].x = queue->queue[queue->last].x;
    queue->queue[i].y = queue->queue[queue->last].y;
    queue->queue[i].movementx = 0;
    queue->queue[i].movementy = 0;
    queue->queue[i].buttons = queue->queue[queue->last].buttons;
    queue->last = i;
}

extern "C" int qb64_custom_event(int event, int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, void *p1, void *p2) {
    if (event == QB64_EVENT_CLOSE) {
        exit_value |= 1;
        return 0;
    } // close

    if (event == QB64_EVENT_KEY) {
        if (v1 == VK + QBVK_PAUSE) {
            if (v2 > 0)
                keydown_vk(v1);
            else
                keyup_vk(v1);
            return 0;
        }
        if (v1 == VK + QBVK_BREAK) {
            if (v2 > 0)
                keydown_vk(v1);
            else
                keyup_vk(v1);
            return 0;
        }
        return -1;
    } // key

    // qb64_custom_event(QB64_EVENT_RELATIVE_MOUSE_MOVEMENT,xPosRelative,yPosRelative,0,0,0,0,0,0,NULL,NULL);
    if (event == QB64_EVENT_RELATIVE_MOUSE_MOVEMENT) {
        qb64_custom_event_relative_mouse_movement(v1, v2);
        return 0;
    } // QB64_EVENT_RELATIVE_MOUSE_MOVEMENT

    if (event == QB64_EVENT_FILE_DROP) {
#ifdef QB64_WINDOWS
        if (totalDroppedFiles > 0)
            sub__finishdrop();

        hdrop = (HDROP)p1;
        totalDroppedFiles = DragQueryFile(hdrop, -1, NULL, 0);
#endif
        return 0;
    }

    return -1; // Unknown command (use for debugging purposes only)
}

int32 func__capslock() {
#ifdef QB64_WINDOWS
    return -GetKeyState(VK_CAPITAL);
#endif
    return 0;
}

int32 func__scrolllock() {
#ifdef QB64_WINDOWS
    return -GetKeyState(VK_SCROLL);
#endif
    return 0;
}

int32 func__numlock() {
#ifdef QB64_WINDOWS
    return -GetKeyState(VK_NUMLOCK);
#endif
    return 0;
}

void toggle_lock_key(int32 key_code) {
#ifdef QB64_WINDOWS
    keybd_event(key_code, 0x45, 1, 0);
    keybd_event(key_code, 0x45, 3, 0);
#endif
}

void sub__capslock(int32 options) {
#ifdef QB64_WINDOWS
    // VK_CAPITAL
    int32 currentState = func__capslock();
    switch (options) {
    case 1: // ON
        if (currentState == -1)
            return;
        break;
    case 2: // OFF
        if (currentState == 0)
            return;
        break;
    }
    // _TOGGLE:
    toggle_lock_key(VK_CAPITAL);
#endif
}

void sub__scrolllock(int32 options) {
#ifdef QB64_WINDOWS
    // VK_SCROLL
    int32 currentState = func__scrolllock();
    switch (options) {
    case 1: // ON
        if (currentState == -1)
            return;
        break;
    case 2: // OFF
        if (currentState == 0)
            return;
        break;
    }
    // _TOGGLE:
    toggle_lock_key(VK_SCROLL);
#endif
}

void sub__numlock(int32 options) {
#ifdef QB64_WINDOWS
    // VK_NUMLOCK
    int32 currentState = func__numlock();
    switch (options) {
    case 1: // ON
        if (currentState == -1)
            return;
        break;
    case 2: // OFF
        if (currentState == 0)
            return;
        break;
    }
    // _TOGGLE:
    toggle_lock_key(VK_NUMLOCK);
#endif
}

void sub__consolefont(qbs *FontName, int FontSize) {
#ifdef QB64_WINDOWS
#    if WINVER >= 0x0600 // this block is not compatible with XP
    SECURITY_ATTRIBUTES SecAttribs = {sizeof(SECURITY_ATTRIBUTES), 0, 1};
    HANDLE cl_conout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &SecAttribs, OPEN_EXISTING, 0, 0);
    static int OneTimePause;
    if (!OneTimePause) { // a slight delay so the console can be properly created and registered with Windows, before we try to change fonts with it.
        Sleep(500);
        OneTimePause = 1; // after the first pause, the console should be created, so we don't need any more delays in the future.
    }
    CONSOLE_FONT_INFOEX info = {0};
    info.cbSize = sizeof(info);
    info.dwFontSize.Y = FontSize; // leave X as zero
    info.FontWeight = FW_NORMAL;
    if (FontName->len > 0) { // if we don't pass a font name, don't change the existing one.
        const size_t cSize = FontName->len;
        wchar_t *wc = new wchar_t[32];
        mbstowcs(wc, (char *)FontName->chr, cSize);
        wcscpy(info.FaceName, wc);
        delete[] wc;
    }

    SetCurrentConsoleFontEx(cl_conout, NULL, &info);
#    endif
#endif
}

void sub__console_cursor(int32 visible, int32 cursorsize, int32 passed) {
#ifdef QB64_WINDOWS
    HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO info;

    GetConsoleCursorInfo(consoleHandle, &info); // get the original info, so we reuse it, unless the user called for a change.

    if (visible == 1)
        info.bVisible = TRUE; // cursor is set to show
    if (visible == 2)
        info.bVisible = FALSE; // set to hide
    if (passed && cursorsize >= 0 && cursorsize <= 100)
        info.dwSize = cursorsize; // the user passed the cursor size, of a suitable size

    SetConsoleCursorInfo(consoleHandle, &info);
#endif
}

int32 func__getconsoleinput() {
#ifdef QB64_WINDOWS
    HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
    INPUT_RECORD irInputRecord;
    DWORD dwEventsRead, fdwMode, dwMode;
    CONSOLE_SCREEN_BUFFER_INFO cl_bufinfo;

    GetConsoleMode(hStdin, (LPDWORD)&dwMode);
    fdwMode = ENABLE_EXTENDED_FLAGS;
    SetConsoleMode(hStdin, fdwMode);
    fdwMode = dwMode | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
    SetConsoleMode(hStdin, fdwMode);

    DWORD numEvents = 0;
    GetNumberOfConsoleInputEvents(hStdin, &numEvents);
    if (numEvents) {
        ReadConsoleInputA(hStdin, &irInputRecord, 1, &dwEventsRead);
        switch (irInputRecord.EventType) {
        case KEY_EVENT: // keyboard input
            consolekey = irInputRecord.Event.KeyEvent.wVirtualScanCode;
            if (!irInputRecord.Event.KeyEvent.bKeyDown)
                consolekey = -consolekey; // positive/negative return of scan codes.
            return 1;
        case MOUSE_EVENT: // mouse input
            consolemousex = irInputRecord.Event.MouseEvent.dwMousePosition.X + 1;
            consolemousey = irInputRecord.Event.MouseEvent.dwMousePosition.Y - cl_bufinfo.srWindow.Top + 1;
            consolebutton = irInputRecord.Event.MouseEvent.dwButtonState; // button state for all buttons
            // SetConsoleMode(hStdin, dwMode);
            return 2;
        }
    }
#endif
    return 0; // no or unhandled input
}

int32 func__cinp(int32 toggle, int32 passed) {
#ifdef QB64_WINDOWS
    int32 temp = consolekey;
    consolekey = 0; // reset the console key, now that we've read it
    if (passed == 0)
        toggle = 1; // default return of positive/negative scan code values
    if (toggle) {
        return temp;
    } else {
        if (temp >= 0)
            return temp;
        return -temp + 128;
    }
#else
    return 0;
#endif
}
