#include "common.cpp"

//substitute Windows functionality
#ifndef QB64_WINDOWS
 //messagebox defines
 #define IDOK                1
 #define IDCANCEL            2
 #define IDABORT             3
 #define IDRETRY             4
 #define IDIGNORE            5
 #define IDYES               6
 #define IDNO                7
 #define MB_OK                       0x00000000L
 #define MB_OKCANCEL                 0x00000001L
 #define MB_ABORTRETRYIGNORE         0x00000002L
 #define MB_YESNOCANCEL              0x00000003L
 #define MB_YESNO                    0x00000004L
 #define MB_RETRYCANCEL              0x00000005L

 int MessageBox(int p1,char* p2,char* p3,int p4){
 cout<<"[MessageBox("<<p3<<","<<p2<<")]";
 exit(0);
 return 0;
 }

 void AllocConsole(){
 return;
 }
 void FreeConsole(){
 return;
 }

 long errno;
#endif

//vc->project->properties->configuration properties->general->configuration type->application(.exe)
//vc->project->properties->configuration properties->general->configuration type->static library(.lib)
extern int QBMAIN(void *);
extern int TIMERTHREAD(void *);
extern qbs* WHATISMYIP();

//directory access defines
#define EPERM           1
#define ENOENT          2
#define ESRCH           3
#define EINTR           4
#define EIO             5
#define ENXIO           6
#define E2BIG           7
#define ENOEXEC         8
#define EBADF           9
#define ECHILD          10
#define EAGAIN          11
#define ENOMEM          12
#define EACCES          13
#define EFAULT          14
#define EBUSY           16
#define EEXIST          17
#define EXDEV           18
#define ENODEV          19
#define ENOTDIR         20
#define EISDIR          21
#define EINVAL          22
#define ENFILE          23
#define EMFILE          24
#define ENOTTY          25
#define EFBIG           27
#define ENOSPC          28
#define ESPIPE          29
#define EROFS           30
#define EMLINK          31
#define EPIPE           32
#define EDOM            33
#define ERANGE          34
#define EDEADLK         36
#define ENAMETOOLONG    38
#define ENOLCK          39
#define ENOSYS          40
#define ENOTEMPTY       41
#define EILSEQ          42

//forward refs
void showvalue(__int64);
void display();
void validatepage(long);
void sub__dest(long);
void sub__source(long);
long func__printwidth(qbs*,long,long);
void sub_cls(long,unsigned long,long);
void qbs_print(qbs*,long);
long func__copyimage(long,long);
long func__dest();
long func__display();
void qbg_sub_view_print(long,long,long);
qbs *qbs_new_txt(const char *);
void qbg_sub_window(long,float,float,float,float,long);
long autodisplay=1;
//GFS forward references
int32 gfs_eof_passed(int32 i);
int32 gfs_eof_reached(int32 i);
int64 gfs_getpos(int32 i);
int32 gfs_fileno_valid(int32 f);
int32 gfs_fileno_freefile();//like FREEFILE
void gfs_fileno_use(int32 f, int32 i);
int32 gfs_open(qbs *filename,int32 access,int32 restrictions, int32 how);
int32 gfs_close(int32 i);
int64 gfs_lof(int32 i);
int32 gfs_setpos(int32 i, int64 position);
int32 gfs_write(int32 i,int64 position,uint8 *data,int64 size);
int32 gfs_read(int32 i,int64 position,uint8 *data,int64 size);
int64 gfs_read_bytes();

/* Generic File System (GFS)
GFS allows OS specific access whilst still maintaining 'pure' C-based routines for
multiplatform compatibility. 'Pure' C-based routines may not allow certain functionality,
such as partial file locking.
GFS handles/indexes are independent of QB64 handles/indexes to allow for internal files
to be open but not intefere with the QB64 file handle numbers.

GFS error codes:
-1 non-specific fail
-2 invalid handle
-3 bad/incorrect file mode
-4 illegal function call (input is out of range)
-5 file not found (win:2)
-6 path not found (win:3)
-7 access/permission denied (win:5,19)
-8 device unavailable/drive invalid (win:15,21)
-9 path/file access error
-10 read past eof

*/

struct gfs_file_struct{//info applicable to all files
uint8 open;
uint8 read;
uint8 write;
uint8 lock_read;
uint8 lock_write;
int64 pos;//-1=unknown
uint8 eof_reached;//read last character of file (set/reset by gfs_read only)
uint8 eof_passed;//attempted to read past eof (set/reset by gfs_read only)
int32 fileno;//link to fileno index
uint8 type;//qb access method (1=RANDOM,2=BINARY,3=INPUT,4=OUTPUT)
int64 record_length;//used by RANDOM
int64 column;//used by OUTPUT/APPEND to tab correctly
};

 #ifdef QB64_WINDOWS
 struct gfs_file_win_struct{//info applicable to WINDOWS OS files
 HANDLE file_handle;
 };
 gfs_file_win_struct *gfs_file_win=(gfs_file_win_struct*)malloc(1);
 #endif

gfs_file_struct *gfs_file=(gfs_file_struct*)malloc(1);

int32 gfs_n=0;
int32 gfs_freed_n=0;
int32 *gfs_freed=(int32*)malloc(1);
int32 gfs_freed_size=0;

int32 *gfs_fileno=(int32*)malloc(1);
int32 gfs_fileno_n=0;

int32 *gfs_fileno_freed=(int32*)malloc(1);
int32 gfs_fileno_freed_size=0;
int32 gfs_fileno_freed_n=0;

static const uint16 codepage437_to_unicode16[] = {
0x0020,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,0x0020
};
uint16 *unicode16_buf=(uint16*)malloc(2);
int32 unicode16_buf_len=1;

void convert_codepage437_to_unicode16(void *buf,int32 len){
static int32 i;
static uint8 *uint8p;
uint8p=(uint8*)buf;
if ((len+1)>unicode16_buf_len){
unicode16_buf=(uint16*)realloc(unicode16_buf,2*(len+1));
if (!unicode16_buf) exit(26374);
unicode16_buf_len=len+1;
}
for (i=0;i<len;i++){
unicode16_buf[i]=codepage437_to_unicode16[uint8p[i]];
}
unicode16_buf[i]=0;//add null terminator
}



qbs *unknown_opcode_mess;

extern unsigned long ercl;

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

//MLP
//long qbshlp1=0;

//special file handle (sfh) system:
struct sfh_struct{
uint8 type;//what type of handle it is
};

sfh_struct *sfh=(sfh_struct*)calloc(1,sizeof(sfh_struct));
uint32 sfh_bufsize=0;

uint32 *sfh_freed=(uint32*)calloc(1,4);
uint32 sfh_freed_bufsize=0;
int32 sfh_nfreed=0;

uint32 sfh_new(){
uint32 i;
//check freed list
if (sfh_nfreed){
sfh_nfreed--;
i=sfh_freed[sfh_nfreed];
}else{
//expand buffer
sfh_bufsize++;
sfh=(sfh_struct*)realloc(sfh,sizeof(sfh_struct)*sfh_bufsize);
i=sfh_bufsize-1;
}
sfh[i].type=255;//undefined
return i;
}

void sfh_free(int32 i){
if ((i<0)||(i>=sfh_bufsize)) return;
if (sfh[i].type){//allocated
sfh[i].type=0;
if (sfh_freed_bufsize<=sfh_nfreed){
sfh_freed_bufsize++;
sfh_freed=(uint32*)realloc(sfh_freed,sfh_freed_bufsize*4);
}
sfh_freed[sfh_nfreed++]=i;
}
}

//network prototype:

//network handles
struct net_tcp_struct{
IPaddress ip;
TCPsocket socket;
uint16 portused;
SDLNet_SocketSet set;
uint8 error;//0=no communication error has occurred
uint8 eof;
	uint8 *buffer;
	uint32 buffer_size;
	uint32 buffer_space;
};
net_tcp_struct *net_tcp=(net_tcp_struct*)calloc(1,sizeof(net_tcp_struct));
uint32 net_tcp_bufsize=1;

uint8 *revert_input_buffer=NULL;
uint32 revert_input_bufsize=0;
int32 revert_input_x=-1;//invalid

void net_tcp_updatebuffer(uint32 i){//i is a valid index in the net_tcp 
static int32 x;
static uint8 byte;

if (net_tcp[i].error) return;

getdata:

x=SDLNet_CheckSockets(net_tcp[i].set,0);
if (x==-1){//error
net_tcp[i].error=1;
return;
}

if (x==0) return;//no change (no new data)

x=SDLNet_TCP_Recv(net_tcp[i].socket,&byte,1);

if (x<=0){//error/no data when there should have been 1+ bytes
net_tcp[i].error=1;
return;
}

//expand buffer if necessary
if (net_tcp[i].buffer_size==net_tcp[i].buffer_space){
if (net_tcp[i].buffer==NULL){
net_tcp[i].buffer=(uint8*)malloc(net_tcp[i].buffer_space+1024);//1K increments
}else{
net_tcp[i].buffer=(uint8*)realloc(net_tcp[i].buffer,net_tcp[i].buffer_space+1024);//1K increments
}
net_tcp[i].buffer_space+=1024;
}

net_tcp[i].buffer[net_tcp[i].buffer_size++]=byte;

goto getdata;
}

uint8 *tcp_feed_ucbuf;
int32 tcp_feed_ucbufsiz;
int32 tcp_feed_offset;











char *fixdir(qbs *filename){
//note: changes the slashes in a filename to make it compatible with the OS
//applied to QB commands: open, bload/bsave, loadfont, loadimage, sndopen/sndplayfile
static long i;
for (i=0;i<filename->len;i++){
#ifdef QB64_WINDOWS
if (filename->chr[i]==47) filename->chr[i]=92;
#else
if (filename->chr[i]==92) filename->chr[i]=47;
#endif
}
return (char*)filename->chr;
}


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

unsigned long pal[256];

extern qbs* nothingstring;

static unsigned long sdl_shiftstate=0;

static unsigned long sdl_scroll_lock=0;
static unsigned long sdl_insert=0;
static unsigned long sdl_scroll_lock_prepared=1;
static unsigned long sdl_insert_prepared=1;

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

long key_repeat_on=0;



void error(long error_number);//for forward references
unsigned long palette_256[256];
unsigned long palette_64[64];

//QB64 2D PROTOTYPE 1.0
SDL_Surface *ts,*ts2;
SDL_PixelFormat pixelformat32;
SDL_PixelFormat pixelformat8;

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

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

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

unsigned char *blend=NULL;
unsigned char *ablend=NULL;
unsigned char *ablend127;
unsigned char *ablend128;
//to save 16MB of RAM, software blend tables are only allocated if a 32-bit image is created
void init_blend(){
unsigned char *cp;
long i,x2,x3,i2,z;
float f,f2,f3;
blend=(unsigned char*)malloc(16777216);
cp=blend;
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 whats 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=(unsigned char*)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);
}


unsigned long display_page_index=0;
unsigned long write_page_index=0;
unsigned long 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;
SDL_Surface *display_surface=NULL;
unsigned long *display_surface_offset=0;

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

switch(im->compatible_mode){

case 1:
/*
SCREEN Mode 1 Syntax:  COLOR [background][,palette]
   ¦ background is the screen color (range = 0-15)
   ¦ 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 (does not use pal[] array)
//use upper palette values to hold selected palette colors
pal[252]=4;
pal[253]=5;
pal[254]=6;
pal[255]=7;
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(long x,long y,unsigned long col){
static unsigned char *cp;
static unsigned long *o32;
static unsigned long 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 (optomized)
 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 (optomized)
 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=blend+(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));
unsigned long nimg=1024;
unsigned long nextimg=0;//-1=none have been assigned

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

//returns an index to free img structure
unsigned long newimg(){
static long 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);
memset(&img[nimg],0,IMG_BUFFERSIZE*sizeof(img_struct));
nimg+=IMG_BUFFERSIZE;
i=nextimg++;
gotindex:
img[i].valid=1;
return i;
}

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


void imgrevert(long i){
static long 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 long i2,i3;
static unsigned short *sp;
i3=im->width*im->height; sp=(unsigned short*)im->offset; for (i2=0;i2<i3;i2++){*sp++=0x0720;}
}

}//imgrevert

long imgframe(unsigned char *o,long x,long y,long bpp){
static long 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=(unsigned long*)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;
}

void sub__freeimage(long i,long passed);//forward ref

long imgnew(long x,long y,long bpp){
static long i,i2,i3;
static img_struct *im;
static unsigned short *sp;
static unsigned long *lp;
i=imgframe(NULL,x,y,bpp);
if (!i) return 0;
im=&img[i];
if (bpp){//graphics
if (bpp==32){
if (!blend) init_blend();
im->offset=(unsigned char*)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=(unsigned char*)calloc(x*y*im->bytes_per_pixel,1);
if (!im->offset){sub__freeimage(-i,1); return 0;}
}
}else{//text
im->offset=(unsigned char*)malloc(x*y*im->bytes_per_pixel);
if (!im->offset){sub__freeimage(-i,1); return 0;}
i3=x*y; sp=(unsigned short*)im->offset; for (i2=0;i2<i3;i2++){*sp++=0x0720;}
}
im->flags|=IMG_FREEMEM;
return i;
}

void sub__font(long f,long i,long passed);//foward def


long imgload(char *filename,long bpp){
static long i,i2,x,y,i3,z2,z3,v,v2,v3,r,g,b,a,t,needt,t2;
static unsigned char *cp,*cp2;
static unsigned long c;
static unsigned long *lp;

static unsigned char *sr=(unsigned char*)malloc(256);
static unsigned char *sg=(unsigned char*)malloc(256);
static unsigned char *sb=(unsigned char*)malloc(256);
static unsigned char *dr=(unsigned char*)malloc(256);
static unsigned char *dg=(unsigned char*)malloc(256);
static unsigned char *db=(unsigned char*)malloc(256);
static unsigned char *link=(unsigned char*)malloc(256);
static long *usedcolor=(long*)malloc(1024);

ts=IMG_Load(filename);
if (!ts) return 0;

if (bpp==-1){

if (write_page->bytes_per_pixel==1){
if (ts->format->BytesPerPixel==1) goto compatible;

//32-->8 bit (best possible color selection)
ts2=SDL_ConvertSurface(ts,&pixelformat32,NULL);
if (!ts2){SDL_FreeSurface(ts); return 0;}
i=imgnew(ts2->w,ts2->h,write_page->compatible_mode);
if (!i){SDL_FreeSurface(ts); return 0;}
//copy write_page's palette
memcpy(img[i].pal,write_page->pal,1024);
//find number of colors
z3=write_page->mask+1;
//build color value table
for (i3=0;i3<z3;i3++){
c=write_page->pal[i3];
db[i3]=c&0xFF; dg[i3]=c>>8&0xFF; dr[i3]=c>>16&0xFF;
}

//reset color used flags
memset(usedcolor,0,1024);
needt=0;
//copy/change colors
cp=(unsigned char*)ts2->pixels; cp2=img[i].offset;
for (y=0;y<img[i].height;y++){
for (x=0;x<img[i].width;x++){
c=*((unsigned long*)(cp+y*ts2->pitch+x*4));
a=c>>24;
if (a==0){
needt=1;
}else{
b=c&0xFF; g=c>>8&0xFF; r=c>>16&0xFF; v=1000; v3=0;
for (i3=0;i3<z3;i3++){
v2=abs(r-(long)dr[i3])+abs(g-(long)dg[i3])+abs(b-(long)db[i3]);
if (v2<v){v3=i3; v=v2;}
}//i3
cp2[y*img[i].width+x]=v3;
usedcolor[v3]++;
}//a==0
}}
//add transparency
if (needt){
//find best transparent color
v=0x7FFFFFFF;
for (x=0;x<z3;x++){
if (usedcolor[x]<=v){
v=usedcolor[x];
t=x;
}
}
//remake with transparency
img[i].transparent_color=t;
//copy/change colors
cp=(unsigned char*)ts2->pixels; cp2=img[i].offset;
for (y=0;y<img[i].height;y++){ for (x=0;x<img[i].width;x++){
c=*((unsigned long*)(cp+y*ts2->pitch+x*4));
a=c>>24; if (a==0){cp2[y*img[i].width+x]=t; goto usedtranscol;}
b=c&0xFF; g=c>>8&0xFF; r=c>>16&0xFF; v=1000; v3=0;
for (i3=0;i3<z3;i3++){
if (i3!=t){
v2=abs(r-(long)dr[i3])+abs(g-(long)dg[i3])+abs(b-(long)db[i3]);
if (v2<v){v3=i3; v=v2;}
}
}//i3
cp2[y*img[i].width+x]=v3;
usedtranscol:;
}}
}//needt
//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 print mode
img[i].print_mode=write_page->print_mode;
SDL_FreeSurface(ts2);
SDL_FreeSurface(ts);
return i;
}//write_page->bytes_per_pixel==1
}//-1

if (bpp==256){
if (ts->format->BytesPerPixel!=1){SDL_FreeSurface(ts); return 0;}
compatible:
ts2=ts;
//check for transparent color in palette
ts=SDL_ConvertSurface(ts2,&pixelformat32,NULL);
if (!ts){SDL_FreeSurface(ts2); return 0;}
//prepare image to write to
if (bpp==-1){
i=imgnew(ts2->w,ts2->h,write_page->compatible_mode);
}else{
i=imgnew(ts2->w,ts2->h,256);
}
if (!i){SDL_FreeSurface(ts2); SDL_FreeSurface(ts); return 0;}
//does a transparent pixel exist?
t=-1;
for (y=0;y<img[i].height;y++){
lp=(unsigned long*)(((char*)ts->pixels)+ts->pitch*y);
for (x=0;x<img[i].width;x++){
if (!(*lp++&0xFF000000)){//alpha==0
//find equivalent 8-bit index
c=*(((unsigned char*)ts2->pixels)+ts2->pitch*y+x);
if (c<ts2->format->palette->ncolors){
img[i].transparent_color=c;
t=c;
goto found_transparent_color;
}
}
}}
found_transparent_color:

//8-->8 bit (best color match)
if (bpp==-1){
img[i].transparent_color=-1;//this will be set later if necessary
//copy write_page's palette
memcpy(img[i].pal,write_page->pal,1024);
//map image's palette to actual palette
//reset color used flags
memset(usedcolor,0,1024);
//find number of colors
z2=ts2->format->palette->ncolors;
z3=write_page->mask+1;
//build color value tables
for (i2=0;i2<z2;i2++){
c=*(unsigned long*)&ts2->format->palette->colors[i2];
sr[i2]=c&0xFF; sg[i2]=c>>8&0xFF; sb[i2]=c>>16&0xFF;
}
for (i3=0;i3<z3;i3++){
c=write_page->pal[i3];
db[i3]=c&0xFF; dg[i3]=c>>8&0xFF; dr[i3]=c>>16&0xFF;
}
//link colors to best matching color
for (i2=0;i2<z2;i2++){
v=1000; link[i2]=0;
for (i3=0;i3<z3;i3++){
v2=abs((long)sr[i2]-(long)dr[i3])+abs((long)sg[i2]-(long)dg[i3])+abs((long)sb[i2]-(long)db[i3]);
if (v2<v){
link[i2]=i3; v=v2;
}
}//i3
}//i2
//change colors
needt=0;
cp=(unsigned char*)ts2->pixels; cp2=img[i].offset;
for (y=0;y<img[i].height;y++){
for (x=0;x<img[i].width;x++){
c=cp[y*ts2->pitch+x];
if (c==t){
needt=1;
}else{
c=link[c];
cp2[y*img[i].width+x]=c;
usedcolor[c]++;
}
}}
//add transparency
if (needt){
t2=t;//backup
//find best transparent color
v=0x7FFFFFFF;
for (x=0;x<z3;x++){
if (usedcolor[x]<=v){
v=usedcolor[x];
t=x;
}
}
//remake with transparency
img[i].transparent_color=t;
//relink colors to best matching color (avoiding t)
for (i2=0;i2<z2;i2++){
v=1000; link[i2]=0;
for (i3=0;i3<z3;i3++){
if (i3!=t){
v2=abs((long)sr[i2]-(long)dr[i3])+abs((long)sg[i2]-(long)dg[i3])+abs((long)sb[i2]-(long)db[i3]);
if (v2<v){
link[i2]=i3; v=v2;
}
}
}//i3
}//i2
//change colors
cp=(unsigned char*)ts2->pixels; cp2=img[i].offset;
for (y=0;y<img[i].height;y++){
for (x=0;x<img[i].width;x++){
c=cp[y*ts2->pitch+x];
if (c==t2){
cp2[y*img[i].width+x]=t;
}else{
cp2[y*img[i].width+x]=link[c];
}
}}
}//needt
//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 print mode
img[i].print_mode=write_page->print_mode;
SDL_FreeSurface(ts2);
SDL_FreeSurface(ts);
return i;
}//bpp==-1

//copy pixel data
cp=(unsigned char*)ts2->pixels; cp2=img[i].offset;
for (i2=0;i2<img[i].height;i2++){
memcpy(cp2,cp,ts2->w);
cp+=ts2->pitch;
cp2+=img[i].width;
}
//update palette
for (i2=ts2->format->palette->ncolors;i2<256;i2++){img[i].pal[i2]=0xFF000000;}
for (i2=0;i2<ts2->format->palette->ncolors;i2++){
c=*(unsigned long*)&ts2->format->palette->colors[i2];
c=0xFF000000+((c>>16)&255)+(c&0xFF00)+((c&255)<<16);
img[i].pal[i2]=c;
}
SDL_FreeSurface(ts2);
SDL_FreeSurface(ts);
return i;
}

ts2=SDL_ConvertSurface(ts,&pixelformat32,NULL);
if (!ts2){SDL_FreeSurface(ts); return 0;}
i=imgnew(ts2->w,ts2->h,32);
if (!i){SDL_FreeSurface(ts2); SDL_FreeSurface(ts); return 0;}
memcpy(img[i].offset,ts2->pixels,ts2->w*ts2->h*4);
SDL_FreeSurface(ts2); SDL_FreeSurface(ts);
return i;
}

void sub__putimage(long dstep1,double f_dx1,double f_dy1,long dstep2,double f_dx2,double f_dy2,long src,long dst,long sstep1,double f_sx1,double f_sy1,long sstep2,double f_sx2,double f_sy2,long passed){
//format & passed bits:
//[(dx1,dy1)[-(dx2,dy2)]][,[src][,[dst][,[(sx1,sy1)[-(sx2,sy2)]][,...?...]]]]
//  1          2            4      8       16         32          

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

no_stretch=0; no_clip=0; no_reverse=1;

flip=0; mirror=0;

if (passed&4){//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
if (s->text){error(5); return;}
sbpp=s->bytes_per_pixel;

if (passed&8){//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 (d->text){error(5); return;}
dbpp=d->bytes_per_pixel;
if ((sbpp==4)&&(dbpp==1)){error(5); return;}
if (s==d){error(5); return;}//cannot put source onto itself!

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

//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&2){//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&16){//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&32){//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

if ((passed&2)&&(passed&32)){//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&2){//(dx1,dy1)-(dx2,dy2)...
if (passed&16){//(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&32){//...(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&16){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 offscreen pixels
if (dx1<0){
if (mirror) fsx2+=((double)dx1)*mx; else fsx1-=((double)dx1)*mx;
dx1=0;
}
if (dy1<0){
if (flip) fsy2+=((double)dy1)*my; else fsy1-=((double)dy1)*my;
dy1=0;
}
if (dx2>=dw){
if (mirror) fsx1+=((double)(dx2-dw+1))*mx; else fsx2-=((double)(dx2-dw+1))*mx;
dx2=dw-1;
}
if (dy2>=dh){
if (flip) fsy1+=((double)(dy2-dh+1))*my; else fsy2-=((double)(dy2-dh+1))*my;
dy2=dh-1;
}
//crop source offscreen pixels
if (w){//gradient cannot be 0
if (fsx1<-0.4999999){
x=(-fsx1-0.499999)/mx+1.0;
if (mirror) dx2-=x; else dx1+=x;
fsx1+=((double)x)*mx;
}
if (fsx2>(((double)sw)-0.5000001)){
x=(fsx2-(((double)sw)-0.500001))/mx+1.0;
if (mirror) dx1+=x; else dx2-=x;
fsx2-=(((double)x)*mx);
}
}//w
if (h){//gradient cannot be 0
if (fsy1<-0.4999999){
y=(-fsy1-0.499999)/my+1.0;
if (flip) dy2-=y; else dy1+=y;
fsy1+=((double)y)*my;
}
if (fsy2>(((double)sh)-0.5000001)){
y=(fsy2-(((double)sh)-0.500001))/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 boundries 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=blend+(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 dest offscreen pixels
if (dx1<0){
if (mirror) sx2+=dx1; else sx1-=dx1;
dx1=0;
}
if (dy1<0){
if (flip) sy2+=dy1; else sy1-=dy1;
dy1=0;
}
if (dx2>=dw){
if (mirror) sx1+=(dx2-dw+1); else sx2-=(dx2-dw+1);
dx2=dw-1;
}
if (dy2>=dh){
if (flip) sy1+=(dy2-dh+1); else sy2-=(dy2-dh+1);
dy2=dh-1;
}
//crop source offscreen pixels
if (sx1<0){
if (mirror) dx2+=sx1; else dx1-=sx1;
sx1=0;
}
if (sy1<0){
if (flip) dy2+=sy1; else dy1-=sy1;
sy1=0;
}
if (sx2>=sw){
if (mirror) dx1+=(sx2-sw+1); else dx2-=(sx2-sw+1);
sx2=sw-1;
}
if (sy2>=sh){
if (flip) dy1+=(sy2-sh+1); else dy2-=(sy2-sh+1);
sy2=sh-1;
}
//<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 boundries 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=blend+(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=blend+(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;

}//done imgput

//font management
#define lastfont 1023
TTF_Font *font[lastfont+1];//NULL=unused index
long fontheight[lastfont+1];
long fontwidth[lastfont+1];
long fontflags[lastfont+1];

long fontopen(char *name,double d_height,long flags){
//flags:
//1 bold TTF_STYLE_BOLD
//2 italic TTF_STYLE_ITALIC
//4 underline TTF_STYLE_UNDERLINE
//8 dontblend (blending is the default in 32-bit alpha-enabled modes)
//16 monospace
static double d,d2;
static long i,y,z,height;
static TTF_Font *tf,*tf2;
static SDL_Surface *ts;
static SDL_Color c;
for (i=32;i<=lastfont;i++){
if (!font[i]){
if (d_height<1.0){
height=d_height*1000;
tf=TTF_OpenFont(name,height);
if (tf==NULL) return 0;
height=TTF_FontHeight(tf);
}else{
height=qbr_double_to_long(d_height);
tf=TTF_OpenFont(name,1000);
if (tf==NULL) return 0;
d=TTF_FontHeight(tf);
d=1000.0/d;
d2=height;
d2*=d;
y=d2;
TTF_CloseFont(tf);
tf=TTF_OpenFont(name,y);
if (tf==NULL) return 0;
if (TTF_FontHeight(tf)>height){
TTF_CloseFont(tf);
y--; tf=TTF_OpenFont(name,y);
if (tf==NULL) return 0;
}
if (TTF_FontHeight(tf)<height){
TTF_CloseFont(tf);
y++; tf=TTF_OpenFont(name,y);
if (tf==NULL) return 0;
}
}//d_height
TTF_SetFontStyle(tf,flags&7);//returns void, so cannot be error checked
font[i]=tf;
fontflags[i]=flags;
fontheight[i]=height;
fontwidth[i]=0;
if (flags&16){
if (TTF_FontFaceIsFixedWidth(tf)){
//render a glyph to test
z=32;
c.r=255; c.g=255; c.b=255; c.unused=0;//dummy values

convert_codepage437_to_unicode16(&z,1);
ts=TTF_RenderUNICODE_Solid(tf,(Uint16*)unicode16_buf,c);
//ts=TTF_RenderText_Solid(tf,(char*)&z,c);//8-bit, 0=clear, 1=text

fontwidth[i]=ts->w;
SDL_FreeSurface(ts);
}else{
TTF_CloseFont(tf);//not a monospace font
return 0;
}
}
return i;
}
}
return 0;//no valid index, will result in ILLEGAL FUNCTION CALL
}

long selectfont(long 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
}















SDL_Rect *modes=NULL;
SDL_Rect **sdl_modes;
long nmodes=0;
long anymode=0;

long x_scale=1,y_scale=1;
long x_offset=0,y_offset=0;
long x_monitor=0,y_monitor=0;


#define AUDIO_CHANNELS 256

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

unsigned long func__sndraw(unsigned char* data,unsigned long bytes);//called by sndsetup
void sndsetup(){
static long sndsetup_called=0;
if (!sndsetup_called){
sndsetup_called=1;
if (Mix_OpenAudio(22050,AUDIO_S16,2,1024)==-1) exit(10001);
atexit(Mix_CloseAudio);
Mix_AllocateChannels(AUDIO_CHANNELS);
 //fix "part of first sound missed" problem in SDL_MIXER
 static unsigned char *cp=(unsigned char*)calloc(8192,2*2);
 static long i;
 i=func__sndraw(cp,8192*2*2);
 if (i){
 sndqueue[sndqueue_next]=i;
 sndqueue_next++; if (sndqueue_next>sndqueue_lastindex) sndqueue_next=0;
 }
}
}



void call_int(long i);

unsigned long frame=0;


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

struct mouse_message{
short x;
short y;
unsigned long buttons;
};
mouse_message mouse_messages[1024];//a circular buffer of mouse messages
long last_mouse_message=0;
long current_mouse_message=0;
long mouse_hideshow_called=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];

long a32;
long 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;
}
}

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;
}
}

uint8 *rm8(){
static uint32 i;//r/m byte
i=*ip++;
switch(i>>6){
case 3:
 return reg8[i&7];
break;
case 0:
 if (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 (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 (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;
}
}

uint16 *rm16(){
static long i;//r/m byte
i=*ip;
switch(i>>6){
case 3:
 ip++; 
 return reg16[i&7];
break;
case 0:
 ip++;
 if (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 (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 (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;
}
}

uint32 *rm32(){
static long i;//r/m byte
i=*ip;
switch(i>>6){
case 3:
 ip++; 
 return reg32[i&7];
break;
case 0:
 ip++;
 if (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 (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 (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;
}
}

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 long i,i2,i3,x,x2,x3,y,y2,y3;
static unsigned char b,b2,b3;
static uint8 *uint8p;
static uint16 *uint16p;
static uint32 *uint32p;
static uint8* dseg;
static long 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:
b32=0; a32=0; seg=seg_ds_ptr; seg_bp=seg_ss_ptr;

i=*ip++;

//read any prefixes
if (i==0x66){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){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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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;
MessageBox(NULL,(char*)unknown_opcode_mess->chr,"X86 Error",MB_OK);

exit(86);
done:
if (*ip) goto next_opcode;

exit(cmem[0]);

}



long screen_last_valid=0;
unsigned char *screen_last=(unsigned char*)malloc(1);
unsigned long screen_last_size=1;
unsigned long pal_last[256];

uint64 asciicode_value=0;
long asciicode_reading=0;
long asciicode_force=0;


long lock_display=0;
long lock_display_required=0;

SDL_Thread *thread;
SDL_Thread *thread2;

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

#include "msbin.c"

//#include "time64.c"
//#include "time64.h"

//int QBMAIN(void *unused);

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

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


struct byte_element_struct
{
uint64 offset;
long length;
};




//nb. abreviations 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 unsigned char *cmem_static_pointer;
unsigned char *cmem_static_base=&cmem[0]+1280+65536;
extern unsigned char *cmem_dynamic_base;
//[1280][DBLOCK][STATIC-><-DYNAMIC][A000-]

unsigned long qbs_cmem_descriptor_space=256; //enough for 64 strings before expansion

extern unsigned long qbs_cmem_sp; //=256;
extern unsigned long cmem_sp; //=65536;
extern unsigned long dblock; //32bit offset of dblock
extern uint64 *nothingvalue;

unsigned long qb64_firsttimervalue;
unsigned long sdl_firsttimervalue;


extern uint32 qbevent;

unsigned char wait_needed=1;

SDL_Surface * screen;
long full_screen=0;//0,1(stretched/closest),2(1:1)
long full_screen_toggle=0;//increments each time ALT+ENTER is pressed
long full_screen_set=-1;//0(windowed),1(stretched/closest),2(1:1)


long vertical_retrace_in_progress=0;
long vertical_retrace_happened=0;





static const char *arrow[] = {
  /* width height num_colors chars_per_pixel */
  "    32    32        3            1",
  /* colors */
  "X c #000000",
  ". c #ffffff",
  "  c None",
  /* pixels */
"X                               ",
"XX                              ",
"X.X                             ",
"X..X                            ",
"X...X                           ",
"X....X                          ",
"X.....X                         ",
"X......X                        ",
"X.......X                       ",
"X........X                      ",
"X.........X                     ",
"X......XXXXX                    ",
"X...X..X                        ",
"X..XX..X                        ",
"X.X  X..X                       ",
"XX   X..X                       ",
"X     X..X                      ",
"      X..X                      ",
"       X..X                     ",
"       X..X                     ",
"        XX                      ",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
  "0,0"
};

static SDL_Cursor *init_system_cursor(const char *image[])
{
  int i, row, col;
  Uint8 data[4*32];
  Uint8 mask[4*32];
  int hot_x, hot_y;

  i = -1;
  for ( row=0; row<32; ++row ) {
    for ( col=0; col<32; ++col ) {
      if ( col % 8 ) {
        data[i] <<= 1;
        mask[i] <<= 1;
      } else {
        ++i;
        data[i] = mask[i] = 0;
      }
      switch (image[4+row][col]) {
        case 'X':
          data[i] |= 0x01;
          mask[i] |= 0x01;//?
          break;
        case '.':
          mask[i] |= 0x01;
          break;
        case ' ':
          break;
      }
    }
  }
  sscanf(image[4+row], "%d,%d", &hot_x, &hot_y);
  return SDL_CreateCursor(data, mask, 32, 32, hot_x, hot_y);
}



unsigned char lock_subsystem=0;

extern unsigned char close_program; //=0;
unsigned char program_wait=0;

extern unsigned char suspend_program;
extern unsigned char stop_program;


long global_counter=0;
extern double last_line;



void end(void);



extern unsigned long new_error;
extern unsigned long error_err; //=0;
extern double error_erl; //=0;
extern unsigned long error_occurred;
extern unsigned long error_goto_line;
extern unsigned long error_handling;
extern unsigned long error_retry;

const char fixerr_strline[]="Line:";
const char fixerr_strcont[]="\nContinue?";
const char fixerr_strunhan[]="Unhandled Error #";
void fix_error(){
static char errtitle[256];//builds message
static char errmess[256];//builds message
static char *cp;
if ((!error_goto_line)||error_handling){
long v,i,i2;//stores response
v=0;

cp=NULL;
if (new_error==1) cp="NEXT without FOR";
if (new_error==2) cp="Syntax error";
if (new_error==3) cp="RETURN without GOSUB";
if (new_error==4) cp="Out of DATA";
if (new_error==5) cp="Illegal function call";
if (new_error==6) cp="Overflow";
if (new_error==7) cp="Out of memory";
if (new_error==8) cp="Label not defined";
if (new_error==9) cp="Subscript out of range";
if (new_error==10) cp="Duplicate definition";
if (new_error==11) cp="Division by zero";
if (new_error==12) cp="Illegal in direct mode";
if (new_error==13) cp="Type mismatch";
if (new_error==14) cp="Out of string space";
//error 15 undefined
if (new_error==16) cp="String formula too complex";
if (new_error==17) cp="Cannot continue";
if (new_error==18) cp="Function not defined";
if (new_error==19) cp="No RESUME";
if (new_error==20) cp="RESUME without error";
//error 21-23 undefined
if (new_error==24) cp="Device timeout";
if (new_error==25) cp="Device fault";
if (new_error==26) cp="FOR without NEXT";
if (new_error==27) cp="Out of paper";
//error 28 undefined
if (new_error==29) cp="WHILE without WEND";
if (new_error==30) cp="WEND without WHILE";
//error 31-32 undefined
if (new_error==33) cp="Duplicate label";
//error 34 undefined
if (new_error==35) cp="Subprogram not defined";
if (new_error==37) cp="Argument-count mismatch";
if (new_error==38) cp="Array not defined";
if (new_error==40) cp="Variable required";
if (new_error==50) cp="FIELD overflow";
if (new_error==51) cp="Internal error";
if (new_error==52) cp="Bad file name or number";
if (new_error==53) cp="File not found";
if (new_error==54) cp="Bad file mode";
if (new_error==55) cp="File already open";
if (new_error==56) cp="FIELD statement active";
if (new_error==57) cp="Device I/O error";
if (new_error==58) cp="File already exists";
if (new_error==59) cp="Bad record length";
if (new_error==61) cp="Disk full";
if (new_error==62) cp="Input past end of file";
if (new_error==63) cp="Bad record number";
if (new_error==64) cp="Bad file name";
if (new_error==67) cp="Too many files";
if (new_error==68) cp="Device unavailable";
if (new_error==69) cp="Communication-buffer overflow";
if (new_error==70) cp="Permission denied";
if (new_error==71) cp="Disk not ready";
if (new_error==72) cp="Disk-media error";
if (new_error==73) cp="Feature unavailable";
if (new_error==74) cp="Rename across disks";
if (new_error==75) cp="Path/File access error";
if (new_error==76) cp="Path not found";
if (new_error==258) cp="Invalid handle";
if (!cp) cp="Unprintable error";

i=0;
memcpy(&errmess[i],&fixerr_strline[0],strlen(fixerr_strline)); i=i+strlen(fixerr_strline);
i2=sprintf(&errmess[i],"%u\n",ercl); i=i+i2;
memcpy(&errmess[i],cp,strlen(cp)); i=i+strlen(cp);
memcpy(&errmess[i],&fixerr_strcont[0],strlen(fixerr_strcont)); i=i+strlen(fixerr_strcont);
errmess[i]=0;

i=0;
memcpy(&errtitle[i],&fixerr_strunhan[0],strlen(fixerr_strunhan)); i=i+strlen(fixerr_strunhan);
i2=sprintf(&errtitle[i],"%u",new_error); i=i+i2;
errtitle[i]=0;

v=MessageBox(NULL,errmess,errtitle,MB_YESNO);

if ((v==IDNO)||(v==IDOK)){close_program=1; end();}
new_error=0;
return;
}
error_err=new_error;
new_error=0;
error_erl=last_line;
error_occurred=1;
QBMAIN(NULL);
return;
}

void error(long error_number){
if (error_number==256){MessageBox(NULL,"Out of stack space","Critical Error",MB_OK); exit(0);}
//generic "Out of memory" error
if (error_number==257){MessageBox(NULL,"Out of memory","Critical Error #1",MB_OK); exit(0);}
//trackable "Out of memory" error
if (error_number==502){MessageBox(NULL,"Out of memory","Critical Error #2",MB_OK); exit(0);}
if (error_number==503){MessageBox(NULL,"Out of memory","Critical Error #3",MB_OK); exit(0);}
if (error_number==504){MessageBox(NULL,"Out of memory","Critical Error #4",MB_OK); exit(0);}
if (error_number==505){MessageBox(NULL,"Out of memory","Critical Error #5",MB_OK); exit(0);}
if (error_number==506){MessageBox(NULL,"Out of memory","Critical Error #6",MB_OK); exit(0);}
if (error_number==507){MessageBox(NULL,"Out of memory","Critical Error #7",MB_OK); exit(0);}
if (error_number==508){MessageBox(NULL,"Out of memory","Critical Error #8",MB_OK); exit(0);}
if (error_number==509){MessageBox(NULL,"Out of memory","Critical Error #9",MB_OK); exit(0);}
if (error_number==510){MessageBox(NULL,"Out of memory","Critical Error #10",MB_OK); exit(0);}
if (error_number==511){MessageBox(NULL,"Out of memory","Critical Error #11",MB_OK); exit(0);}
if (error_number==512){MessageBox(NULL,"Out of memory","Critical Error #12",MB_OK); exit(0);}
if (error_number==513){MessageBox(NULL,"Out of memory","Critical Error #13",MB_OK); exit(0);}
if (error_number==514){MessageBox(NULL,"Out of memory","Critical Error #14",MB_OK); exit(0);}
if (error_number==515){MessageBox(NULL,"Out of memory","Critical Error #15",MB_OK); exit(0);}
if (error_number==516){MessageBox(NULL,"Out of memory","Critical Error #16",MB_OK); exit(0);}
if (error_number==517){MessageBox(NULL,"Out of memory","Critical Error #17",MB_OK); exit(0);}

if (!new_error){
if ((new_error==256)||(new_error==257)) fix_error();//critical error!
if (error_number<=0) error_number=5;//Illegal function call
new_error=error_number;
qbevent=1;
}
}

double get_error_erl(){
return error_erl;
}

unsigned long get_error_err(){
return error_err;
}

void end(){
while(!stop_program) Sleep(100);
SDL_KillThread(thread);
while(1) Sleep(100);
}



//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.
*/
unsigned long mem_static_size;
extern unsigned char *mem_static;
extern unsigned char *mem_static_pointer;
extern unsigned char *mem_static_limit;

unsigned char *mem_static_malloc(unsigned long size){
if ((mem_static_pointer+=size)<mem_static_limit) return mem_static_pointer-size;
mem_static_size=(mem_static_size<<1)+size;
mem_static=(unsigned char*)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(unsigned char* 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{
unsigned char *offset;
unsigned char *top;
unsigned long size;
unsigned long 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;
long cmem_dynamic_next_link=0;
long cmem_dynamic_free_link=0;
unsigned long cmem_dynamic_free_list[147136];
unsigned char *cmem_dynamic_malloc(unsigned long size){
static long i;
static unsigned char *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(unsigned char *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;
}

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

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

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

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

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

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

//gosub-return handling
extern unsigned long next_return_point; //=0;
extern unsigned long *return_point; //=(unsigned long*)malloc(4*16384);
extern unsigned long return_points; //=16384;
void more_return_points(){
if (return_points>2147483647) error(256);
return_points*=2;
return_point=(unsigned long*)realloc(return_point,return_points*4);
if (return_point==NULL) error(256);
}




void sub__sndplay(unsigned long);

unsigned char *soundwave(double frequency,double length,double volume,double fadein,double fadeout);
long soundwave_bytes=0;

void qb64_generatesound(double f,double l,unsigned char wait){
sndsetup();
static unsigned char* data;
static unsigned long handle;
data=soundwave(f,l,1,0,0);
handle=func__sndraw(data,soundwave_bytes);
if (handle){
if (wait){
sndqueue_wait=sndqueue_next;
suspend_program|=2;
qbevent=1;
}
sndqueue[sndqueue_next]=handle;
sndqueue_next++; if (sndqueue_next>sndqueue_lastindex) sndqueue_next=0;
}else free(data);
}

unsigned char *soundwave(double frequency,double length,double volume,double fadein,double fadeout){
sndsetup();
static unsigned char *data;
static long i;
static short x,lastx;
static short* sp;
static double samples_per_second=22050.0;

//calculate total number of samples required
static double samples;
static long samplesi;
samples=length*samples_per_second;
samplesi=samples; if (!samplesi) samplesi=1;

soundwave_bytes=samplesi*4;
data=(unsigned char*)malloc(soundwave_bytes);
sp=(short*)data;

static long direction;
direction=1;

static double value;
value=0;

static double volume_multiplier;
volume_multiplier=volume*32767.0;

static long waveend;
waveend=0;

static double gradient;
//frequency*4.0*length is the total distance value will travel (+1,-2,+1[repeated])
//samples is the number of steps to do this in
if (samples) gradient=(frequency*4.0*length)/samples; else gradient=0;//avoid division by 0

lastx=1;//set to 1 to avoid passing initial comparison
for (i=0;i<samplesi;i++){
x=value*volume_multiplier;
*sp++=x;
*sp++=x;
if (x>0){
if (lastx<=0){
waveend=i;
}
}
lastx=x;
if (direction){
if ((value+=gradient)>=1.0){direction=0; value=2.0-value;}
}else{
if ((value-=gradient)<=-1.0){direction=1; value=-2.0-value;}
}
}//i

if (waveend) soundwave_bytes=waveend*4;

return (unsigned char*)data;
}

long wavesize(double length){
static long samples;
samples=length*22050.0; if (samples==0) samples=1;
return samples*4;
}



unsigned char keyon[65536];

qbs* singlespace;


qbs *qbs_malloc=(qbs*)calloc(sizeof(qbs)*65536,1);//~1MEG
unsigned long qbs_malloc_next=0;//the next idex in qbs_malloc to use
unsigned long *qbs_malloc_freed=(unsigned long*)malloc(4*65536);
unsigned long qbs_malloc_freed_size=65536;
unsigned long qbs_malloc_freed_num=0;//number of freed qbs descriptors

/*MLP
unsigned long *dbglist=(unsigned long*)malloc(4*10000000);
unsigned long dbglisti=0;
unsigned long dbgline=0;
*/

qbs *qbs_new_descriptor(){
//MLP //qbshlp1++;
if (qbs_malloc_freed_num){
/*MLP
static qbs *s;
s=(qbs*)memset((void *)qbs_malloc_freed[--qbs_malloc_freed_num],0,sizeof(qbs));
s->dbgl=dbgline;
return s;
*/
return (qbs*)memset((void *)qbs_malloc_freed[--qbs_malloc_freed_num],0,sizeof(qbs));
}
if (qbs_malloc_next==65536){
qbs_malloc=(qbs*)calloc(sizeof(qbs)*65536,1);//~1MEG
qbs_malloc_next=0;
}
/*MLP
dbglist[dbglisti]=(unsigned long)&qbs_malloc[qbs_malloc_next];
static qbs* s;
s=(qbs*)&qbs_malloc[qbs_malloc_next++];
s->dbgl=dbgline;
dbglisti++;
return s;
*/
return &qbs_malloc[qbs_malloc_next++];
}

void qbs_free_descriptor(qbs *str){
//MLP //qbshlp1--;
if (qbs_malloc_freed_num==qbs_malloc_freed_size){
 qbs_malloc_freed_size*=2;
 qbs_malloc_freed=(unsigned long*)realloc(qbs_malloc_freed,qbs_malloc_freed_size*4);
 if (!qbs_malloc_freed) error(508);
}
qbs_malloc_freed[qbs_malloc_freed_num]=(unsigned long)str;
qbs_malloc_freed_num++;
return;
}

//Used to track strings in 16bit memory
unsigned long *qbs_cmem_list=(unsigned long*)malloc(65536*4);
unsigned long  qbs_cmem_list_lasti=65535;
unsigned long  qbs_cmem_list_nexti=0;
//Used to track strings in 32bit memory
unsigned long *qbs_list=(unsigned long*)malloc(65536*4);
unsigned long  qbs_list_lasti=65535;
unsigned long  qbs_list_nexti=0;
//Used to track temporary strings for later removal when they fall out of scope
//*Some string functions delete a temporary string automatically after they have been
// passed one to save memory. In this case qbstring_templist[?]=0xFFFFFFFF
unsigned long *qbs_tmp_list=(unsigned long*)calloc(65536*4,1);//first index MUST be 0
unsigned long  qbs_tmp_list_lasti=65535;
extern unsigned long qbs_tmp_list_nexti;
//entended string memory

unsigned char *qbs_data=(unsigned char*)malloc(1048576);
unsigned long qbs_data_size=1048576;
unsigned long qbs_sp=0;


void qbs_free(qbs *str){
if (str->tmplisti){
 qbs_tmp_list[str->tmplisti]=0xFFFFFFFF;
 while (qbs_tmp_list[qbs_tmp_list_nexti-1]==0xFFFFFFFF){
 qbs_tmp_list_nexti--;
 }
}
if (str->fixed||str->readonly){
 qbs_free_descriptor(str);
 return;
}
if (str->in_cmem){
 qbs_cmem_list[str->listi]=0xFFFFFFFF;
 if ((qbs_cmem_list_nexti-1)==str->listi) qbs_cmem_list_nexti--;
}else{
 qbs_list[str->listi]=0xFFFFFFFF;
 retry:
 if (qbs_list[qbs_list_nexti-1]==0xFFFFFFFF){
 qbs_list_nexti--;
 if (qbs_list_nexti) goto retry;
 }
 if (qbs_list_nexti){
 qbs_sp=((qbs*)qbs_list[qbs_list_nexti-1])->chr-qbs_data+((qbs*)qbs_list[qbs_list_nexti-1])->len+32;
 if (qbs_sp>qbs_data_size) qbs_sp=qbs_data_size;//adding 32 could overflow buffer!
 }else{
 qbs_sp=0;
 }
}
qbs_free_descriptor(str);
return;
}

void qbs_cmem_concat_list(){
unsigned long i;
unsigned long d;
qbs *tqbs;
d=0;
for (i=0;i<qbs_cmem_list_nexti;i++){
 if (qbs_cmem_list[i]!=0xFFFFFFFF){ 
  if (i!=d){  
   tqbs=(qbs*)qbs_cmem_list[i];
   tqbs->listi=d;
   qbs_cmem_list[d]=(unsigned long)tqbs;
  }
 d++;
 }
}
qbs_cmem_list_nexti=d;
//if string listings are taking up more than half of the list array double the list array's size
if (qbs_cmem_list_nexti>=(qbs_cmem_list_lasti/2)){
qbs_cmem_list_lasti*=2;
qbs_cmem_list=(unsigned long*)realloc(qbs_cmem_list,(qbs_cmem_list_lasti+1)*4);
if (!qbs_cmem_list) error(509);
}
return;
}

void qbs_concat_list(){
unsigned long i;
unsigned long d;
qbs *tqbs;
d=0;
for (i=0;i<qbs_list_nexti;i++){
 if (qbs_list[i]!=0xFFFFFFFF){
  if (i!=d){
   tqbs=(qbs*)qbs_list[i];
   tqbs->listi=d;
   qbs_list[d]=(unsigned long)tqbs;
  }
 d++;
 }
}
qbs_list_nexti=d;
//if string listings are taking up more than half of the list array double the list array's size
if (qbs_list_nexti>=(qbs_list_lasti/2)){
qbs_list_lasti*=2;
qbs_list=(unsigned long*)realloc(qbs_list,(qbs_list_lasti+1)*4);
if (!qbs_list) error(510);
}
return;
}

void qbs_tmp_concat_list(){
if (qbs_tmp_list_nexti>=(qbs_tmp_list_lasti/2)){
qbs_tmp_list_lasti*=2;
qbs_tmp_list=(unsigned long*)realloc(qbs_tmp_list,(qbs_tmp_list_lasti+1)*4);
if (!qbs_tmp_list) error(511);
}
return;
}




void qbs_concat(unsigned long bytesrequired){
//this does not change indexing, only ->chr pointers and the location of their data
static long i;
static unsigned char *dest;
static qbs *tqbs;
dest=(unsigned char*)qbs_data;
if (qbs_list_nexti){
qbs_sp=0;
 for (i=0;i<qbs_list_nexti;i++){
  if (qbs_list[i]!=0xFFFFFFFF){
   tqbs=(qbs*)qbs_list[i];
   if ((tqbs->chr-dest)>32){
   if (tqbs->len) {memmove(dest,tqbs->chr,tqbs->len);}
   tqbs->chr=dest;       
   }
   dest=tqbs->chr+tqbs->len;
   qbs_sp=dest-qbs_data;
  }
 }
}

if (((qbs_sp*2)+(bytesrequired+32))>=qbs_data_size){
static unsigned char *oldbase;
oldbase=qbs_data;
qbs_data_size=qbs_data_size*2+bytesrequired;
qbs_data=(unsigned char*)realloc(qbs_data,qbs_data_size);
if (qbs_data==NULL) error(512);//realloc failed!
for (i=0;i<qbs_list_nexti;i++){
if (qbs_list[i]!=0xFFFFFFFF){
tqbs=(qbs*)qbs_list[i];
tqbs->chr=tqbs->chr-oldbase+qbs_data;
}
}
}
return;
}

//as the cmem stack has a limit if bytesrequired cannot be met this exits and returns an error
//the cmem stack cannot after all be extended!
//so bytesrequired is only passed to possibly generate an error, or not generate one
void qbs_concat_cmem(unsigned long bytesrequired){
//this does not change indexing, only ->chr pointers and the location of their data
long i;
unsigned char *dest;
qbs *tqbs;
dest=(unsigned char*)dblock;
qbs_cmem_sp=qbs_cmem_descriptor_space;
if (qbs_cmem_list_nexti){
 for (i=0;i<qbs_cmem_list_nexti;i++){
  if (qbs_cmem_list[i]!=0xFFFFFFFF){
   tqbs=(qbs*)qbs_cmem_list[i];
   if (tqbs->chr!=dest){
   if (tqbs->len) {memmove(dest,tqbs->chr,tqbs->len);}
   tqbs->chr=dest;
      //update cmem_descriptor [length][offset]
      if (tqbs->cmem_descriptor){tqbs->cmem_descriptor[0]=tqbs->len; tqbs->cmem_descriptor[1]=(unsigned short)(unsigned long)(tqbs->chr-dblock);}
   }
   dest+=tqbs->len;
   qbs_cmem_sp+=tqbs->len;
  }
 }
}
if ((qbs_cmem_sp+bytesrequired)>cmem_sp) error(513);
return;
}

qbs *qbs_new_cmem(long size,unsigned char tmp){
if ((qbs_cmem_sp+size)>cmem_sp) qbs_concat_cmem(size);
qbs *newstr;
newstr=qbs_new_descriptor();
newstr->len=size;
if ((qbs_cmem_sp+size)>cmem_sp) qbs_concat_cmem(size);
newstr->chr=(unsigned char*)dblock+qbs_cmem_sp;
qbs_cmem_sp+=size;
newstr->in_cmem=1;
if (qbs_cmem_list_nexti>qbs_cmem_list_lasti) qbs_cmem_concat_list();
newstr->listi=qbs_cmem_list_nexti; qbs_cmem_list[newstr->listi]=(unsigned long)newstr; qbs_cmem_list_nexti++;
if (tmp){
if (qbs_tmp_list_nexti>qbs_tmp_list_lasti) qbs_tmp_concat_list();
newstr->tmplisti=qbs_tmp_list_nexti; qbs_tmp_list[newstr->tmplisti]=(unsigned long)newstr; qbs_tmp_list_nexti++;
newstr->tmp=1;
}else{
//alloc string descriptor in DBLOCK (4 bytes)
cmem_sp-=4; newstr->cmem_descriptor=(unsigned short*)(dblock+cmem_sp); if (cmem_sp<qbs_cmem_sp) error(514);
newstr->cmem_descriptor_offset=cmem_sp;
  //update cmem_descriptor [length][offset]
  newstr->cmem_descriptor[0]=newstr->len; newstr->cmem_descriptor[1]=(unsigned short)(unsigned long)(newstr->chr-dblock);
}
return newstr;
}

qbs *qbs_new(long,unsigned char);

qbs *qbs_new_txt(const char *txt){
qbs *newstr;
newstr=qbs_new_descriptor();
newstr->len=strlen(txt);
newstr->chr=(unsigned char*)txt;
if (qbs_tmp_list_nexti>qbs_tmp_list_lasti) qbs_tmp_concat_list();
newstr->tmplisti=qbs_tmp_list_nexti; qbs_tmp_list[newstr->tmplisti]=(unsigned long)newstr; qbs_tmp_list_nexti++;
newstr->tmp=1;
newstr->readonly=1;
return newstr;
}

qbs *qbs_new_txt_len(const char *txt,long len){
qbs *newstr;
newstr=qbs_new_descriptor();
newstr->len=len;
newstr->chr=(unsigned char*)txt;
if (qbs_tmp_list_nexti>qbs_tmp_list_lasti) qbs_tmp_concat_list();
newstr->tmplisti=qbs_tmp_list_nexti; qbs_tmp_list[newstr->tmplisti]=(unsigned long)newstr; qbs_tmp_list_nexti++;
newstr->tmp=1;
newstr->readonly=1;
return newstr;
}







//note: qbs_new_fixed detects if string is in DBLOCK
qbs *qbs_new_fixed(unsigned char *offset,unsigned long size,unsigned char tmp){
qbs *newstr;
newstr=qbs_new_descriptor();
newstr->len=size;
newstr->chr=offset;
newstr->fixed=1;
if (tmp){
if (qbs_tmp_list_nexti>qbs_tmp_list_lasti) qbs_tmp_concat_list();
newstr->tmplisti=qbs_tmp_list_nexti; qbs_tmp_list[newstr->tmplisti]=(unsigned long)newstr; qbs_tmp_list_nexti++;
newstr->tmp=1;
}else{
//is it in DBLOCK?
if ((offset>(cmem+1280))&&(offset<(cmem+66816))){
//alloc string descriptor in DBLOCK (4 bytes)
cmem_sp-=4; newstr->cmem_descriptor=(unsigned short*)(dblock+cmem_sp); if (cmem_sp<qbs_cmem_sp) error(515);
newstr->cmem_descriptor_offset=cmem_sp;
  //update cmem_descriptor [length][offset]
  newstr->cmem_descriptor[0]=newstr->len; newstr->cmem_descriptor[1]=(unsigned short)(unsigned long)(newstr->chr-dblock);
}
}
return newstr;
}

qbs *qbs_new(long size,unsigned char tmp){
static qbs *newstr;
if ((qbs_sp+size+32)>qbs_data_size) qbs_concat(size+32);
newstr=qbs_new_descriptor();
newstr->len=size;
newstr->chr=qbs_data+qbs_sp;
qbs_sp+=size+32;
if (qbs_list_nexti>qbs_list_lasti) qbs_concat_list();
newstr->listi=qbs_list_nexti; qbs_list[newstr->listi]=(unsigned long)newstr; qbs_list_nexti++;
if (tmp){
if (qbs_tmp_list_nexti>qbs_tmp_list_lasti) qbs_tmp_concat_list();
newstr->tmplisti=qbs_tmp_list_nexti; qbs_tmp_list[newstr->tmplisti]=(unsigned long)newstr; qbs_tmp_list_nexti++;
newstr->tmp=1;
}
return newstr;
}

void qbs_maketmp(qbs *str){
//WARNING: assumes str is a non-tmp string in non-cmem
if (qbs_tmp_list_nexti>qbs_tmp_list_lasti) qbs_tmp_concat_list();
str->tmplisti=qbs_tmp_list_nexti; qbs_tmp_list[str->tmplisti]=(unsigned long)str; qbs_tmp_list_nexti++;
str->tmp=1;
}

qbs *qbs_set(qbs *deststr,qbs *srcstr){
long i;
qbs *tqbs;
//fixed deststr
if (deststr->fixed){
 if (srcstr->len>=deststr->len){
  memcpy(deststr->chr,srcstr->chr,deststr->len);
 }else{
  memcpy(deststr->chr,srcstr->chr,srcstr->len);
  memset(deststr->chr+srcstr->len,32,deststr->len-srcstr->len);//pad with spaces
 }
 goto qbs_set_return;
}
//non-fixed deststr

//can srcstr be aquired by deststr?
if (srcstr->tmp){
if (srcstr->fixed==0){
if (srcstr->readonly==0){
if (srcstr->in_cmem==deststr->in_cmem){
if (deststr->in_cmem){
 //unlist deststr and acquire srcstr's list index
 qbs_cmem_list[deststr->listi]=0xFFFFFFFF;
 qbs_cmem_list[srcstr->listi]=(unsigned long)deststr;
 deststr->listi=srcstr->listi;
}else{
 //unlist deststr and acquire srcstr's list index
 qbs_list[deststr->listi]=0xFFFFFFFF;
 qbs_list[srcstr->listi]=(unsigned long)deststr;
 deststr->listi=srcstr->listi;
}

qbs_tmp_list[srcstr->tmplisti]=0xFFFFFFFF;
if (srcstr->tmplisti==(qbs_tmp_list_nexti-1)) qbs_tmp_list_nexti--;//correct last tmp index for performance

deststr->chr=srcstr->chr;
deststr->len=srcstr->len;
qbs_free_descriptor(srcstr);
  //update cmem_descriptor [length][offset]
  if (deststr->cmem_descriptor){deststr->cmem_descriptor[0]=deststr->len; deststr->cmem_descriptor[1]=(unsigned short)(unsigned long)(deststr->chr-dblock);}
return deststr;//nb. This return cannot be changed to a goto qbs_set_return!
}}}}

//srcstr is equal length or shorter
if (srcstr->len<=deststr->len){
 memcpy(deststr->chr,srcstr->chr,srcstr->len);
 deststr->len=srcstr->len;
 goto qbs_set_return;
}

//srcstr is longer
if (deststr->in_cmem){
 if (deststr->listi==(qbs_cmem_list_nexti-1)){//last index
  if (((unsigned long)deststr->chr+srcstr->len)<=(dblock+cmem_sp)){//space available
   memcpy(deststr->chr,srcstr->chr,srcstr->len);
   deststr->len=srcstr->len;  
   qbs_cmem_sp=(unsigned long)deststr->chr+deststr->len-(unsigned long)dblock;
   goto qbs_set_return;
  }
  goto qbs_set_cmem_concat_required;
 }
 //deststr is not the last index so locate next valid index
 i=deststr->listi+1;
 qbs_set_nextindex:
 if (qbs_cmem_list[i]!=0xFFFFFFFF){
  tqbs=(qbs*)qbs_cmem_list[i];
  if (tqbs==srcstr){
   if (srcstr->tmp==1) goto skippedtmpsrcindex;
  }
  if ((deststr->chr+srcstr->len)>tqbs->chr) goto qbs_set_cmem_concat_required;
  memcpy(deststr->chr,srcstr->chr,srcstr->len);
  deststr->len=srcstr->len;
  goto qbs_set_return;
 }
 skippedtmpsrcindex:
 i++;
 if (i!=qbs_cmem_list_nexti) goto qbs_set_nextindex;
 //all next indexes invalid!
 qbs_cmem_list_nexti=deststr->listi+1;//adjust nexti
 if (((unsigned long)deststr->chr+srcstr->len)<=(dblock+cmem_sp)){//space available
   memmove(deststr->chr,srcstr->chr,srcstr->len);//overlap possible due to sometimes aquiring srcstr's space
   deststr->len=srcstr->len;
   qbs_cmem_sp=(unsigned long)deststr->chr+deststr->len-(unsigned long)dblock;
   goto qbs_set_return;
 }
qbs_set_cmem_concat_required:
//srcstr could not fit in deststr
//"realloc" deststr
qbs_cmem_list[deststr->listi]=0xFFFFFFFF;//unlist
if ((qbs_cmem_sp+srcstr->len)>cmem_sp){//must concat!
qbs_concat_cmem(srcstr->len);
}
if (qbs_cmem_list_nexti>qbs_cmem_list_lasti) qbs_cmem_concat_list();
deststr->listi=qbs_cmem_list_nexti;
qbs_cmem_list[qbs_cmem_list_nexti]=(unsigned long)deststr; qbs_cmem_list_nexti++; //relist
deststr->chr=(unsigned char*)dblock+qbs_cmem_sp;
deststr->len=srcstr->len;
qbs_cmem_sp+=deststr->len;
memcpy(deststr->chr,srcstr->chr,srcstr->len);
goto qbs_set_return;
}


//not in cmem
 if (deststr->listi==(qbs_list_nexti-1)){//last index
  if (((unsigned long)deststr->chr+srcstr->len)<=((unsigned long)qbs_data+qbs_data_size)){//space available
   memcpy(deststr->chr,srcstr->chr,srcstr->len);
   deststr->len=srcstr->len;
   qbs_sp=(unsigned long)deststr->chr+deststr->len-(unsigned long)qbs_data;
   goto qbs_set_return;
  }
  goto qbs_set_concat_required;
 }
 //deststr is not the last index so locate next valid index
 i=deststr->listi+1;
 qbs_set_nextindex2:
 if (qbs_list[i]!=0xFFFFFFFF){
  tqbs=(qbs*)qbs_list[i];
  if (tqbs==srcstr){
   if (srcstr->tmp==1) goto skippedtmpsrcindex2;
  }
  if ((deststr->chr+srcstr->len)>tqbs->chr) goto qbs_set_concat_required;
  memcpy(deststr->chr,srcstr->chr,srcstr->len);
  deststr->len=srcstr->len;
  goto qbs_set_return;
 }
 skippedtmpsrcindex2:
 i++;
 if (i!=qbs_list_nexti) goto qbs_set_nextindex2;
 //all next indexes invalid!

 qbs_list_nexti=deststr->listi+1;//adjust nexti 
 if (((unsigned long)deststr->chr+srcstr->len)<=((unsigned long)qbs_data+qbs_data_size)){//space available
   memmove(deststr->chr,srcstr->chr,srcstr->len);//overlap possible due to sometimes aquiring srcstr's space
   deststr->len=srcstr->len;
   qbs_sp=(unsigned long)deststr->chr+deststr->len-(unsigned long)qbs_data;
   goto qbs_set_return;
 }

qbs_set_concat_required:
//srcstr could not fit in deststr
//"realloc" deststr
qbs_list[deststr->listi]=0xFFFFFFFF;//unlist
if ((qbs_sp+srcstr->len)>qbs_data_size){//must concat!
qbs_concat(srcstr->len);
}
if (qbs_list_nexti>qbs_list_lasti) qbs_concat_list();
deststr->listi=qbs_list_nexti;
qbs_list[qbs_list_nexti]=(unsigned long)deststr; qbs_list_nexti++; //relist

deststr->chr=qbs_data+qbs_sp;
deststr->len=srcstr->len;
qbs_sp+=deststr->len;
memcpy(deststr->chr,srcstr->chr,srcstr->len);

//(fall through to qbs_set_return)
qbs_set_return:
if (srcstr->tmp){//remove srcstr if it is a tmp string
qbs_free(srcstr);
}
  //update cmem_descriptor [length][offset]
  if (deststr->cmem_descriptor){deststr->cmem_descriptor[0]=deststr->len; deststr->cmem_descriptor[1]=(unsigned short)(unsigned long)(deststr->chr-dblock);}
return deststr;
}

qbs *qbs_add(qbs *str1,qbs *str2){
qbs *tqbs;
if (!str2->len) return str1;//pass on
if (!str1->len) return str2;//pass on
//may be possible to acquire str1 or str2's space but...
//1. check if dest has enough space (because its data is already in the correct place)
//2. check if source has enough space
//3. give up
//nb. they would also have to be a tmp, var. len str in ext memory!
//brute force method...
tqbs=qbs_new(str1->len+str2->len,1);
memcpy(tqbs->chr,str1->chr,str1->len);
memcpy(tqbs->chr+str1->len,str2->chr,str2->len);

//exit(qbs_sp);
if (str1->tmp) qbs_free(str1);
if (str2->tmp) qbs_free(str2);
return tqbs;
}

qbs *qbs_ucase(qbs *str){
unsigned long i;
unsigned char *c;
if (!str->len) return str;//pass on
qbs *tqbs=NULL;
if (str->tmp){ if (!str->fixed){ if (!str->readonly){ if (!str->in_cmem){ tqbs=str; }}}}
if (!tqbs){
//also pass on if already uppercase
c=str->chr;
for (i=0;i<str->len;i++){
 if ((*c>=97)&&(*c<=122)) goto qbs_ucase_cantpass;
 c++;
}
return str;
qbs_ucase_cantpass:
tqbs=qbs_new(str->len,1); memcpy(tqbs->chr,str->chr,str->len);
}
c=tqbs->chr;
for (i=0;i<tqbs->len;i++){
 if ((*c>=97)&&(*c<=122)) *c-=32;
 c++;
}
if (tqbs!=str) if (str->tmp) qbs_free(str);
return tqbs;
}

qbs *qbs_lcase(qbs *str){
unsigned long i;
unsigned char *c;
if (!str->len) return str;//pass on
qbs *tqbs=NULL;
if (str->tmp){ if (!str->fixed){ if (!str->readonly){ if (!str->in_cmem){ tqbs=str; }}}}
if (!tqbs){
//also pass on if already lowercase
c=str->chr;
for (i=0;i<str->len;i++){
 if ((*c>=65)&&(*c<=90)) goto qbs_lcase_cantpass;
 c++;
}
return str;
qbs_lcase_cantpass:
tqbs=qbs_new(str->len,1); memcpy(tqbs->chr,str->chr,str->len);
}
c=tqbs->chr;
for (i=0;i<tqbs->len;i++){
 if ((*c>=65)&&(*c<=90)) *c+=32;
 c++;
}
if (tqbs!=str) if (str->tmp) qbs_free(str);
return tqbs;
}

qbs *func_chr(long value){
qbs *tqbs;
if ((value<0)||(value>255)){
tqbs=qbs_new(0,1);
error(5);
}else{
tqbs=qbs_new(1,1);
tqbs->chr[0]=value;
}
return tqbs;
}


qbs *func_varptr_helper(unsigned char type,unsigned short 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_left(qbs *str,long l){
if (l>str->len) l=str->len;
if (l<0) l=0;
if (l==str->len) return str;//pass on
if (str->tmp){ if (!str->fixed){ if (!str->readonly){ if (!str->in_cmem){ str->len=l; return str; }}}}
qbs *tqbs;
tqbs=qbs_new(l,1);
if (l) memcpy(tqbs->chr,str->chr,l);
if (str->tmp) qbs_free(str);
return tqbs;
}

qbs *qbs_right(qbs *str,long l){
if (l>str->len) l=str->len;
if (l<0) l=0;
if (l==str->len) return str;//pass on
if (str->tmp){ if (!str->fixed){ if (!str->readonly){ if (!str->in_cmem){
str->chr=str->chr+(str->len-l);
str->len=l; return str;
}}}}
qbs *tqbs;
tqbs=qbs_new(l,1);
if (l) memcpy(tqbs->chr,str->chr+str->len-l,l);
tqbs->len=l;
if (str->tmp) qbs_free(str);
return tqbs;
}

qbs *func_mksmbf(float val){
static qbs *tqbs;
tqbs=qbs_new(4,1);
if (_fieeetomsbin(&val,(float*)tqbs->chr)==1) {error(5); tqbs->len=0;}
return tqbs;
}
qbs *func_mkdmbf(double val){
static qbs *tqbs;
tqbs=qbs_new(8,1);
if (_dieeetomsbin(&val,(double*)tqbs->chr)==1) {error(5); tqbs->len=0;}
return tqbs;
}

float func_cvsmbf(qbs *str){
static float val;
if (str->len<4) {error(5); return 0;}
if (_fmsbintoieee((float*)str->chr,&val)==1) {error(5); return 0;}
return val;
}
double func_cvdmbf(qbs *str){
static double val;
if (str->len<8) {error(5); return 0;}
if (_dmsbintoieee((double*)str->chr,&val)==1) {error(5); return 0;}
return val;
}

qbs *b2string(char v){ static qbs *tqbs; tqbs=qbs_new(1,1); *((char*)(tqbs->chr))=v; return tqbs;}
qbs *ub2string(char v){ static qbs *tqbs; tqbs=qbs_new(1,1); *((unsigned char*)(tqbs->chr))=v; return tqbs;}
qbs *i2string(short v){ static qbs *tqbs; tqbs=qbs_new(2,1); *((short*)(tqbs->chr))=v; return tqbs;}
qbs *ui2string(short v){ static qbs *tqbs; tqbs=qbs_new(2,1); *((unsigned short*)(tqbs->chr))=v; return tqbs;}
qbs *l2string(long v){ static qbs *tqbs; tqbs=qbs_new(4,1); *((long*)(tqbs->chr))=v; return tqbs;}
qbs *ul2string(unsigned long v){ static qbs *tqbs; tqbs=qbs_new(4,1); *((unsigned long*)(tqbs->chr))=v; return tqbs;}
qbs *i642string(int64 v){ static qbs *tqbs; tqbs=qbs_new(8,1); *((int64*)(tqbs->chr))=v; return tqbs;}
qbs *ui642string(uint64 v){ static qbs *tqbs; tqbs=qbs_new(8,1); *((uint64*)(tqbs->chr))=v; return tqbs;}
qbs *s2string(float v){ static qbs *tqbs; tqbs=qbs_new(4,1); *((float*)(tqbs->chr))=v; return tqbs;}
qbs *d2string(double v){ static qbs *tqbs; tqbs=qbs_new(8,1); *((double*)(tqbs->chr))=v; return tqbs;}
qbs *f2string(long double v){ static qbs *tqbs; tqbs=qbs_new(32,1); memset(tqbs->chr,0,32); *((long double*)(tqbs->chr))=v; return tqbs;}
qbs *bit2string(unsigned long bsize,int64 v){
static qbs* tqbs;
tqbs=qbs_new(8,1);
bmask=~(-(((int64)1)<<bsize));
*((int64*)(tqbs->chr))=v&bmask;
tqbs->len=(bsize+7)>>3;
return tqbs;
}
qbs *ubit2string(unsigned long bsize,uint64 v){
static qbs* tqbs;
tqbs=qbs_new(8,1);
bmask=~(-(((int64)1)<<bsize));
*((uint64*)(tqbs->chr))=v&bmask;
tqbs->len=(bsize+7)>>3;
return tqbs;
}

char string2b(qbs*str){ if (str->len<1) {error(5); return 0;} else {return *((char*)str->chr);} }
unsigned char string2ub(qbs*str){ if (str->len<1) {error(5); return 0;} else {return *((unsigned char*)str->chr);} }
short string2i(qbs*str){ if (str->len<2) {error(5); return 0;} else {return *((short*)str->chr);} }
unsigned short string2ui(qbs*str){ if (str->len<2) {error(5); return 0;} else {return *((unsigned short*)str->chr);} }
long string2l(qbs*str){ if (str->len<4) {error(5); return 0;} else {return *((long*)str->chr);} }
unsigned long string2ul(qbs*str){ if (str->len<4) {error(5); return 0;} else {return *((unsigned long*)str->chr);} }
int64 string2i64(qbs*str){ if (str->len<8) {error(5); return 0;} else {return *((int64*)str->chr);} }
uint64 string2ui64(qbs*str){ if (str->len<8) {error(5); return 0;} else {return *((uint64*)str->chr);} }
float string2s(qbs*str){ if (str->len<4) {error(5); return 0;} else {return *((float*)str->chr);} }
double string2d(qbs*str){ if (str->len<8) {error(5); return 0;} else {return *((double*)str->chr);} }
long double string2f(qbs*str){ if (str->len<32) {error(5); return 0;} else {return *((long double*)str->chr);} }
uint64 string2ubit(qbs*str,unsigned long bsize){
if (str->len<((bsize+7)>>3)) {error(5); return 0;}
bmask=~(-(((int64)1)<<bsize));
return (*(uint64*)str->chr)&bmask;
}
int64 string2bit(qbs*str,unsigned long bsize){
if (str->len<((bsize+7)>>3)) {error(5); return 0;}
bmask=~(-(((int64)1)<<bsize));
bval64=((*(uint64*)str->chr)&bmask)<<boff;
if (bval64&(((int64)1)<<(bsize-1))) return (bval64|(~bmask));
return bval64;
}

void sub_lset(qbs *dest,qbs *source){
if (new_error) return;
if (source->len>=dest->len){
if (dest->len) memcpy(dest->chr,source->chr,dest->len);
return;
}
if (source->len) memcpy(dest->chr,source->chr,source->len);
memset(dest->chr+source->len,32,dest->len-source->len);
}

void sub_rset(qbs *dest,qbs *source){
if (new_error) return;
if (source->len>=dest->len){
if (dest->len) memcpy(dest->chr,source->chr,dest->len);
return;
}
if (source->len) memcpy(dest->chr+dest->len-source->len,source->chr,source->len);
memset(dest->chr,32,dest->len-source->len);
}




qbs *func_space(long spaces){
static qbs *tqbs;
if (spaces<0) spaces=0;
tqbs=qbs_new(spaces,1);
if (spaces) memset(tqbs->chr,32,spaces);
return tqbs;
}

qbs *func_string(long characters,long asciivalue){
static qbs *tqbs;
if (characters<0) characters=0;
tqbs=qbs_new(characters,1);
if (characters) memset(tqbs->chr,asciivalue&0xFF,characters);
return tqbs;
}

long func_instr(long start,qbs *str,qbs *substr,long passed){
//QB64 difference: start can be 0 or negative
//justification-start could be larger than the length of string to search in QBASIC
static unsigned char *limit,*base;
static unsigned char firstc;
if (!passed) start=1;
if (!str->len) return 0;
if (start<1){
start=1;
if (!substr->len) return 0;
}
if (start>str->len) return 0;
if (!substr->len) return start;
if ((start+substr->len-1)>str->len) return 0;
limit=str->chr+str->len;
firstc=substr->chr[0];
base=str->chr+start-1;
nextchar:
base=(unsigned char*)memchr(base,firstc,limit-base);
if (!base) return 0;
if ((base+substr->len)>limit) return 0;
if (!memcmp(base,substr->chr,substr->len)) return base-str->chr+1;
base++;
if ((base+substr->len)>limit) return 0;
goto nextchar;
}

void sub_mid(qbs *dest,long start,long l,qbs* src,long passed){
if (new_error) return;
static long src_offset;
if (!passed) l=src->len;
src_offset=0;
if (dest==nothingstring) return;//quiet exit, error has already been reported!
if (start<1){
l=l+start-1;
src_offset=-start+1;//src_offset is a byte offset with base 0!
start=1;
}
if (l<=0) return;
if (start>dest->len) return;
if ((start+l-1)>dest->len) l=dest->len-start+1;
//start and l are now reflect a valid region within dest
if (src_offset>=src->len) return;
if (l>(src->len-src_offset)) l=src->len-src_offset;
//src_offset and l now reflect a valid region within src
if (dest==src){
if ((start-1)!=src_offset) memmove(dest->chr+start-1,src->chr+src_offset,l);
}else{
memcpy(dest->chr+start-1,src->chr+src_offset,l);
}
}

qbs *func_mid(qbs *str,long start,long l,long passed){
static qbs *tqbs;
if (passed){
 if (start<1) {l=l-1+start; start=1;}
 if ((l>=1)&&(start<=str->len)){
 if ((start+l)>str->len) l=str->len-start+1;
 }else{
 l=0; start=1;//nothing!
 }
}else{
 if (start<1) start=1;
 l=str->len-start+1;
 if (l<1){
 l=0; start=1;//nothing!
 }
}
if ((start==1)&&(l==str->len)) return str;//pass on
if (str->tmp){ if (!str->fixed){ if (!str->readonly){ if (!str->in_cmem){//acquire
str->chr=str->chr+(start-1);
str->len=l;
return str;
}}}}
tqbs=qbs_new(l,1);
if (l) memcpy(tqbs->chr,str->chr+start-1,l);
if (str->tmp) qbs_free(str);
return tqbs;
}

qbs *qbs_ltrim(qbs *str){
if (!str->len) return str;//pass on
if (*str->chr!=32) return str;//pass on
if (str->tmp){ if (!str->fixed){ if (!str->readonly){ if (!str->in_cmem){//acquire?
qbs_ltrim_nextchar:
if (*str->chr==32){
str->chr++;
if (--str->len) goto qbs_ltrim_nextchar;
}
return str;
}}}}
long i;
i=0;
qbs_ltrim_nextchar2: if (str->chr[i]==32) {i++; if (i<str->len) goto qbs_ltrim_nextchar2;}
qbs *tqbs;
tqbs=qbs_new(str->len-i,1);
if (tqbs->len) memcpy(tqbs->chr,str->chr+i,tqbs->len);
if (str->tmp) qbs_free(str);
return tqbs;
}

qbs *qbs_rtrim(qbs *str){
if (!str->len) return str;//pass on
if (str->chr[str->len-1]!=32) return str;//pass on
if (str->tmp){ if (!str->fixed){ if (!str->readonly){ if (!str->in_cmem){//acquire?
qbs_rtrim_nextchar:
if (str->chr[str->len-1]==32){
if (--str->len) goto qbs_rtrim_nextchar;
}
return str;
}}}}
long i;
i=str->len;
qbs_rtrim_nextchar2: if (str->chr[i-1]==32) {i--; if (i) goto qbs_rtrim_nextchar2;}
//i is the number of characters to keep
qbs *tqbs;
tqbs=qbs_new(i,1);
if (i) memcpy(tqbs->chr,str->chr,i);
if (str->tmp) qbs_free(str);
return tqbs;
}

qbs *qbs_inkey(){
if (new_error) return qbs_new(0,1);
qbs *tqbs;
Sleep(0);
tqbs=qbs_new(2,1);
if (cmem[0x41a]!=cmem[0x41c]){
//MessageBox(NULL,"Key detected","Key detected",MB_OK);

tqbs->chr[0]=cmem[0x400+cmem[0x41a]];
tqbs->chr[1]=cmem[0x400+cmem[0x41a]+1];
if (tqbs->chr[0]) tqbs->len=1;
cmem[0x41a]+=2;
if (cmem[0x41a]==62) cmem[0x41a]=30;
}else{
tqbs->len=0;
}
return tqbs;
}

//STR() functions
//singed integers
qbs *qbs_str(int64 value){
qbs *tqbs;
tqbs=qbs_new(20,1);
#ifdef QB64_WINDOWS
 tqbs->len=sprintf((char*)tqbs->chr,"% I64i",value);
#else
 tqbs->len=sprintf((char*)tqbs->chr,"% lli",value);
#endif
return tqbs;
}
qbs *qbs_str(int32 value){
qbs *tqbs;
tqbs=qbs_new(11,1);
tqbs->len=sprintf((char*)tqbs->chr,"% i",value);
return tqbs;
}
qbs *qbs_str(int16 value){
qbs *tqbs;
tqbs=qbs_new(6,1);
tqbs->len=sprintf((char*)tqbs->chr,"% i",value);
return tqbs;
}
qbs *qbs_str(int8 value){
qbs *tqbs;
tqbs=qbs_new(4,1);
tqbs->len=sprintf((char*)tqbs->chr,"% i",value);
return tqbs;
}
//unsigned integers
qbs *qbs_str(uint64 value){
qbs *tqbs;
tqbs=qbs_new(21,1);
#ifdef QB64_WINDOWS
 tqbs->len=sprintf((char*)tqbs->chr," %I64u",value);
#else
 tqbs->len=sprintf((char*)tqbs->chr," %ull",value);
#endif
return tqbs;
}
qbs *qbs_str(uint32 value){
qbs *tqbs;
tqbs=qbs_new(11,1);
tqbs->len=sprintf((char*)tqbs->chr," %u",value);
return tqbs;
}
qbs *qbs_str(uint16 value){
qbs *tqbs;
tqbs=qbs_new(6,1);
tqbs->len=sprintf((char*)tqbs->chr," %u",value);
return tqbs;
}
qbs *qbs_str(uint8 value){
qbs *tqbs;
tqbs=qbs_new(4,1);
tqbs->len=sprintf((char*)tqbs->chr," %u",value);
return tqbs;
}



unsigned char func_str_fmt[7];
unsigned char qbs_str_buffer[32];
unsigned char qbs_str_buffer2[32];

qbs *qbs_str(float value){
static qbs *tqbs;
tqbs=qbs_new(16,1);
static long l,i,i2,i3,digits,exponent;
l=sprintf((char*)&qbs_str_buffer,"% .6E",value);
//IMPORTANT: assumed l==14
if (l==13){memmove(&qbs_str_buffer[12],&qbs_str_buffer[11],2); qbs_str_buffer[11]=48; l=14;}

digits=7;
for (i=8;i>=1;i--){
if (qbs_str_buffer[i]==48){
digits--;
}else{
if (qbs_str_buffer[i]!=46) break;
}
}//i
//no significant digits? simply return 0
if (digits==0){
tqbs->len=2; tqbs->chr[0]=32; tqbs->chr[1]=48;//tqbs=[space][0]
return tqbs;
}
//calculate exponent
exponent=(qbs_str_buffer[11]-48)*100+(qbs_str_buffer[12]-48)*10+(qbs_str_buffer[13]-48);
if (qbs_str_buffer[10]==45) exponent=-exponent;
if ((exponent<=6)&&((exponent-digits)>=-8)) goto asdecimal;
//fix up exponent to conform to QBASIC standards
//i. cull trailing 0's after decimal point (use digits to help)
//ii. cull leading 0's of exponent
i3=0;
i2=digits+2;
if (digits==1) i2--;//don't include decimal point
for (i=0;i<i2;i++) {tqbs->chr[i3]=qbs_str_buffer[i]; i3++;}
for (i=9;i<=10;i++) {tqbs->chr[i3]=qbs_str_buffer[i]; i3++;}
exponent=abs(exponent);
//i2=13;
//if (exponent>9) i2=12;
i2=12;//override: if exponent is less than 10 still display a leading 0
if (exponent>99) i2=11;
for (i=i2;i<=13;i++) {tqbs->chr[i3]=qbs_str_buffer[i]; i3++;}
tqbs->len=i3;
return tqbs;
/////////////////////
asdecimal:
//calculate digits after decimal point in var. i
i=-(exponent-digits+1);
if (i<0) i=0;
func_str_fmt[0]=37;//"%"
func_str_fmt[1]=32;//" "
func_str_fmt[2]=46;//"."
func_str_fmt[3]=i+48;
func_str_fmt[4]=102;//"f"
func_str_fmt[5]=0;
tqbs->len=sprintf((char*)tqbs->chr,(const char*)&func_str_fmt,value);
if (tqbs->chr[1]==48){//must manually cull leading 0
memmove(tqbs->chr+1,tqbs->chr+2,tqbs->len-2);
tqbs->len--;
}
return tqbs;
}

qbs *qbs_str(double value){
static qbs *tqbs;
tqbs=qbs_new(32,1);
static long l,i,i2,i3,digits,exponent;

l=sprintf((char*)&qbs_str_buffer,"% .15E",value);
//IMPORTANT: assumed l==23
if (l==22){memmove(&qbs_str_buffer[21],&qbs_str_buffer[20],2); qbs_str_buffer[20]=48; l=23;}

//check if the 16th significant digit is 9, if it is round to 15 significant digits
if (qbs_str_buffer[17]==57){
sprintf((char*)&qbs_str_buffer2,"% .14E",value);
memmove(&qbs_str_buffer,&qbs_str_buffer2,17);
qbs_str_buffer[17]=48;
}
qbs_str_buffer[18]=68; //change E to D (QBASIC standard)
digits=16;
for (i=17;i>=1;i--){
if (qbs_str_buffer[i]==48){
digits--;
}else{
if (qbs_str_buffer[i]!=46) break;
}
}//i
//no significant digits? simply return 0
if (digits==0){
tqbs->len=2; tqbs->chr[0]=32; tqbs->chr[1]=48;//tqbs=[space][0]
return tqbs;
}
//calculate exponent
exponent=(qbs_str_buffer[20]-48)*100+(qbs_str_buffer[21]-48)*10+(qbs_str_buffer[22]-48);
if (qbs_str_buffer[19]==45) exponent=-exponent;
//OLD if ((exponent<=15)&&((exponent-digits)>=-16)) goto asdecimal;
if ((exponent<=15)&&((exponent-digits)>=-17)) goto asdecimal;
//fix up exponent to conform to QBASIC standards
//i. cull trailing 0's after decimal point (use digits to help)
//ii. cull leading 0's of exponent
i3=0;
i2=digits+2;
if (digits==1) i2--;//don't include decimal point
for (i=0;i<i2;i++) {tqbs->chr[i3]=qbs_str_buffer[i]; i3++;}
for (i=18;i<=19;i++) {tqbs->chr[i3]=qbs_str_buffer[i]; i3++;}
exponent=abs(exponent);
//i2=22;
//if (exponent>9) i2=21;
i2=21;//override: if exponent is less than 10 still display a leading 0
if (exponent>99) i2=20;
for (i=i2;i<=22;i++) {tqbs->chr[i3]=qbs_str_buffer[i]; i3++;}
tqbs->len=i3;
return tqbs;
/////////////////////
asdecimal:
//calculate digits after decimal point in var. i
i=-(exponent-digits+1);
if (i<0) i=0;
func_str_fmt[0]=37;//"%"
func_str_fmt[1]=32;//" "
func_str_fmt[2]=46;//"."
if (i>9){
func_str_fmt[3]=49;//"1"
func_str_fmt[4]=(i-10)+48;
}else{
func_str_fmt[3]=48;//"0"
func_str_fmt[4]=i+48;
}
func_str_fmt[5]=102;//"f"
func_str_fmt[6]=0;
tqbs->len=sprintf((char*)tqbs->chr,(const char*)&func_str_fmt,value);
if (tqbs->chr[1]==48){//must manually cull leading 0
memmove(tqbs->chr+1,tqbs->chr+2,tqbs->len-2);
tqbs->len--;
}
return tqbs;
}

qbs *qbs_str(long double value){
//not fully implemented
return qbs_str((double)value);
}


long qbs_equal(qbs *str1,qbs *str2){
if (str1->len!=str2->len) return 0;
if (memcmp(str1->chr,str2->chr,str1->len)==0) return -1;
return 0;
}
long qbs_notequal(qbs *str1,qbs *str2){
if (str1->len!=str2->len) return -1;
if (memcmp(str1->chr,str2->chr,str1->len)==0) return 0;
return -1;
}
long qbs_greaterthan(qbs *str1,qbs *str2){
static int32 i;
if (str1->len<=str2->len){
i=memcmp(str1->chr,str2->chr,str1->len);
if (i>0) return -1;
return 0;
}else{
i=memcmp(str1->chr,str2->chr,str2->len);
if (i<0) return 0;
return -1;
}
}
long qbs_lessthan(qbs *str1,qbs *str2){
static long i;
if (str1->len<=str2->len){
if (!str1->len) if (str2->len) return -1; else return 0;
i=memcmp(str1->chr,str2->chr,str1->len);
if (i<0) return -1;
return 0;
}else{
i=memcmp(str1->chr,str2->chr,str2->len);
if (i>=0) return 0;
return -1;
}
}
long qbs_lessorequal(qbs *str1,qbs *str2){
static long i;
if (str1->len<=str2->len){
i=memcmp(str1->chr,str2->chr,str1->len);
if (i<=0) return -1;
return 0;
}else{
i=memcmp(str1->chr,str2->chr,str2->len);
if (i>=0) return 0;
return -1;
}
}
long qbs_greaterorequal(qbs *str1,qbs *str2){
static long i;
//greater?
if (str1->len<=str2->len){
i=memcmp(str1->chr,str2->chr,str1->len);
if (i>0) return -1;
if (i==0) if (str1->len==str2->len) return -1;//equal?
return 0;
}else{
i=memcmp(str1->chr,str2->chr,str2->len);
if (i<0) return 0;
return -1;
}
}

long qbs_asc(qbs *str,unsigned long i){//unsigned long speeds up checking for negative
i--;
if (i<str->len){
return str->chr[i];
}
error(5);
return 0;
}


long qbs_asc(qbs *str){
if (str->len) return str->chr[0];
error(5);
return 0;
}

long qbs_len(qbs *str){
return str->len;
}


//QBG BLOCK
long qbg_mode=-1;//-1 means not initialized!
long qbg_text_only;
//text & graphics modes
long qbg_height_in_characters, qbg_width_in_characters;
long qbg_top_row, qbg_bottom_row;
long qbg_cursor_x, qbg_cursor_y;
long qbg_character_height, qbg_character_width;
unsigned long qbg_color, qbg_background_color;
//text mode ONLY
long qbg_cursor_show;
long qbg_cursor_firstvalue, qbg_cursor_lastvalue;//these values need revision
//graphics modes ONLY
long qbg_width, qbg_height;
float qbg_x, qbg_y;
long qbg_bits_per_pixel, qbg_pixel_mask; //for monochrome modes 1b, for 16 color 1111b, for 256 color 11111111b
long qbg_bytes_per_pixel;
long qbg_clipping_or_scaling;//1=clipping, 2=clipping and scaling
long qbg_view_x1, qbg_view_y1, qbg_view_x2, qbg_view_y2;
long 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;
long qbg_pages;
unsigned long *qbg_pageoffsets;
long *qbg_cursor_x_previous; //used to recover old cursor position
long *qbg_cursor_y_previous;
long qbg_active_page;
unsigned char *qbg_active_page_offset;
long qbg_visual_page;
unsigned char *qbg_visual_page_offset;
long qbg_color_assign[256];//for modes with quasi palettes!
unsigned long pal_mode10[2][9];













unsigned char charset8x8[256][8][8];
unsigned char charset8x16[256][16][8];

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

void lineclip(long x1,long y1,long x2,long y2,long xmin,long ymin,long xmax,long ymax){
static double mx,my,y,x,d;
static long xdis,ydis;
//is it a single point? (needed to avoid "division by 0" errors)
lineclip_skippixels=0;

if (x1==x2){ if (y1==y2){
if (x1>=xmin){ if (x1<=xmax){ if (y1>=ymin){ if (y1<=ymax){
goto singlepoint;
}}}}
lineclip_draw=0;
return;
}}

if (x1>=xmin){ if (x1<=xmax){ if (y1>=ymin){ if (y1<=ymax){
goto gotx1y1;
}}}}
mx=(x2-x1)/fabs((double)(y2-y1)); my=(y2-y1)/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)y2-(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)/fabs((double)(y1-y2)); my=(y1-y2)/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(unsigned long attribute,unsigned long col,long passed){
static long r,g,b;
if (new_error) 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;
//..._color_assign[attribute]=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(unsigned long col1,unsigned long col2,unsigned long bordercolor,long passed){
if (new_error) return;
if (!passed){
//performs no action if nothing passed (as in QBASIC for some modes)
return;
}

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;
return;
}
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;
return;
}
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;
return;
}
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;
return;
}
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;
return;
}
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;
return;
}
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];
return;
}
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];
return;
}
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];
return;
}
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];
return;
}
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];
}
}
return;
}
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;
return;
}
error:
error(5);
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(long n){
static long i,i2;
//add new page indexes if necessary
if (n>=pages){
i=n+1;
page=(long*)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(long mode,long color_switch,long active_page,long visual_page,long refresh,long passed){
if (new_error) return;

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

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

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
  ¦ 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 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
//  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
//  ¦ 128K page size, page range is 0 (128K) or 0-1 (256K)
//  ¦ 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
  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
  ¦ 64K page size, page range is 0 (64K);
    128K page size, page range is 0 (128K) or 0-1 (256K)
  ¦ 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
  ¦ 80 x 25 text format, 8 x 8 character box
  ¦ 64K page size, page ranges are 0 (64K), 0-1 (128K), or 0-3 (246K)
  ¦ 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
  ¦ 40 x 25 text format, character box size 8 x 8
  ¦ 32K page size, page ranges are 0-1 (64K), 0-3 (128K), or 0-7 (256K)
  ¦ 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:
  ¦ Supports Olivetti (R) Personal Computers models M24, M240, M28,
    M280, M380, M380/C, M380/T and AT&T (R) Personal Computers 6300
    series
  ¦ 640 x 400 graphics
  ¦ 80 x 25 text format, 8 x 16 character box
  ¦ 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
  ¦ 720 x 348 graphics
  ¦ 80 x 25 text format, 9 x 14 character box
  ¦ 2 screen pages (1 only if a second display adapter is installed)
  ¦ PALETTE statement not supported
*/
//Note: QB64 will not support SCREEN 3

/*
SCREEN 2: 640 x 200 graphics
  ¦ 80 x 25 text format with character box size of 8 x 8
  ¦ 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
  ¦ 40 x 25 text format, 8 x 8 character box
  ¦ 16 background colors and one of two sets of 3 foreground colors assigned
    using COLOR statement with CGA
  ¦ 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
  ¦ 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)
  ¦ 16 colors assigned to 2 attributes
  ¦ 16 colors assigned to any of 16 attributes (with CGA or EGA)
  ¦ 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=(long*)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)

}//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();
write_page->draw_ta=0.0; write_page->draw_scale=1.0; //reset DRAW attributes (of write_page)
}
}//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();
}
}//passed&8

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

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

void sub_pcopy(long src,long dst){
if (new_error) 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(long option,long value1,long value2,long passed){
//[{#|LPRINT}][?],[?]
static long i,i2;

if (new_error) return;
width8050switch=0;

if (option==0){//WIDTH [?][,?]
static unsigned long 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 long f,f2,width,height;

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

width=value1; height=value2;

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

if (!(passed&1)){//width ommited
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 ommited
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=(unsigned char*)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 ommited
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
  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
  ¦ 64K page size, page range is 0 (64K);
    128K page size, page range is 0 (128K) or 0-1 (256K)
  ¦ 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
  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
  ¦ 128K page size, page range is 0 (128K) or 0-1 (256K)
  ¦ 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(1,window_x1,window_y1,window_x2,window_y2,1); 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(1,window_x1,window_y1,window_x2,window_y2,1); 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
  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
  ¦ 64K page size, page range is 0 (64K);
    128K page size, page range is 0 (128K) or 0-1 (256K)
  ¦ 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
  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
  ¦ 128K page size, page range is 0 (128K) or 0-1 (256K)
  ¦ 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(1,window_x1,window_y1,window_x2,window_y2,1); 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(1,window_x1,window_y1,window_x2,window_y2,1); 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
  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
  ¦ 64K page size, page range is 0 (64K);
    128K page size, page range is 0 (128K) or 0-1 (256K)
  ¦ 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
  ¦ 80 x 25 or 80 x 43 text format, 8 x 14 or 8 x 8 character box size
  ¦ 128K page size, page range is 0 (128K) or 0-1 (256K)
  ¦ 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(1,window_x1,window_y1,window_x2,window_y2,1); 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(1,window_x1,window_y1,window_x2,window_y2,1); 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 [?][,?]

//file/device?
//...
//printer?
//...
}

error:
error(5);
return;
}

void pset_and_clip(long x,long y,unsigned long col){

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

static unsigned char *cp;
static unsigned long *o32;
static unsigned long 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 (optomized)
 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 (optomized)
 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=blend+(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,unsigned long col){
static long x1,y1,x2,y2,i,width,img_width,x,y,d_width,a,a2,v1,v2,v3;
static unsigned char *p,*cp,*cp2,*cp3;
static unsigned long *lp,*lp_last,*lp_first;
static unsigned long *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
//optomized
//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=blend+(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(long x1,long y1,long x2,long y2,unsigned long col){
//assumes:
//actual coordinates passed
//left->right, top->bottom order
//on-screen
static long i,width,img_width,x,y,d_width,a,a2,v1,v2,v3;
static unsigned char *p,*cp,*cp2,*cp3;
static unsigned long *lp,*lp_last,*lp_first;
static unsigned long *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
//optomized
//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=blend+(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 long 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(long x1,long y1,long x2,long y2,unsigned long col){
static long 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,unsigned long col,unsigned long style){
static long 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=_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 sub_line(long step1,float x1,float y1,long step2,float x2,float y2,unsigned long col,long bf,unsigned long style,long passed){
if (new_error) return;
if (write_page->text){error(5); return;}
//format: [{STEP}][(?,?)]-[{STEP}](?,?),[?],[{B|BF}],[?]
//                  1 2                  4            8

//[[{STEP}](?,?)]-[{STEP}](?,?)[,[?][,[{B|BF}][,?]]]
//          1                     2             4

//adjust coordinates and qb graphics cursor position based on STEP
if (passed&1){
if (step1){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 (step2){x2=write_page->x+x2; y2=write_page->y+y2;}
write_page->x=x2; write_page->y=y2;

if (bf==0){//line
if ((passed&4)==0) style=0xFFFF;
if ((passed&2)==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&4)==0) style=0xFFFF;
if ((passed&2)==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&2)==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(long step,float x,float y,unsigned long fillcol,unsigned long bordercol,long passed){

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

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

if (step){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=(unsigned char*)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=blend+(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=blend+(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=blend+(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=blend+(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=blend+(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(long step,float x,float y,unsigned long fillcol,unsigned long bordercol,long passed){

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

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

if (step){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=(unsigned char*)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(long step,float x,float y,unsigned long fillcol,unsigned long bordercol,qbs *backgroundstr,long passed){
if (new_error) return;
if (write_page->text){error(5); return;}
if (passed&4){error(5); return;}

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

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

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

if (step){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=(unsigned char*)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 sub_paint(long step,float x,float y,qbs *fillstr,unsigned long bordercol,qbs *backgroundstr,long passed){
if (new_error) return;

//uses 2 buffers, a and b, and swaps between them for reading and creating
static unsigned long fillcol=0;//stub
static unsigned long a_n=0;
static unsigned short *a_x=(unsigned short*)malloc(2*65536),*a_y=(unsigned short*)malloc(2*65536);
static unsigned char *a_t=(unsigned char*)malloc(65536);
static unsigned long b_n=0;
static unsigned short *b_x=(unsigned short*)malloc(2*65536),*b_y=(unsigned short*)malloc(2*65536);
static unsigned char *b_t=(unsigned char*)malloc(65536);
static unsigned char *done=(unsigned char*)calloc(640*480,1);
static long ix,iy,i,t,x2,y2;
static unsigned long offset;
static unsigned char *cp;
static unsigned short *sp;
static unsigned long backgroundcol;

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

//STEP 1: create the tile in a buffer (tile) using the source string
static unsigned char tilestr[256];
static unsigned char tile[8][64];
static long sx,sy;
static long bytesperrow;
static long row2offset;
static long row3offset;
static long row4offset;
static long byte;
static long bitvalue;
static long c;
if (fillstr->len==0){error(5); return;}
if (qbg_bits_per_pixel==4){
if (fillstr->len>256){error(5); return;}
}else{
if (fillstr->len>64){error(5); return;}
}
memset(&tilestr[0],0,256);
memcpy(&tilestr[0],fillstr->chr,fillstr->len);
sx=8; sy=fillstr->len; //defaults
if (qbg_bits_per_pixel==8) sx=1;
if (qbg_bits_per_pixel==4){
if (fillstr->len&3){
sy=(fillstr->len-(fillstr->len&3)+4)>>2;
}else{
sy=fillstr->len>>2;
}
bytesperrow=sx>>3; if (sx&7) bytesperrow++;
row2offset=bytesperrow;
row3offset=bytesperrow*2;
row4offset=bytesperrow*3;
}
if (qbg_bits_per_pixel==2) sx=4;
//use modified "PUT" routine to create the tile
cp=&tilestr[0];
{//layer
static long x,y;
for (y=0;y<sy;y++){
if (qbg_bits_per_pixel==4){
bitvalue=128;
byte=0;
}
for (x=0;x<sx;x++){
//get colour
if (qbg_bits_per_pixel==8){
 c=*cp;
 cp++;
}
if (qbg_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 (qbg_bits_per_pixel==1){
 if (!(x&7)){
  byte=*cp;
  cp++;
 }
 c=(byte&128)>>7; byte<<=1;
}
if (qbg_bits_per_pixel==2){
 if (!(x&3)){
  byte=*cp;
  cp++;
 }
 c=(byte&192)>>6; byte<<=2;
}
//"pset" color
tile[x][y]=c;
}//x
if (qbg_bits_per_pixel==4) cp+=(bytesperrow*4);
if (qbg_bits_per_pixel==1){
 if (sx&7) cp++;
}
if (qbg_bits_per_pixel==2){
 if (sx&3) cp++;
}
}//y
}//unlayer
//tile created!

//STEP 2: establish border and background colors
if ((passed&2)==0) bordercol=qbg_color;
bordercol&=qbg_pixel_mask;

backgroundcol=0;//default
if (passed&4){
if (backgroundstr->len==0){error(5); return;}
if (backgroundstr->len>255){error(5); return;}
if (qbg_bits_per_pixel==1){
c=backgroundstr->chr[0];
if ((c>0)&&(c<255)) backgroundcol=-1;//unclear definition
if (c==255) backgroundcol=1;
}
if (qbg_bits_per_pixel==2){
backgroundcol=-1;//unclear definition
x2=backgroundstr->chr[0];
y2=x2&3;
x2>>=2; if ((x2&3)!=y2) goto uncleardef;
x2>>=2; if ((x2&3)!=y2) goto uncleardef;
x2>>=2; if ((x2&3)!=y2) goto uncleardef;
backgroundcol=y2;
}
if (qbg_bits_per_pixel==4){
backgroundcol=-1;//unclear definition
y2=0;
x2=4; if (backgroundstr->len<4) x2=backgroundstr->len;
c=0; memcpy(&c,backgroundstr->chr,x2);
x2=c&255; c>>=8; if ((x2!=0)&&(x2!=255)) goto uncleardef;
y2|=(x2&1);
x2=c&255; c>>=8; if ((x2!=0)&&(x2!=255)) goto uncleardef;
y2|=((x2&1)<<1);
x2=c&255; c>>=8; if ((x2!=0)&&(x2!=255)) goto uncleardef;
y2|=((x2&1)<<2);
x2=c&255; c>>=8; if ((x2!=0)&&(x2!=255)) goto uncleardef;
y2|=((x2&1)<<3);
backgroundcol=y2;
}
if (qbg_bits_per_pixel==8){
backgroundcol=backgroundstr->chr[0];
}
}
uncleardef:

//STEP 3: perform tile'd fill
if (step){qbg_x+=x; qbg_y+=y;}else{qbg_x=x; qbg_y=y;}
if (qbg_clipping_or_scaling){
if (qbg_clipping_or_scaling==2){
ix=qbr_float_to_long(qbg_x*qbg_scaling_x+qbg_scaling_offset_x)+qbg_view_offset_x;
iy=qbr_float_to_long(qbg_y*qbg_scaling_y+qbg_scaling_offset_y)+qbg_view_offset_y;
}else{
ix=qbr_float_to_long(qbg_x)+qbg_view_offset_x; iy=qbr_float_to_long(qbg_y)+qbg_view_offset_y;
}
}else{
ix=qbr_float_to_long(qbg_x); iy=qbr_float_to_long(qbg_y);
}

//return if offscreen
if ((ix<qbg_view_x1)||(iy<qbg_view_y1)||(ix>qbg_view_x2)||(iy>qbg_view_y2)){
return;
}

offset=iy*qbg_width+ix;

//return if first point is the bordercolor
if (qbg_active_page_offset[offset]==bordercol) return;

//return if first point is the same as the tile color used and is not the background color
fillcol=tile[ix%sx][iy%sy];
if ((fillcol==qbg_active_page_offset[offset])&&(fillcol!=backgroundcol)) return;
qbg_active_page_offset[offset]=fillcol;




//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){
fillcol=tile[x2%sx][y2%sy];
//no tile check required when moving horizontally!
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){
fillcol=tile[x2%sx][y2%sy];
//no tile check required when moving horizontally!
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){
fillcol=tile[x2%sx][y2%sy];
if ((fillcol!=qbg_active_page_offset[offset])||(fillcol==backgroundcol)){
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){
fillcol=tile[x2%sx][y2%sy];
if ((fillcol!=qbg_active_page_offset[offset])||(fillcol==backgroundcol)){
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,qbg_width*qbg_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 sub_circle(long step,double x,double y,double r,unsigned long col,double start,double end,double aspect,long passed){
if (new_error) return;

//                                                                 &1         &2           &4         &8
//[{STEP}](?,?),?[,[?][,[?][,[?][,?]]]]



//data
static double pi= 3.1415926535897932,pi2=6.2831853071795865;
static long line_to_start,line_from_end;
static long 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 long pixels;
static double tmp;
static long tmpi;
static long i;
static long exclusive;
static double arc1,arc2,arc3,arc4,arcinc;
static double px2,py2;
static long x2,y2;
static long x3,y3;
static long lastplotted_x2,lastplotted_y2;
static long lastchecked_x2,lastchecked_y2;

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

//lines to & from centre
if (!(passed&2)) start=0;
if (!(passed&4)) 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 (step){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 differece 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=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&8)) 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&1)) 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=sin(arcinc);
cosb=cos(arcinc);
lastplotted_x2=-1;
lastchecked_x2=-1;
i=0;

if (line_to_start){
px=cos(start); py=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=cos(end); py=sin(end);
x2=px*xspan+0.5; y2=py*yspan-0.5;
fast_line(ix,iy,ix+x2,iy-y2,col);
}

}//sub_circle

unsigned long point(long x,long 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,long passed){
static long 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 qbg_pset(long step,float x,float y,unsigned long col,long passed){
if (new_error) return;



static long x2,y2;
if (!write_page->compatible_mode){error(5); return;}
//Special Format: [{STEP}](?,?),[?]
if (step){write_page->x+=x; write_page->y+=y;}else{write_page->x=x; write_page->y=y;}
if (!(passed&1)) 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(long step,float x,float y,unsigned long col,long passed){
if (new_error) return;

if (!passed){
col=write_page->background_color;
passed=1;
}
qbg_pset(step,x,y,col,passed);
return;
}


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

void printchr(long character){
static unsigned long x,x2,y,y2,w,h,z,z2,z3,a,a2,a3,color,background_color,f;
static unsigned long *lp;
static unsigned char *cp;
static img_struct *im;
static SDL_Surface *ts;
static SDL_Color c,c2;

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
character&=255;
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];
c.r=255; c.g=255; c.b=255; c.unused=0;//dummy values
c2.r=255; c2.g=255; c2.b=255; c2.unused=0;//dummy values


//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]&8)){

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Solid(font[f],(Uint16*)unicode16_buf,c);
//ts=TTF_RenderText_Solid(font[f],(char*)&character,c);//8-bit, 0=clear, 1=text

if (ts==NULL) return;
w=ts->w;
if (x2=fontwidth[f]) if (x2!=w) w=x2;//render width too large!  
switch(im->print_mode){
case 3:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 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=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 if (*cp++) pset(x+x2,y+y2,color);
 }}
 break;
case 2:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 if (!(*cp++)) pset(x+x2,y+y2,background_color);
 }}
break;
default:
break;
}//z
SDL_FreeSurface(ts);
return;
}//1-8 bit

//assume 32-bit blended
a=(color>>24)+1;
a2=(background_color>>24)+1;
z=color&0xFFFFFF;
z2=background_color&0xFFFFFF;
//8 bit, 0=background -> 255=foreground

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Shaded(font[f],(Uint16*)unicode16_buf,c,c2);
//ts=TTF_RenderText_Shaded(font[f],(char*)&character,c,c2);

if (ts==NULL) return;
w=ts->w;
if (x2=fontwidth[f]) if (x2!=w) w=x2;//render width too large!  
switch(im->print_mode){
case 3:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 z3=*cp++;
 if (z3!=255) pset(x+x2,y+y2,(((255-z3)*a2)>>8<<24)+z2);
 if (z3) pset(x+x2,y+y2,((z3*a)>>8<<24)+z);
 }}
break;
case 1:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 z3=*cp++;
 //if (z3!=255) pset(x+x2,y+y2,(((255-z3)*a2)>>8<<24)+z2);
 if (z3) pset(x+x2,y+y2,((z3*a)>>8<<24)+z);
 }}
break;
case 2:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 z3=*cp++;
 if (z3!=255) pset(x+x2,y+y2,(((255-z3)*a2)>>8<<24)+z2);
 //if (z3) pset(x+x2,y+y2,((z3*a)>>8<<24)+z);
 }}
break;
default:
break;
}
SDL_FreeSurface(ts);
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


//prints a character at pixel offset x,y of image surface i
//returns the width in pixels of the character printed
//this is used for printing un-kerned characters
//cannot be used on a text surface!
long printchr2(long x,long y,unsigned long character,long i){
static unsigned long x2,y2,w,h,z,z2,z3,a,a2,a3,color,background_color,f;
static unsigned long *lp;
static unsigned char *cp;
static img_struct *im;
static SDL_Surface *ts;
static SDL_Color c,c2;

im=&img[i];
color=im->color;
background_color=im->background_color;

//precalculations
character&=255;
f=im->font;
h=fontheight[f];
c.r=255; c.g=255; c.b=255; c.unused=0;//dummy values
c2.r=255; c2.g=255; c2.b=255; c2.unused=0;//dummy values

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]&8)){

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Solid(font[f],(Uint16*)unicode16_buf,c);
//ts=TTF_RenderText_Solid(font[f],(char*)&character,c);//8-bit, 0=clear, 1=text

if (ts==NULL) return 0;
w=ts->w;
if (x2=fontwidth[f]) if (x2!=w) w=x2;//render width too large!  
switch(im->print_mode){
case 3:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 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=((unsigned char*)ts->pixels)+y2*ts->pitch;
 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=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 if (!(*cp++)) pset_and_clip(x+x2,y+y2,background_color);
 }}
break;
default:
break;
}//z
SDL_FreeSurface(ts);
return w;
}//1-8 bit

//assume 32-bit blended
a=(color>>24)+1;
a2=(background_color>>24)+1;
z=color&0xFFFFFF;
z2=background_color&0xFFFFFF;
//8 bit, 0=background -> 255=foreground

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Shaded(font[f],(Uint16*)unicode16_buf,c,c2);
//ts=TTF_RenderText_Shaded(font[f],(char*)&character,c,c2);

if (ts==NULL) return 0;
w=ts->w;
if (x2=fontwidth[f]) if (x2!=w) w=x2;//render width too large!  
switch(im->print_mode){
case 3:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 z3=*cp++;
 if (z3!=255) pset_and_clip(x+x2,y+y2,(((255-z3)*a2)>>8<<24)+z2);
 if (z3) pset_and_clip(x+x2,y+y2,((z3*a)>>8<<24)+z);
 }}
break;
case 1:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 z3=*cp++;
 //if (z3!=255) pset_and_clip(x+x2,y+y2,(((255-z3)*a2)>>8<<24)+z2);
 if (z3) pset_and_clip(x+x2,y+y2,((z3*a)>>8<<24)+z);
 }}
break;
case 2:
 for (y2=0;y2<h;y2++){
 cp=((unsigned char*)ts->pixels)+y2*ts->pitch;
 for (x2=0;x2<w;x2++){
 z3=*cp++;
 if (z3!=255) pset_and_clip(x+x2,y+y2,(((255-z3)*a2)>>8<<24)+z2);
 //if (z3) pset_and_clip(x+x2,y+y2,((z3*a)>>8<<24)+z);
 }}
break;
default:
break;
}
SDL_FreeSurface(ts);
return w;
}//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_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;
}//z
return 8;

}//printchr2



long chrwidth(long character){
static unsigned long x;
static img_struct *im;
static SDL_Surface *ts;
static SDL_Color c;
im=write_page;
if (x=fontwidth[im->font]) return x;
character&=255;
//int minx,maxx,miny,maxy,advance;
//TTF_GlyphMetrics(font[im->font], character,  &minx,  &maxx,  &miny,  &maxy,  &advance);
c.r=255; c.g=255; c.b=255; c.unused=0;//dummy values
if (im->alpha_disabled||(fontflags[im->font]&8)||(im->bytes_per_pixel==1)){

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Solid(font[im->font],(Uint16*)unicode16_buf,c);
//ts=TTF_RenderText_Solid(font[im->font],(char*)&character,c);//8-bit, 0=clear, 1=text

}else{

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Shaded(font[im->font],(Uint16*)unicode16_buf,c,c);
//ts=TTF_RenderText_Shaded(font[im->font],(char*)&character,c,c);//8-bit 0-255

}
if (ts==NULL) return 0;
x=ts->w;
SDL_FreeSurface(ts);
return x;
}//chrwidth


void newline(){
static unsigned long *lp;
static unsigned short *sp;
static long 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 (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=((unsigned short*)(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 long 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 tab(){
static long 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;
}

//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
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
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;
}

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

/*
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++){
entered_new_line=0;//beginning a new line was the last action (so don't add a new one)
character=str->chr[i];

//###special characters

if (character==28){
//advance one cursor position
//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
//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 boundries
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 boundries
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){
//clears text viewport
//clears bottom row
//moves cursor to top-left of text viewport
sub_cls(NULL,NULL,0);
goto skip;
}

if (character==11){
write_page->cursor_x=1; write_page->cursor_y=write_page->top_row;
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
 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
 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){
qb64_generatesound(783.99,0.2,0);
Sleep(250);
goto skip;
}

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

//###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+w)>write_page->width){
newline();
//entered_new_line not set, a character will follow
}
}

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

//###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();



/*
null_length:

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

//hold cursor
hold_cursor:
*/

return;

}


long qbs_cleanup(unsigned long base,long passvalue){
while (qbs_tmp_list_nexti>base) { qbs_tmp_list_nexti--; if(qbs_tmp_list[qbs_tmp_list_nexti]!=0xFFFFFFFF)qbs_free((qbs*)qbs_tmp_list[qbs_tmp_list_nexti]); }//clear any temp. strings created
return passvalue;
}



void qbg_sub_window(long screen,float x1,float y1,float x2,float y2,long passed){
if (new_error) return;
static float i;
static float old_x,old_y;

if (write_page->text) goto qbg_sub_window_error;
if ((!passed)&&screen) goto qbg_sub_window_error;//SCREEEN passed without any other arguements!

//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){
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 (!screen){
i=y1; y1=y2; y2=i;
}
//Note: Window's coordinates are not based on prev. WINDOW values
write_page->clipping_or_scaling=2;
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=-x1*write_page->scaling_x; //scaling offset should be applied before scaling
write_page->scaling_offset_y=-y1*write_page->scaling_y;
if (!screen){
write_page->scaling_offset_y=-y2*write_page->scaling_y+(write_page->view_y2-write_page->view_y1);
}
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(long topline,long bottomline,long passed){
if (new_error) return;

static long 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(long coords_relative_to_screen,long x1,long y1,long x2,long y2,long fillcolor,long bordercolor,long passed){
if (new_error) 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
long 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 (coords_relative_to_screen) goto error;
if (passed&2) goto error;
if (passed&4) 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 (coords_relative_to_screen==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 argurments 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;
}

//recalculate window values based on new viewport (if necessary)
if (write_page->clipping_or_scaling==2){//WINDOW'ing in use
write_page->scaling_x=((float)(write_page->view_x2-write_page->view_x1))/(write_page->window_x2-write_page->window_x1);
write_page->scaling_y=((float)(write_page->view_y2-write_page->view_y1))/(write_page->window_y2-write_page->window_y1);
write_page->scaling_offset_x=-write_page->window_x1*write_page->scaling_x;
write_page->scaling_offset_y=-write_page->window_y1*write_page->scaling_y;
if (write_page->window_y2<write_page->window_y1) write_page->scaling_offset_y=-write_page->window_y2*write_page->scaling_y+write_page->view_y2;
}

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

if (passed&4){//bordercolor
static long 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 sub_cls(long method,unsigned long use_color,long passed){
if (new_error) return;
static long characters,i;
static unsigned short *sp;
static unsigned short clearvalue;

//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 (short) 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=(unsigned short*)&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=(unsigned short*)&write_page->offset[(write_page->height-1)*write_page->width*2];
 for (i=0;i<characters;i++){sp[i]=clearvalue;}
 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;
}

return;
}

if (method==0){//clear everything
if (write_page->text){
 characters=write_page->height*write_page->width;
 sp=(unsigned short*)write_page->offset;
 for (i=0;i<characters;i++){sp[i]=clearvalue;}
 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;
}

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;
}

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=(unsigned short*)&write_page->offset[(write_page->top_row-1)*write_page->width*2];
 for (i=0;i<characters;i++){sp[i]=clearvalue;}
 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;
 }
 return;
}
}

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



void qbg_sub_locate(long row,long column,long cursor,long start,long stop,long passed){
static long h,w,i;
if (new_error) 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;
 unsigned long 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,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 (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&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;
write_page->cursor_lastvalue=stop;
 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;
long hexoct2uint64(qbs* h){
//returns 0=failed
//        1=HEX value (default if unspecified)
//        2=OCT value
static long i,i2;
static uint64 result;
result=0;
static long 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 (!type) return 0;
if (h->len==2) return type;

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){
//unsigned _int64 max=18446744073709551615 (decimal, 20 chars)
//                   =1777777777777777777777 (octal, 22 chars)
//                   =FFFFFFFFFFFFFFFF (hex, 16 chars)
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

}



//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"};
unsigned char significant_digits[1024];
long num_significant_digits;

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


void qbs_input(long numvariables,unsigned char newline){
if (new_error) return;

static long autodisplay_backup;
autodisplay_backup=autodisplay;
autodisplay=1;

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

unsigned long qbs_tmp_base=qbs_tmp_list_nexti;

static long 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;

long i,i2,i3,i4,i5,i6;
long 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:

long 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 arguements
//ASSUME LEADING & TRALING SPACES REMOVED!
unsigned char valid;
unsigned char neg;
long completewith;
long l;
unsigned char *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
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=111;//"O"
 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:          –9223372036854775808 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
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=111;//"O"
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=(unsigned char*)uint64_max[0];
}else{
if (i4<19) goto typechecked_int64;
if (i4>19) {valid=0; goto typechecked;}
if (neg) cp2=(unsigned char*)int64_max_neg[0]; else cp2=(unsigned char*)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){
#ifdef QB64_WINDOWS
 sscanf((char*)qbs_input_arguements[argn]->chr,"%I64u",(uint64*)qbs_input_variableoffsets[argn]);
#else
 sscanf((char*)qbs_input_arguements[argn]->chr,"%llu",(uint64*)qbs_input_variableoffsets[argn]);
#endif
}else{
#ifdef QB64_WINDOWS
 sscanf((char*)qbs_input_arguements[argn]->chr,"%I64i",(int64*)qbs_input_variableoffsets[argn]);
#else
 sscanf((char*)qbs_input_arguements[argn]->chr,"%lli",(int64*)qbs_input_variableoffsets[argn]);
#endif
}
goto typechecked;
}
}
}

//check ISFLOAT type?
//[-]9999[.]9999[E/D][+/-]99999
if (qbs_input_variabletypes[argn]&ISFLOAT){
static long digits_before_point;
static long digits_after_point;
static long zeros_after_point;
static long 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
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=111;//"O"
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:           ±3.402823    E+38 to ±1.401298    E-45
//WIKIPEDIA:    ±3.4028234   E+38 to ?
//OTHER SOURCE: ±3.402823466 E+38 to ±1.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-long 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=(unsigned char*)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=(unsigned char*)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 ±1.7976931 D+308 to ±4.940656 D-324
//WIKIPEDIA:    ±1.7976931348623157 D+308 to ???
//OTHER SOURCE: ±1.7976931348623157 D+308 to ±2.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-long 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=(unsigned char*)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=(unsigned char*)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 long 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 (SDL_GetTicks()&512) i=0;
}
if (i!=showing_cursor){
showing_cursor^=1;
static long x,y,x2,y2,fx,fy,alpha,cw;
static unsigned long 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{
SDL_Delay(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 long x,y,x2,y2,fx,fy,cw,alpha;
static unsigned long 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;
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);
}

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;
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;
}

if (inpstr2->len>=255) goto qbs_input_next;

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

//perform actual update
qbs_print(key,0);

qbs_set(inpstr,inpstr2);

goto qbs_input_next;

}//qbs_input

const char *func_val_max[]={"1797693134862315"};
double func_val(qbs *s){
static unsigned char *val_max;
val_max=(unsigned char*)func_val_max[0];
static long i,i2,step,c,num_significant_digits,most_significant_digit_position;
static long num_exponent_digits;
static long negate,negate_exponent;
static unsigned char significant_digits[16];
static int64 exponent_value;
static unsigned char built_number[32];
static double return_value;
static int64 value;
static int64 hex_value;
static long hex_digits;
if (!s->len) return 0;
value=0;
negate_exponent=0;
num_exponent_digits=0;
num_significant_digits=0;
most_significant_digit_position=0;
step=0;
exponent_value=0;
negate=0;

i=0;
for (i=0;i<s->len;i++){
c=s->chr[i];

if ((c==32)||(c==9)) goto whitespace;

if (c==38){//&
if (step==0) goto hex;
goto finish;
}

if (c==45){//-
if (step==0){negate=1; step=1; goto checked;}
if (step==3){negate_exponent=1; step=4; goto checked;}
goto finish;
}

if (c==43){//+
if (step==0){step=1; goto checked;}
if (step==3){step=4; goto checked;}
goto finish;
}

if ((c>=48)&&(c<=57)){//0-9

if (step<=1){//before decimal point
step=1;
if ((num_significant_digits)||(c>48)){
most_significant_digit_position++;
if (num_significant_digits>=16) goto checked;
significant_digits[num_significant_digits]=c;
num_significant_digits++;
value=value*10+c-48;
}
}

if (step==2){//after decimal point
if ((num_significant_digits==0)&&(c==48)) most_significant_digit_position--;
if ((num_significant_digits)||(c>48)){
if (num_significant_digits>=16) goto checked;
significant_digits[num_significant_digits]=c;
num_significant_digits++;
}
}

if (step>=3){//exponent
step=4;
if ((num_exponent_digits)||(c>48)){
if (num_exponent_digits>=18) goto finish;
exponent_value*=10; exponent_value=exponent_value+c-48;//precalculate
num_exponent_digits++;
}
}

goto checked;
}

if (c==46){//.
if (step>1) goto finish;
step=2; goto checked;
}

if ((c==68)||(c==69)||(c==100)||(c==101)){//D,E,d,e
if (step>2) goto finish;
step=3; goto checked;
}

goto finish;//invalid character
checked:
whitespace:;
}
finish:;

if (num_significant_digits==0) return 0;

if (exponent_value==0){
if (num_significant_digits==most_significant_digit_position){
if (negate) value=-value;
return value;
}
}

if (negate_exponent) exponent_value=-exponent_value;
i=0;
if (negate) {built_number[i]=45; i++;}//-
if (num_significant_digits){
 for (i2=0;i2<num_significant_digits;i2++){
 if (i2==1){built_number[i]=46; i++;}
 built_number[i]=significant_digits[i2]; i++;
 }
 built_number[i]=69; i++;//E
 //adjust exponent value appropriately
 //if most_significant_digit_position=1, then no change need occur
 exponent_value=exponent_value+most_significant_digit_position-1;
 //range check exponent value & return an error if necessary
 if (exponent_value>308){error(6); return 0;}
 if (exponent_value<-324)return 0;
 //range check significant digits (if necessary)
 //LIMIT FROM QB4.5 ---> PRINT VAL("1.797693134862315D+308"), the largest possible value
 if (exponent_value==308){
 for (i2=0;i2<num_significant_digits;i2++){
 if (significant_digits[i2]>val_max[i2]){error(6); return 0;}
 if (significant_digits[i2]<val_max[i2]) break;
 }
 }
 //add exponent's value
 #ifdef QB64_WINDOWS
  i2=sprintf((char*)&built_number[i],"%I64i",exponent_value);
 #else
  i2=sprintf((char*)&built_number[i],"%lli",exponent_value);
 #endif
 i=i+i2;
}else{
 built_number[i]=48; i++;//0
}
built_number[i]=0;//NULL terminate
sscanf((char*)&built_number[0],"%lf",&return_value);
return return_value;

hex://hex/oct
if (i>=(s->len-2)) return 0;
c=s->chr[i+1];
if ((c==79)||(c==111)){//"O"or"o"
 hex_digits=0;
 hex_value=0;
 for (i=i+2;i<s->len;i++){
 c=s->chr[i];
 if ((c>=48)&&(c<=55)){//0-7
 c-=48;
 hex_value<<=3;
 hex_value|=c;
 if (hex_digits||c) hex_digits++; 
  if (hex_digits>=22){
  if ((hex_digits>22)||(s->chr[i-21]>49)){error(6); return 0;}
  }
 }else break;
 }//i
 return hex_value;
}
if ((c==72)||(c==104)){//"H"or"h"
 hex_digits=0;
 hex_value=0;
 for (i=i+2;i<s->len;i++){
 c=s->chr[i];
 if ( ((c>=48)&&(c<=57)) || ((c>=65)&&(c<=70)) || ((c>=97)&&(c<=102)) ){//0-9 or A-F or a-f
 if ((c>=48)&&(c<=57)) c-=48;
 if ((c>=65)&&(c<=70)) c-=55;
 if ((c>=97)&&(c<=102)) c-=87;
 hex_value<<=4;
 hex_value|=c;
 if (hex_digits||c) hex_digits++;
 if (hex_digits>16) {error(6); return 0;}
 }else break;
 }//i
 return hex_value;
}
return 0;//& followied by unknown
}






long unsupported_port_accessed=0;

long H3C7_palette_register_read_index=0;
long H3C8_palette_register_index=0;
long H3C9_next=0;
long H3C9_read_next=0;

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

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);
}

unsigned long rnd_seed=327680;
void sub_randomize (double seed,long passed){
if (new_error) return;

if (passed){
//Dim As Uinteger m = cptr(Uinteger Ptr, @n)[1]
static unsigned long m;
m=((unsigned long*)&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 short 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,long passed){
if (new_error) return 0;

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

double func_timer(double accuracy,long passed){
if (new_error) return 0;
static unsigned long x;
static double d;
static float f;
x=SDL_GetTicks();
x-=sdl_firsttimervalue;
x+=qb64_firsttimervalue;
//make timer value loop after midnight
//note: there are 86400000 milliseconds in 24hrs(1 day)
x%=86400000;
d=x;//convert to double
d/=1000.0;//convert from ms to sec
//reduce accuracy
if (!passed){
accuracy=18.2;
}else{
if (accuracy<=0.0){error(5); return 0;}
accuracy=1.0/accuracy;
}
d*=accuracy;
d=qbr(d);
d/=accuracy;
if (!passed){f=d; d=f;}
return d;
}

void sub__limit(double fps){
if (new_error) return;

static double prev=0,ms,now,x;

if (fps<=0.0){error(5); return;}
ms=1000.0/fps;
if (ms>60000.0){error(5); return;}//max. 1 min delay between frames allowed to avoid accidental lock-up of program

now=SDL_GetTicks();

if (prev==0.0){//first call?
prev=now;
return;
}
if (now<prev){//value looped?
prev=now;
return;
}

//calculate wait time required in ms
x=now-prev;//elapsed ms since last call
if (x<ms){//delay required
	Sleep(ms-x+1);//+1 added to avoid rounding down which erodes accuracy leading to overspeed
	prev=prev+ms;
}else{//took too long, adjust prev to current time
//note: a minor overshoot of up to 16ms is recoverable
if (x<=(ms+16.0)) prev=prev+ms; else prev=now;
}

}


void sub_sound(double frequency,double lengthinclockticks){
sndsetup();
if (new_error) return;
//note: there are 18.2 clock ticks per second
if ((frequency<37.0)&&(frequency!=0)) goto error;
if (frequency>32767.0) goto error;
if (lengthinclockticks<0.0) goto error;
if (lengthinclockticks>65535.0) goto error;
if (lengthinclockticks==0.0) return;
qb64_generatesound(frequency,lengthinclockticks/18.2,1);
return;
error:
error(5);
}



long generic_put(long i,long offset,unsigned char *cp,long 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 (new_error) return 0;
if (gfs_fileno_valid(i)!=1){error(52); return 0;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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;
}

long generic_get_bytes_read;
long generic_get(long i,long offset,unsigned char *cp,long 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 (new_error) return 0;
if (gfs_fileno_valid(i)!=1){error(52); return 0;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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 (new_error) 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;}
error(53); return;//default assumption: 'file not found'
}

gfs_fileno_use(i,x);

static gfs_file_struct *f; f=&gfs_file[x];

f->type=type; if (type==5) f->type=4;

if (type==1){//set record length
f->record_length=128;
if (passed) if (record_length!=-1) f->record_length=record_length;
}

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
}

}


void sub_close(int32 i2,int32 passed){
if (new_error) return;
int32 i;

if (passed){

//tcp/ip
if (i2<0){
i=-(i2+1);
if ((i>=0)&&(i<sfh_bufsize)){
if ((sfh[i].type>=1)&&(sfh[i].type<=3)){	
if (sfh[i].type!=1){
SDLNet_TCP_DelSocket(net_tcp[i].set,net_tcp[i].socket);
SDLNet_FreeSocketSet(net_tcp[i].set);
}
SDLNet_TCP_Close(net_tcp[i].socket);
if (net_tcp[i].buffer) free(net_tcp[i].buffer);
sfh_free(i);
}//types 1-3
}
return;
}//i2<0

if (gfs_fileno_valid(i2)==1) gfs_close(gfs_fileno[i2]);
return;

}//passed

//tcp/ip
for (i=0;i<sfh_bufsize;i++){
 if ((sfh[i].type>=1)&&(sfh[i].type<=3)) sub_close(-i-1,1);
}

for (i=1;i<=gfs_fileno_n;i++){
 if (gfs_fileno_valid(i)==1) gfs_close(gfs_fileno[i]);
}

}//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

if (i<0){//TCP/IP feed buffer
if (tcp_feed_offset>=tcp_feed_ucbufsiz) return -1;
return tcp_feed_ucbuf[tcp_feed_offset++];
}

static uint8 c;
static int32 e;
if (e=gfs_read(i,-1,&c,1)){
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);
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);
}

void file_input_nextitem(long i,long lastc){
if (i<0) return;
//this may require reversing a bit too!
long 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(long i,qbs *str,long extraspace,long tab,long newline){
if (new_error) return;
static long x,x2,x3,x4;
static int32 e;

//tcp/ip?
//note: spacing considerations such as 'extraspace' & 'tab' are ignored
if (i<0){
x=-(i+1);
if (x>=sfh_bufsize){error(52); return;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return;}
//valid tcp/ip connection
if (net_tcp[x].error) return;
//obselete message check
if ((newline==0)&&(str->len==0)) return;
//send formatted data
static unsigned char header_byte;
x2=str->len; if (extraspace) x2++;
if (x2<=4){header_byte=x2; x3=0;}
if ((x2>4)&&(x2<=255)){header_byte=5; x3=1;}
if ((x2>255)&&(x2<=65535)){header_byte=6; x3=2;}
if (x2>65535){header_byte=7; x3=4;}
if (!newline){
if (!tab){
header_byte|=128;
}
}
//build message
static unsigned char *cp;
cp=(unsigned char*)malloc(1+4+x2+16);//***fix
*cp=header_byte;
*((uint32*)(&cp[1]))=x2;
memcpy(&cp[1+x3],str->chr,str->len);
if (extraspace) cp[1+x3+x2-1]=32;

x4=SDLNet_TCP_Send(net_tcp[x].socket,cp,1+x3+x2);
free(cp);
//Returns: the number of bytes sent. If the number returned is less than len, then an error occured, such as the client disconnecting. 
if (x4!=(1+x3+x2)) net_tcp[x].error=1;

return;
}


if (gfs_fileno_valid(i)!=1){error(52); return;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[i];
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
}

gfs->column+=str->len;

//add extra spaces as needed
static long nspaces;
static short 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%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=0;
}
}

//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
unsigned short n_digits;
unsigned char n_digit[256];
int64 n_exp;//if 0, there is one digit in front of the decimal place
unsigned char n_neg;//if 1, the number is negative
unsigned char n_hex;//if 1, the digits are in hexidecimal and n_exp should be ignored
                    //if 2, the digits are in octal and n_exp should be ignored
                    //(consider revising variable name n_hex)

long n_roundincrement(){
static long 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;
long n_float(){
//return value: Bit 0=successful
//data
static unsigned char built[256];
static int64 value;
uint64 uvalue;
static long i,i2,i3;
static unsigned char *max;
max=(unsigned char*)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;
}

//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
#ifdef QB64_WINDOWS
i2=sprintf((char*)&built[i],"%I64i",n_exp);
#else
i2=sprintf((char*)&built[i],"%lli",n_exp);
#endif
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;
long n_int64(){
//return value: Bit 0=successful
//data
static int64 value;
uint64 uvalue;
static long i,i2;
static unsigned char *max; static unsigned char *neg_max;
static int64 v0=build_int64(0x80000000,0x00000000);
static int64 v1=build_int64(0x7FFFFFFF,0xFFFFFFFF);
max=(unsigned char*)range_int64_max[0]; neg_max=(unsigned char*)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;
}

//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;
long n_uint64(){
//return value: Bit 0=successful
//data
static int64 value;
uint64 uvalue;
static long i,i2;
static unsigned char *max;
static int64 v0=build_uint64(0xFFFFFFFF,0xFFFFFFFF);
max=(unsigned char*)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;
}

//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;
}

long n_inputnumberfromdata(unsigned char *data,unsigned long *data_offset,unsigned long 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 long i,i2;
static long step,c;
static long exponent_digits;
static unsigned char negate_exponent;
static int64 exponent_value;
static long 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
if (c==38){//&
(*data_offset)++; if (*data_offset>=data_size) goto gotnumber;
c=data[*data_offset];
if (c==44){(*data_offset)++; goto gotnumber;}
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 (n_digits==256) return 1;//Overflow
 n_digit[n_digits]=c;
 n_digits++;
 n_hex=1;
 goto nexthexchr;
 }
 return 3;//Syntax error
}
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 (n_digits==256) return 1;//Overflow
 n_digit[n_digits]=c;
 n_digits++;
 n_hex=2;
 goto nexthexchr2;
 }
 return 3;//Syntax error
}
return 3;//Syntax error
}//&

readnextchr:
if (c==44){(*data_offset)++; 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;}
return 3;//Syntax error
}

if (c==43){//+
if (step==0){step=1; goto nextchr;}
if (step==3){step=4; goto nextchr;}
return 3;//Syntax error
}

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) 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
if (n_digits==0) n_exp=-1;
step=2; goto nextchr;
}

if ((c==68)||(c==69)||(c==100)||(c==101)){//D,E,d,e
if (step>2) return 3;//Syntax error
step=3; goto nextchr;
}

return 3;//Syntax error
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
}





















long n_inputnumberfromfile(long fileno){
//return values:
//0=success
//1=overflow
//2=eof
//3=failed (no further errors)

//data
static long i,i2;
static long step,c;
static long exponent_digits;
static unsigned char negate_exponent;
static int64 exponent_value;
static long return_value;

//tcp/ip specific data
static qbs *str,*character;
long nextc,x,x2,x3,x4;
long i1;
long inspeechmarks;
static uint8 *ucbuf;
static uint32 ucbufsiz;
static int32 info;

//tcp/ip?
if (fileno<0){
x=-(fileno+1);
if (x>=sfh_bufsize){error(52); return 3;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return 3;}
//valid tcp/ip connection
revert_input_x=x;
if (net_tcp[x].eof==2) return 3;
if (net_tcp[x].error){net_tcp[x].eof=2; return 3;}
net_tcp_updatebuffer(x);
x2=net_tcp[x].buffer_size; if (!x2){net_tcp[x].eof=2; return 3;}
i=0;
ucbuf=NULL;
ucbufsiz=0;
append_message:
x3=net_tcp[x].buffer[i];
info=x3;
i++;
if (x3&120){
if (ucbufsiz) free(ucbuf);
//deststr->len=0;
net_tcp[x].eof=2;
return 3;
}//invalid bits set [01111000]
x3&=7;
/*
0 0 byte message
1 1 byte message
2 2 byte message
3 3 byte message
4 4 byte message
5 1 byte length descriptor + ? byte message
6 2 byte length descriptor + ? byte message
7 4 byte length descriptor + ? byte message
*/
if (x3<=4) x4=0;
if (x3==5) x4=1;
if (x3==6) x4=2;
if (x3==7) x4=4;
if (x4){
if (x2<(i+x4)){
if (ucbufsiz) free(ucbuf);
net_tcp[x].eof=2;
return 3;
}//not enough data (for length descriptor)
if (x4==1) x3=*((uint8*)(net_tcp[x].buffer+i));
if (x4==2) x3=*((uint16*)(net_tcp[x].buffer+i));
if (x4==4) x3=*((uint32*)(net_tcp[x].buffer+i));
i+=x4;
}
if (x2<(i+x3)){
if (ucbufsiz) free(ucbuf);
net_tcp[x].eof=2;
return 3;
}//not enough data (for message)
//add message to buffer ucbuf
if (!ucbufsiz) ucbuf=(uint8*)malloc(x3); else ucbuf=(uint8*)realloc(ucbuf,ucbufsiz+x3);
memcpy(ucbuf+ucbufsiz,net_tcp[x].buffer+i,x3);
ucbufsiz+=x3;
i+=x3;
if (info&128) goto append_message; //append another message?
//share the 'feed' buffer
tcp_feed_ucbuf=ucbuf;
tcp_feed_ucbufsiz=ucbufsiz;
tcp_feed_offset=0;
x4=i;
}

if (fileno>=0){
if (gfs_fileno_valid(fileno)!=1){error(52); return 3;}//Bad file name or number
fileno=gfs_fileno[fileno];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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
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;
}
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==100)||(c==101)){//D,E,d,e
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);
if (fileno<0){
	//adjust tcp[].buffer
		//revert_input_check (tag) add data that might need to be reverted to the buffer
		if (revert_input_bufsize==0){
		revert_input_buffer=(uint8*)malloc(x4);
		}else{
		revert_input_buffer=(uint8*)realloc(revert_input_buffer,revert_input_bufsize+x4);
		}
		memmove(revert_input_buffer+revert_input_bufsize,net_tcp[x].buffer,x4);
		revert_input_bufsize+=x4;
	if (x2-x4) memmove(net_tcp[x].buffer,net_tcp[x].buffer+x4,x2-x4);
	net_tcp[x].buffer_size-=x4;
	//free ucbuf
	free(ucbuf);
	//set eof to 0 if previous input has not failed (success)
	net_tcp[x].eof=0;
	}
return 0;//success

error:
file_input_nextitem(fileno,c);
	if (fileno<0){
	//free ucbuf
	free(ucbuf);
	//set eof
	net_tcp[x].eof=2;
	return 3;
	}
return return_value;
}

void sub_file_line_input_string(int32 fileno,qbs *deststr){
if (new_error) return;
static qbs *str,*character;
int32 c,nextc;
int32 inspeechmarks;

if (gfs_fileno_valid(fileno)!=1){error(52); return;}//Bad file name or number
fileno=gfs_fileno[fileno];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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);
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;
}
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(fileno); 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(fileno,c);
goto returnstr;
}
c=file_input_chr(fileno); if (c==-2) return;
goto nextstr;
//return string
returnstr:
qbs_set(deststr,str);
qbs_free(str);
qbs_free(character);
return;
}

void revert_input_check(){
static uint32 x;
x=revert_input_x;
if (x==-1) return;
revert_input_x=-1;//reset for next time

if (net_tcp[x].eof==2){
//assign correct eof value
net_tcp[x].eof=1;
//revert tcp[x] buffer
if (revert_input_bufsize){
	//expand buffer?
	if ((net_tcp[x].buffer_size+revert_input_bufsize)>net_tcp[x].buffer_space){
	if (net_tcp[x].buffer==NULL){
	net_tcp[x].buffer=(uint8*)malloc(net_tcp[x].buffer_size+revert_input_bufsize);
	}else{
	net_tcp[x].buffer=(uint8*)realloc(net_tcp[x].buffer,net_tcp[x].buffer_size+revert_input_bufsize);
	}
	net_tcp[x].buffer_space=net_tcp[x].buffer_size+revert_input_bufsize;
	}
if (net_tcp[x].buffer_size){
memmove(net_tcp[x].buffer+revert_input_bufsize,net_tcp[x].buffer,net_tcp[x].buffer_size);
}
memcpy(net_tcp[x].buffer,revert_input_buffer,revert_input_bufsize);
net_tcp[x].buffer_size+=revert_input_bufsize;
}//bufsize>0
}//eof==2

//free the buffer
if (revert_input_bufsize){revert_input_bufsize=0; free(revert_input_buffer);}

}

void sub_file_input_string(int32 fileno,qbs *deststr){
if (new_error) return;
static qbs *str,*character;
long c,nextc,x,x2,x3,x4;
long i,i1;
long inspeechmarks;
static uint8 *ucbuf;
static uint32 ucbufsiz;
static int32 info;

//tcp/ip?
//note: spacing considerations are ignored
if (fileno<0){
x=-(fileno+1);
if (x>=sfh_bufsize){error(52); return;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return;}
//valid tcp/ip connection
revert_input_x=x;
if (net_tcp[x].eof==2) return;
if (net_tcp[x].error){net_tcp[x].eof=2; return;}

net_tcp_updatebuffer(x);


x2=net_tcp[x].buffer_size;
if (!x2){deststr->len=0; net_tcp[x].eof=2; return;}

i=0;
ucbuf=NULL;
ucbufsiz=0;
append_message:
x3=net_tcp[x].buffer[i];
info=x3;
i++;
if (x3&120){
if (ucbufsiz) free(ucbuf);
deststr->len=0;
net_tcp[x].eof=2;
return;
}//invalid bits set [01111000]
x3&=7;
/*
0 0 byte message
1 1 byte message
2 2 byte message
3 3 byte message
4 4 byte message
5 1 byte length descriptor + ? byte message
6 2 byte length descriptor + ? byte message
7 4 byte length descriptor + ? byte message
*/
if (x3<=4) x4=0;
if (x3==5) x4=1;
if (x3==6) x4=2;
if (x3==7) x4=4;
if (x4){
if (x2<(i+x4)){
if (ucbufsiz) free(ucbuf);
deststr->len=0;
net_tcp[x].eof=2;
return;
}//not enough data (for length descriptor)
if (x4==1) x3=*((uint8*)(net_tcp[x].buffer+i));
if (x4==2) x3=*((uint16*)(net_tcp[x].buffer+i));
if (x4==4) x3=*((uint32*)(net_tcp[x].buffer+i));
i+=x4;
}
if (x2<(i+x3)){
if (ucbufsiz) free(ucbuf);
deststr->len=0;
net_tcp[x].eof=2;
return;
}//not enough data (for message)
//add message to buffer ucbuf
if (!ucbufsiz) ucbuf=(uint8*)malloc(x3); else ucbuf=(uint8*)realloc(ucbuf,ucbufsiz+x3);
memcpy(ucbuf+ucbufsiz,net_tcp[x].buffer+i,x3);
ucbufsiz+=x3;
i+=x3;
if (info&128) goto append_message; //append another message?
//adjust tcp[].buffer
	//revert_input_check (tag) add data that might need to be reverted to the buffer
	if (revert_input_bufsize==0){
	revert_input_buffer=(uint8*)malloc(i);
	}else{
	revert_input_buffer=(uint8*)realloc(revert_input_buffer,revert_input_bufsize+i);
	}
	memmove(revert_input_buffer+revert_input_bufsize,net_tcp[x].buffer,i);
	revert_input_bufsize+=i;
if (x2-i) memmove(net_tcp[x].buffer,net_tcp[x].buffer+i,x2-i);
net_tcp[x].buffer_size-=i;

//set return string
str=qbs_new(ucbufsiz,0);
memcpy(str->chr,ucbuf,ucbufsiz);
qbs_set(deststr,str);
free(ucbuf); //free ucbuf
net_tcp[x].eof=0;
return;
}

if (gfs_fileno_valid(fileno)!=1){error(52); return;}//Bad file name or number
fileno=gfs_fileno[fileno];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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 (new_error) return 0;
static long 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 (new_error) return 0;
static long 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(unsigned char *data,unsigned long *data_offset,unsigned long data_size,qbs *deststr){
if (new_error) return;
static qbs *str,*character;
static long 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(unsigned char *data,unsigned long *data_offset,unsigned long data_size,long typ){
if (new_error) return 0;
static long i;
static int64 maxval,minval;
static int64 value;
static unsigned long 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/...  
  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;
}




































long double func_file_input_float(long fileno,long typ){
if (new_error) return 0;
static long 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/...  
  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







//revise following!
void *byte_element(uint64 offset,long 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;
((unsigned long*)p)[2]=length;
return p;
}

void call_interrupt(long intno, void *inregs,void *outregs){
if (new_error) 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));
MessageBox(NULL,(char*)s->chr,"CALL INTERRUPT: size",MB_OK);
qbs_set(s,qbs_str( ((unsigned char*)(ele->offset))[0] ));
MessageBox(NULL,(char*)s->chr,"CALL INTERRUPT: value",MB_OK);
*/
/* 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(long intno, void *inregs,void *outregs){
if (new_error) 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 (new_error) return;
static byte_element_struct *ele;
static int32 x,x2;

//tcp/ip?
if (i<0){
x=-(i+1);
if (x>=sfh_bufsize){error(52); return;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return;}
net_tcp_updatebuffer(x);
ele=(byte_element_struct*)element;
if (net_tcp[x].buffer_size<ele->length){net_tcp[x].eof=1; return;}
net_tcp[x].eof=0;
memcpy((void*)(ele->offset),net_tcp[x].buffer,ele->length);
x2=net_tcp[x].buffer_size-ele->length;
if (x2) memmove(net_tcp[x].buffer,net_tcp[x].buffer+ele->length,x2);
net_tcp[x].buffer_size-=ele->length;
return;
}

if (gfs_fileno_valid(i)!=1){error(52); return;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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 (new_error) return;
static int32 x,x2,x3,x4;

//tcp/ip?
if (i<0){
x=-(i+1);
if (x>=sfh_bufsize){error(52); return;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return;}
if (str->fixed){//following method is only for variable length strings
sub_get(i,offset,byte_element((uint64)str->chr,str->len),passed);
}
net_tcp_updatebuffer(x);
static qbs* tqbs;
tqbs=qbs_new(net_tcp[x].buffer_size,1);
if (net_tcp[x].buffer_size){
memcpy(tqbs->chr,net_tcp[x].buffer,net_tcp[x].buffer_size);
}
net_tcp[x].buffer_size=0;
net_tcp[x].eof=0;
qbs_set(str,tqbs);
return;
}

if (gfs_fileno_valid(i)!=1){error(52); return;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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)
sub_get(gfs->fileno,offset,byte_element((uint64)str->chr,str->len),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=((unsigned short*)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 (new_error) return;
static byte_element_struct *ele;
static int32 x,x2;

//tcp/ip?
if (i<0){
x=-(i+1);
if (x>=sfh_bufsize){error(52); return;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return;}
//valid tcp/ip connection
if (net_tcp[x].error) return;
//send unformatted data
ele=(byte_element_struct*)element;
x2=SDLNet_TCP_Send(net_tcp[x].socket,(void*)ele->offset,ele->length);
//Returns: the number of bytes sent. If the number returned is less than len, then an error occured, such as the client disconnecting. 
if (x2!=ele->length) net_tcp[x].error=1;
return;
}

if (gfs_fileno_valid(i)!=1){error(52); return;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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 (new_error) return;
static byte_element_struct *ele;
static int32 x;
static uint8 *data;

//tcp/ip?
if (i<0){
x=-(i+1);
if (x>=sfh_bufsize){error(52); return;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return;}
//valid tcp/ip connection
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_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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;
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)&0xFFFFFFFFFFFF);
((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(int32 step1,float x1f,float y1f,int32 step2,float x2f,float y2f,void *element,uint32 mask,int32 passed){
if (new_error) 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 (step1){x1f=read_page->x+x1f; y1f=read_page->y+y1f;}
read_page->x=x1f; read_page->y=y1f;
if (step2){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;

//boundry checking (if no mask colour was passed)
if (passed){
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(int32 step,float x1f,float y1f,void *element,int32 clip,int32 option,uint32 mask,int32 passed){
//                                                                                                &1                    
if (new_error) 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,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;

//boundry 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 unacclocated 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 long x,y;
static long sx,sy,c,px,py;
static unsigned char *cp;
static long *lp;

sx=((unsigned short*)ele->offset)[0];
sy=((unsigned short*)ele->offset)[1];
cp=(unsigned char*)(ele->offset+4);
lp=(long*)cp;


static long sizeinbytes;
static long byte;
static long bitvalue;
static long bytesperrow;
static long row2offset;
static long row3offset;
static long row4offset;

static long 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
*/

}

void sub_date(qbs* date){
if (new_error) return;
return;//stub
}

qbs *func_date(){
//mm-dd-yyyy
//0123456789
static time_t qb64_tm_val;
static tm *qb64_tm;
//struct tm {
//        int tm_sec;     /* seconds after the minute - [0,59] */
//        int tm_min;     /* minutes after the hour - [0,59] */
//        int tm_hour;    /* hours since midnight - [0,23] */
//        int tm_mday;    /* day of the month - [1,31] */
//        int tm_mon;     /* months since January - [0,11] */
//        int tm_year;    /* years since 1900 */
//        int tm_wday;    /* days since Sunday - [0,6] */
//        int tm_yday;    /* days since January 1 - [0,365] */
//        int tm_isdst;   /* daylight savings time flag */
//        };
static long x,x2,i;
static qbs *str;
str=qbs_new(10,1);
str->chr[2]=45; str->chr[5]=45;//-
time(&qb64_tm_val); if (qb64_tm_val==-1){error(5); str->len=0; return str;}
qb64_tm=localtime(&qb64_tm_val); if (qb64_tm==NULL){error(5); str->len=0; return str;}
x=qb64_tm->tm_mon; x++; 
i=0; str->chr[i]=x/10+48; str->chr[i+1]=x%10+48;
x=qb64_tm->tm_mday;
i=3; str->chr[i]=x/10+48; str->chr[i+1]=x%10+48;
x=qb64_tm->tm_year; x+=1900;
i=6;
x2=x/1000; x=x-x2*1000; str->chr[i]=x2+48; i++;
x2=x/100; x=x-x2*100; str->chr[i]=x2+48; i++;
x2=x/10; x=x-x2*10; str->chr[i]=x2+48; i++;
str->chr[i]=x+48;
return str;
}








void sub_time(qbs* str){
if (new_error) return;
return;//stub
}

qbs *func_time(){
//23:59:59 (hh:mm:ss)
//01234567
static time_t qb64_tm_val;
static tm *qb64_tm;
//struct tm {
//        int tm_sec;     /* seconds after the minute - [0,59] */
//        int tm_min;     /* minutes after the hour - [0,59] */
//        int tm_hour;    /* hours since midnight - [0,23] */
//        int tm_mday;    /* day of the month - [1,31] */
//        int tm_mon;     /* months since January - [0,11] */
//        int tm_year;    /* years since 1900 */
//        int tm_wday;    /* days since Sunday - [0,6] */
//        int tm_yday;    /* days since January 1 - [0,365] */
//        int tm_isdst;   /* daylight savings time flag */
//        };
static long x,x2,i;
static qbs *str;
str=qbs_new(8,1);
str->chr[2]=58; str->chr[5]=58;//:
time(&qb64_tm_val); if (qb64_tm_val==-1){error(5); str->len=0; return str;}
qb64_tm=localtime(&qb64_tm_val); if (qb64_tm==NULL){error(5); str->len=0; return str;}
x=qb64_tm->tm_hour;
i=0; str->chr[i]=x/10+48; str->chr[i+1]=x%10+48;
x=qb64_tm->tm_min;
i=3; str->chr[i]=x/10+48; str->chr[i+1]=x%10+48;
x=qb64_tm->tm_sec;
i=6; str->chr[i]=x/10+48; str->chr[i+1]=x%10+48;
return str;
}


long func_csrlin(){
return write_page->cursor_y;
}
long func_pos(long ignore){
return write_page->cursor_x;
}



double func_log(double value){
if (value<=0){error(5);return 0;}
return log(value);
}

//FIX
double func_fix_double(double value){
if (value<0) return ceil(value); else return floor(value);
}
long double func_fix_float(long double value){
if (value<0) return ceil(value); else return floor(value);
}

//EXP
double func_exp_single(double value){
if (value<=88.02969){
return exp(value);
}
error(6); return 0;
}
long double func_exp_float(long double value){
if (value<=709.782712893){
return exp(value);
}
error(6); return 0;
}

long sleep_break=0;
void sub_sleep(long seconds,long passed){
if (new_error) return;
static uint64 start;
static uint64 milliseconds;
static uint64 stop;
static uint64 now;
sleep_break=0;

if (!passed) seconds=0;
if (seconds<=0){
while ((!sleep_break)&&(!stop_program)){
SDL_Delay(32);
}
return;
}

start=SDL_GetTicks();
milliseconds=seconds;
milliseconds*=1000;
stop=start+milliseconds;
if (stop>4294966295){error(6); return;}//cannot process wait time correctly!
wait:
if (sleep_break||stop_program) return;
now=SDL_GetTicks();
if (now<(stop-64)){//more than 64 milliseconds remain!
SDL_Delay(32);
goto wait;
}
if (now<stop) goto wait;
}


qbs *func_oct(int64 value,int32 neg_bits){

static int32 i,i2,i3,x,x2,neg;
static int64 value2;
static qbs *str;

str=qbs_new(22,1);

//negative?
if ((value>>63)&1) neg=1; else neg=0;

//calc. most significant bit
i2=0;
value2=value;
if (neg){
	for (i=1;i<=64;i++){
	if (!(value2&1)) i2=i;
	value2>>=1;
	}
	if (i2>=neg_bits){
	//doesn't fit in neg_bits, so expand to next 16/32/64 boundary
	i3=64;
	if (i2<32) i3=32;
	if (i2<16) i3=16;
	i2=i3;
	}else i2=neg_bits;
}else{
	for (i=1;i<=64;i++){
	if (value2&1) i2=i;
	value2>>=1;
	}
}

if (!i2){str->chr[0]=48; str->len=1; return str;}//"0"

//calc. number of characters required in i3
i3=i2/3; if ((i3*3)!=i2) i3++;

//build string
str->len=i3;
i3--;
x=0; x2=0;
for (i=1;i<=i2;i++){
if (value&1) x2|=(1<<x);
value>>=1;
x++;
if (x==3){str->chr[i3--]=x2+48; x2=0; x=0;}
}
if (x) str->chr[i3]=x2+48;

return str;

}

//note: QBASIC uses 8 characters for SINGLE/DOUBLE or generates "OVERFLOW" if this range is exceeded
//      QB64   uses 8 characters for SINGLE/DOUBLE/FLOAT but if this range is exceeded
//      it uses up to 16 characters before generating an "OVERFLOW" error
//performs overflow check before calling func_hex
qbs *func_oct_float(long double value){
static qbs *str;
static int64 ivalue;
static int64 uivalue;
//ref: uint64 0-18446744073709551615
//      int64 –9223372036854775808 to 9223372036854775807
if ((value>=9.223372036854776E18)||(value<=-9.223372036854776E18)){
//note: ideally, the following line would be used, however, qbr_longdouble_to_uint64 just does the same as qbr
//if ((value>=1.844674407370956E19)||(value<=-9.223372036854776E18)){
str=qbs_new(0,1); error(6);//Overflow
return str;
}
if (value>=0){
uivalue=qbr_longdouble_to_uint64(value);
ivalue=uivalue;
}else{
ivalue=qbr(value);
}
return func_oct(ivalue,32);
}

qbs *func_hex(int64 value,int32 neg_size){
//note: negative int64 values can be treated as positive uint64 values (and vise versa)

static int32 i,i2,i3,x,neg;
static int64 value2;
static qbs *str;

str=qbs_new(16,1);

value2=value;
i2=0; i3=0;
for (i=1;i<=16;i++){
if (value2&15) i2=i;//most significant digit of positive value
if ((value2&15)!=15){
i3=i;//most significant digit of negative value
if ((((value2&8)==0)&&(i!=16))) i3++;//for a negative number to fit into 4/8 characters, its top bit must be on
}
x=value2&15; if (x>9) x+=55; else x+=48; str->chr[16-i]=x;
value2>>=4;
}
if (!i2){str->chr[0]=48; str->len=1; return str;}//"0"

//negative?
if ((value>>63)&1) neg=1; else neg=0;

//change i2 from sig-digits to string-output-digits
if (neg){
if (i3<=neg_size){
i2=neg_size;//extend to minimum character size
}else{
//didn't fit in recommended size, expand to either 4, 8 or 16 appropriately
i2=16;
if (i3<=8) i2=8;
if (i3<=4) i2=4;
}
}//neg

//adjust string to the left to remove unnecessary characters
if (i2!=16){
memmove(str->chr,str->chr+(16-i2),i2);
str->len=i2;
}

return str;

}

//note: QBASIC uses 8 characters for SINGLE/DOUBLE or generates "OVERFLOW" if this range is exceeded
//      QB64   uses 8 characters for SINGLE/DOUBLE/FLOAT but if this range is exceeded
//      it uses up to 16 characters before generating an "OVERFLOW" error
//performs overflow check before calling func_hex
qbs *func_hex_float(long double value){
static qbs *str;
static int64 ivalue;
static int64 uivalue;
//ref: uint64 0-18446744073709551615
//      int64 –9223372036854775808 to 9223372036854775807
if ((value>=9.223372036854776E18)||(value<=-9.223372036854776E18)){
//note: ideally, the following line would be used, however, qbr_longdouble_to_uint64 just does the same as qbr
//if ((value>=1.844674407370956E19)||(value<=-9.223372036854776E18)){
str=qbs_new(0,1); error(6);//Overflow
return str;
}
if (value>=0){
uivalue=qbr_longdouble_to_uint64(value);
ivalue=uivalue;
}else{
ivalue=qbr(value);
}
return func_hex(ivalue,8);
}

long func_lbound(long *array,long index,long 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];
}

long func_ubound(long *array,long index,long 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;
}

static unsigned char port60h_event[256];
static long port60h_events=0;


long func_inp(long port){
static long 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(long port,long andexpression,long xorexpression,long passed){
if (new_error) 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 long 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 long tab_spc_cr_size; //=1;//default
qbs *func_tab(long pos){
//returns a string to advance to the horizontal position "pos" on either
//the current line or the next line.
static long w,div;
//calculate width in spaces & current position
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);
 }
}
static qbs *tqbs;
if ((pos<-32768)||(pos>32767)){
tqbs=qbs_new(0,1);
error(7); return tqbs;//Overflow
}
if (pos>w) pos%=w;
if (pos<1) pos=1;
static long size,spaces,cr;
size=0; spaces=0; cr=0;
if (write_page->cursor_x>pos){
cr=1;
size=tab_spc_cr_size;
spaces=pos/div; if (pos%div) spaces++;
spaces--;//don't put a space on the dest position
size+=spaces;
}else{
spaces=(pos-write_page->cursor_x)/div; if ((pos-write_page->cursor_x)%div) spaces++;
size=spaces;
}
//build custom string
tqbs=qbs_new(size,1);
if (cr){
tqbs->chr[0]=13; if (tab_spc_cr_size==2) tqbs->chr[1]=10;
memset(&tqbs->chr[tab_spc_cr_size],32,spaces);
}else{
memset(tqbs->chr,32,spaces);
}
return tqbs;
}

qbs *func_spc(long spaces){
static qbs *tqbs;
if ((spaces<-32768)||(spaces>32767)){tqbs=qbs_new(0,1); error(7); 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;
}

//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);
}

return tqbs;

}

float func_pmap(float val,long option){
static long x,y;
if (new_error) 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;
}



unsigned long func_screen(long y,long x,long returncol,long passed){

static unsigned char chr[65536];
static long x2,y2,x3,y3,i,i2,i3;
static unsigned long col,firstcol;
unsigned char *cp;

if (!passed) returncol=0;

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,long offset,long size){
if (new_error) return;
static 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 long 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(fixdir(tqbs),ios::binary|ios::out);
if (fh.is_open()==NULL){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,long offset,long passed){
if (new_error) return;
static unsigned char header[7];
static 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 long 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(fixdir(tqbs),ios::binary|ios::in);
if (fh.is_open()==NULL){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,ios::end);
file_size=fh.tellg();
fh.seekg(7,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 (gfs_fileno_valid(i)!=1){error(52); return 0;}//Bad file name or number
i=gfs_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
error(75); return 0;//assume[-9]: path/file access error
}
return size;
}

int32 func_eof(int32 i){
static long pos,lof;



//tcp/ip?
static long x;
if (i<0){
x=-(i+1);
if (x>=sfh_bufsize){error(52); return 0;}
if ((sfh[x].type!=2)&&(sfh[x].type!=3)){error(52); return 0;}
//valid tcp/ip connection
if (net_tcp[x].eof) return -1;
return 0;
}

if (gfs_fileno_valid(i)!=1){error(52); return 0;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[i];
if (gfs->type!=3){
if (gfs_eof_passed(i)==1) return -1;
}else{
if (gfs_eof_reached(i)==1) return -1;
}
return 0;

}

void sub_seek(int32 i,int64 pos){
if (new_error) return;
if (gfs_fileno_valid(i)!=1){error(52); return;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[i];
	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_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[i];
	if (gfs->type==1){//RANDOM
	return gfs_getpos(i)/gfs->record_length+1;
	}
	if (gfs->type==2){//BINARY
	return gfs_getpos(i);
	}
//APPEND/OUTPUT/INPUT
int64 pos;
pos=gfs_getpos(i);
if (!pos) return 1;
pos--;
pos/=128;
pos++;
return pos;
}

qbs *func_input(int32 n,int32 i,int32 passed){
if (new_error) 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_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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;
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)

}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){SDL_Delay(1); goto waitforinput;}
return str;
}
}

double func_sqr(double value){
if (value<0){error(5); return 0;}
return sqrt(value);
}

void sub_beep(){
sndsetup();
qb64_generatesound(783.99,0.2,0);
Sleep(250);
}

#define SND_CAPABILITY_SYNC 1
#define SND_CAPABILITY_VOL 2
#define SND_CAPABILITY_PAUSE 4
#define SND_CAPABILITY_LEN 8
#define SND_CAPABILITY_SETPOS 16

struct snd_struct{
unsigned long handle;
Mix_Chunk *sample;
unsigned char free;
unsigned char playing;
unsigned char paused;
float volume;
float volume_mult1;
float posx;
float posy;
float posz;
unsigned long start_time;
unsigned long paused_time;
unsigned char looping;
unsigned long limit;
double len;
unsigned char capability;
};
snd_struct snd[AUDIO_CHANNELS];
Mix_Music *stream=NULL;
long stream_free=0;
long stream_loaded=0;
long stream_playing=0;
unsigned long snd_free_handle=2;
long stream_type=0;
long stream_paused=0;
float stream_volume=1;
float stream_volume_mult1=1;
unsigned long stream_start_time=0;
unsigned long stream_paused_time=0;
double stream_setpos=0;
long stream_looping=0;
double stream_limit=0;
long stream_limited=0;
double stream_len=-1;
unsigned char stream_capability;

void snd_check(){
sndsetup();
static long i,i2,i3;
//check stream
if (stream_loaded){
if (stream_free){
//still playing?
if (stream_playing&&(!stream_looping)) if (!Mix_PlayingMusic()) stream_playing=0;
if (!stream_playing){
Mix_FreeMusic(stream);
stream=NULL;
}
}
}
//check samples
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle){
if (snd[i].free){
//still playing?
if (snd[i].playing&&(!snd[i].looping)) if (!Mix_Playing(i)) snd[i].playing=0;
if (!snd[i].playing){
snd[i].handle=0;
//free sample data if unrequired by any other sample
i3=1;
for (i2=0;i2<AUDIO_CHANNELS;i2++){
if (snd[i2].handle){
if (snd[i2].sample==snd[i].sample){i3=0; break;}
}}
if (i3) Mix_FreeChunk(snd[i].sample);
}//!stream_playing
}//free
}//handle
}//i
}//snd_check






unsigned long func__sndraw(unsigned char* data,unsigned long bytes){
sndsetup();
//***unavailable to QB64 user***
static long i,i2,i3;
//find available index
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==0){
memset(&snd[i],0,sizeof(snd_struct));//clear structure
Mix_Volume(i,128);//restore full volume
snd[i].volume=1;
snd[i].volume_mult1=1;
snd[i].sample=Mix_QuickLoad_RAW((Uint8*)data,bytes);
//length_in_sec=size_in_bytes/samples_per_sec/bytes_per_sample/channels
snd[i].len=((double)snd[i].sample->alen)/22050.0/2.0/2.0;
snd[i].handle=snd_free_handle; snd_free_handle++; if (snd_free_handle==0) snd_free_handle=2;
snd[i].capability=SND_CAPABILITY_SYNC;
return snd[i].handle;
}
}
return 0;//no free indexes
}







unsigned long func__sndopen(qbs* filename,qbs* requirements,long passed){
sndsetup();
if (new_error) return 0;

static qbs *s1=NULL;
if (!s1) s1=qbs_new(0,0);
static qbs *req=NULL;
if (!req) req=qbs_new(0,0);
static qbs *s3=NULL;
if (!s3) s3=qbs_new(0,0);

static unsigned char r[32];
static long i,i2,i3;
//check requirements
memset(r,0,32);
if (passed){
if (requirements->len){
i=1;
qbs_set(req,qbs_ucase(requirements));//convert tmp str to perm str
nextrequirement:
i2=func_instr(i,req,qbs_new_txt(","),1);
if (i2){
qbs_set(s1,func_mid(req,i,i2-i,1));
}else{
qbs_set(s1,func_mid(req,i,req->len-i+1,1));
}
qbs_set(s1,qbs_rtrim(qbs_ltrim(s1)));
if (qbs_equal(s1,qbs_new_txt("SYNC"))){r[0]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("VOL"))){r[1]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("PAUSE"))){r[2]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("LEN"))){r[3]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("SETPOS"))){r[4]++; goto valid;}
error(5); return 0;//invalid requirements
valid:
if (i2){i=i2+1; goto nextrequirement;}
for (i=0;i<32;i++) if (r[i]>1){error(5); return 0;}//cannot define requirements twice
}//->len
}//passed
qbs_set(s1,qbs_add(filename,qbs_new_txt_len("\0",1)));//s1=filename+CHR$(0)
if (r[0]){//"SYNC"
if (r[4]) return 0;//"SETPOS" unsupported
//find available index
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==0){
memset(&snd[i],0,sizeof(snd_struct));//clear structure
Mix_Volume(i,128);//restore full volume
snd[i].volume=1;
snd[i].volume_mult1=1;
snd[i].sample=Mix_LoadWAV(fixdir(s1));
if (snd[i].sample==NULL) return 0;
//length_in_sec=size_in_bytes/samples_per_sec/bytes_per_sample/channels
snd[i].len=((double)snd[i].sample->alen)/22050.0/2.0/2.0;
snd[i].handle=snd_free_handle; snd_free_handle++; if (snd_free_handle==0) snd_free_handle=2;
snd[i].capability=SND_CAPABILITY_SYNC+r[1]*SND_CAPABILITY_VOL+r[2]*SND_CAPABILITY_PAUSE+r[3]*SND_CAPABILITY_LEN;
return snd[i].handle;
}
}
return 0;//no free indexes
}else{//not "SYNC"
//stream
	//dealloc current stream?
	if (stream_loaded){
	if (!stream_free){error(5); return 0;}
  Mix_FreeMusic(stream);
	stream_loaded=0;
	}
stream=NULL;

//check requirements
stream_len=-1;
if (r[3]){//"LEN"
//detect length (if possible)
static Mix_Chunk *tmpsample;
tmpsample=Mix_LoadWAV(fixdir(s1));
if (tmpsample){
stream_len=((double)tmpsample->alen)/22050.0/2.0/2.0;
Mix_FreeChunk(tmpsample);
}else{
return 0;//capability unavailable
}
}

stream=Mix_LoadMUS(fixdir(s1));
if (!stream) return 0;
Mix_VolumeMusic(128);//restore full volume
stream_volume=1;
stream_volume_mult1=1;
stream_paused=0;
stream_setpos=0;
stream_looping=0;
stream_free=0;
//check requirements?
stream_type=0;
i=Mix_GetMusicType(stream);
if (i==6) stream_type=1;//mp3

if (r[4]){//"SETPOS"
if (stream_type!=1){
 Mix_FreeMusic(stream);
 return 0;//capability unavailable
}
}

stream_capability=r[1]*SND_CAPABILITY_VOL+r[2]*SND_CAPABILITY_PAUSE+r[3]*SND_CAPABILITY_LEN+r[4]*SND_CAPABILITY_SETPOS;
stream_loaded=1;
return 1;
}
}

double func__sndlen(unsigned long handle){
sndsetup();
static long i;
if (handle==0) return 0;//default response
if (handle==1){
if (stream_len==-1){error(5); return 0;}
if (!(stream_capability&SND_CAPABILITY_LEN)){error(5); return 0;}//unrequested capability
return stream_len;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return 0;}
if (!(snd[i].capability&SND_CAPABILITY_LEN)){error(5); return 0;}//unrequested capability
return snd[i].len;
}
}//i
error(5); return 0;//invalid handle
}

void sub__sndlimit(unsigned long handle,double limit){
sndsetup();
if (new_error) return;
//limit=0 means no limit
static long i;
if (handle==0) return;//default response
if (handle==1){
if (!stream_loaded){error(5); return;}
if (stream_free){error(5); return;}
stream_limit=limit;
return;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}
snd[i].limit=limit*1000;
return;
}
}//i
error(5); return;//invalid handle
}

void sub__sndstop(unsigned long handle){
sndsetup();
if (new_error) return;
static long i;
if (handle==0) return;//default response
if (handle==1){
if (!stream_loaded){error(5); return;}
if (stream_free){error(5); return;}
Mix_HaltMusic();//stop music
stream_paused=0;
stream_playing=0;
stream_setpos=0;
stream_looping=0;
return;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}
Mix_HaltChannel(i);
snd[i].playing=0;
snd[i].paused=0;
snd[i].looping=0;
return;
}
}//i
error(5); return;//invalid handle
}

long sndloop_call=0;

void sub__sndsetpos(unsigned long handle,double sec){
sndsetup();
if (new_error) return;
static unsigned long ms;
if (sec<0){error(5); return;}//must be >=0
if (handle==0) return;//default response
if (handle==1){
if (!stream_loaded){error(5); return;}
if (stream_free){error(5); return;}
if (stream_type!=1){error(5); return;}//only mp3 supports setpos
if (!(stream_capability&SND_CAPABILITY_SETPOS)){error(5); return;}//unrequested capability
if (stream_looping){error(5); return;}//setpos unavailable while looping
//still playing?
if (stream_playing&&(!stream_looping)) if (!Mix_PlayingMusic()) stream_playing=0;
if (stream_playing){//could also be paused
Sleep(100);//without this, music sometimes causes a GPF!
Mix_RewindMusic();
Sleep(100);//without this, music sometimes causes a GPF!
Mix_SetMusicPosition(sec);
ms=sec*1000.0+0.5;
stream_start_time=SDL_GetTicks()-ms;
if (stream_paused) stream_paused_time=SDL_GetTicks();
}else{
//music not playing, buffer the request
stream_setpos=sec;
}
return;
}
error(5); return;//samples do not support this function in sdl_mixer
}

double func__sndgetpos(unsigned long handle){
sndsetup();
static long i;
if (handle==0) return 0;//default response
if (handle==1){
if (!stream_loaded){error(5); return 0;}
if (stream_free){error(5); return 0;}
//still playing?
if (stream_playing&&(!stream_looping)) if (!Mix_PlayingMusic()) stream_playing=0;
if (!stream_playing) return 0;
if (stream_paused) return(((double)(stream_paused_time-stream_start_time))/1000.0);
return(((double)(SDL_GetTicks()-stream_start_time))/1000.0);
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return 0;}
//still playing?
if (snd[i].playing&&(!snd[i].looping)) if (!Mix_Playing(i)) snd[i].playing=0;
if (!snd[i].playing) return 0;
if (snd[i].paused) return(((double)(snd[i].paused_time-snd[i].start_time))/1000.0);
return(((double)(SDL_GetTicks()-snd[i].start_time))/1000.0);
}
}//i
error(5); return 0;//invalid handle
}

void sub__sndbal(unsigned long handle,double x,double y,double z,long passed){
sndsetup();
if (new_error) return;
//any optional parameter not passed is assumed to be 0 (which is what NULL equates to)
static long i,v,i2,i3;
static double d,d2,d3;
if (handle==0) return;//default response
if (handle==1){
if (!stream_loaded){error(5); return;}
if (stream_free){error(5); return;}
if (!(stream_capability&SND_CAPABILITY_VOL)){error(5); return;}//unrequested capability
//unsupported, but emulate distance by using a volume change
d=sqrt(x*x+y*y+z*z);

if (d<1) d=0;
if (d>1000) d=1000;
d=1000-d;
d=d/1000.0;
stream_volume_mult1=d;

v=stream_volume*129; if (v==129) v=128;
Mix_VolumeMusic(v*stream_volume_mult1);
return;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}
if (!(snd[i].capability&SND_CAPABILITY_VOL)){error(5); return;}//unrequested capability
if ((x==0)&&(z==0)) z=1;
snd[i].posx=x; snd[i].posy=y; snd[i].posz=z;
d=atan2(x,z)*57.295779513;
if (d<0) d=360+d;
i2=d+0.5; if (i2==360) i2=0;//angle
d2=sqrt(x*x+y*y+z*z);//distance
if (d2<1) d2=1;
if (d2>999.9) d2=999.9;
i3=d2/3.90625;
Mix_SetPosition(i,i2,i3);
return;
}
}//i
error(5); return;//invalid handle
}

void sub__sndplay(unsigned long handle){
sndsetup();
if (new_error) return;
static long i;
static unsigned long ms;
if (handle==0) return;//default response
	//stream?
	if (handle==1){
	if (!stream_loaded){error(5); return;}
	if (stream_free){error(5); return;}
	if (stream_paused){	
  Mix_ResumeMusic();	
  stream_start_time=stream_start_time+(SDL_GetTicks()-stream_paused_time);
  stream_paused=0;
	return;
	}
  stream_looping=0;
  Mix_HaltMusic();//in case music is already playing
	if (sndloop_call){
	if (stream_limit){error(5); return;}//limit invalid
  if (Mix_PlayMusic(stream,-1)==-1) return;
	stream_limited=0;
	stream_looping=1;
  }else{
	if (Mix_PlayMusic(stream,0)==-1) return;
	if (stream_limit) stream_limited=1; else stream_limited=0;
  }
  stream_start_time=SDL_GetTicks();
	if (stream_setpos){
	Sleep(100);	
	Mix_SetMusicPosition(stream_setpos); 
  ms=stream_setpos*1000.0+0.5;
	stream_start_time=SDL_GetTicks()-ms;  
  stream_setpos=0; 
  }
	stream_playing=1;
  return;
	}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}
if (snd[i].paused){
Mix_Resume(i);
//augment start_time
snd[i].start_time=snd[i].start_time+(SDL_GetTicks()-snd[i].paused_time);
snd[i].paused=0;
return;
}
snd[i].looping=0;
Mix_HaltChannel(i);//in case sound is already playing
//remind sdl_mixer of sound's position?
if (snd[i].posx||snd[i].posy||snd[i].posz) sub__sndbal(handle,snd[i].posx,snd[i].posy,snd[i].posz,-1);
if (sndloop_call){
	if (snd[i].limit){
	error(5); return;//limit invalid
	}else{
	if(Mix_PlayChannel(i,snd[i].sample,-1)==-1) return;	
	}
snd[i].looping=1;
}else{
	if (snd[i].limit){
	if(Mix_PlayChannelTimed(i,snd[i].sample,0,snd[i].limit)==-1) return;
	}else{
	if(Mix_PlayChannel(i,snd[i].sample,0)==-1) return;
	}
}
snd[i].start_time=SDL_GetTicks();
snd[i].playing=1;
return;
}
}//i
error(5); return;//invalid handle
}

void sub__sndloop(unsigned long handle){
sndsetup();
if (new_error) return;
static long i;
if (handle==0) return;//default response
if (handle==1){
if (!stream_loaded){error(5); return;}
if (stream_free){error(5); return;}
sub__sndstop(handle);
stream_setpos=0;
sndloop_call=1; sub__sndplay(handle); sndloop_call=0;
return;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}
sub__sndstop(handle);
sndloop_call=1; sub__sndplay(handle); sndloop_call=0;
return;
}
}//i
error(5); return;//invalid handle
}

unsigned long func__sndcopy(unsigned long handle){
sndsetup();
if (new_error) return 0;
static long i,i2;
if (handle==0) return 0;//default response
if (handle==1){error(5); return 0;}//cannot copy a stream
//find handle
for (i2=0;i2<AUDIO_CHANNELS;i2++){
if (snd[i2].handle==handle){
	if (snd[i2].free){error(5); return 0;}
  if (!(snd[i2].capability&SND_CAPABILITY_SYNC)){error(5); return 0;}//unrequested capability
  //find free index
  for (i=0;i<AUDIO_CHANNELS;i++){
	if (snd[i].handle==0){
	memset(&snd[i],0,sizeof(snd_struct));//clear structure
  Mix_Volume(i,128);//restore full volume
	snd[i].volume=1;
  snd[i].volume_mult1=1;
  snd[i].sample=snd[i2].sample;
	//...
  snd[i].handle=snd_free_handle; snd_free_handle++; if (snd_free_handle==0) snd_free_handle=2;
	return snd[i].handle;
	}
	}
	return 0;//no free indexes
}
}//i2
error(5); return 0;//invalid handle
}

long sub__sndvol_error;
void sub__sndvol(unsigned long handle,float volume){
sndsetup();
if (new_error) return;
static long i,v;
sub__sndvol_error=1;
if ((volume<0)||(volume>1)){error(5); return;}
v=volume*129; if (v==129) v=128;
if (handle==0) {sub__sndvol_error=0;return;}//default response
if (handle==1){
if (!stream_loaded){error(5); return;}//invalid handle
if (stream_free){error(5); return;}
if (!(stream_capability&SND_CAPABILITY_VOL)){error(5); return;}//unrequested capability
Mix_VolumeMusic((float)v*stream_volume_mult1);
stream_volume=volume;
sub__sndvol_error=0;
return;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}
if (!(snd[i].capability&SND_CAPABILITY_VOL)){error(5); return;}//unrequested capability
Mix_Volume(i,(float)v*snd[i].volume_mult1);
snd[i].volume=volume;
sub__sndvol_error=0;
return;
}
}//i
error(5); return;//invalid handle
}

void sub__sndpause(unsigned long handle){
sndsetup();
if (new_error) return;
static long i;
if (handle==0) return;//default response
if (handle==1){
if (!stream_loaded){error(5); return;}
if (stream_free){error(5); return;}
if (!(stream_capability&SND_CAPABILITY_PAUSE)){error(5); return;}//unrequested capability
if (stream_paused) return;
//still playing?
if (stream_playing&&(!stream_looping)) if (!Mix_PlayingMusic()) stream_playing=0;
if (!stream_playing) return;
Mix_PauseMusic();
stream_paused_time=SDL_GetTicks();
stream_paused=1;
return;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}
if (!(snd[i].capability&SND_CAPABILITY_PAUSE)){error(5); return;}//unrequested capability
if (snd[i].paused) return;
//still playing?
if (snd[i].playing&&(!snd[i].looping)) if (!Mix_Playing(i)) snd[i].playing=0;
if (!snd[i].playing) return;
Mix_Pause(i);
snd[i].paused_time=SDL_GetTicks();
snd[i].paused=1;
return;
}
}//i
error(5); return;//invalid handle
}

long func__sndpaused(unsigned long handle){
sndsetup();
static long i;
if (handle==0) return 0;//default response
if (handle==1){
if (!stream_loaded){error(5); return 0;}
if (stream_free){error(5); return 0;}
if (!(stream_capability&SND_CAPABILITY_PAUSE)){error(5); return 0;}//unrequested capability
if (stream_paused) return -1;
return 0;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return 0;}
if (!(snd[i].capability&SND_CAPABILITY_PAUSE)){error(5); return 0;}//unrequested capability
if (snd[i].paused) return -1;
return 0;
}
}//i
error(5); return 0;//invalid handle
}

long func__sndplaying(unsigned long handle){
sndsetup();
static long i;
if (handle==0) return 0;//default response
if (handle==1){
if (!stream_loaded){error(5); return 0;}
if (stream_free){error(5); return 0;}
//still playing?
if (stream_playing&&(!stream_looping)) if (!Mix_PlayingMusic()) stream_playing=0;
if (stream_paused) return 0;
if (stream_playing) return -1;
return 0;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return 0;}
//still playing?
if (snd[i].playing&&(!snd[i].looping)) if (!Mix_Playing(i)) snd[i].playing=0;
if (snd[i].paused) return 0;
if (snd[i].playing) return -1;
return 0;
}
}//i
error(5); return 0;//invalid handle
}

void sub__sndclose(unsigned long handle){
sndsetup();
if (new_error) return;
static long i;
if (handle==0) return;//default response
if (handle==1){
if (!stream_loaded){error(5); return;}//invalid handle
if (stream_free){error(5); return;}//freed already
stream_free=1;
snd_check();
return;
}
//find handle
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==handle){
if (snd[i].free){error(5); return;}//freed already
snd[i].free=1;
snd_check();
return;
}
}//i
error(5); return;//invalid handle
}

void sndcloseall(){
sndqueue_played=0; sndqueue_first=sndqueue_next;//invalidate items on the current PLAY sound queue
if (stream_loaded){
if (!stream_free){
sub__sndstop(1);
sub__sndclose(1);
}
}
static long i;
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle){
if (!snd[i].free){
sub__sndstop(snd[i].handle);
sub__sndclose(snd[i].handle);
}
}
}//i
}

//"macros"
void sub__sndplayfile(qbs *filename,long sync,double volume,long passed){
sndsetup();
if (new_error) return;
static unsigned long handle;
static long setvolume;
static qbs *syncstr=NULL;
if (!syncstr) syncstr=qbs_new(0,0);
setvolume=0;
if (passed&2){
if ((volume<0)||(volume>1)){error(5); return;}
if (volume!=1) setvolume=1;
}
if ((!setvolume)&&(!sync)) syncstr->len=0;
if ((setvolume)&&(!sync)) qbs_set(syncstr,qbs_new_txt("VOL"));
if ((!setvolume)&&(sync)) qbs_set(syncstr,qbs_new_txt("SYNC"));
if ((setvolume)&&(sync)) qbs_set(syncstr,qbs_new_txt("SYNC,VOL"));
if (syncstr->len){
handle=func__sndopen(filename,syncstr,1);
}else{
handle=func__sndopen(filename,NULL,0);
}
if (handle==0) return;
if (setvolume) sub__sndvol(handle,volume);
sub__sndplay(handle);
sub__sndclose(handle);
}

void sub__sndplaycopy(unsigned long handle,double volume,long passed){
sndsetup();
if (new_error) return;
unsigned long handle2;
handle2=func__sndcopy(handle);
if (!handle2) return;//an error has already happened
if (passed){
sub__sndvol(handle2,volume);
if (sub__sndvol_error){
sub__sndclose(handle2);
return;
}
}
sub__sndplay(handle2);
sub__sndclose(handle2);
}


qbs *func_command_str=NULL;
qbs *func_command(){
static qbs *tqbs;
tqbs=qbs_new(func_command_str->len,1);
memcpy(tqbs->chr,func_command_str->chr,func_command_str->len);
return tqbs;
}

long shell_call_in_progress=0;
static long cmd_available=-1;

void sub_shell(qbs *str,long passed){
if (new_error) return;
 //exit full screen mode if necessary
 static long full_screen_mode;
 full_screen_mode=full_screen;
 if (full_screen_mode){
 full_screen_set=0;
 do{
 Sleep(0);
 }while(full_screen);
 }//full_screen_mode
static qbs *strz=NULL;
static long i;

if (!strz) strz=qbs_new(0,0);
if (passed) if (str->len==0) passed=0;//"" means launch a CONSOLE
if (passed){

#ifdef QB64_WINDOWS

static STARTUPINFO si;
static PROCESS_INFORMATION pi;

//cmd.exe available?
if (cmd_available==-1){
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
qbs_set(strz,qbs_new_txt("cmd.exe /c ver"));
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        CREATE_NO_WINDOW, // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
WaitForSingleObject( pi.hProcess, INFINITE ); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread );
cmd_available=1;
}else{
cmd_available=0;
}
}//cmd_available==-1

if (cmd_available==1){

qbs_set(strz,qbs_add(qbs_new_txt("cmd.exe /c "),str));
qbs_set(strz,qbs_add(strz,qbs_new_txt_len("\0",1)));
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        DETACHED_PROCESS, // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
shell_call_in_progress=1;
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles. 
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
shell_call_in_progress=0;
goto shell_complete;
}
goto shell_complete;//failed

}else{

qbs_set(strz,qbs_add(qbs_new_txt("command.com /c "),str));
qbs_set(strz,qbs_add(strz,qbs_new_txt_len("\0",1)));
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        CREATE_NEW_CONSOLE, 		// No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
shell_call_in_progress=1;
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles. 
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
shell_call_in_progress=0;
goto shell_complete;
}
goto shell_complete;//failed

}//cmd_available

#else

qbs_set(strz,qbs_add(str,qbs_new_txt_len("\0",1)));
shell_call_in_progress=1;
system((char*)strz->chr);
shell_call_in_progress=0;

#endif

}else{

#ifdef QB64_WINDOWS
//SHELL (with no parameters)
AllocConsole();
qbs_set(strz,qbs_new_txt_len("COMMAND\0",8));
shell_call_in_progress=1;
system((char*)strz->chr);
shell_call_in_progress=0;
FreeConsole();
goto shell_complete;
#endif

}

shell_complete:
 //reenter full screen mode if necessary
 if (full_screen_mode){
 full_screen_set=full_screen_mode;
 do{
 Sleep(0);
 }while(!full_screen);
 }//full_screen_mode
}

void sub_shell2(qbs *str){
if (new_error) return;
static qbs *strz=NULL;
static long i;
if (!strz) strz=qbs_new(0,0);
if (!str->len){error(5); return;}//cannot launch a hidden console

#ifdef QB64_WINDOWS

static STARTUPINFO si;
static PROCESS_INFORMATION pi;

//cmd.exe available?
if (cmd_available==-1){
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
qbs_set(strz,qbs_new_txt("cmd.exe /c ver"));
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        CREATE_NO_WINDOW, // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
WaitForSingleObject( pi.hProcess, INFINITE ); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread );
cmd_available=1;
}else{
cmd_available=0;
}
}//cmd_available==-1

if (cmd_available==1){

qbs_set(strz,qbs_add(qbs_new_txt("cmd.exe /c "),str));
qbs_set(strz,qbs_add(strz,qbs_new_txt_len("\0",1)));
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        CREATE_NO_WINDOW, // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
shell_call_in_progress=1;
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles. 
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
shell_call_in_progress=0;
goto shell_complete;
}
goto shell_complete;//failed

}else{

qbs_set(strz,qbs_add(qbs_new_txt("command.com /c "),str));
qbs_set(strz,qbs_add(strz,qbs_new_txt_len("\0",1)));
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        CREATE_NEW_CONSOLE, //note: cannot hide new console, but can preserve existing one
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
shell_call_in_progress=1;
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles. 
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
shell_call_in_progress=0;
goto shell_complete;
}
goto shell_complete;//failed

}//cmd_available

#else

qbs_set(strz,qbs_add(str,qbs_new_txt_len("\0",1)));
shell_call_in_progress=1;
system((char*)strz->chr);
shell_call_in_progress=0;
return;

#endif

shell_complete:;
}


//shell3 launches 'str' but does not wait for it to complete
void sub_shell3(qbs *str){
if (new_error) return;
static qbs *strz=NULL;
static long i;
if (!strz) strz=qbs_new(0,0);
if (!str->len){error(5); return;}//console unsupported

#ifdef QB64_WINDOWS

static STARTUPINFO si;
static PROCESS_INFORMATION pi;

//cmd.exe available?
if (cmd_available==-1){
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
qbs_set(strz,qbs_new_txt("cmd.exe /c ver"));
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        CREATE_NO_WINDOW, // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
WaitForSingleObject( pi.hProcess, INFINITE ); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread );
cmd_available=1;
}else{
cmd_available=0;
}
}//cmd_available==-1

if (cmd_available==1){

qbs_set(strz,qbs_add(qbs_new_txt("cmd.exe /c "),str));
qbs_set(strz,qbs_add(strz,qbs_new_txt_len("\0",1)));
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        DETACHED_PROCESS, // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
//ref: The created process remains in the system until all threads within the process have terminated and all handles to the process and any of its threads have been closed through calls to CloseHandle. The handles for both the process and the main thread must be closed through calls to CloseHandle. If these handles are not needed, it is best to close them immediately after the process is created. 
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
goto shell_complete;
}
goto shell_complete;//failed

}else{

qbs_set(strz,qbs_add(qbs_new_txt("command.com /c "),str));
qbs_set(strz,qbs_add(strz,qbs_new_txt_len("\0",1)));
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) );
if(CreateProcess(
        NULL,           // No module name (use command line)
        (char*)&strz->chr[0], // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        CREATE_NEW_CONSOLE, //note: cannot hide new console, but can preserve existing one
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
){
//ref: The created process remains in the system until all threads within the process have terminated and all handles to the process and any of its threads have been closed through calls to CloseHandle. The handles for both the process and the main thread must be closed through calls to CloseHandle. If these handles are not needed, it is best to close them immediately after the process is created. 
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
goto shell_complete;
}
goto shell_complete;//failed

}//cmd_available

#else

qbs_set(strz,qbs_add(str,qbs_new_txt_len("\0",1)));
shell_call_in_progress=1;
system((char*)strz->chr);//*revise: this doesn't launch a detached process
shell_call_in_progress=0;
return;

#endif

shell_complete:;
}


void sub_kill(qbs *str){
if (new_error) return;
static qbs *strz=NULL;
if (!strz) strz=qbs_new(0,0);
qbs_set(strz,qbs_add(str,qbs_new_txt_len("\0",1)));
static long i;
if (remove(fixdir(strz))){
i=errno;
if (i==ENOENT){error(53); return;}//file not found
if (i==EACCES){error(75); return;}//path/file access error
error(64);//bad file name (assumed)
}
}

void sub_name(qbs *oldname,qbs *newname){
if (new_error) return;
static qbs *strz=NULL;
if (!strz) strz=qbs_new(0,0);
static qbs *strz2=NULL;
if (!strz2) strz2=qbs_new(0,0);
static long i;
qbs_set(strz,qbs_add(oldname,qbs_new_txt_len("\0",1)));
qbs_set(strz2,qbs_add(newname,qbs_new_txt_len("\0",1)));
if (rename(fixdir(strz),fixdir(strz2))){
i=errno;
if (i==ENOENT){error(53); return;}//file not found
if (i==EINVAL){error(64); return;}//bad file name
if (i==EACCES){error(75); return;}//path/file access error
error(5);//Illegal function call (assumed)
}
}

void sub_chdir(qbs *str){
if (new_error) return;
static qbs *strz=NULL;
if (!strz) strz=qbs_new(0,0);
qbs_set(strz,qbs_add(str,qbs_new_txt_len("\0",1)));
if (chdir(fixdir(strz))==-1){
//assume errno==ENOENT
error(76);//path not found
}
}

void sub_mkdir(qbs *str){
if (new_error) return;
static qbs *strz=NULL;
if (!strz) strz=qbs_new(0,0);
qbs_set(strz,qbs_add(str,qbs_new_txt_len("\0",1)));
#ifdef QB64_LINUX
 if (mkdir(fixdir(strz),0770)==-1){
#else
 if (mkdir(fixdir(strz))==-1){
#endif
if (errno==EEXIST){error(75); return;}//path/file access error
//assume errno==ENOENT
error(76);//path not found
}

}

void sub_rmdir(qbs *str){
if (new_error) return;
static qbs *strz=NULL;
if (!strz) strz=qbs_new(0,0);
qbs_set(strz,qbs_add(str,qbs_new_txt_len("\0",1)));
if (rmdir(fixdir(strz))==-1){
if (errno==ENOTEMPTY){error(75); return;}//path/file access error
//assume errno==ENOENT
error(76);//path not found
}
}

//Use FindFirstFile to creates a directory listing for QB FILES command

double pow2(double x,double y){
if (x<0){
if (y!=floor(y)){error(5); return 0;}
}
return pow(x,y);
}

int32 func_freefile(){
return gfs_fileno_freefile();
}

void sub__mousehide(){
mouse_hideshow_called=1;
static int x,y;
SDL_GetMouseState(&x,&y);
SDL_WarpMouse(x,y);
SDL_ShowCursor(0);
SDL_WarpMouse(x,y);
}
void sub__mouseshow(){
mouse_hideshow_called=1;
static int x,y;
SDL_GetMouseState(&x,&y);
SDL_WarpMouse(x,y);
SDL_ShowCursor(1);
SDL_WarpMouse(x,y);
}

float func__mousex(){
static long x,x2;
static float f;
x=mouse_messages[current_mouse_message].x;
x-=x_offset;
if (x<0) x=0;
x/=x_scale;
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];
x=x/x2+1;
f=f/(float)x2+0.5f;
 //if cint(f)<>x then adjust f so it does
 x2=qbr_float_to_long(f);
 if (x2>x) f-=0.001f;
 if (x2<x) f+=0.001f;
return f;
}
return x;
}

float func__mousey(){
static long y,y2;
static float f;
y=mouse_messages[current_mouse_message].y;
y-=y_offset;
if (y<0) y=0;
y/=y_scale;
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];
y=y/y2+1;
f=f/(float)y2+0.5f;
 //if cint(f)<>y then adjust f so it does
 y2=qbr_float_to_long(f);
 if (y2>y) f-=0.001f;
 if (y2<y) f+=0.001f;
return f;
}
return y;
}

long func__mouseinput(){
if (current_mouse_message==last_mouse_message) return 0;
current_mouse_message++; if (current_mouse_message>1023) current_mouse_message=0;
return -1;
}

long func__mousebutton(long i){
if (i<1){error(5); return 0;}
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;
}
if (mouse_messages[current_mouse_message].buttons&(1<<(i-1))) return -1;
return 0;
}

long func__mousewheel(){
static uint32 x;
x=mouse_messages[current_mouse_message].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(long 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 long 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(long i){

if (i==0x33){

if (cpu.ax==0){
cpu.ax=0xFFFF;//mouse installed
cpu.bx=2;
return;
}

if (cpu.ax==1){sub__mouseshow(); return;}
if (cpu.ax==2){sub__mousehide(); return;}
if (cpu.ax==3){
//return the current mouse status
//buttons
cpu.bx=mouse_messages[last_mouse_message].buttons&1;
if (mouse_messages[last_mouse_message].buttons&4) cpu.bx+=2;
//x,y offsets
static long current_mouse_message_backup;
current_mouse_message_backup=current_mouse_message;
current_mouse_message=last_mouse_message;
static float mx,my;
mx=func__mousex(); my=func__mousey();
current_mouse_message=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
//obselete 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;
}

//MessageBox(NULL,"Unknown MOUSE Sub-function","Call Interrupt Error",MB_OK);
//exit(cpu.ax);

return;
}

}


//unsigned char *soundwave(double frequency,double length,double volume,double fadein,double fadeout,unsigned char *data);
//unsigned char *soundwavesilence(double length,unsigned char *data);


/*
Formats:
A[#|+|-][0-64]
   0-64 is like temp. Lnumber, 0 is whatever the current default is




*/
void sub_play(qbs *str){
sndsetup();
static unsigned char *b,*wave,*wave2,*wave3;
static double d;
static long i,bytes_left,a,x,x2,x3,x4,x5,wave_bytes,wave_base;
static long o=4;
static double t=120;//quarter notes per minute (120/60=2 per second)
static double l=4;
static double pause=1.0/8.0;//ML 0.0, MN 1.0/8.0, MS 1.0/4.0
static double length,length2;//derived from l and t
static double frequency;
static double mb=0;
static double v=50;

static long n;//the semitone-intervaled note to be played
static long n_changed;//+,#,- applied?
static int64 number;
static long number_entered;
static long followup;//1=play note
static long playit;
static unsigned long handle=NULL;
static long fullstops=0;
b=str->chr;
bytes_left=str->len;
wave=NULL;
wave_bytes=0;
n_changed=0;
n=0;
number_entered=0;
number=0;
followup=0;
length=1.0/(t/60.0)*(4.0/l);
playit=0;
wave_base=0;//point at which new sounds will be inserted

next_byte:
if ((bytes_left--)||followup){

if (bytes_left<0){i=32; goto follow_up;}

i=*b++;
if (i==32) goto next_byte;
if (i>=97&&i<=122) a=i-32; else a=i;

if (i==61){//= (+VARPTR$)
if (fullstops){error(5); return;}
if (number_entered){error(5); return;}
number_entered=2;
//VARPTR$ reference
/*
'BYTE=1
'INTEGER=2
'STRING=3 SUB-STRINGS must use "X"+VARPTR$(string$)
'SINGLE=4
'INT64=5
'FLOAT=6
'DOUBLE=8
'LONG=20
'BIT=64+n
*/
if (bytes_left<3){error(5); return;}
i=*b++; bytes_left--;//read type byte
x=*(unsigned short*)b; b+=2; bytes_left-=2;//read offset within DBLOCK
//note: allowable _BIT type variables in VARPTR$ are all at a byte offset and are all
//      padded until the next byte
d=0;
switch(i){
case 1:
d=*(char*)(dblock+x);
break;
case (1+128):
d=*(unsigned char*)(dblock+x);
break;
case 2:
d=*(short*)(dblock+x);
break;
case (2+128):
d=*(unsigned short*)(dblock+x);
break;
case 4:
d=*(float*)(dblock+x);
break;
case 5:
d=*(int64*)(dblock+x);
break;
case (5+128):
d=*(int64*)(dblock+x); //unsigned conversion is unsupported!
break;
case 6:
d=*(long double*)(dblock+x);
break;
case 8:
d=*(double*)(dblock+x);
break;
case 20:
d=*(long*)(dblock+x);
break;
case (20+128):
d=*(unsigned long*)(dblock+x);
break;
default:
//bit type?
if ((i&64)==0){error(5); return;}
x2=i&63;
if (x2>56){error(5); return;}//valid number of bits?
//create a mask
static int64 i64num,mask,i64x;
mask=(((int64)1)<<x2)-1;
i64num=(*(int64*)(dblock+x))&mask;
//signed?
if (i&128){
mask=((int64)1)<<(x2-1);
if (i64num&mask){//top bit on?
mask=-1; mask<<=x2; i64num+=mask;
}
}//signed
d=i64num;
}
if (d>2147483647.0||d<-2147483648.0){error(5); return;}//out of range value!
number=qbr_double_to_long(d);
goto next_byte;
}

//read in a number
if ((i>=48)&&(i<=57)){
if (fullstops||(number_entered==2)){error(5); return;}
if (!number_entered){number=0; number_entered=1;}
number=number*10+i-48;
goto next_byte;
}

//read fullstops
if (i==46){
if (followup!=7&&followup!=1&&followup!=4){error(5); return;}
fullstops++;
goto next_byte;
}

follow_up:

if (followup==8){//V...
if (!number_entered){error(5); return;}
number_entered=0;
if (number>100){error(5); return;}
v=number;
followup=0; if (bytes_left<0) goto done;
}//8

if (followup==7){//P...
if (number_entered){
number_entered=0;
if (number<1||number>64){error(5); return;}
length2=1.0/(t/60.0)*(4.0/((double)number));
}else{
length2=length;
}
d=length2; for (x=1;x<=fullstops;x++){d/=2.0; length2=length2+d;} fullstops=0;

soundwave_bytes=wavesize(length2);
if (!wave){
 //create buffer
 wave=(unsigned char*)calloc(soundwave_bytes,1); wave_bytes=soundwave_bytes;
 wave_base=0;
}else{
 //increase buffer?
 if ((wave_base+soundwave_bytes)>wave_bytes){
 wave=(unsigned char*)realloc(wave,wave_base+soundwave_bytes);
 memset(wave+wave_base,0,wave_base+soundwave_bytes-wave_bytes);
 wave_bytes=wave_base+soundwave_bytes;
 }
}
if (i!=44){
wave_base+=soundwave_bytes;
}

playit=1;
followup=0;
if (i==44) goto next_byte;
if (bytes_left<0) goto done;
}//7

if (followup==6){//T...
if (!number_entered){error(5); return;}
number_entered=0;
if (number<32||number>255){error(5); return;}
t=number;
length=1.0/(t/60.0)*(4.0/l);
followup=0; if (bytes_left<0) goto done;
}//6

if (followup==5){//M...
if (number_entered){error(5); return;}
switch(a){
case 76://L
pause=0;
break;
case 78://N
pause=1.0/8.0;
break;
case 83://S
pause=1.0/4.0;
break;

case 66://MB
if (!mb){
mb=1;
if (playit){
 handle=func__sndraw(wave,wave_bytes);
 if (handle){
  sndqueue[sndqueue_next]=handle;
  sndqueue_next++; if (sndqueue_next>sndqueue_lastindex) sndqueue_next=0;
 }else{free(wave);}
 wave=(unsigned char*)calloc(4,1); handle=func__sndraw(wave,4);
 if (handle){
  sndqueue_wait=sndqueue_next; suspend_program|=2; qbevent=1;
  sndqueue[sndqueue_next]=handle;
  sndqueue_next++; if (sndqueue_next>sndqueue_lastindex) sndqueue_next=0;
 }else{free(wave);}
}
playit=0;
wave=NULL;
}
break;
case 70://MF
if (mb){
mb=0;
//preceding MB content incorporated into MF block
}
break;
default:
error(5); return;
}
followup=0; goto next_byte;
}//5

if (followup==4){//N...
if (!number_entered){error(5); return;}
number_entered=0;
if (number>84){error(5); return;}
n=-33+number;
goto followup1;
followup=0; if (bytes_left<0) goto done;
}//4

if (followup==3){//O...
if (!number_entered){error(5); return;}
number_entered=0;
if (number>6){error(5); return;}
o=number;
followup=0; if (bytes_left<0) goto done;
}//3

if (followup==2){//L...
if (!number_entered){error(5); return;}
number_entered=0;
if (number<1||number>64){error(5); return;}
l=number;
length=1.0/(t/60.0)*(4.0/l);
followup=0; if (bytes_left<0) goto done;
}//2

if (followup==1){//A-G...
if (i==45){//-
 if (n_changed||number_entered){error(5); return;}
 n_changed=1; n--;
 goto next_byte;
}
if (i==43||i==35){//+,#
 if (n_changed||number_entered){error(5); return;}
 n_changed=1; n++;
goto next_byte;
}
followup1:
if (number_entered){
 number_entered=0;
 if (number<0||number>64){error(5); return;}
 if (!number) length2=length; else length2=1.0/(t/60.0)*(4.0/((double)number));
}else{
 length2=length;
}//number_entered
d=length2; for (x=1;x<=fullstops;x++){d/=2.0; length2=length2+d;} fullstops=0;
//frequency=(2^(note/12))*440
frequency=pow(2.0,((double)n)/12.0)*440.0;

//create wave
wave2=soundwave(frequency,length2*(1.0-pause),v/100.0,NULL,NULL);
if (pause>0){
wave2=(unsigned char*)realloc(wave2,soundwave_bytes+wavesize(length2*pause));
memset(wave2+soundwave_bytes,0,wavesize(length2*pause));
soundwave_bytes+=wavesize(length2*pause);
}

if (!wave){
 //adopt buffer
 wave=wave2; wave_bytes=soundwave_bytes;
 wave_base=0;
}else{
 //mix required?
 if (wave_base==wave_bytes) x=0; else x=1;
 //increase buffer?
 if ((wave_base+soundwave_bytes)>wave_bytes){
 wave=(unsigned char*)realloc(wave,wave_base+soundwave_bytes);
 memset(wave+wave_base,0,wave_base+soundwave_bytes-wave_bytes);
 wave_bytes=wave_base+soundwave_bytes;
 }
 //mix or copy
 if (x){
  //mix
  static short *sp,*sp2;
  sp=(short*)(wave+wave_base);
  sp2=(short*)wave2;
  x2=soundwave_bytes/2;
  for (x=0;x<x2;x++){
  x3=*sp2++;
  x4=*sp;
  x4+=x3;
  if (x4>32767) x4=32767;
  if (x4<-32767) x4=-32767;
  *sp++=x4;
  }//x 
 }else{
  //copy
  memcpy(wave+wave_base,wave2,soundwave_bytes);  
 }//x
 free(wave2);
}
if (i!=44){
wave_base+=soundwave_bytes;
}

playit=1;
n_changed=0;
followup=0; 
if (i==44) goto next_byte;
if (bytes_left<0) goto done;
}//1

if (a>=65&&a<=71){
//modify a to represent a semitonal note (n) interval
switch(a){
//[c][ ][d][ ][e][f][ ][g][ ][a][ ][b]
// 0  1  2  3  4  5  6  7  8  9  0  1
case 65: n=9; break;
case 66: n=11; break;
case 67: n=0; break;
case 68: n=2; break;
case 69: n=4; break;
case 70: n=5; break;
case 71: n=7; break;
}
n=n+(o-2)*12-9;
followup=1;
goto next_byte;
}//a

if (a==76){//L
followup=2;
goto next_byte;
}

if (a==77){//M
followup=5;
goto next_byte;
}

if (a==78){//N
followup=4;
goto next_byte;
}

if (a==79){//O
followup=3;
goto next_byte;
}

if (a==84){//T
followup=6;
goto next_byte;
}

if (a==60){//<
o--; if (o<0) o=0;
goto next_byte;
}

if (a==62){//>
o++; if (o>6) o=6;
goto next_byte;
}

if (a==80){//P
followup=7;
goto next_byte;
}

if (a==86){//V
followup=8;
goto next_byte;
}

error(5); return;
}//bytes_left
done:
if (number_entered||followup){error(5); return;}//unhandled data




if (playit){
handle=func__sndraw(wave,wave_bytes);
if (handle){
sndqueue[sndqueue_next]=handle;
sndqueue_next++; if (sndqueue_next>sndqueue_lastindex) sndqueue_next=0;
}

//creating a "blocking" sound object
if (!mb){
wave=(unsigned char*)calloc(4,1); handle=func__sndraw(wave,4);
if (handle){
sndqueue_wait=sndqueue_next; suspend_program|=2; qbevent=1;
sndqueue[sndqueue_next]=handle;
sndqueue_next++; if (sndqueue_next>sndqueue_lastindex) sndqueue_next=0;
}else{free(wave);}

}//!mb




}//playit

}



//2D PROTOTYPE QB64<->C CALLS

//Creating/destroying an image surface:

long func__newimage(long x,long y,long bpp,long passed){
static long i;
if (new_error) 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;
}

long func__loadimage(qbs *f,long bpp,long passed){
static qbs *tqbs=NULL,*nullt=NULL;
static long i;
if (new_error) return 0;
//validate bpp
if (passed){
if ((bpp!=32)&&(bpp!=256)){error(5); return 0;}
}else{
if (write_page->text){error(5); return 0;}
bpp=-1;
}
if (!f->len) return -1;//return invalid handle if null length string
if (!tqbs) tqbs=qbs_new(0,0);
if (!nullt){nullt=qbs_new(1,0); nullt->chr[0]=0;}
qbs_set(tqbs,qbs_add(f,nullt));
i=imgload(fixdir(tqbs),bpp);
if (!i) return -1;//failed
return -i;
}

long func__copyimage(long i,long passed){
static long i2,bytes;
static img_struct *s,*d;
if (new_error) 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];
//duplicate structure
i2=newimg();
d=&img[i2];
memcpy(d,s,sizeof(img_struct));
//duplicate pixel data
bytes=d->width*d->height*d->bytes_per_pixel;
d->offset=(unsigned char*)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=(unsigned long*)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(long i,long passed){
if (new_error) return;
 if (passed){
  if (i>=0){//validate i
  error(5); return;//The SCREEN's pages cannot be freed!
  }else{
  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
if (img[i].flags&IMG_FREEPAL) free(img[i].pal);//free palette
freeimg(i);
}

void freeallimages(){
static long i;
//note: handles 0 & -1(1) are reserved
for (i=2;i<nextimg;i++){
if (img[i].valid){
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(long i){ 
if (new_error) 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(long i){ 
if (new_error) 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];
}

long func__source(){
return -read_page_index;
}

long func__dest(){
return -write_page_index;
}

long func__display(){
return -display_page_index;
}

//Changing the settings of an image surface:

void sub__blend(long i,long passed){
if (new_error) 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].bytes_per_pixel!=4){error(5); return;}
img[i].alpha_disabled=0;
}

void sub__dontblend(long i,long passed){
if (new_error) 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].bytes_per_pixel!=4) return;
img[i].alpha_disabled=1;
}


void sub__clearcolor(long none,unsigned long c,long i,long passed){
//-->                                        1      2
//id.specialformat = "[{_NONE}][?][,?]"
//sub__clearcolor(NULL, 0 ,*__LONG_TMPLAYER,3);
if (new_error) return;
static img_struct *im;
static long z;
static unsigned long *lp,*last;
static unsigned char b_max,b_min,g_max,g_min,r_max,r_min;
static unsigned char *cp,*clast,v;
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];
//text?
if (im->text){
if (none&&(!(passed&1))) return; //you can disable clearcolor using _CLEARCOLOR _NONE in text modes
error(5); return;
}
//palette?
if (im->pal){
if (none){
if (passed&1){error(5); return;}//invalid options
im->transparent_color=-1;
return;
}
if (!(passed&1)){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 (none){
if (passed&1){error(5); return;}//invalid options
return;//no action
}
if (!(passed&1)){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(long a,unsigned long c,unsigned long c2,long i,long passed){
//-->                                1               2              4
static img_struct *im;
static long z;
static unsigned long *lp,*last;
static unsigned char b_max,b_min,g_max,g_min,r_max,r_min,a_max,a_min;
static unsigned char *cp,*clast,v;
if (new_error) return;
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];
if (im->pal){error(5); return;}//does not work on paletted images!
if (a<0||a>255){error(5); return;}//invalid range
if (passed&2){
//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) swap(b_min,b_max);
if (g_min>g_max) swap(g_min,g_max);
if (r_min>r_max) swap(r_min,r_max);
if (a_min>a_max) 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 infomation about an image surface:

long func__width(long i,long passed){
if (new_error) 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].width;
}

long func__height(long i,long passed){
if (new_error) 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].height;
}

long func__pixelsize(long i,long passed){
if (new_error) 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;
}

long func__clearcolor(long i,long passed){
if (new_error) 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 -1;
if (img[i].compatible_mode==32) return 0;
return img[i].transparent_color;
}

long func__blend(long i,long passed){
if (new_error) 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;
}

unsigned long func__defaultcolor(long i,long passed){
if (new_error) 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;
}

unsigned long func__backgroundcolor(long i,long passed){
if (new_error) 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:

unsigned long func__palettecolor(long n,long i,long passed){
if (new_error) 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(long n,unsigned long c,long i,long passed){
if (new_error) 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(long i,long i2,long passed){
if (new_error) 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;}
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;}
swap(i,i2);
memcpy(img[i2].pal,img[i].pal,1024);
}

void sub__printstring(long step,double f_x,double f_y,qbs* text,long i,long passed){
if (new_error) 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;
}
static img_struct *im;
im=&img[i];
if (im->text){error(5); return;}//graphics modes only
if (!text->len) return;
//default is no kerning, assume kerning is off
static long x,y,i2,w;
x=f_x; y=f_y;
for (i2=0;i2<text->len;i2++){
w=printchr2(x,y,text->chr[i2],i);
x=x+w;
}
}

long func__printwidth(qbs* text,long i,long passed){
if (new_error) return 0;
static long i2,w2;
static unsigned long character;
static unsigned long x2,w,f;
static SDL_Surface *ts;
static SDL_Color c,c2;
static img_struct *im;

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;
}
im=&img[i];
if (im->text){error(5); return 0;}//graphics modes only
if (!text->len) return 0;

//default is no kerning, assume kerning is off

w2=0;
for (i2=0;i2<text->len;i2++){
character=text->chr[i2];

//precalculations
character&=255;
f=im->font;
c.r=255; c.g=255; c.b=255; c.unused=0;//dummy values
c2.r=255; c2.g=255; c2.b=255; c2.unused=0;//dummy values

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]&8)){

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Solid(font[f],(Uint16*)unicode16_buf,c);
//ts=TTF_RenderText_Solid(font[f],(char*)&character,c);//8-bit, 0=clear, 1=text

if (ts==NULL) return 0;
w=ts->w;
if (x2=fontwidth[f]) if (x2!=w) w=x2;//render width too large!  
SDL_FreeSurface(ts);
w2+=w;
goto nexti2;
}//1-8 bit

//assume 32-bit blended
//8 bit, 0=background -> 255=foreground

convert_codepage437_to_unicode16(&character,1);
ts=TTF_RenderUNICODE_Shaded(font[f],(Uint16*)unicode16_buf,c,c2);
//ts=TTF_RenderText_Shaded(font[f],(char*)&character,c,c2);

if (ts==NULL) return 0;
w=ts->w;
if (x2=fontwidth[f]) if (x2!=w) w=x2;//render width too large!  
SDL_FreeSurface(ts);
w2+=w;
goto nexti2;

}//custom font

//default fonts
w2+=8;

nexti2:;
}//i2

return w2;
}//printwidth


long func__loadfont(qbs *filename,double size,qbs *requirements,long passed){
//f=_FONTLOAD(ttf_filename$,height[,"bold,italic,underline,monospace,dontblend"])
//1 bold TTF_STYLE_BOLD
//2 italic TTF_STYLE_ITALIC
//4 underline TTF_STYLE_UNDERLINE
//8 dontblend (blending is the default in 32-bit alpha-enabled modes)
//16 monospace
if (new_error) return 0;
static qbs *s1=NULL;
if (!s1) s1=qbs_new(0,0);
static qbs *req=NULL;
if (!req) req=qbs_new(0,0);
static qbs *s3=NULL;
if (!s3) s3=qbs_new(0,0);
static unsigned char r[32];
static long i,i2,i3;
//validate
if (size<0.001){error(5); return 0;}
if (size>=1000.5) return 0;
memset(r,0,32);
if (passed){
if (requirements->len){
i=1;
qbs_set(req,qbs_ucase(requirements));//convert tmp str to perm str
nextrequirement:
i2=func_instr(i,req,qbs_new_txt(","),1);
if (i2){
qbs_set(s1,func_mid(req,i,i2-i,1));
}else{
qbs_set(s1,func_mid(req,i,req->len-i+1,1));
}
qbs_set(s1,qbs_rtrim(qbs_ltrim(s1)));
if (qbs_equal(s1,qbs_new_txt("BOLD"))){r[0]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("ITALIC"))){r[1]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("UNDERLINE"))){r[2]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("DONTBLEND"))){r[3]++; goto valid;}
if (qbs_equal(s1,qbs_new_txt("MONOSPACE"))){r[4]++; goto valid;}
error(5); return 0;//invalid requirements
valid:
if (i2){i=i2+1; goto nextrequirement;}
for (i=0;i<32;i++) if (r[i]>1){error(5); return 0;}//cannot define requirements twice
}//->len
}//passed
qbs_set(s1,qbs_add(filename,qbs_new_txt_len("\0",1)));//s1=filename+CHR$(0)
i=r[0]+(r[1]<<1)+(r[2]<<2)+(r[3]<<3)+(r[4]<<4);
i=fontopen(fixdir(s1),size,i);
if (!i) return -1;
return i;
}

void sub__font(long f,long i,long passed){
//_FONT "?[,?]"
static long i2;
static img_struct *im;
if (new_error) 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
i2=0;
if (f==8) i2=1;
if (f==9) i2=1;
if (f==14) i2=1;
if (f==15) i2=1;
if (f==16) i2=1;
if (f==17) i2=1;
if (f>=32&&f<=lastfont){
if (font[f]) i2=1;
}
if (!i2){error(258); return;}

if (im->text&&((fontflags[f]&16)==0)){error(5); return;}//only monospace fonts can be used on text surfaces
//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;
}

long func__fontwidth(long f,long passed){
static long i2;
if (new_error) 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];
}

long func__fontheight(long f,long passed){
static long i2;
if (new_error) 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];
}

long func__font(long i,long passed){
if (new_error) 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(long f){
if (new_error) return;
static long 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
TTF_CloseFont(font[f]);
font[f]=NULL;
}

void sub__printmode(long mode,long i,long passed){
if (new_error) 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
}

long func__printmode(long i,long passed){
if (new_error) 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;
}


unsigned long matchcol(long r,long g,long b){
static long v,v2,n,n2,best,c;
static long *p;
p=(long*)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;
}

unsigned long matchcol(long r,long g,long b,long i){
static long v,v2,n,n2,best,c;
static long *p;
p=(long*)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;
}

unsigned long func__rgb(long r,long g,long b,long i,long passed){
if (new_error) 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

unsigned long func__rgba(long r,long g,long b,long a,long i,long passed){
if (new_error) 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

long func__alpha(unsigned long col,long i,long passed){
if (new_error) 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
}

long func__red(unsigned long col,long i,long passed){
if (new_error) 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
}

long func__green(unsigned long col,long i,long passed){
if (new_error) 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
}

long func__blue(unsigned long col,long i,long passed){
if (new_error) 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(){
exit_blocked=0;//allow exit via X-box or CTRL+BREAK
//1. set the display page as the destination page
sub__dest(func__display());
//2. VIEW PRINT bottomline,bottomline
static long 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 unsigned long qbs_tmp_base;
qbs_tmp_base=qbs_tmp_list_nexti;
while(qbs_cleanup(qbs_tmp_base,qbs_notequal(qbs_inkey(),qbs_new_txt("")))){
SDL_Delay(0);
}
//6. Enable autodisplay
autodisplay=1;
//7. Wait for a new keypress
do{
SDL_Delay(0);
if (stop_program) end();
}while(qbs_cleanup(qbs_tmp_base,qbs_equal(qbs_inkey(),qbs_new_txt(""))));
//8. end program
close_program=1;
end();
exit(0);//<-- should never happen
}

unsigned char pu_dig[1024];//digits (left justified)
long pu_ndig;//number of digits
long 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
long pu_neg;
unsigned char pu_buf[1024];//a buffer for preprocessing
unsigned char pu_exp_char=69; //"E"

long print_using(qbs *f, long s2, qbs *dest, qbs* pu_str){
//type: 1=numeric, 2=string
if (new_error) return 0;
static long x,x2,s,stage,len,chrsleft,z,z2,z3,z4,i,i2,i3,type;
static unsigned char c;
static long leading_plus,dollar_sign,asterisk_spaces,digits_before_point,commas;
static long decimal_point,digits_after_point,trailing_plus,exponent_digits, trailing_minus;
static long 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 appropriatly
extra_sign_space=0;
if (exponent_digits){
 if ((leading_plus==0)&&(trailing_plus==0)&&(trailing_minus==0)){digits_before_point--; extra_sign_space=1;}
}else{
 //the following don'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=i;
 i=digits_before_point/4;//for every 4 digits, one digit will be used up by a comma
 digits_before_point-=i;
 }
}

//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{
 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 long buf_size;//buf_size is an estimation of size required
static unsigned char *cp,*buf=NULL;
static long 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=(unsigned char*)malloc(buf_size);
cp=buf;
count=0;//char count
i=0;

if (asterisk_spaces) asterisk_spaces=42; else asterisk_spaces=32;//chraracter 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){
 //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 (i<pu_ndig) *cp++=pu_dig[i++]; 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 (i<pu_ndig) *cp++=pu_dig[i++]; else *cp++=48;
 count++;
 }
 //round last digit (requires a repass)
 if (!rounded){
 if (i<pu_ndig){
 if (pu_dig[i]>=53){//>="5" (round 5 up)
 z=i-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 (i<pu_ndig) *cp++=pu_dig[i++]; 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 (i<pu_ndig) *cp++=pu_dig[i++]; else *cp++=48;
 }
 count++;
 }
 //round last digit (requires a repass)
 if (!rounded){
 if (i<pu_ndig){
 if (pu_dig[i]>=53){//>="5" (round 5 up)
 z=i-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 long 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;
}

long print_using_integer64(qbs* format, int64 value, long start, qbs *output){
if (new_error) return 0;
#ifdef QB64_WINDOWS
 pu_ndig=sprintf((char*)pu_buf,"% I64i",value);
#else
 pu_ndig=sprintf((char*)pu_buf,"% lli",value);
#endif
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;
}

long print_using_uinteger64(qbs* format, uint64 value, long start, qbs *output){
if (new_error) return 0;
#ifdef QB64_WINDOWS
 pu_ndig=sprintf((char*)pu_dig,"%I64u",value);
#else
 pu_ndig=sprintf((char*)pu_dig,"%llu",value);
#endif
pu_neg=0;
pu_dp=0;
start=print_using(format,start,output,NULL);
return start;
}

long print_using_single(qbs* format, float value, long start, qbs *output){
if (new_error) return 0;
static long i,len,neg_exp;
static unsigned char 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;
}

long print_using_double(qbs* format, double value, long start, qbs *output){
if (new_error) return 0;
static long i,len,neg_exp;
static unsigned char 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
long print_using_float(qbs* format, double value, long start, qbs *output){
if (new_error) return 0;
static long i,len,neg_exp;
static unsigned char 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=70; //"F"
start=print_using(format,start,output,NULL);
pu_exp_char=69; //"E"
return start;
}

void sub_run_init(){
//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,1|2);
//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;
}



void sub_run(qbs* f){
if (new_error) return;
//run program
static qbs *str=NULL;
if (str==NULL) str=qbs_new(0,0);
static long i;
static qbs *strz=NULL;
if (!strz) strz=qbs_new(0,0);
static char *cp;
qbs_set(str,f);
//if there is no extension (an existing "." in the string), qb64 adds .exe (lowercase)
for (i=0;i<f->len;i++){
if (f->chr[i]==46) goto extension_ok;
}
//add .exe
qbs_set(str,qbs_add(str,qbs_new_txt(".exe")));
extension_ok:
//if the last character is "." it is removed
if (str->chr[str->len-1]==46) str->len--;
//normalize dir slashes
fixdir(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

}

void sub__icon(long i){
if (new_error) return;
static long i2;
static SDL_Surface *surface=NULL;
static unsigned long *o,*o2;
static long x,n,c,i3,c2;
//verify image i
 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;}
//create new SDL 32x32 surface for icon call if necessary
if (!surface){
Uint32 rmask, gmask, bmask, amask;
    bmask = 0x000000ff;
    gmask = 0x0000ff00;
    rmask = 0x00ff0000;
    amask = 0xff000000;
surface = SDL_CreateRGBSurface(SDL_SWSURFACE,32,32,32,rmask,gmask,bmask,amask);
if (!surface) return;//no error required for icon failure
}
//prepare i2
i2=func__newimage(32,32,32,1);
i3=func__dest();
sub__dest(i2);
sub_cls(NULL,0,2);
sub__dest(i3);
sub__dontblend(i2,1);
sub__putimage(NULL,NULL,NULL,NULL,NULL,NULL,-i,i2,NULL,NULL,NULL,NULL,NULL,NULL,12); //note: ideally, i should have its aspect ratio respected when putting onto i2
//copy i2 onto SDL surface
o=img[-i2].offset32;
o2=(unsigned long*)surface->pixels;
n=32*32;
for (x=0;x<n;x++){
c=o[x];
	//note: SDL does not support alpha blended pixels in icons yet, this is a work-around
	c2=((c>>24)&255);
	if (c2<127) c2=0; else c2=255;
o2[x]=(c&0xFFFFFF)+(c2<<24);
}
SDL_WM_SetIcon(surface,NULL);
sub__freeimage(i2,1);
}

void sub__autodisplay(){
autodisplay=1;
}

void sub__display(){
//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();
}

long sub_draw_i;
unsigned char *sub_draw_cp;
long sub_draw_len;

long draw_num_invalid;
long draw_num_undefined;
double draw_num(){
static long 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 (new_error) return;

/*
Todo:
-address color switchback/maintain (eg. when PSET called)
-address ta & scale reset (eg. on mode change/image change/page change)

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 unsigned long col;
static long x,c,prefix_b,prefix_n,offset;
static unsigned char *stack_s[8192];
static unsigned short stack_len[8192];
static unsigned short stack_i[8192];
static long 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){
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=sin(d); cos_ta=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){px2=px+px2*draw_scale; py2=py+py2*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=sin(d); cos_ta=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(NULL,xx,yy,c64c,c64,NULL,3);
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)){sub_draw_i++; goto nextchar;}//skip whitespace

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;
}



#ifdef QB64_LINUX 
extern char** environ;
#define envp environ
#else /* WINDOWS */
//extern char** _environ;
#define envp _environ
#endif
size_t environ_count;

qbs *func_environ(qbs *name)
{
static char *cp;
static qbs *tqbs;
static long bytes;
cp=getenv((char*)name->chr);
if (cp){
bytes=strlen(cp);
tqbs=qbs_new(bytes,1);
memcpy(tqbs->chr,cp,bytes); 
}else{
tqbs=qbs_new(0,1);
}
return tqbs;
}

qbs *func_environ(long number)
{
static qbs *tqbs;
static char *cp;
static long bytes;
if (number<=0){tqbs=qbs_new(0,1); error(5); return tqbs;}
if (number>=environ_count){tqbs=qbs_new(0,1); return tqbs;}
cp=*(envp+number-1);
bytes=strlen(cp);
tqbs=qbs_new(bytes,1);
memcpy(tqbs->chr,cp,bytes);
return tqbs;
}

void sub_environ(qbs *str)
{
static char *cp;
cp=(char*)malloc(str->len+1);
cp[str->len]=0;//add NULL terminator
memcpy(cp,str->chr,str->len);
putenv(cp);
free(cp);
environ_count++;
}


void showvalue(__int64 v){
static qbs* s=NULL;
if (s==NULL) s=qbs_new(0,0);
qbs_set(s,qbs_str(v));
MessageBox(NULL,(char*)s->chr,"showvalue",MB_OK);
}






//network prototype:

uint8 net_tcp_init_done=0;
void net_tcp_init(){
if (!net_tcp_init_done){
net_tcp_init_done=1;
if(SDLNet_Init()==-1){
//assume success
}
}
}

int32 func__connected(int32 i){
if (new_error) return 0;
//validate
if (i>=0){error(52); return 0;}//Bad file name or number
i=-(i+1);
if (i>=sfh_bufsize){error(52); return 0;}
if ((sfh[i].type<1)||(sfh[i].type>3)){error(52); return 0;}
if (net_tcp[i].error) return 0;
if ((sfh[i].type==2)||(sfh[i].type==3)){
	net_tcp_updatebuffer(i);//attempt to trigger error state by updating buffer
	if (net_tcp[i].error) return 0;
}
return -1;
}

qbs *func__connectionaddress(int32 i){
static qbs *tqbs,*tqbs2,*str=NULL,*str2=NULL;
if (new_error) goto error;
if (!str) str=qbs_new(0,0);
if (!str2) str2=qbs_new(0,0);

//validate
if (i>=0){error(52); goto error;}//Bad file name or number
i=-(i+1);
if (i>=sfh_bufsize){error(52); goto error;}
if ((sfh[i].type<1)||(sfh[i].type>3)){error(52); goto error;}

if (sfh[i].type==1){//host (1) returns its own address
qbs_set(str,qbs_new_txt("TCP/IP:"));//network type
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str(net_tcp[i].portused))));//port
qbs_set(str,qbs_add(str,qbs_new_txt(":")));
tqbs2=WHATISMYIP();
if (tqbs2->len){
qbs_set(str,qbs_add(str,tqbs2));
}else{
//global IP unavailable, use local IP
//IP4
static IPaddress ip;
if(SDLNet_ResolveHost(&ip,"localhost",0)==-1) goto error;
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)(ip.host)&255))));
qbs_set(str,qbs_add(str,qbs_new_txt(".")));
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)((ip.host)>>8)&255))));
qbs_set(str,qbs_add(str,qbs_new_txt(".")));
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)((ip.host)>>16)&255))));
qbs_set(str,qbs_add(str,qbs_new_txt(".")));
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)((ip.host)>>24)&255))));
}
tqbs=qbs_new(str->len,1);
memmove(tqbs->chr,str->chr,str->len);
return tqbs;
}

//assume connection/client socket (2/3)
static IPaddress *pip;
pip=SDLNet_TCP_GetPeerAddress(net_tcp[i].socket);
qbs_set(str,qbs_new_txt("TCP/IP:"));//network type
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str(net_tcp[i].portused))));//port
qbs_set(str,qbs_add(str,qbs_new_txt(":")));
//IP4
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)(pip->host)&255))));
qbs_set(str,qbs_add(str,qbs_new_txt(".")));
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)((pip->host)>>8)&255))));
qbs_set(str,qbs_add(str,qbs_new_txt(".")));
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)((pip->host)>>16)&255))));
qbs_set(str,qbs_add(str,qbs_new_txt(".")));
qbs_set(str,qbs_add(str,qbs_ltrim(qbs_str((int32)((pip->host)>>24)&255))));
tqbs=qbs_new(str->len,1);
memmove(tqbs->chr,str->chr,str->len);
return tqbs;

error:
tqbs=qbs_new(0,1);
return tqbs;
}



int32 func__openhost(qbs* method){
if (new_error) return 0;

//generic data
static qbs *s=NULL; if (!s){s=qbs_new(0,0);}
static qbs *s2=NULL; if (!s2){s2=qbs_new(0,0);}
static qbs *s3=NULL; if (!s3){s3=qbs_new(0,0);}
static double d;
static int32 i,i2,portused;

qbs_set(s,method);
qbs_set(s2,qbs_ucase(s));

//TCP/IP Network
qbs_set(s3,qbs_new_txt("TCP/IP:"));
if (func_instr(NULL,s2,s3,NULL)==1){
qbs_set(s,func_mid(s,8,NULL,NULL));
net_tcp_init();
//***assume*** string s contains an integer port number
static IPaddress ip;
static TCPsocket socket;
d=func_val(s);
i=qbr_double_to_long(d);
//***assume*** port number is within valid range
portused=i;
if(SDLNet_ResolveHost(&ip,NULL,i)==-1) return 0;
socket=SDLNet_TCP_Open(&ip);
if(!socket) return 0;

i2=sfh_new();
sfh[i2].type=1;//TCPIP host
if (i2>=net_tcp_bufsize) net_tcp=(net_tcp_struct*)realloc(net_tcp,sizeof(net_tcp_struct)*(++net_tcp_bufsize));
net_tcp[i2].ip=ip;
net_tcp[i2].socket=socket;
net_tcp[i2].error=0;
net_tcp[i2].buffer=NULL; net_tcp[i2].buffer_size=0; net_tcp[i2].buffer_space=0;
net_tcp[i2].portused=i;

return -1-i2;

}//TCP/IP

error(5); return 0;

}

int32 func__openconnection(int32 host){



static int32 i2;

//validate host handle
host=-(host+1);
if ((host<0)||(host>=sfh_bufsize)){error(258); return 0;}//invalid handle
if (sfh[host].type!=1){error(258); return 0;}//invalid handle
static TCPsocket socket;
socket=SDLNet_TCP_Accept(net_tcp[host].socket);
if (!socket) return 0;

//create socket set for watching
static SDLNet_SocketSet set;
set=SDLNet_AllocSocketSet(1);
if (!set){
SDLNet_TCP_Close(socket);
return 0;
}
//add socket to set
if (SDLNet_TCP_AddSocket(set,socket)!=1){
SDLNet_FreeSocketSet(set);
SDLNet_TCP_Close(socket);
return 0;
}

i2=sfh_new();
sfh[i2].type=3;//TCPIP connection
if (i2>=net_tcp_bufsize) net_tcp=(net_tcp_struct*)realloc(net_tcp,sizeof(net_tcp_struct)*(++net_tcp_bufsize));
net_tcp[i2].socket=socket;
net_tcp[i2].set=set;
net_tcp[i2].error=0;
net_tcp[i2].buffer=NULL; net_tcp[i2].buffer_size=0; net_tcp[i2].buffer_space=0;
net_tcp[i2].eof=0;
net_tcp[i2].portused=net_tcp[host].portused;

return -1-i2;

}

int32 func__openclient(qbs* method){
if (new_error) return 0;

//generic data
static qbs *z=NULL; if (!z){z=qbs_new(1,0); z->chr[0]=0;}
static qbs *s=NULL; if (!s){s=qbs_new(0,0);}
static qbs *s2=NULL; if (!s2){s2=qbs_new(0,0);}
static qbs *s3=NULL; if (!s3){s3=qbs_new(0,0);}
static double d;
static int32 i,i2,portused;

qbs_set(s,method);
qbs_set(s2,qbs_ucase(s));

//TCP/IP Network
qbs_set(s3,qbs_new_txt("TCP/IP:"));
if (func_instr(NULL,s2,s3,NULL)==1){
qbs_set(s,func_mid(s,8,NULL,NULL));
net_tcp_init();

//read port number
qbs_set(s3,qbs_new_txt(":"));
i=func_instr(NULL,s,s3,NULL);
if (!i){error(5); return 0;}
qbs_set(s3,func_mid(s,1,i-1,1)); qbs_set(s,func_mid(s,i+1,NULL,NULL));
d=func_val(s3);
i2=qbr_double_to_long(d);
//***assume*** port number is within valid range
portused=i2;
//read address
if (!s->len){error(5); return 0;}
//null terminate address
qbs_set(s3,qbs_add(s,z));
static IPaddress ip;
static TCPsocket socket;
if(SDLNet_ResolveHost(&ip,(char*)s3->chr,i2)==-1) return 0;
socket=SDLNet_TCP_Open(&ip);
if (!socket) return 0;

//create socket set for watching
static SDLNet_SocketSet set;
set=SDLNet_AllocSocketSet(1);
if (!set){
SDLNet_TCP_Close(socket);
return 0;
}
//add socket to set
if (SDLNet_TCP_AddSocket(set,socket)!=1){
SDLNet_FreeSocketSet(set);
SDLNet_TCP_Close(socket);
return 0;
}

i2=sfh_new();
sfh[i2].type=2;//TCPIP client
if (i2>=net_tcp_bufsize) net_tcp=(net_tcp_struct*)realloc(net_tcp,sizeof(net_tcp_struct)*(++net_tcp_bufsize));
net_tcp[i2].ip=ip;
net_tcp[i2].socket=socket;
net_tcp[i2].set=set;
net_tcp[i2].error=0;
net_tcp[i2].buffer=NULL; net_tcp[i2].buffer_size=0; net_tcp[i2].buffer_space=0;
net_tcp[i2].eof=0;
net_tcp[i2].portused=portused;
return -1-i2;

}//TCP/IP

error(5); return 0;

}

int32 func__exit(){
exit_blocked=1;
static int32 x;
x=exit_value;
exit_value=0;
return x;
}

qbs *internal_clipboard=NULL;//used only if clipboard services unavailable

void sub__clipboard(qbs *text){
#ifdef QB64_WINDOWS
static uint8 *textz;
static HGLOBAL h;
if (OpenClipboard(NULL)){
EmptyClipboard();
h=GlobalAlloc(GMEM_MOVEABLE,text->len+1); if (h){
textz=(uint8*)GlobalLock(h); if (textz){
memcpy(textz,text->chr,text->len);
textz[text->len]=0;
GlobalUnlock(h);
SetClipboardData(CF_TEXT,h);
}
}
CloseClipboard();
}
#else
if (internal_clipboard==NULL) internal_clipboard=qbs_new(0,0);
qbs_set(internal_clipboard,text);
#endif
}

qbs *func__clipboard(){
#ifdef QB64_WINDOWS
static qbs *text;
static uint8 *textz;
static HGLOBAL h;
if (OpenClipboard(NULL)){
if (IsClipboardFormatAvailable(CF_TEXT)){
h=GetClipboardData(CF_TEXT); if (h){
textz=(uint8*)GlobalLock(h); if (textz){
text=qbs_new(strlen((char*)textz),1);
memcpy(text->chr,textz,text->len);
GlobalUnlock(h);
CloseClipboard();
return text;
}
}
}
CloseClipboard();
}
text=qbs_new(0,1);
return text;
#else
if (internal_clipboard==NULL) internal_clipboard=qbs_new(0,0);
return internal_clipboard;
#endif
}

void sub__fullscreen(int32 method){
//ref: "[{_OFF|_STRETCH|_SQUAREPIXELS}]"
//          1      2           3
int32 x;
if (method==0) x=1;
if (method==1) x=0;
if (method==2) x=1;
if (method==3) x=2;
if (full_screen==x) return;
full_screen_set=x;
while(full_screen_set!=-1) Sleep(32);
//try alternative if attempted method was unavailable
if (full_screen!=x){
if (x==1){
full_screen_set=2;
while(full_screen_set!=-1) Sleep(32);
}
if (x==2){
full_screen_set=1;
while(full_screen_set!=-1) Sleep(32);
}
}
}

int32 func__fullscreen(){
return full_screen;
}

extern ontimer_struct *ontimer;


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,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






int32 gfs_new(){
static int32 i;
if (gfs_freed_n){
i=gfs_freed[--gfs_freed_n];
}else{
i=gfs_n;
gfs_n++;
gfs_file=(gfs_file_struct*)realloc(gfs_file,gfs_n*sizeof(gfs_file_struct));
 #ifdef QB64_WINDOWS
 gfs_file_win=(gfs_file_win_struct*)realloc(gfs_file_win,gfs_n*sizeof(gfs_file_win_struct));
 #endif
}
ZeroMemory(&gfs_file[i],sizeof(gfs_file_struct));
 #ifdef QB64_WINDOWS
 ZeroMemory(&gfs_file_win[i],sizeof(gfs_file_win_struct));
 #endif
return i;
}

int32 gfs_validhandle(int32 i){
if ((i<0)||(i>=gfs_n)) return 0;
if (gfs_file[i].open) return 1;
return 0;
}

int32 gfs_fileno_valid(int32 f){
//returns: -2	invalid handle
//			1	in use
//			0	unused
if (f<=0) return -2;
if (f<=gfs_fileno_n){
if (gfs_fileno[f]==-1) return 0; else return 1;
}
gfs_fileno=(int32*)realloc(gfs_fileno,(f+1)*4);
memset(gfs_fileno+gfs_fileno_n+1,-1,(f-gfs_fileno_n)*4);
if (f>gfs_fileno_n+1){
 //add any indexes between gfs_fileno_n and f to free list
 static int32 x;
 x=gfs_fileno_freed_n+(f-gfs_fileno_n-1);
 if (x>gfs_fileno_freed_size){gfs_fileno_freed=(int32*)realloc(gfs_fileno_freed,x*4); gfs_fileno_freed_size=x;}
 x=gfs_fileno_n+1;
 while(x<f){
 gfs_fileno_freed[gfs_fileno_freed_n++]=x;
 x++;
 }
}
gfs_fileno_n=f;
return 0;
}

int32 gfs_fileno_freefile(){//like FREEFILE
if (gfs_fileno_freed_n) return gfs_fileno_freed[gfs_fileno_freed_n-1];
return gfs_fileno_n+1;
}

void gfs_fileno_use(int32 f, int32 i){
//assumes valid handles
gfs_fileno[f]=i;
gfs_file[i].fileno=f;
}

void gfs_fileno_free(int32 f){//note: called by gfs_free (DO NOT CALL THIS FUNCTION)
gfs_fileno[f]=-1;
if (gfs_fileno_freed_n+1>gfs_fileno_freed_size){gfs_fileno_freed=(int32*)realloc(gfs_fileno_freed,(gfs_fileno_freed_n+1)*4); gfs_fileno_freed_size++;}
gfs_fileno_freed[gfs_fileno_freed_n++]=f;
}

int32 gfs_free(int32 i){
if (!gfs_validhandle(i)) return -2;//invalid handle
if (gfs_freed_size<=gfs_freed_n){
gfs_freed_size++;
gfs_freed=(int32*)realloc(gfs_freed,gfs_freed_size*4);
}
gfs_file[i].open=0;
if (gfs_file[i].fileno) gfs_fileno_free(gfs_file[i].fileno);
gfs_freed[gfs_freed_n++]=i;
return 0;
}

int32 gfs_close(int32 i){
static int32 x;
if (x=gfs_free(i)) return x;

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 CloseHandle(f_w->file_handle);
 return 0;
 #endif

return -1;
}

int64 gfs_lof(int32 i){
if (!gfs_validhandle(i)) return -2;//invalid handle
static gfs_file_struct *f;
f=&gfs_file[i];

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 static int64 bytes;
 *((int32*)&bytes)=GetFileSize(f_w->file_handle,(DWORD*)(((int32*)&bytes)+1));
 if ((bytes&0xFFFFFFFF)==0xFFFFFFFF){
 if (GetLastError()!=NO_ERROR) return -3;//bad/incorrect file mode
 }
 return bytes;
 #endif

return -1;
}

int32 gfs_open(qbs *filename,int32 access,int32 restrictions, int32 how){

//filename - an OS compatible filename (doesn't need to be NULL terminated)
//access - 1=read, 2=write, 3=read and write
//restrictions - 1=others cannot read, 2=others cannot write, 3=others cannot read or write(exclusive access)
//how - 1=create(if it doesn't exist), 2=create(if it doesn't exist) & truncate
//      3=create(if it doesn't exist)+undefined access[get whatever access is available]
static int32 i,x,x2,x3,e;
static qbs *filenamez=NULL;
if (!filenamez) filenamez=qbs_new(0,0);
qbs_set(filenamez,qbs_add(filename,qbs_new_txt_len("\0",1)));
i=gfs_new();
static gfs_file_struct *f;
f=&gfs_file[i];
if (access&1) f->read=1;
if (access&2) f->write=1;
if (restrictions&1) f->lock_read=1;
if (restrictions&2) f->lock_write=1;
f->pos=0;

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 x=0;
 if (access&1)x|=GENERIC_READ;
 if (access&2)x|=GENERIC_WRITE;
 x2=FILE_SHARE_READ|FILE_SHARE_WRITE;
 if (restrictions&1)x2^=FILE_SHARE_READ;
 if (restrictions&2)x2^=FILE_SHARE_WRITE;
  /*
  #define CREATE_NEW          1
  #define CREATE_ALWAYS       2
  #define OPEN_EXISTING       3
  #define OPEN_ALWAYS         4
  #define TRUNCATE_EXISTING   5
  */ 
 x3=OPEN_EXISTING;
 if (how) x3=OPEN_ALWAYS;
 undefined_retry: 
 f_w->file_handle=CreateFile(fixdir(filenamez),x,x2,NULL,x3,FILE_ATTRIBUTE_NORMAL,NULL);
 if (f_w->file_handle==INVALID_HANDLE_VALUE){

 if (how==3){
 //attempt read access only
 x=GENERIC_READ;
 f->read=1;
 f->write=0;
 how++;
 goto undefined_retry;
 }

 if (how==4){
 //attempt write access only
 x=GENERIC_WRITE;
 f->read=0;
 f->write=1;
 how++;
 goto undefined_retry;
 }

 e=GetLastError();
 if (e==3) return -6;
 if ((e==4)||(e==29)||(e==30)) return -9;
 if ((e==5)||(e==19)||(e==33)||(e==32)) return -7;
 if ((e==15)||(e==21)) return -8;
 if (e==2) return -5;
 //showvalue(e);
 return -5;//assume (2)
 }//invalid handle

 if (how==2){
 //truncate file if size is not 0
 static DWORD GetFileSize_low,GetFileSize_high;
 GetFileSize_low=GetFileSize(f_w->file_handle,&GetFileSize_high);
 if (GetFileSize_low||GetFileSize_high){
 CloseHandle(f_w->file_handle);
 x3=TRUNCATE_EXISTING;
 f_w->file_handle=CreateFile(fixdir(filenamez),x,x2,NULL,x3,FILE_ATTRIBUTE_NORMAL,NULL);

 if (f_w->file_handle==INVALID_HANDLE_VALUE){
 e=GetLastError();
 if (e==3) return -6;
 if ((e==4)||(e==29)||(e==30)) return -9;
 if ((e==5)||(e==19)||(e==33)||(e==32)) return -7;
 if ((e==15)||(e==21)) return -8;
 if (e==2) return -5;
 //showvalue(e);
 return -5;//assume (2)
 }//invalid handle

 }
 }//how==2
 f->open=1;
 return i;
 #endif


return -1;//non-specific fail

}

int32 gfs_setpos(int32 i, int64 position){
if (!gfs_validhandle(i)) return -2;//invalid handle
if (position<0) return -4;//illegal function call

static gfs_file_struct *f;
f=&gfs_file[i];

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 if (SetFilePointer(f_w->file_handle,(int32)position,(long*)(((int32*)&position)+1),FILE_BEGIN)==0xFFFFFFFF){/*Note that it is not an error to set the file pointer to a position beyond the end of the file. The size of the file does not increase until you call the SetEndOfFile, WriteFile, or WriteFileEx function.*/
 if (GetLastError()!=NO_ERROR){
 return -3;//bad file mode
 }
 }
 f->pos=position;
 return 0;
 #endif

return -1;
}

int64 gfs_getpos(int32 i){
if (!gfs_validhandle(i)) return -2;//invalid handle
static gfs_file_struct *f;
f=&gfs_file[i];
return f->pos;
}

int32 gfs_write(int32 i,int64 position,uint8 *data,int64 size){
if (!gfs_validhandle(i)) return -2;//invalid handle
static int32 e;
static gfs_file_struct *f;
f=&gfs_file[i];
if (!f->write) return -3;//bad file mode
if (size<0) return -4;//illegal function call
static int32 x;
if (position!=-1){
if (x=gfs_setpos(i,position)) return x;//(pass on error)
}

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 static uint32 size2;
 static int64 written=0;
 while(size){
 if (size>4294967295){
 size2=4294967295;
 size-=4294967295;
 }else{
 size2=size; size=0;
 }
 if (!WriteFile(f_w->file_handle,data,size2,(unsigned long*)&written,NULL)){
 e=GetLastError();
 if ((e==5)||(e==33)) return -7;//permission denied
 return -9;//assume: path/file access error
 }
 data+=written; f->pos+=written;
 if (written!=size2) return -1;
 }
 return 0;
 #endif

return -1;
}


int64 gfs_read_bytes_value;
int64 gfs_read_bytes(){
return gfs_read_bytes_value;
}

int32 gfs_read(int32 i,int64 position,uint8 *data,int64 size){
gfs_read_bytes_value=0;
if (!gfs_validhandle(i)) return -2;//invalid handle
static int32 e;
static gfs_file_struct *f;
f=&gfs_file[i];
if (!f->read) return -3;//bad file mode
if (size<0) return -4;//illegal function call
static int32 x;
if (position!=-1){
if (x=gfs_setpos(i,position)) return x;//(pass on error)
}

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 static uint32 size2;
 static int64 bytesread=0;
 while(size){
 if (size>4294967295){
 size2=4294967295;
 size-=4294967295;
 }else{
 size2=size; size=0;
 }

 if (ReadFile(f_w->file_handle,data,size2,(unsigned long*)&bytesread,NULL)){
 data+=bytesread; f->pos+=bytesread; gfs_read_bytes_value+=bytesread;
  if (bytesread!=size2){   
   ZeroMemory(data,size+(size2-bytesread));//nullify remaining buffer
   f->eof_passed=1; return -10;
  }//eof passed
 }else{
 //error
 e=GetLastError();
 if ((e==5)||(e==33)) return -7;//permission denied
 //showvalue(e);
 return -9;//assume: path/file access error
 }
 }
 return 0;
 #endif

return -1;
}

int32 gfs_eof_reached(int32 i){
if (!gfs_validhandle(i)) return -2;//invalid handle
if (gfs_getpos(i)>=gfs_lof(i)) return 1;
return 0;
}

int32 gfs_eof_passed(int32 i){
if (!gfs_validhandle(i)) return -2;//invalid handle
static gfs_file_struct *f;
f=&gfs_file[i];
if (f->eof_passed) return 1;
return 0;
}

int32 gfs_lock(int32 i,int64 offset_start,int64 offset_end){
//if offset_start==-1, 'from beginning' (typically offset 0) is assumed
//if offset_end==-1, 'to end/infinity' is assumed
//range is inclusive of start and end
if (!gfs_validhandle(i)) return -2;//invalid handle
static gfs_file_struct *f;
f=&gfs_file[i];

if (offset_start==-1) offset_start=0;
if (offset_start<0) return -4;//illegal function call
if (offset_end<-1) return -4;//illegal function call
//note: -1 equates to highest uint64 value (infinity)
//      All other negative end values are illegal

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 uint64 bytes;
 bytes=offset_end;
 if (bytes!=-1) bytes=bytes-offset_start+1;
 if (!LockFile(f_w->file_handle,*((DWORD*)(&offset_start)),*(((DWORD*)(&offset_start))+1),*((DWORD*)(&bytes)),*(((DWORD*)(&bytes))+1))){
 //failed
 static int32 e;
 e=GetLastError();
 if ((e==5)||(e==33)) return -7;//permission denied
 //showvalue(e);
 return -9;//assume: path/file access error
 }
 return 0;
 #endif

return -1;
}

int32 gfs_unlock(int32 i,int64 offset_start,int64 offset_end){
//if offset_start==-1, 'from beginning' (typically offset 0) is assumed
//if offset_end==-1, 'to end/infinity' is assumed
//range is inclusive of start and end
if (!gfs_validhandle(i)) return -2;//invalid handle
static gfs_file_struct *f;
f=&gfs_file[i];

if (offset_start==-1) offset_start=0;
if (offset_start<0) return -4;//illegal function call
if (offset_end<-1) return -4;//illegal function call
//note: -1 equates to highest uint64 value (infinity)
//      All other negative end values are illegal

 #ifdef QB64_WINDOWS
 static gfs_file_win_struct *f_w;
 f_w=&gfs_file_win[i];
 uint64 bytes;
 bytes=offset_end;
 if (bytes!=-1) bytes=bytes-offset_start+1;
 if (!UnlockFile(f_w->file_handle,*((DWORD*)(&offset_start)),*(((DWORD*)(&offset_start))+1),*((DWORD*)(&bytes)),*(((DWORD*)(&bytes))+1))){
 //failed
 static int32 e;
 e=GetLastError(); 
 if ((e==5)||(e==33)||(e==158)) return -7;//permission denied
 //showvalue(e);
 return -9;//assume: path/file access error
 }
 return 0;
 #endif

return -1;
}

void sub_lock(int32 i,int64 start,int64 end,int32 passed){
if (new_error) return;
if (gfs_fileno_valid(i)!=1){error(52); return;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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 (new_error) return;
if (gfs_fileno_valid(i)!=1){error(52); return;}//Bad file name or number
i=gfs_fileno[i];//convert fileno to gfs index
static gfs_file_struct *gfs;
gfs=&gfs_file[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
}

}

int32 func__screenimage(){

#ifdef QB64_WINDOWS

static HWND hwnd;
hwnd=GetDesktopWindow();
static RECT rect;
GetWindowRect(hwnd,&rect);
static int32 x,y;
x=rect.right-rect.left;
y=rect.bottom-rect.top;
static HDC hdc;
hdc=GetDC(hwnd);
static HDC hdc2;
hdc2=CreateCompatibleDC(hdc);
static HBITMAP bitmap;
bitmap=CreateCompatibleBitmap(hdc,x,y);

SelectObject(hdc2,bitmap);
BitBlt(        hdc2, 
               0,0, 
               x,y, 
               hdc, 
               0,0,
               SRCCOPY);


    static BITMAPFILEHEADER   bmfHeader;  
    static BITMAPINFOHEADER   bi;
    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = x;    
    bi.biHeight = y;  
    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,i3;
i2=func__dest();
i=func__newimage(x,y,32,1);
sub__dest(i);
GetDIBits(hdc2,bitmap,0,y,write_page->offset,(BITMAPINFO*)&bi, DIB_RGB_COLORS);
sub__setalpha(255,NULL,NULL,NULL,0);

i3=func__newimage(x,y,32,1);
sub__dontblend(i,1);
sub__dontblend(i3,1);
sub__putimage(NULL,0,y-1,NULL,x-1,0,i,i3,NULL,NULL,NULL,NULL,NULL,NULL,15);
sub__freeimage(i,1);
sub__blend(i3,1);
sub__dest(i2);

DeleteObject(bitmap);
DeleteDC(hdc2);
ReleaseDC(NULL,hdc);

return i3;

/*
//   FUNCTION: CaptureAnImage(HWND hWnd)
//
//   PURPOSE: Captures a screenshot into a window and then saves it in a .bmp file.
//
//   COMMENTS: 
//
//      Note: This sample will attempt to create a file called captureqwsx.bmp 
//        

int CaptureAnImage(HWND hWnd)
{
    HDC hdcScreen;
    HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    hdcScreen = GetDC(NULL);
    hdcWindow = GetDC(hWnd);

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcWindow); 

    if(!hdcMemDC)
    {
        MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
        goto done;
    }

    // Get the client area for size calculation
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);

    //This is the best stretch mode
    SetStretchBltMode(hdcWindow,HALFTONE);

    //The source DC is the entire screen and the destination DC is the current window (HWND)
    if(!StretchBlt(hdcWindow, 
               0,0, 
               rcClient.right, rcClient.bottom, 
               hdcScreen, 
               0,0,
               GetSystemMetrics (SM_CXSCREEN),
               GetSystemMetrics (SM_CYSCREEN),
               SRCCOPY))
    {
        MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
        goto done;
    }
    
    // Create a compatible bitmap from the Window DC
    hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
    
    if(!hbmScreen)
    {
        MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
        goto done;
    }

    // Select the compatible bitmap into the compatible memory DC.
    SelectObject(hdcMemDC,hbmScreen);
    
    // Bit block transfer into our compatible memory DC.
    if(!BitBlt(hdcMemDC, 
               0,0, 
               rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, 
               hdcWindow, 
               0,0,
               SRCCOPY))
    {
        MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
        goto done;
    }

    // Get the BITMAP from the HBITMAP
    GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
     
    BITMAPFILEHEADER   bmfHeader;    
    BITMAPINFOHEADER   bi;
     
    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = bmpScreen.bmWidth;    
    bi.biHeight = bmpScreen.bmHeight;  
    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;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
    // have greater overhead than HeapAlloc.
    HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize); 
    char *lpbitmap = (char *)GlobalLock(hDIB);    

    // Gets the "bits" from the bitmap and copies them into a buffer 
    // which is pointed to by lpbitmap.
    GetDIBits(hdcWindow, hbmScreen, 0,
        (UINT)bmpScreen.bmHeight,
        lpbitmap,
        (BITMAPINFO *)&bi, DIB_RGB_COLORS);

    // A file is created, this is where we will save the screen capture.
    HANDLE hFile = CreateFile(L"captureqwsx.bmp",
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, NULL);   
    
    // Add the size of the headers to the size of the bitmap to get the total file size
    DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
 
    //Offset to where the actual bitmap bits start.
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); 
    
    //Size of the file
    bmfHeader.bfSize = dwSizeofDIB; 
    
    //bfType must always be BM for Bitmaps
    bmfHeader.bfType = 0x4D42; //BM   
 
    DWORD dwBytesWritten = 0;
    WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
    
    //Unlock and Free the DIB from the heap
    GlobalUnlock(hDIB);    
    GlobalFree(hDIB);

    //Close the handle for the file that was created
    CloseHandle(hFile);
       
    //Clean up
done:
    DeleteObject(hbmScreen);
    ReleaseDC(hWnd, hdcMemDC);
    ReleaseDC(NULL,hdcScreen);
    ReleaseDC(hWnd,hdcWindow);

    return 0;
}
*/

#endif

return -1;
}

void sub__screenclick(int32 x,int32 y){

#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;
input.mi.dwFlags=MOUSEEVENTF_LEFTUP;
SendInput(1,&input,sizeof(INPUT));

#endif

}

void sub__screenprint(qbs *txt){

#ifdef QB64_WINDOWS

static int32 i,s,x,vk,c;

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

}




int main( int argc, char* argv[] )
{

ontimer[0].allocated=1;
ontimer[0].id=0;
ontimer[0].state=0;
ontimer[0].active=0;

static long i,i2,i3,i4;
static unsigned char c,c2,c3,c4;
static long x,x2,x3,x4;
static long y,y2,y3,y4;
static long z,z2,z3,z4;
static float f,f2,f3,f4;
static unsigned char *cp,*cp2,*cp3,*cp4;

//switch to directory of this EXE file
#ifdef QB64_WINDOWS
#ifndef QB64_MICROSOFT
static char *exepath=(char*)malloc(65536);
GetModuleFileName(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);
#endif
#endif


{
/* For bounds check on numeric ENVIRON$ */
char **p = envp;
while (*p++);
environ_count = p - envp;
}

memset(font,0,sizeof(font));

fontwidth[8]=8; fontwidth[14]=8; fontwidth[16]=8;
fontheight[8]=8; fontheight[14]=14; fontheight[16]=16;
fontflags[8]=16; fontflags[14]=16; fontflags[16]=16;//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]=16; fontflags[14+1]=16; fontflags[16+1]=16;//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));

//unsigned char *asmcodep=(unsigned char*)&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;

//WINDOWS SPECIFIC CONTENT
FreeConsole();
//END WINDOWS SPECIFIC CONTENT

//struct tm:
//        int tm_sec;     /* seconds after the minute - [0,59] */
//        int tm_min;     /* minutes after the hour - [0,59] */
//        int tm_hour;    /* hours since midnight - [0,23] */
//        int tm_mday;    /* day of the month - [1,31] */
//        int tm_mon;     /* months since January - [0,11] */
//        int tm_year;    /* years since 1900 */
//        int tm_wday;    /* days since Sunday - [0,6] */
//        int tm_yday;    /* days since January 1 - [0,365] */
//        int tm_isdst;   /* daylight savings time flag */
tm *qb64_tm;
time_t qb64_tm_val;
time_t qb64_tm_val_old;
//call both timing routines as close as possible to each other to maximize accuracy
//wait for second "hand" to "tick over"/move
time(&qb64_tm_val_old);
//note: time() returns the time as seconds elapsed since midnight, January 1, 1970, or -1 in the case of an error. 
if (qb64_tm_val_old!=-1){
	do{
	time(&qb64_tm_val);
	}while(qb64_tm_val==qb64_tm_val_old);
}else{
	qb64_tm_val=0;//time unknown! (set to midnight, January 1, 1970)
}
sdl_firsttimervalue=SDL_GetTicks();
//calculate localtime as milliseconds past midnight
qb64_tm=localtime(&qb64_tm_val);
/* re: localtime()
Return a pointer to the structure result, or NULL if the date passed to the function is:
Before midnight, January 1, 1970.
After 03:14:07, January 19, 2038, UTC (using _time32 and time32_t).
After 23:59:59, December 31, 3000, UTC (using _time64 and __time64_t).
*/
if (qb64_tm){
	qb64_firsttimervalue=qb64_tm->tm_hour*3600+qb64_tm->tm_min*60+qb64_tm->tm_sec;
	qb64_firsttimervalue*=1000;
}else{	
	qb64_firsttimervalue=0;//time unknown! (set to midnight)
}
/* Used as folows for calculating TIMER value:
x=SDL_GetTicks();
x-=sdl_firsttimervalue;
x+=qb64_firsttimervalue;
*/



for (i=0;i<32;i++) sub_file_print_spaces[i]=32;


port60h_event[0]=128;


mem_static_size=1048576;//1MEG
mem_static=(unsigned char*)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=(unsigned long)&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;

unknown_opcode_mess=qbs_new(0,0);
qbs_set(unknown_opcode_mess,qbs_new_txt_len("Unknown Opcode (  )\0",20));

i=argc;
if (i>1){
//calculate required size of COMMAND$ string
i2=0;
for (i=1;i<argc;i++){
i2+=strlen(argv[i]);
if (i!=1) i2++;//for a space
}
//create COMMAND$ string
func_command_str=qbs_new(i2,0);
//build COMMAND$ string
i3=0;
for (i=1;i<argc;i++){
if (i!=1){func_command_str->chr[i3]=32; i3++;}
memcpy(&func_command_str->chr[i3],argv[i],strlen(argv[i])); i3+=strlen(argv[i]);
}
}else{
func_command_str=qbs_new(0,0);
}


//init SDL
if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_CDROM|SDL_INIT_TIMER) < 0 ) {
        fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
        exit(100);
    }
atexit(SDL_Quit);



//init truetype .ttf/.fon font library
if (TTF_Init()==-1) exit(7000);
atexit(TTF_Quit);



sdl_modes=SDL_ListModes(NULL,SDL_FULLSCREEN|SDL_HWSURFACE);
//count modes
if(sdl_modes){
if(sdl_modes==(SDL_Rect **)-1){
anymode=1;//*unhandled!
}else{
for(i=0;sdl_modes[i];++i){
nmodes++;
}
}
}//modes
modes=(SDL_Rect*)malloc(nmodes*sizeof(SDL_Rect));
for(i=0;sdl_modes[i];++i){
modes[i]=*sdl_modes[i];

}


//SDL_Rect *modes=NULL;



memset(snd,0,sizeof(snd));

//set key repeat rate
SDL_EnableKeyRepeat(500,30); key_repeat_on=1;
//safer to use default key repeat rates which aren't dissimilar to those used above


//enable unicode
SDL_EnableUNICODE(1);

//init fake keyb. cyclic buffer
cmem[0x41a]=30; cmem[0x41b]=0; //head
cmem[0x41c]=30; cmem[0x41d]=0; //tail

#ifdef QB64_WINDOWS
 SDL_WM_SetIcon(SDL_LoadBMP(".\\data\\qb64icon.bmp"), NULL);
#else
 SDL_WM_SetIcon(SDL_LoadBMP("./data/qb64icon.bmp"), NULL);
#endif

SDL_WM_SetCaption("Untitled",NULL);



int int_x,int_y;
SDL_GetMouseState(&int_x,&int_y);
mouse_messages[0].x=int_x;
mouse_messages[0].y=int_y;
mouse_messages[0].buttons=0;

//SDL_Cursor * mycursor=SDL_CreateCursor
		//(Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y);
SDL_Cursor * mycursor=init_system_cursor(arrow);
SDL_SetCursor(mycursor);

ifstream fh;

//check for required files
#ifdef QB64_WINDOWS
 fh.open (".\\data\\qb64icon.bmp",ios::binary|ios::out|ios::in);
#else
 fh.open ("./data/qb64icon.bmp",ios::binary|ios::out|ios::in);
#endif
if (fh.is_open()){
fh.close();
}else{MessageBox(NULL,"./data/qb64icon.bmp","Common file missing!",MB_OK); exit(1);}


//load the default 256 color palette
#ifdef QB64_WINDOWS
 fh.open (".\\data\\qb64.pal",ios::binary|ios::out|ios::in);
#else
 fh.open ("./data/qb64.pal",ios::binary|ios::out|ios::in);
#endif
if (fh.is_open()){
fh.read((char*)&palette_256,256*4);
fh.close();
}else{MessageBox(NULL,"./data/qb64.pal","Common file missing!",MB_OK); exit(1);}
for(i=0;i<256;i++) palette_256[i]|=0xFF000000;

#ifdef QB64_WINDOWS
 fh.open (".\\data\\qb64ega.pal",ios::binary|ios::out|ios::in);
#else
 fh.open ("./data/qb64ega.pal",ios::binary|ios::out|ios::in);
#endif
if (fh.is_open()){
fh.read((char*)&palette_64,64*4);
fh.close();
}else{MessageBox(NULL,"./data/qb64ega.pal","Common file missing!",MB_OK); exit(1);}
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;

//load the 8x8 character set
#ifdef QB64_WINDOWS
 fh.open (".\\data\\charset8.raw",ios::binary|ios::out|ios::in);
#else
 fh.open ("./data/charset8.raw",ios::binary|ios::out|ios::in);
#endif
if (fh.is_open()){
fh.read((char*)&charset8x8,256*8*8);
fh.close();
}else{MessageBox(NULL,"./data/charset8.raw","Common file missing!",MB_OK); exit(1);}

//load the 8x16 character set
#ifdef QB64_WINDOWS
 fh.open (".\\data\\chrset16.raw",ios::binary|ios::out|ios::in);
#else
 fh.open ("./data/chrset16.raw",ios::binary|ios::out|ios::in);
#endif
if (fh.is_open()){
fh.read((char*)&charset8x16,256*16*8);
fh.close();
}else{MessageBox(NULL,"./data/chrset16.raw","Common file missing!",MB_OK); exit(1);}


//get 32bit argb pixel format
ts=SDL_CreateRGBSurface(NULL,8,8,32,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
pixelformat32=*ts->format;
SDL_FreeSurface(ts);
ts=SDL_CreateRGBSurface(NULL,8,8,8,NULL,NULL,NULL,NULL);
pixelformat8=*ts->format;
SDL_FreeSurface(ts);

qbg_screen(0,NULL,NULL,NULL,NULL,1);
width8050switch=1;//reaffirm switch reset by above command

    thread = SDL_CreateThread (QBMAIN, NULL  );
    if ( thread == NULL ) {
        fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError());
        return 0;
    }

    thread2 = SDL_CreateThread (TIMERTHREAD, NULL  );
    if ( thread2 == NULL ) {
        fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError());
        return 0;
    }


lock_display_required=1;



long update=0;//0=update input,1=update display

main_loop:

//update timer bytes in cmem
static unsigned long cmem_ticks;
static double cmem_ticks_double;

cmem_ticks=SDL_GetTicks();
cmem_ticks-=sdl_firsttimervalue;
cmem_ticks+=qb64_firsttimervalue;
//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;
if (key_repeat_on){SDL_EnableKeyRepeat(0,0); key_repeat_on=0;}
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) goto end_program;

update^=1;//toggle update

//buffered sound/play notes
//(safe even if sndsetup not called)
if (sndqueue_first!=sndqueue_next){
if (sndqueue_played==0){
 //note: this only happens the first time a sound is ever played (through this queue)
 sndqueue_played=1;
 if (sndqueue_first==sndqueue_wait){suspend_program^=2; sndqueue_wait=-1;}
 sub__sndplay(sndqueue[sndqueue_first]);
 sndqueue_first++;
}else{
 i=sndqueue_first-1; if (i==-1) i=sndqueue_lastindex;
 if (!func__sndplaying(sndqueue[i])){
 sub__sndclose(sndqueue[i]);
 if (sndqueue_first==sndqueue_wait){suspend_program^=2; sndqueue_wait=-1;}
 sub__sndplay(sndqueue[sndqueue_first]);
 sndqueue_first++; if (sndqueue_first>sndqueue_lastindex) sndqueue_first=0;
 }
}
}

//update display?
if (update==1){
update_display_only:
if (autodisplay) display();
}//update==1

vertical_retrace_in_progress=0;

if (shell_call_in_progress) goto main_loop;

if (update==0){

//correct SDL key repeat error after losing input focus while a key is held
//occurs when switching to another window, SHELL-ing, etc.
//SDL_APPMOUSEFOCUS	The application has mouse focus.
//SDL_APPINPUTFOCUS	The application has keyboard focus
//SDL_APPACTIVE	The application is visible
static long state;
state=SDL_GetAppState();
if ( (!(state&SDL_APPINPUTFOCUS)) || (!(state&SDL_APPACTIVE)) ){//lost focus
if (key_repeat_on){SDL_EnableKeyRepeat(0,0); key_repeat_on=0;}
}else{
//active
if (!key_repeat_on){SDL_EnableKeyRepeat(500,30); key_repeat_on=1;}
}

//update input
static long scancode;
static const unsigned char sdlk_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
};

static SDL_Event event;

    while ( SDL_PollEvent(&event) ) {

asciicode_force=0;
        switch (event.type) {
/*
case SDL_ACTIVEEVENT:
 #ifdef QB64_WINDOWS
 if (shell_call_in_progress){
 if (event.active.gain==0){//lost focus
 if (full_screen){ 
 SDL_WM_IconifyWindow();
 }//full screen
 }//lost focus
 }//shell
 #endif
break;
*/

case SDL_KEYUP:
if (event.key.keysym.sym){//ignore NULL (unknown key)
if (event.key.keysym.sym==SDLK_PRINT) goto ignore_sdl_keyup;
if (event.key.keysym.sym==SDLK_SYSREQ) goto ignore_sdl_keyup;

sdl_shiftstate=event.key.keysym.mod;//update sdl shiftstate
if (event.key.keysym.sym<65536){
scancode=-1; if (event.key.keysym.sym<1024) scancode=sdlk_2_scancode[event.key.keysym.sym];
keyon[event.key.keysym.sym]=0;
//prepare virtual locks for future toggle
if (event.key.keysym.sym==SDLK_SCROLLOCK) sdl_scroll_lock_prepared=1;
if (event.key.keysym.sym==SDLK_INSERT) sdl_insert_prepared=1;
if (port60h_events==256){memmove(port60h_event,port60h_event+1,255); port60h_events=255;}
port60h_event[port60h_events]=scancode+128;
port60h_events++;
}
 if ((event.key.keysym.sym==SDLK_LALT)||(event.key.keysym.sym==SDLK_RALT)){
 if (asciicode_reading){
 asciicode_reading=0; 
 asciicode_force=1;
 event.key.keysym.sym=(SDLKey)65536;//unknown sym
 event.key.keysym.unicode=asciicode_value&255;
 asciicode_value=0;
 goto pushkey; 
 }
 }
}
ignore_sdl_keyup:;
break;

case SDL_KEYDOWN:
if (event.key.keysym.sym){//ignore 0 (unknown key)

sdl_shiftstate=event.key.keysym.mod;//update sdl shiftstate
pushkey:
sleep_break=1;
static unsigned long key;
key=event.key.keysym.sym;
/*
(&H417 in QB)
40:0017   2  Keyboard status bits; see Keyboard Shift Status Flags
40:0019   1  Current (accumulating) value of Alt+numpad pseudo-key input;
             normally 0.  When [Alt] is released, value is stored in
             keyboard buffer at 001e.
40:001a   2  Addr of keyboard buffer head (keystroke at that addr is next)
40:001c   2  Address of keyboard buffer tail
***range from 30 to 60 even number only***
***if head=tail then no data is available***
40:001e  32  Keyboard buffer.  BIOS stores keystrokes here (head and tail
             point to addresses from 041eH to 043dH inclusive).
***first byte is ASCII CHR or 0***
***second byte is the scancode***
*/
//"If the high 9 bits of the character are 0, then this maps to the equivalent ASCII character"
if (event.key.keysym.sym<65536){
scancode=-1; if (event.key.keysym.sym<1024) scancode=sdlk_2_scancode[event.key.keysym.sym];
keyon[event.key.keysym.sym]=1;
if (port60h_events==256){memmove(port60h_event,port60h_event+1,255); port60h_events=255;}
port60h_event[port60h_events]=scancode;
port60h_events++;
}



//update virtual locks
if ((event.key.keysym.sym==SDLK_SCROLLOCK)&&(sdl_scroll_lock_prepared)){sdl_scroll_lock=(sdl_scroll_lock+1)&1; sdl_scroll_lock_prepared=0;}
if ((event.key.keysym.sym==SDLK_INSERT)&&(sdl_insert_prepared)){sdl_insert=(sdl_insert+1)&1; sdl_insert_prepared=0;}
//ALT-ENTER handling
if (keyon[SDLK_RALT]||keyon[SDLK_LALT]){
if ((event.key.keysym.sym==SDLK_KP_ENTER)||(event.key.keysym.sym==SDLK_RETURN)){
full_screen_toggle++;
goto silent_key;
}
}
//CTRL-BREAK handling
if (keyon[SDLK_RCTRL]||keyon[SDLK_LCTRL]){
if ((event.key.keysym.sym==SDLK_BREAK)||(event.key.keysym.sym==SDLK_PAUSE)||(event.key.keysym.sym==302)){//scroll lock can occur instead of break!
if (exit_blocked){exit_value|=2; goto silent_key;}
goto end_program;
}
}
/* Skip special keys
  SDLK_NUMLOCK		= 300,
	SDLK_CAPSLOCK		= 301,
	SDLK_SCROLLOCK		= 302,
	SDLK_RSHIFT		= 303,
	SDLK_LSHIFT		= 304,
	SDLK_RCTRL		= 305,
	SDLK_LCTRL		= 306,
	SDLK_RALT		= 307,
  SDLK_LALT		= 308,
*/


if ((key>=300)&&(key<=308)){
goto silent_key;
}


//ALT+number(ascii-code)
//note: "Compose" an ASCII code for digits typed on the numeric keypad while ALT is held down. The code is the number (modulo 256).
if ((event.key.keysym.sym==SDLK_LALT)||(event.key.keysym.sym==SDLK_RALT)){
asciicode_value=0;
asciicode_reading=0;
}
/* Numeric keypad
	SDLK_KP0		= 256,
	SDLK_KP1		= 257,
	SDLK_KP2		= 258,
	SDLK_KP3		= 259,
	SDLK_KP4		= 260,
	SDLK_KP5		= 261,
	SDLK_KP6		= 262,
	SDLK_KP7		= 263,
	SDLK_KP8		= 264,
	SDLK_KP9		= 265,*/
if (keyon[SDLK_RALT]||keyon[SDLK_LALT]){
if ((event.key.keysym.sym>=256)&&(event.key.keysym.sym<=265)){
asciicode_value*=10;
asciicode_value+=(event.key.keysym.sym-256);
asciicode_reading=1;
goto silent_key;
}
}

//BREAK handling
if ((event.key.keysym.sym==SDLK_BREAK)||(event.key.keysym.sym==SDLK_PAUSE)){
/*MLP
cerr<<"\n new debug report:";
cerr<<"\n qbs_list_lasti="; cerr<<qbs_list_lasti;
cerr<<"\n qbs_list_nexti="; cerr<<qbs_list_nexti;
cerr<<"\n qbs_tmp_list_lasti="; cerr<<qbs_tmp_list_lasti;
cerr<<"\n qbs_tmp_list_nexti="; cerr<<qbs_tmp_list_nexti;
cerr<<"\n qbs_data_size="; cerr<<qbs_data_size;
cerr<<"\n qbs_sp="; cerr<<qbs_sp;
cerr<<"\n qbshlp1="; cerr<<qbshlp1;
static qbs *dbgs;
for (x=0;x<dbglisti;x++){
for (x2=1;x2<qbs_malloc_freed_num;x2++){
if (qbs_malloc_freed[x2]==dbglist[x]) goto freed;
}//x2
dbgs=(qbs*)dbglist[x];
cerr<<"\n unfreed string:";
cerr<<"\n len:"; cerr<<dbgs->len;
cerr<<"\n dbgline:"; cerr<<dbgs->dbgl;
if (x3=dbgs->len){
if (x3>100) x3=100;
x4=dbgs->chr[x3-1];
dbgs->chr[x3-1]=0;
cerr<<"\n data ["; cerr<<dbgs->chr; cerr<<"]";
dbgs->chr[x3-1]=x4;
}//len
freed:;
}//x
*/
suspend_program|=1;
qbevent=1;
goto silent_key;
}else{
if (suspend_program&1){
suspend_program^=1;
goto silent_key;
}
}

if (asciicode_force) goto asciicode_forced;
if ((event.key.keysym.unicode&0xFF80)==0){
asciicode_forced:
c=event.key.keysym.unicode&0xFF;
if (c){

//patch to force Linux to recognise [Delete] key
//(was returning ASCII character 127)
#ifdef QB64_LINUX
if (c==127){
scancode=83;
goto forcescancodekey;
}
#endif

//add to cyclic buffer
i=cmem[0x41a];
i2=cmem[0x41c];
i3=i2+2;
if (i3==62) i3=30;
if (i!=i3){//fits in buffer
if (c==9){
if (sdl_shiftstate&(KMOD_LSHIFT|KMOD_RSHIFT)){x=15; goto specialscancode;}
}
if (sdl_shiftstate&(KMOD_LALT|KMOD_RALT)){
if (event.key.keysym.sym==SDLK_KP_PERIOD) goto silent_key;
if (event.key.keysym.sym==SDLK_KP_DIVIDE){x=164; goto specialscancode;}
if (event.key.keysym.sym==SDLK_KP_MULTIPLY){x=55; goto specialscancode;}
if (event.key.keysym.sym==SDLK_KP_MINUS){x=74; goto specialscancode;}
if (event.key.keysym.sym==SDLK_KP_PLUS){x=78; goto specialscancode;}
if (scancode==15){x=165; goto specialscancode;}//Tab
if ((scancode>=51)&&(scancode<=53)){x=scancode; goto specialscancode;}// , . /
if (scancode==14){x=14; goto specialscancode;}//Backspace
if (scancode==1){x=1; goto specialscancode;}//ESC
if (scancode==133){x=139; goto specialscancode;}//F11
if (scancode==134){x=140; goto specialscancode;}//F12
if ((scancode>=16)&&(scancode<=50)){
x=scancode; goto specialscancode;
}
if ((scancode>=2)&&(scancode<=13)){
x=scancode-2+120; goto specialscancode;
}
}
cmem[0x400+i2]=c;
cmem[0x400+i2+1]=scancode;
cmem[0x41c]=i3;//fix tail location
goto gotkey;
}
}//c!=0
}else{
//printf("An International Character.\n");
goto gotkey;
}

//ASCII translation not possible... pass CHR$(0)+scan code
if (event.key.keysym.sym==SDLK_SYSREQ) goto silent_key;
forcescancodekey:
i=cmem[0x41a];
i2=cmem[0x41c];
i3=i2+2;
if (i3==62) i3=30;
if (i!=i3){//buffer is not full
x=scancode;
//prioritise alt(most)->ctrl->shift(least)
if (sdl_shiftstate&(KMOD_LALT|KMOD_RALT)){
//alt held
if (event.key.keysym.sym==SDLK_KP_PERIOD) goto silent_key;
if (event.key.keysym.sym==SDLK_KP_DIVIDE){x=164; goto specialscancode;}
if (event.key.keysym.sym==SDLK_KP_MULTIPLY){x=55; goto specialscancode;}
if (event.key.keysym.sym==SDLK_KP_MINUS){x=74; goto specialscancode;}
if (event.key.keysym.sym==SDLK_KP_PLUS){x=78; goto specialscancode;}
if (scancode==15){x=165; goto specialscancode;}//Tab
if ((scancode>=51)&&(scancode<=53)){x=scancode; goto specialscancode;}// , . /
if (scancode==14){x=14; goto specialscancode;}//Backspace
if (scancode==1){x=1; goto specialscancode;}//ESC
if (scancode==133){x=139; goto specialscancode;}//F11
if (scancode==134){x=140; goto specialscancode;}//F12
if ((scancode>=2)&&(scancode<=13)){
x=scancode-2+120; goto specialscancode;
}
if ((scancode>=16)&&(scancode<=50)){
x=scancode; goto specialscancode;
}
//review following?:
if ((x>=59)&&(x<=68)){x+=45; goto specialscancode;}
if ((x>=71)&&(x<=83)){x=x-71+151; goto specialscancode;}
}else{//alt not held
if (sdl_shiftstate&(KMOD_LCTRL|KMOD_RCTRL)){
//ctrl held
if (event.key.keysym.sym==SDLK_PRINT){x=148; goto specialscancode;}//Prt-Scrn
if (event.key.keysym.sym==SDLK_KP_DIVIDE){x=149; goto specialscancode;}
if (event.key.keysym.sym==SDLK_KP_MULTIPLY){x=150; goto specialscancode;}
if (scancode==41) goto silent_key;//~ or `
if (scancode==2) goto silent_key;//1
if ((scancode>=4)&&(scancode<=6)) goto silent_key;//3,4,5
if (scancode==7){//6
cmem[0x400+i2]=30; cmem[0x400+i2+1]=scancode; cmem[0x41c]=i3; goto gotkey;
}
if ((scancode>=8)&&(scancode<=11)) goto silent_key;//7,8,9,0
if (scancode==12){//-
cmem[0x400+i2]=31; cmem[0x400+i2+1]=scancode; cmem[0x41c]=i3; goto gotkey;
}
if (scancode==13) goto silent_key;//= or +
if (scancode==15){x=148; goto specialscancode;}
if (scancode==39) goto silent_key;//; or :
if (scancode==40) goto silent_key;//' or "
if ((scancode>=51)&&(scancode<=53)) goto silent_key;//,,<,.,>,/,?
if (scancode==69) goto silent_key;//num-lock
if (scancode==133){x=137; goto specialscancode;}
if (scancode==134){x=138; goto specialscancode;}
if (scancode==1){//ESC (*cannot be tested in windows)
cmem[0x400+i2]=27; cmem[0x400+i2+1]=scancode; cmem[0x41c]=i3; goto gotkey;
}
//review following?:
if ((x>=59)&&(x<=68)){x+=35; goto specialscancode;}
if (x==55){x=114; goto specialscancode;}
if (x==75){x=115; goto specialscancode;}
if (x==77){x=116; goto specialscancode;}
if (x==79){x=117; goto specialscancode;}
if (x==81){x=118; goto specialscancode;}
if (x==71){x=119; goto specialscancode;}
if (x==73){x=132; goto specialscancode;}
if (x==72){x=141; goto specialscancode;}
if (x==74){x=142; goto specialscancode;}
if (x==76){x=143; goto specialscancode;}
if (x==78){x=144; goto specialscancode;}
if (x==80){x=145; goto specialscancode;}
if (x==82){x=146; goto specialscancode;}
if (x==83){x=147; goto specialscancode;}
}else{//ctrl not held
if (sdl_shiftstate&(KMOD_LSHIFT|KMOD_RSHIFT)){
//shift held
if (scancode==133){x=135; goto specialscancode;}//F11
if (scancode==134){x=136; goto specialscancode;}//F12
if ((event.key.keysym.sym>=SDLK_KP0)&&(event.key.keysym.sym<=SDLK_KP9)){//SHIFT+numpad->number's ASCII value
cmem[0x400+i2]=event.key.keysym.sym-SDLK_KP0+48;
cmem[0x400+i2+1]=scancode;//(unused)
cmem[0x41c]=i3;//fix tail location
goto gotkey;
}
if ((event.key.keysym.sym==SDLK_KP_PERIOD)){//SHIFT+numpad .->"."
cmem[0x400+i2]=46;
cmem[0x400+i2+1]=scancode; cmem[0x41c]=i3; goto gotkey;
}
//review following?:
if ((x>=59)&&(x<=68)){x+=25; goto specialscancode;}
}//shift held
}//alt not held
}//ctrl not held
if (event.key.keysym.sym==SDLK_PRINT) goto silent_key;
specialscancode:



cmem[0x400+i2]=0;
cmem[0x400+i2+1]=x;
cmem[0x41c]=i3;//fix tail location
goto gotkey;
}
gotkey:;
silent_key:;
}
ignore_key:;
break;

case SDL_MOUSEMOTION:
last_mouse_message++; if (last_mouse_message>1023) last_mouse_message=0;
//delete oldest message if too many exist in the queue
if (last_mouse_message==current_mouse_message){
current_mouse_message++; if (current_mouse_message>1023) current_mouse_message=0;
}
mouse_messages[last_mouse_message].x=event.motion.x;
mouse_messages[last_mouse_message].y=event.motion.y;
mouse_messages[last_mouse_message].buttons=event.motion.state;
break;

case SDL_MOUSEBUTTONUP:
static unsigned long oldbuttonstate;
oldbuttonstate=mouse_messages[last_mouse_message].buttons;
last_mouse_message++; if (last_mouse_message>1023) last_mouse_message=0;
//delete oldest message if too many exist in the queue
if (last_mouse_message==current_mouse_message){
current_mouse_message++; if (current_mouse_message>1023) current_mouse_message=0;
}
mouse_messages[last_mouse_message].x=event.button.x;
mouse_messages[last_mouse_message].y=event.button.y;
oldbuttonstate=oldbuttonstate^(1<<(event.button.button-1));
mouse_messages[last_mouse_message].buttons=oldbuttonstate;
break;

case SDL_MOUSEBUTTONDOWN:
static unsigned long oldbuttonstate2;
oldbuttonstate2=mouse_messages[last_mouse_message].buttons;
last_mouse_message++; if (last_mouse_message>1023) last_mouse_message=0;
//delete oldest message if too many exist in the queue
if (last_mouse_message==current_mouse_message){
current_mouse_message++; if (current_mouse_message>1023) current_mouse_message=0;
}
mouse_messages[last_mouse_message].x=event.button.x;
mouse_messages[last_mouse_message].y=event.button.y;
oldbuttonstate2=oldbuttonstate2|(1<<(event.button.button-1));
mouse_messages[last_mouse_message].buttons=oldbuttonstate2;
break;

case SDL_QUIT:
if (exit_blocked) exit_value|=1; else goto end_program;
break;

}
}

//update shift status bits
/*
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 (keyon[SDLK_RSHIFT]) x|=1;
if (keyon[SDLK_LSHIFT]) x|=2;
if (keyon[SDLK_LCTRL]||keyon[SDLK_RCTRL]) x|=4;
if (keyon[SDLK_LALT]||keyon[SDLK_RALT]) x|=8;
//note: scroll lock state is emulated (off by default)
if (sdl_scroll_lock) x|=16;
if (sdl_shiftstate&KMOD_NUM) x|=32;
if (sdl_shiftstate&KMOD_CAPS) x|=64;
//note: insert state is emulated (off by default)
if (sdl_insert) x|=128;
cmem[0x417]=x;
/*
typedef enum {
	KMOD_NONE  = 0x0000,
	KMOD_LSHIFT= 0x0001,
	KMOD_RSHIFT= 0x0002,
	KMOD_LCTRL = 0x0040,
	KMOD_RCTRL = 0x0080,
	KMOD_LALT  = 0x0100,
	KMOD_RALT  = 0x0200,
	KMOD_LMETA = 0x0400,
	KMOD_RMETA = 0x0800,
	KMOD_NUM   = 0x1000,
	KMOD_CAPS  = 0x2000,
	KMOD_MODE  = 0x4000,
	KMOD_RESERVED = 0x8000
} SDLMod;
*/

/*
0:418h                   Extended Shift Status
 (interpret the work pressed as "being held down")
               7 6 5 4 3 2 1 0
               x . . . . . . .      Ins key is pressed
               . x . . . . . .      Caps Lock key is pressed (returns same as caps for 417!!!, not held status)
               . . x . . . . .      Num Lock key is pressed (same as 417!)
               . . . x . . . .      Scroll Lock key is pressed
               . . . . x . . .      Pause key locked (always 0)
               . . . . . x . .      SysReq key is pressed (always 0)
               . . . . . . x .      Left Alt key is pressed (helps to distinguish)
               . . . . . . . x      Left Ctrl key is pressed
*/
x=0;
if (keyon[SDLK_LCTRL]) x|=1;
if (keyon[SDLK_LALT]) x|=2;
if (keyon[SDLK_SYSREQ]) x|=4;
if (keyon[SDLK_BREAK]||keyon[SDLK_PAUSE]) x|=8;
if (keyon[SDLK_SCROLLOCK]) x|=16;
if (keyon[SDLK_NUMLOCK]) x|=32;
if (keyon[SDLK_CAPSLOCK]) x|=64;
if (keyon[SDLK_INSERT]) 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 (keyon[SDLK_RCTRL]) x|=4;
if (keyon[SDLK_RALT]) x|=8;
x|=16;
cmem[0x496]=x;

if (shell_call_in_progress) goto main_loop;



//safe even if sndsetup not called
static double pos;
if (stream_limited){
if (stream_loaded){
if (stream_playing){//worth checking?
pos=func__sndgetpos(1);
if (pos>=stream_limit){
sub__sndstop(1);
stream_limited=0;
}}}}

}//update==0

goto main_loop;

end_program:
stop_program=1;
qbevent=1;
SDL_WaitThread(thread, NULL);
SDL_ShowCursor(0);
SDL_FreeCursor(mycursor);

exit(0);
}

//display updates the visual page onto the visible window/monitor
void display(){

//general variables
static long i,i2,i3,i4;
static unsigned char c,c2,c3,c4;
static long x,x2,x3,x4;
static long y,y2,y3,y4;
static long z,z2,z3,z4;
static float f,f2,f3,f4;
static unsigned char *cp,*cp2,*cp3,*cp4;

static unsigned char *pixeldata=(unsigned char*)malloc(1);
static long pixeldatasize=1;
static long paldata[256];
unsigned long *pixel;

frame++;//~32 fps

if (lock_display==1){lock_display=2; Sleep(0);}
if (lock_display==0){


//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 long mode_square,mode_stretch;

//find best fullscreen mode(s) (eg. square/"1:1", stretched)
mode_square=-1;
mode_stretch=-1;
z2=0x7FFFFFFF; z3=0x7FFFFFFF;
for (i=0;i<nmodes;i++){
if (modes[i].w>=640){
if (modes[i].w>=x&&modes[i].h>=y){
z=modes[i].w-x+modes[i].h-y;
 //square
 if (z<=z2){
 //is it square?
 if (modes[i].w/4==modes[i].h/3){
 mode_square=i; z2=z;
 }//square
 }//z2<=z
 //stretch
 if (z<=z3){
 mode_stretch=i; z3=z;
 }//z<=z3
}//>x,>y
}//ignore?
}//i

static long full_screen_change;
full_screen_change=0;

if (full_screen_set!=-1){
if (full_screen_set==0) goto full_screen0;
if (full_screen_set==1) goto full_screen1;
if (full_screen_set==2) goto full_screen2;
}

if (full_screen_toggle){
full_screen_toggle--;

if (full_screen==0){
full_screen1:
if (mode_stretch>-1){//can be displayed
full_screen=1;
 full_screen_change=1;
 screen_last_valid=0;
 if (!mouse_hideshow_called) SDL_ShowCursor(0);
}
goto full_screen_toggle_done;
}

if (full_screen==1){
full_screen2:
if (mode_square>-1&&mode_square!=mode_stretch){//usable 1:1 mode exists (that isn't same as stretched mode)
full_screen=2;
 full_screen_change=1;
 screen_last_valid=0;
 if (!mouse_hideshow_called) SDL_ShowCursor(0);
goto full_screen_toggle_done;
}
}
//back to windowed mode
full_screen0:
full_screen=0;
 full_screen_change=1;
 screen_last_valid=0;
 if (!mouse_hideshow_called) SDL_ShowCursor(1);
}
full_screen_toggle_done:
full_screen_set=-1;


//set the screen mode if necessary

x_offset=0; y_offset=0;
x_scale=1; y_scale=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];
}

//check for y-stretch flag?

if (full_screen){

//double res. of "small" surfaces to avoid video driver incompatibilities
if (x<=512&&y<=384){
x*=2; y*=2;
y_scale*=2; x_scale*=2;
}

x2=x; y2=y;
if (full_screen==1){
x=modes[mode_stretch].w; y=modes[mode_stretch].h;
}
if (full_screen==2){
x=modes[mode_square].w; y=modes[mode_square].h;
}

//calculate offsets
x_offset=(x-x2)/2; y_offset=(y-y2)/2;

}//full_screen



//adjust monitor resolution
if ((x!=x_monitor)||(y!=y_monitor)||full_screen_change){
x_monitor=x; y_monitor=y;
if (full_screen) z=SDL_FULLSCREEN; else z=0;
display_surface=SDL_SetVideoMode(x,y,32,z);
display_surface_offset=(unsigned long*)display_surface->pixels;
pixel=(unsigned long*)display_surface->pixels;//***remove later***
}
if (!screen_last_valid){
memset(display_surface->pixels,0,x_monitor*y_monitor*4);
}

//update the screen data

if (!display_page->compatible_mode){//text

//removed following line on 22/1/09, questionable performace gain vs visual loss
//if (frame&1) goto screen_refresh_unrequired;//~16fps for text mode

static long show_flashing_last=0;
static long show_cursor_last=0;
static long check_last;
static unsigned char *cp,*cp2,*cp_last;
static unsigned long *lp;
static long cx,cy;
static long cx_last=-1,cy_last=-1;
static long show_cursor;
static long show_flashing;
static unsigned char chr,col,chr_last,col_last;
static long screen_changed;
static long qbg_y_offset;


static long f,f_pitch,f_width,f_height;//font info
f=display_page->font; f_width=fontwidth[f]; f_height=fontheight[f];

//realloc buffer if necessary
i=display_page->width*display_page->height*2;
if (screen_last_size!=i){
free(screen_last);
screen_last=(unsigned char*)malloc(i);
screen_last_size=i;
}

check_last=screen_last_valid;
if (!check_last){
 //set pal_last (no prev pal was avilable to compare to)
 memcpy(&pal_last[0],display_page->pal,256*4);
}else{
 //if palette has changed, update pal_last and draw all characters
 if (memcmp(&pal_last[0],display_page->pal,256*4)){
 memcpy(&pal_last[0],display_page->pal,256*4);
 check_last=0;
 }
}

//calculate cursor position (base 0)
cx=display_page->cursor_x-1; cy=display_page->cursor_y-1;

//show cursor?
if (frame&4) show_cursor=1; else show_cursor=0;

//show flashing?
if (frame&8) show_flashing=1; else show_flashing=0;

qbg_y_offset=y_offset*x_monitor+x_offset;//the screen base offset
cp=display_page->offset;//read from
cp_last=screen_last;//written to for future comparisons

//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:
screen_changed=1;
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 (f>=32){//custom font
static SDL_Surface *ts=NULL;
static SDL_Color c;
c.r=255; c.g=255; c.b=255; c.unused=0;//dummy values
if (ts) SDL_FreeSurface(ts);//free previously created buffer

z=codepage437_to_unicode16[chr];
ts=TTF_RenderUNICODE_Solid(font[f],(Uint16*)&z,c);
//z=chr;
//ts=TTF_RenderText_Solid(font[f],(char*)&z,c);//8-bit, 0=clear, 1=text

cp2=(unsigned char*)ts->pixels;
f_pitch=ts->pitch-f_width;
}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
c2=(col>>4)&7;//background col
c3=col>>7;//flashing?
if (c3&&show_flashing) c=c2;
i2=display_page->pal[c];
i3=display_page->pal[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 long v1,v2;
static unsigned char from_bottom;//bottom is the 2nd bottom scanline in width ?x25
static unsigned char half_cursor;//if set, overrides all following values
static unsigned char size;//if 0, no cursor is drawn, if 255, from begin to bottom
static unsigned char 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 long 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=display_page->pal[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=display_page->pal[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=display_page->pal[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 (!screen_changed) goto screen_refresh_unrequired;
goto screen_refreshed;

}//text

if (display_page->bits_per_pixel==32){
//scaled refresh
if (x_scale==2){
static unsigned long *lp;
static unsigned long *lp2;
static unsigned long c;
lp=display_page->offset32;
lp2=display_surface_offset+x_monitor*y_offset+x_offset;
x2=display_page->width;
z=x2*8;
y2=display_page->height;
for (y=0;y<y2;y++){
for (x=0;x<x2;x++){
c=*lp++; *lp2++=c; *lp2++=c;
}
lp2+=(x_monitor-(x2*2));
if (y_scale==2){memcpy(lp2,lp2-x_monitor,z);lp2+=x_monitor;}
}
goto screen_refreshed;
}//x_scale==2
if (x_scale==1){
static unsigned long *lp;
static unsigned long *lp2;
lp=display_page->offset32;
lp2=display_surface_offset+x_monitor*y_offset+x_offset;
x2=display_page->width;
z=x2*4;
y2=display_page->height;
for (y=0;y<y2;y++){
memcpy(lp2,lp,z);
lp2+=x_monitor;
if (y_scale==2){memcpy(lp2,lp,z);lp2+=x_monitor;}
lp+=x2;
}
goto screen_refreshed;
}//x_scale==1
exit(48341);
}//32

//assume <=256 colors using palette

/*
//update SCREEN 10 palette
if (qbg_mode==10){
//pal_mode10[0-1][0-8]
i2=SDL_GetTicks()&512;
if (i2) i2=1;
for (i=0;i<=3;i++){
pal[i]=pal_mode10[i2][qbg_color_assign[i]];
}
}
*/

i=display_page->width*display_page->height;
i2=1<<display_page->bits_per_pixel;//unique colors
//data changed?
if (i!=pixeldatasize){
free(pixeldata);
pixeldata=(unsigned char*)malloc(i);
pixeldatasize=i;
goto update_display;
}
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 screen_refresh_unrequired;//no need to update display
update_display:
memcpy(pixeldata,display_page->offset,i);
memcpy(paldata,display_page->pal,i2*4);

if (x_scale==2){
static unsigned char *cp;
static unsigned long *lp2;
static unsigned long c;
cp=pixeldata;
lp2=display_surface_offset+x_monitor*y_offset+x_offset;
x2=display_page->width;
y2=display_page->height;
z=x2*8;
for (y=0;y<y2;y++){
for (x=0;x<x2;x++){
c=paldata[*cp++]; *lp2++=c; *lp2++=c;
}//x
lp2+=(x_monitor-(x2*2));
if (y_scale==2){memcpy(lp2,lp2-x_monitor,z);lp2+=x_monitor;}
}//y
goto screen_refreshed;
}

if (x_scale==1){
static unsigned char *cp;
static unsigned long *lp2;
static unsigned long c;
cp=pixeldata;
lp2=display_surface_offset+x_monitor*y_offset+x_offset;
x2=display_page->width;
y2=display_page->height;
z=x2*4;
for (y=0;y<y2;y++){
for (x=0;x<x2;x++){
*lp2++=paldata[*cp++];
}//x
lp2+=(x_monitor-x2);
if (y_scale==2){memcpy(lp2,lp2-x_monitor,z);lp2+=x_monitor;}
}//y
goto screen_refreshed;
}

error(4621);

screen_refreshed:
SDL_UpdateRect(display_surface,0,0,0,0);

screen_last_valid=1;
display_page_invalid:

/*
static long qbg_y_offset;//obselete?
qbg_y_offset=0;

//Video mode

x_offset=0; y_offset=0;
x_scale=1; y_scale=1;

if (qbg_text_only){
qbg_width=qbg_width_in_characters*qbg_character_width;
qbg_height=qbg_height_in_characters*qbg_character_height;
}
x=qbg_width; y=qbg_height;

//SCREEN 2/8 custom resolution
if (x==640&&y==200){
y_scale=2;
x=640; y=400;
}

x_monitor=x; y_monitor=y;

if (full_screen){

//fix 320x200 full screen resolution (SDL bug/incompatibility in low res. modes)
if (x==320&&y==200){
x_scale=2; y_scale=2;
x=640; y=400;
x_monitor=x; y_monitor=y;
}


//Check against available resolutions by priorities
//1. Exact match?
for (i=0;i<nmodes;i++){
if (modes[i]->w==x&&modes[i]->h==y) goto modeok;
}
//2. Same x, larger y?
y2=1000000;
for (i=0;i<nmodes;i++){
if (modes[i]->w==x){
if (modes[i]->h>y){
if (modes[i]->h<y2) y2=modes[i]->h;
}
}
}
if (y2!=1000000){
y_monitor=y2;
y_offset=(y_monitor-y)/2;
goto modeok;
}

//No mode seemed appropriate! Continue with default window resolution.

}//fullscreen
modeok:

if ((qbg_program_window_width!=x_monitor)||(qbg_program_window_height!=y_monitor)||(full_screen_in_use!=full_screen)){
qbg_program_window_width=x_monitor; qbg_program_window_height=y_monitor;


screen = SDL_SetVideoMode(qbg_program_window_width, qbg_program_window_height, 32, SDL_SWSURFACE|(SDL_FULLSCREEN*full_screen));

pixel=(unsigned long*)screen->pixels;
full_screen_in_use=full_screen;
}
if (!screen_last_valid){
memset(pixel,0,qbg_program_window_width*qbg_program_window_height);
}

if (qbg_text_only){
//if (frame&1) goto screen_refresh_unrequired;//~16fps for text mode
static long show_flashing_last=0;
static long show_cursor_last=0;
static long check_last;
static unsigned char *cp,*cp2,*cp_last;
static unsigned long *lp;
static long cx,cy;
static long cx_last=-1,cy_last=-1;
static long show_cursor;
static long show_flashing;
static unsigned char chr,col,chr_last,col_last;
static long screen_changed;

//realloc buffer if necessary
i=qbg_height_in_characters*qbg_width_in_characters*2;
if (screen_last_size!=i){
free(screen_last);
screen_last=(unsigned char*)malloc(i);
screen_last_size=i;
}

check_last=screen_last_valid;
if (!check_last){
 //set pal_last
 memcpy(&pal_last[0],&pal[0],256*4);
}else{
 //if palette has changed, update pal_last and draw all characters
 if (memcmp(&pal_last[0],&pal[0],256*4)){
 memcpy(&pal_last[0],&pal[0],256*4);
 check_last=0;
 }
}

//calculate cursor position (base 0)
cx=qbg_cursor_x; cy=qbg_cursor_y;
if (qbg_visual_page!=qbg_active_page){
cx=qbg_cursor_x_previous[qbg_visual_page]; cy=qbg_cursor_y_previous[qbg_visual_page];
}
cx--; cy--;

//show cursor?
if (frame&4) show_cursor=1; else show_cursor=0;

//show flashing?
if (frame&8) show_flashing=1; else show_flashing=0;

qbg_y_offset=(qbg_width*x_scale*y_offset);
cp=qbg_visual_page_offset;
cp_last=screen_last;

//outer loop
y2=0;
for (y=0;y<qbg_height_in_characters;y++){
x2=0;
for (x=0;x<qbg_width_in_characters;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:
screen_changed=1;
cp_last-=2; *cp_last=chr; cp_last++; *cp_last=col; cp_last++;

//draw the character
if (qbg_character_height==8){
cp2=&charset8x8[chr][0][0];
}else{
if (qbg_character_height==16){
cp2=&charset8x16[chr][0][0];
}else{//assume 14
cp2=&charset8x16[chr][1][0];
}
}
if (qbg_character_width==16) z2=1; else z2=0;
//set the correct color
c=col&0xF;//foreground col
c2=(col>>4)&7;//background col
c3=col>>7;//flashing?
if (c3&&show_flashing) c=c2;
i2=pal[c];
i3=pal[c2];
lp=pixel+y2*qbg_width_in_characters*qbg_character_width+x2+qbg_y_offset;
z=qbg_width_in_characters*qbg_character_width-qbg_character_width;

//inner loop
for (y3=0;y3<qbg_character_height;y3++){
for (x3=0;x3<qbg_character_width;x3++){
if (*cp2) *lp=i2; else *lp=i3;
if (z2){
if (x3&z2) cp2++;
}else{
cp2++;
}
lp++;
}
lp+=z;
}//y3,x3



//draw cursor?
if (qbg_cursor_show&&show_cursor&&(cx==x)&&(cy==y)){
static long v1,v2;
static unsigned char from_bottom;//bottom is the 2nd bottom scanline in width ?x25
static unsigned char half_cursor;//if set, overrides all following values
static unsigned char size;//if 0, no cursor is drawn, if 255, from begin to bottom
static unsigned char begin;//only relevant if from_bottom was not specified
v1=qbg_cursor_firstvalue;
v2=qbg_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:

if (half_cursor){
//half cursor
y3=qbg_character_height-1;
size=qbg_character_height/2;
c=col&0xF;//foreground col
i2=pal[c];
draw_half_curs:
lp=pixel+(y2+y3)*qbg_width_in_characters*qbg_character_width+x2+qbg_y_offset;
for (x3=0;x3<qbg_character_width;x3++){
*lp=i2;
lp++;
}
y3--;
size--;
if (size) goto draw_half_curs;
}else{
if (from_bottom){
//cursor from bottom
y3=qbg_character_height-1;
if (y3==15) y3=14;//leave bottom line blank in 8x16 char set
c=col&0xF;//foreground col
i2=pal[c];
draw_curs_from_bottom:
lp=pixel+(y2+y3)*qbg_width_in_characters*qbg_character_width+x2+qbg_y_offset;
for (x3=0;x3<qbg_character_width;x3++){
*lp=i2;
lp++;
}
y3--;
size--;
if (size) goto draw_curs_from_bottom;
}else{
//cursor from begin using size
if (begin<qbg_character_height){
y3=begin;
c=col&0xF;//foreground col
i2=pal[c];
if (size==255) size=qbg_character_height-begin;
draw_curs_from_begin:
lp=pixel+(y2+y3)*qbg_width_in_characters*qbg_character_width+x2+qbg_y_offset;
for (x3=0;x3<qbg_character_width;x3++){
*lp=i2;
lp++;
}
y3++;
size--;
if (size) goto draw_curs_from_begin;
}
}
}
}//draw cursor?
nocursor:



//outer loop
skip:
x2=x2+qbg_character_width;
}
y2=y2+qbg_character_height;
}

show_flashing_last=show_flashing;
show_cursor_last=show_cursor;
cx_last=cx;
cy_last=cy;
screen_last_valid=1;

if (!screen_changed) goto screen_refresh_unrequired;
goto screen_refreshed;
}//text only









//graphical modes

//update SCREEN 10 palette
if (qbg_mode==10){
//pal_mode10[0-1][0-8]
i2=SDL_GetTicks()&512;
if (i2) i2=1;
for (i=0;i<=3;i++){
pal[i]=pal_mode10[i2][qbg_color_assign[i]];
}
}

i=qbg_width*qbg_height;
i2=1<<qbg_bits_per_pixel;//i2 is number of colors

//data changed?
if (i!=pixeldatasize){
free(pixeldata);
pixeldata=(unsigned char*)malloc(i);
pixeldatasize=i;
goto update_display;
}
if (memcmp(pixeldata,qbg_visual_page_offset,i)) goto update_display;

//palette changed?
if (memcmp(paldata,pal,i2*4)) goto update_display;

//force update because of mode change?
if (!screen_last_valid) goto update_display;

goto screen_refresh_unrequired;//no need to update display

update_display:

memcpy(pixeldata,qbg_visual_page_offset,i);
memcpy(paldata,pal,i2*4);
screen_last_valid=1;

//scaled refresh

if (x_scale==2){
static unsigned char *cp;
static unsigned long *lp;
static unsigned long c;
cp=pixeldata;
lp=pixel+(qbg_width*x_scale*y_offset);
for (y=0;y<qbg_height;y++){
for (x=0;x<qbg_width;x++){
c=paldata[*cp++]; *lp++=c; *lp++=c;
}//x
 if (y_scale==2){
 memcpy(lp,lp-qbg_width*2,qbg_width<<3);
 lp+=(qbg_width*2);
 }
}//y
goto screen_refreshed;
}

if (x_scale==1){
static unsigned char *cp;
static unsigned long *lp;
static unsigned long c;
cp=pixeldata;
lp=pixel+(qbg_width*x_scale*y_offset);
for (y=0;y<qbg_height;y++){
for (x=0;x<qbg_width;x++){
*lp++=paldata[*cp++];
}//x
 if (y_scale==2){
 memcpy(lp,lp-qbg_width,qbg_width<<2);
 lp+=qbg_width;
 }
}//y
goto screen_refreshed;
}


/*





//full screen 320x200 resolution correction
if (full_screen){
if (qbg_width==320&&qbg_height==200){
static unsigned char *cp;
static unsigned long *lp,*lp2;
static unsigned long c;
cp=qbg_visual_page_offset;
lp=pixel;
lp2=pixel+640;
for (y=0;y<200;y++){
for (x=0;x<320;x++){
c=pal[*cp++];
*lp++=c; *lp++=c;
*lp2++=c; *lp2++=c;
}
lp+=640; lp2+=640;
}
goto screen_refreshed;
}
}

//SCREEN 2/8 double y-axis
if (qbg_width==640&&qbg_height==200){
unsigned char *cp;
unsigned long *lp;
cp=qbg_visual_page_offset;
lp=pixel;
i2=qbg_program_window_width*qbg_program_window_height;
i3=0;
for (i=0;i<i2;i++){
*lp=pal[*cp];
i3++;
if (i3==640){cp-=640;i3=-640;}
cp++;
lp++;
}
goto screen_refreshed;
}

if (qbg_bytes_per_pixel==1){
static unsigned char *cp;
static unsigned long *lp;
cp=pixeldata;
lp=pixel+qbg_y_offset*qbg_width;
i2=qbg_width*qbg_height;

for (i=0;i<i2;i++){
*lp=pal[*cp];
cp++;
lp++;
}


}
*/

//screen_refreshed:
//SDL_UpdateRect(screen, 0, 0, 0, 0);
//SDL_UpdateRect(screen_surface,0,0,0,0);

//update display surface
//memcpy(display_page_surface->pixels,display_page->offset,1024*768*4);



//SDL_UpdateRect(display_page_surface,0,0,0,0);
screen_refresh_unrequired:;


}//lock_display==0

if (lock_display==1){lock_display=2; Sleep(0);}

if (autodisplay==-1) autodisplay=0;

}