//accdis.cpp Copyright (C) 1990-91 I.Pedley (CTPP)  Mon 24-Feb-1992 at 16:57:27
//first TSR application

//accdis.ctp Copyright (C) 1989-97 I.Pedley (CTPP) Sun 27-Apr-1997 at 15:30:02
//time to grow up this is our OS version

#include {chaoskey.htm}
#include {ctype.htm}
#include {string.htm}
#include {stdlib.htm}
#include {accdis.htm}

VD  dvblit(VD);

#define SUCCESS  0L
#define FAILURE -1L

struct FIELD
{
    SI  pos;
    SI  row;
    SI  col;
    UI  w;
    SI  sign;
    SI  digits;
    SI  dec_digits;
    SI  argsize;
    SI  base;
    CH  fgd;
    CH  bgd;
    CH  type;
    CH* shadow;
    CH* dest;
    SI  limits;
    SI  isarray;
    CH* format;
    SI  zerofill;
};

struct FIELD_ARRAY
{
    FIELD* first;
    SI     width;
    SI     depth;
    SI     home;
    SI     end;
};


VD  longtoascii(UL longint,CH* buf,UL digits,UL sign,
                 UL zerofill,UL base,UL dec_digits,UL shift);
VD  doubletoascii(DB* d,CH* buf,UL fw,UL dec_digits,UL sign,UL zerofill);

VD  numtoascii(FIELD* f);

VD idle(VD){}


#define CGA_ADDRESS   0xb8000
#define MDA_ADDRESS   0xb0000

    UI  mda_mode;

    ACCDIS  cga={
                 (VD*)&cga,
                 idle,
                 mdafgd,mdabgd,
                 mdasetcurpos,
                 NULL,      //hardware cursor needs no clrcursor
                 mdasetcursiz,
                 mdaputc,
                 mdaprint,
                 mdaprin,
                 mdascroll,
                 getkey,
                 NULL,
                 NULL,
                 NULL,
                 (CH*)CGA_ADDRESS,
//                 7,0,0,0,80,2,25,0
//                 7,0,0,0,80,2,200,0
                 7,0,0,0,80,2,50,0
                };
    ACCDIS  mda={
                 (VD*)&mda,
                 idle,
                 mdafgd,mdabgd,
                 mdasetcurpos,
                 NULL,      //hardware cursor needs no clrcursor
                 mdasetcursiz,
                 mdaputc,
                 mdaprint,
                 mdaprin,
                 mdascroll,
                 getkey,
                 NULL,
                 NULL,
                 NULL,
                 (CH*)MDA_ADDRESS,
//                 7,0,0,0,80,2,25,0
                 7,0,0,0,80,2,200,0
                };

    ACCDIS  mem={
                 (VD*)&mem,
                 idle,
                 memfgd,membgd,
                 memsetcurpos,
                 NULL,      //needs no clrcursor
                 memsetcursiz,
                 memputc,
                 memprint,
                 memprin,
                 memscroll,
                 getkey,                    //<--allows accept to memory buffer
                 NULL,                      //...may allow password entry?
                 NULL,
                 NULL,
                 NULL,
                 7,0,0,0,80,1,100,0
                };
    ACCDIS  vmem={
                 (VD*)&vmem,
                 idle,
                 vmemfgd,vmembgd,
                 NULL,      //needs no clrcursor
                 vmemsetcurpos,
                 vmemsetcursiz,
                 vmemputc,
                 vmemprint,
                 vmemprin,
                 vmemscroll,
                 getkey,                    //<--allows accept to memory buffer
                 NULL,                      //...may allow password entry?
                 NULL,
                 NULL,
                 NULL,
                 7,0,0,0,80,2,50,0
                };

VD  vesaputc(VD* usr,CH c,UL row,UL col);
VD  vesaprin(VD* usr,CH* str,UL row,UL col,UL len);
VD  vesaprint(VD* usr,CH* str,UL row,UL col);
VD  vesasetcurpos(VD* usr,UL row,UL col);
VD  vesaclrcurpos(VD* usr,UL row,UL col);
VD  vesascroll  (VD* usr);
    ACCDIS  avesa={
                 (VD*)&avesa,
                 idle,
                 mdafgd,mdabgd,
                 vesasetcurpos,
                 vesaclrcurpos,
                 memsetcursiz,
                 vesaputc,
                 vesaprint,
                 vesaprin,
                 vesascroll,
                 getkey,
                 NULL,
                 NULL,
                 NULL,
                 NULL,
                 7,0,0,0,80,2,200,0
                };

    ACCDIS* AA=&cga;


SL  mdaset(UL mode)
{
    mda_mode=mode;
    SI screen_rows=25;
    switch(mode)
        {
         case 0:
//            _setvideomode(_TEXTMONO);
            AA=&mda;
            break;
         default:
         case 1:
//            _setvideomode(_TEXTC80);
//            _settextrows (screen_rows=cga.rows=25);
            AA=&cga;
            break;
         case 2:
//            _setvideomode(_VRES16COLOR);
            break;
         case 3:
//            _setvideomode(_TEXTC80);
//            _settextrows (screen_rows=cga.rows=50);
            AA=&cga;
            break;
        }
    return screen_rows;
}

VD  AccdisSet(ACCDIS* a)
{
    if(!a){AA=&cga;}
    else  {AA=a;}
}
//VD  AccdisSetIdleFunction(VD(*idlefunc)(VD))

VD  AccdisSetIdleFunction(VD* idlefunc)
{
    if(idlefunc==NULL){AA->idle=idle;}
    else              {AA->idle=idlefunc;}
}

VD  sound_beep(UL hz)
{
//    sound(hz);
//    delay(150);
//    nosound();
}

#define MID_FIELD    0
#define HOME_FIELD   1
#define END_FIELD    2
#define TOP_FIELD    4
#define BOTTOM_FIELD 8
#define HASH_FIELD 0x80

#define BADARG -1

VD  fieldchar(ACCDIS* A,FIELD* cf,CH c,UL field_pos)
{
    (*A->putc)(A->usr,c,cf->row,cf->col+field_pos);
    CH* ptr=cf->shadow;
    ptr[field_pos]=c;
}

VD  makepattern(FIELD* cf,CH* pattern)
{
    CH fill;
    switch(cf->base)
        {
         case 2:
            fill='1';break;
         case 10:
            fill='9';break;
         case 16:
            fill='x';break;
         default:
            fill='X';break;
        }
    memset(pattern,fill,cf->w);
    if(cf->dec_digits){pattern[cf->digits]='.';}

    switch(cf->type)
        {
         case '&':
            pattern[0]=pattern[1]='z';break;
         case 'M':
         case 'D':
            pattern[2]=pattern[5]='/';break;
         case 'T':
            pattern[5]=':';
         case 't':
            pattern[2]=':';break;
         case 'f':
         case 'F':
            pattern[4]=':';break;
         case 'H':
            for(fill=0;fillw/3;fill++)
                {
                 pattern[fill*3+2]=' ';
                }
            break;
         default:
            break;
        }
    if(cf->sign)
        {
         switch(cf->type)
            {
             case 't':
             case 'D':break;
             default:*pattern='+';
            }
        }
}

VD  outhexdword(UL dword,UL row,UL col);

//SL  getkey(VD);

UL  inkey (ACCDIS* A,UL* key,CH* c,UL row,UL col)
{
    (*A->setcurpos)(A->usr,row,col);

    if(A->insert)                          //if insert on
        {(*A->setcursiz)(A->usr,0x0007);}  //set block cursor
//    else{(*A->setcursiz)(A->usr,0x0c0e);}
    else{(*A->setcursiz)(A->usr,0x0607);}

//    while(!((*A->getkey)(_NKEYBRD_READY))){(*A->idle)();}
//    while(!(*A->getkey)(_NKEYBRD_READY)){(*A->idle)();}
//    *key=   (*A->getkey)(_NKEYBRD_READ);
    *key=   (*A->getkey1)();
    *c=*key;
    if(A->callback!=NULL)
        {
         //A->callback(*key,row,col,A->callbackuserptr);//COMPILER BUG TO FIX
         (*A->callback)(*key,row,col,A->callbackuserptr);//works OK like this
        }

    if(A->clrcurpos){(*A->clrcurpos)(A->usr,row,col);}
    return *key;
}

VD  netutility(VD);
VD  sysutility(VD);

SL fieldedit(ACCDIS* A,FIELD* cf,SL *curpos)
{
    CH* ptr;
    CH* ptr1;
    CH* fptr;
    CH* pattern;
    SL  n,row,col;
    SL  fgd,bgd;

    SL  acol=cf->col;
    SL  arow=cf->row;
    SL  digits=cf->digits;
    SL  field_width=cf->w;
    SL  sign=cf->sign&1;
    SL  min;
    SL  max;

    SL  field_pos;
    CH  c,d;
    UL  key;
    UL  day,month,year;     //for date field immediate validation
    UL  end=0;

    pattern=(CH*)malloc(field_width);
    if(pattern==NULL){return ESC;}
    makepattern(cf,pattern);

    if(*curpos>field_width-1){*curpos=field_width-1;} //set initial cursor position

    field_pos=*curpos;row=arow;
    fgd=cf->fgd;
    bgd=cf->bgd;
    (*A->setfgd)(A->usr,fgd);
    (*A->setbgd)(A->usr,bgd);
//    (*A->setfgd)(cf->fgd);
//    (*A->setbgd)(cf->bgd);
    switch(cf->type)
        {
         case 'H':max--;break;
         case '&':min=2;break;
         case 'M':
         case 'D':
         case 't':sign=0;break;
        }
    min=sign;max=cf->w-1;

L01:
//    (*A->putstrn)(A->usr,cf->shadow,cf->row+1,cf->col,cf->w);
    if(field_poscpl+1))
//                {field_pos+=A->cpl;  goto L01;}
//            else if(cf->limits&END_FIELD)
            if(cf->limits&END_FIELD)
                    {
                     if(field_posmin)             {field_pos=0;    goto L01;}
            else if(cf->limits&HOME_FIELD){sound_beep(500);goto L01;}
                 else                     {end=0;          goto L02;}
            break;
         case BACKSPACE: //overwrite this char with space
            if(field_pos>min){fieldchar(A,cf,0x20,field_pos);field_pos--;goto L01;}
            else if(cf->limits&HOME_FIELD){sound_beep(500);goto L01;}
                 else                 {end=0;          goto L02;}
            break;
         case NLCUR:
            if(field_pos>min)         {field_pos--;    goto L01;}
            else if(cf->limits&HOME_FIELD){sound_beep(500);goto L01;}
                 else                 {end=0;          goto L02;}
            break;
         case NRCUR:
            if(field_poslimits&END_FIELD) {sound_beep(500);goto L01;}
                 else                 {                goto L02;}
            break;
         case NHOME:
            if(cf->limits&HOME_FIELD)
                {if(field_pos>min)      {field_pos=0;    goto L01;}
                 else                   {sound_beep(500);goto L01;} }
            else                        {end=0;          goto L02;}
            break;
         case NEND:
            if((cf->type=='s')&&(cf->shadow[field_pos]!=0x20))
                {
                 field_pos=max;
                 while(cf->shadow[field_pos]==0x20)
                    {
                     field_pos--;
                     if(!field_pos){goto L01;}
                    }
                 field_pos++;
                 goto L01;
                }
            else if(field_poslimits&END_FIELD) {sound_beep(500);goto L01;}
                 else                     {                goto L02;}
            break;
         case NUCUR:
            if(cf->limits&TOP_FIELD)      {sound_beep(500);goto L01;}
            else                          {end=0;          goto L02;}
            break;
         case NDCUR:
            if(cf->limits&BOTTOM_FIELD)   {sound_beep(500);goto L01;}
            else                          {                goto L02;}
            break;
         case (CTRL|'a'):
            if (cf->type=='s')
                {for(n=0;ninsert^=1;goto L01;break;
         case NDEL:
            if(*fptr=='X')
               {
                for(n=field_pos+1;nshadow;
                    d=ptr[n];
                    fieldchar(A,cf,d,n-1);
                   }
                fieldchar(A,cf,' ',field_width-1);
               }
            end=0;
            goto L01;
            break;
         default:
                                            //chuck out other keys we don't want
            if((key&0x7f)<=0x1b){goto L01;} //ignore ESC and F1-F12 combinations
                                            //(raw ESC ie 0x1b is handled above)
            if (key&0x80)       {goto L01;} //ignore control keys

            if(c==0)
                {if(end)
                      {sound_beep(500);goto L01;}
                 else {end=1;goto L01;}
                }
            if(*fptr=='9')
                {if(c=='.'&&field_pos<=digits)          //*******debug this
                    {
                     if((cf->type=='D')||(cf->type=='M'))
                        {
                         fieldchar(A,cf,'/',2);
                         fieldchar(A,cf,'/',5);
                         field_pos=max;
                        }
                     else if(cf->type=='T')
                        {
                         fieldchar(A,cf,':',2);
                         fieldchar(A,cf,':',5);
                         field_pos=max;
                        }
                     else if(cf->type=='t')
                        {
                         fieldchar(A,cf,':',2);
                         field_pos=max;
                        }
                     else
                        {
                         n=digits-1;
                         while(fptr>pattern+sign)
                            {
                             ptr=cf->shadow;
                             c=ptr[--field_pos];
                             fieldchar(A,cf,' ',field_pos);
                             fieldchar(A,cf,c,n);
                             n--;fptr--;
                            }
//                         for(;n>sign;n--)         //subtle change 19.9.97
                         for(;n>=sign;n--)
                            {fieldchar(A,cf,' ',n);}
                        }
                     if((cf->type=='d'|cf->type=='v')&&cf->dec_digits)
                        {
                         field_pos=digits+1;
                         fptr=pattern+field_pos;col=acol+field_pos;
                         fieldchar(A,cf,'.',digits);goto L01;
                        }
                     else{field_pos=max;
                          if(cf->limits&END_FIELD){sound_beep(500);goto L01;}
                          else                    {key=NRCUR;      goto L02;}
                         }
                    }
                 else if(c<'0'|c>'9')
                     {
                      if(cf->sign)
                         {if(c=='+'|c=='-')
                             {
                              fieldchar(A,cf,c,0);goto L01;
                             }
                          else
                             {sound_beep(500);goto L01;}
                         }
                      else{sound_beep(500);goto L01;}
                     }
                }
            else if(*fptr=='X')
                {if(A->insert)             //if insert on
                    {
                     if(field_posshadow+field_pos;
                         memmove(ptr+1,ptr,field_width-field_pos-1);
                         *ptr=c;
                         (*A->putstrn)(A->usr,cf->shadow,cf->row,cf->col,cf->w);
//                         goto L03a;
                        }
//                     for(n=field_width-2;n>=field_pos;n--)
//                        {
//                         ptr=cf->shadow;
//                         d=ptr[n];
//                         fieldchar(A,cf,d,n+1);
//                        }
                    }
                }
            else if(*fptr=='.')
                {if(c!='.')     {sound_beep(500);goto L01;} }
            else if(*fptr=='x')
                {if(c<'0'|c>'9')
                    {if(c<'a'|c>'f')
                        {sound_beep(500);goto L01;}
                    }
                }
            break;
        }//switch key
    if(cf->type=='?'){c='?';}
L03:
    fieldchar(A,cf,c,field_pos);          //char into screen_buffer and shadow
L03a:
    if(field_pos==max){
                       if(cf->limits&END_FIELD)
                           {
                            if(end){sound_beep(500);}
                            else   {end=1;}
                            goto L01;
                           }
                       else{key=NRCUR;goto L02;}
                      }
                                                    //overflow into next field
    field_pos++;
    goto L01;
L02:
    if((cf->type=='D')||(cf->type=='M'))
        {
         fptr=cf->shadow;
         day  =atoi(fptr);    fptr+=3;
         month=atoi(fptr);    fptr+=3;
         year =atoi(fptr);
         if((!day)&&(!month)&&(!year))         {goto AOK;}  //OK if field left blank
//Millennium date adjustment
         if(cf->type=='D')//zero year in 99/99/99 dates means year 2000
             {if((!day)|(!month))              {goto FAIL;}}
         else{if((!day)|(!month)|(!year))      {goto FAIL;}
              if((year/100)!=19)
              if((year/100)!=20)             {goto FAIL;}
             }
//         if(!day  |!month  |!year)          {goto FAIL;}
         if(day>31|month>12)                {goto FAIL;}
         if(month==4|month==6|month==9|month==11)
             {if(day>30){goto FAIL;}}
         else if(month==2)
                 {if(!(year%4))
                      {if(day>29){goto FAIL;}}
                  else{if(day>28){goto FAIL;}}
                 }
         AOK:
             goto L04;

         FAIL:
             sound_beep(500);goto L01;
        }
L04:
    free(pattern);
    *curpos=field_pos;
    if(cf->sign)                        //zero backfill signed numerics
        {for(n=0;nshadow;
             c=ptr[n];
             if(c==' '){fieldchar(A,cf,'0',n);}
            }
        }
    (*A->setcursiz)(A->usr,0x2000);
    return key&0xffff;
}



//386 processor EFLAGS register bit meanings
#define FLGPAGING   0x80000000
#define FLGVM       0x00020000
#define FLGRESTART  0x00010000
#define FLGNT       0x00004000
#define FLGIOPL     0x00003000
#define FLGOF       0x00000800
#define FLGDF       0x00000400
#define FLGIF       0x00000200
#define FLGTF       0x00000100
#define FLGSF       0x00000080
#define FLGZF       0x00000040
#define FLGAF       0x00000010
#define FLGPF       0x00000004
#define FLGCF       0x00000001

#define LINPRESENT  0x00000001
#define LINRW       0x00000002
#define LINUS       0x00000004
#define LINACCESSED 0x00000020
#define LINDIRTY    0x00000040
#define mLINAVL     0x00000e00

#define FALSE       0
#define TRUE        1

UL  getcr0(VD)
{
#asm
    mov     eax,cr0
#endasm
}
UL  getcr3(VD)
{
#asm
    mov     eax,cr3
#endasm
}

SL  linvalid(UL lin)
{
    UL  cr0=getcr0();
//    if(!(cr0&FLGPAGING))
//        {
//         if(lin&0x80000000){return FALSE;}
         //catch-all for memory accesses which fault a 4Gb selector
         if((lin&-8)==-8){return FALSE;}
         return TRUE;
//        }

    UL  cr3=getcr3();
    UL  direntry=lin>>22;
        direntry<<=2;
        direntry+=cr3;
    if(!(*(UL*)direntry&LINPRESENT)){return FALSE;}

    UL  page=*(UL*)direntry;
        page&=0xfffff000;
    UL  tableentry=lin>>12;
        tableentry&=0x3ff;
        tableentry<<=2;
        tableentry+=page;

    if(!(*(UL*)tableentry&LINPRESENT)){return FALSE;}
    return TRUE;
}

SL  evaluatefield(CH* format,CH** nextptr,FIELD* f,UL row,UL col,UL fgd,UL bgd,CH** args)
{
    CH* fptr=format;
    CH* argptr=*args;
    SL  m,argctr=0;

    memset(f,0,sizeof(FIELD));
    f->row=row;
    f->col=col;
    f->fgd=fgd;
    f->bgd=bgd;
    f->format=format;
    fptr++;

    if(*fptr=='#')                     //field width precedes arg on stack
        {
         fptr++;
         argptr=args[argctr];
         f->w=*(UL*)argptr;
//         if(f->w==0){return 0;}
         argctr++;
         f->limits|=HASH_FIELD;
         goto DOTYPE;                  //skip numeric evaluation
        }                              //as this type is mainly for strings

    if(*fptr=='&')                     //display actual data address itself
        {fptr++;                       //as 32 bit hex rather than data at address
        }

//handle sign attribute
    switch(*fptr)
        {
         case '+':f->sign=1;fptr++;break;
         case '-':f->sign=5;fptr++;break;
         case '(':f->sign=3;fptr++;break;
         case ')':f->sign=7;fptr++;break;
         default :f->sign=0;
        }
//handle zerofill attribute
    switch(*fptr)
        {
         case '0':f->zerofill=1;break;
         default :f->zerofill=0;
        }
//get field width
    f->w=f->digits=strtol(fptr,&fptr,10);

//handle decimal point if present
    switch(*fptr)
        {
         case '.':fptr++;
                  f->dec_digits=strtol(fptr,&fptr,10);
                  f->digits   -=(f->dec_digits+1);
                  if(f->dec_digits<0){return 0;}
                  break;
         default :f->dec_digits=0;
        }
DOTYPE:
//type should be next
    f->type=*fptr++;
//assign field destination pointer
    f->dest=args[argctr];

//handle field width for special types
    switch(f->type)
        {
         case 'M':f->w=f->digits=10;break;      //special Millennium Date type
         case 'D':f->w=f->digits=8 ;break;           //special Date type
         case 't':f->w=f->digits=5 ;break;           //special Time type
//         case 'T':f->w=f->digits=11;break;           //special Time type
         case 'T':f->w=f->digits=8 ;break;           //special Time type
         case 'f':f->w=f->digits=9 ;break;           //special seg:offset   type
         case 'F':f->w=f->digits=13;break;           //special seg:offset32 type
         case 'h':f->w=f->digits=f->w*2;break;
         case 'H':f->w=f->digits=f->w*3;break;
        }

//TEST FOR INVALID LINEAR ADDRESSING now field widths are known
    if(!(linvalid((UL)f->dest))){f->type='?';}

//do default field widths if f->w is still 0
    if(!f->w)
        {
         switch(f->type)
             {
//              case 'a':f->w=*(UC*)f->dest;  break;
              case 'a':if(*(UC*)f->dest!=0xff){f->w=*(UC*)f->dest;}else{f->w=2;}break;
//              case 's':if(!(f->limits&HASH_FIELD)){f->w=astrlen(f->dest);}break;
              case 's':if(!(f->limits&HASH_FIELD)){f->w=strlen(f->dest);}break;
              case 'c':f->w=2;break;
              case 'p':
              case 'x':
              case 'i':f->w=*(UI*)f->dest;f->digits=0;
                       do{f->digits++;f->w/=10;}while(f->w);f->w=f->digits;break;//f->w=4;
              case 'P':
              case 'X':
              case 'v':
              case 'd':
              case 'l':f->w=8;break;
              case '&':f->digits=f->w=10;f->zerofill=1;break;
              case 'I':f->w=15;break;   //16.8.09 new IP display type
             }
        }

//allocate shadow
    if(f->sign&2){f->w++;}         //allow for bracketed signed numbers
    if(f->w)
        {
         f->shadow=(CH*)calloc(f->w+1,1);
         if(f->shadow==NULL){return 0;}
        }
//initialise shadow to spaces
    memset(f->shadow,0x20,f->w);

//set argsize and filter bad arg type specifiers
    switch(f->type)
        {
         case '?':
         case 'a':
         case 'S':
         case 's':f->argsize=f->w;  break;
         case 'h':f->argsize=f->w/2;break;
         case 'H':f->argsize=f->w/3;break;
         case 'c':f->argsize=1;     break;
         case 'i':
         case 'p':
         case 'x':f->argsize=2;    break;
         case 'f':
         case 'l':
         case 'P':
         case 'X':
         case 'v':
         case 'V':
         case 'Z':
         case 'M':
         case 'D':
         case 't':
         case '&':
         case 'T':
         case 'I':f->argsize=4;    break;
         case 'F':f->argsize=6;    break;
         case 'd':f->argsize=8;    break;
         default :return 0;
        }
//set base for special types x&X (although these can be represented by %ix %lx)
    switch(f->type)
        {
         case 'c':
         case 'i':
         case 'p':
         case 'l':
         case 'v':
         case 'V':
         case 'Z':
         case 'd':
         case 'M':
         case 'D':
         case 't':
         case 'T':
         case 'P':
         case 'I':f->base=10;      break;
         case '&':
         case 'h':
         case 'H':
         case 'f':
         case 'F':
         case 'x':
         case 'X':f->base=16;      break;
         default :f->base=0;       break;   //strings are set to base 0
        }
//check for base postfix
    switch(*fptr)
        {
         case 'b':f->base=2; fptr++;break;
         case 'd':f->base=10;fptr++;break;
         case 'x':f->base=16;fptr++;break;
         case 'n':fptr++;f->base=strtol(fptr,&fptr,10);
                  if(!f->base){return 0;}
                  break;
        }
    if(*fptr=='p')                     //evaluate change in position
        {fptr++;
         m=strtol(fptr,&fptr,10);
         row=m/100;
         col=m%100;
        }
    if(*fptr=='C')                     //evaluate change in column
       {fptr++;
        m=strtol(fptr,&fptr,10);
        col=m%100;
       }

    f->pos=(row<<8)+col;
    f->row=row;
    f->col=col;
    if(*fptr=='c')
       {fptr++;
        m=strtol(fptr,&fptr,10);
        if(m>=100){fgd=m/100;}else{fgd=0;}
        bgd=m%100;
       }
    f->fgd=fgd;
    f->bgd=bgd;

    *nextptr=fptr;
    return argctr+1;
//    return SUCCESS;
}

//NEW accdis.cpp Copyright (C) 1989-95 I.Pedley (CTPP) Tue 11-Jul-1995 at 20:14:10
//hard-linked jobby


SL  display(CH* format,...)
{
        return vaccept(AA,AA->row,AA->col,format,0,&format+1); //watch
}
SL  accept(CH* format,...)
{
        return vaccept(AA,AA->row,AA->col,format,1,&format+1); //watch
}

SL  count_arrays(CH* format)
{
//Determine number of arrays present in format string
    SL  arrays=0;
    CH* fptr=format;
    while(*fptr!=0)
        {
         if(*fptr=='%')
            {
             switch(*(fptr+1))
                {
                 case '%':
                 case '|':
                 case '{':
                 case '}':
                 case '&':
                    fptr++;
                    break;
                }
            }
         else if(*fptr=='{')
             {if(*(fptr+1)=='{'){fptr++;}arrays++;}
         fptr++;
        }
    return arrays;
}
SL  count_fields(CH* format)
{
//simple count of % characters, skipping %z's and special characters
    SL  fields=0;
    CH* fptr=format;
    CH* sptr;
    while(*fptr!=0)
        {if(*fptr=='%')
            {
             strtol(fptr+1,&sptr,10);
             if(*sptr=='z')
                {
                 fptr=sptr+1;
                }
             else
                {
                 switch(*(fptr+1))
                    {
                     case '%':
                     case '|':
                     case '{':
                     case '}':
                        fptr+=2;
                        break;
                     default:
                        fields++;
                        break;
                    }
                }
            }
         fptr++;
        }
    return fields;
}
VD  column_sort(FIELD* cf,SL width)
{
    SL n,m,o;
    FIELD* first=cf-width;
    FIELD tmp;
    for(n=0;nfirst[m].col)
                {
                 tmp=first[n];
                 first[n]=first[m];
                 first[m]=tmp;
                 o=first[n].limits;
                 first[n].limits=first[m].limits;
                 first[m].limits=o;
                }
            }
        }

}
//#pragma 0xffffff7f
//#pragma 1

    UL  accdisarrayinc=1;

VD  displaychar(ACCDIS* A,CH c,SL fgd,SL bgd,SL* row,SL* col)
{
    if(c=='$') {c='%';}
    if(c=='\r'){if(A->bpc==2){*col=0;return;}}
    if((c=='\n')&&(A->bpc==2))
               {
                (*row)+=1;
                if((*row)>=A->rows)
                    {
                     UL n=0;
                     UL m;
                     m=((A->cpl*(A->rows-1))*A->bpc);
                     while(nmem[n]=A->mem[n+(A->cpl*A->bpc)];
                         n++;
                        }
                     (*A->setfgd)(A->usr,fgd);(*A->setbgd)(A->usr,bgd);
                     (*row)=A->rows-1;
                     for(n=0;ncpl;n++)
                        {
                         (*A->putc)(A->usr,' ',(*row),n);
                        }
                     *col=0;
                    }
                return;
               }
    (*A->setfgd)(A->usr,fgd);(*A->setbgd)(A->usr,bgd);
    (*A->putc)(A->usr,c,*row,*col);(*col)+=1;
    if(*col>A->cpl-1){(*row)+=1;(*col)-=A->cpl;}
}
#pragma breakpoints on
SL  evaluate_array(ACCDIS* A,CH* format,CH** nextptr,FIELD_ARRAY* cfa,UL nfa,FIELD* cf,UL nf,SL* fctr,CH** args,SL* row,SL* col,SL fgd,SL bgd,SL ffgd,SL fbgd)
{
    CH* nptr,*sptr,*argptr;
    UL  acol,o,fret,argctr=0,sa=0,fl,structsize;
    format++;
    if(*format=='{')    //it's a structure array
        {
         format++;sa=1;
        }
    if(*format=='%')
        {
         format++;argctr++;         //skip array size argument already known
        }
    else{strtol(format,&format,10);}//skip array size digits already known
    nptr=format;
    acol=*col;
    cfa->first=cf;
    argptr=*args;
    for(o=0;odepth;o++)
        {
         *col=acol;
         format=nptr;
         structsize=0;
         while(*format!='}')
            {
             if(*format=='%')
                {
                 fl=strtol(format+1,&sptr,10);
                 if(*sptr=='z')
                    {
                     structsize+=fl;
                     format=sptr+1;
                    }
                 else
                 {
                  switch(*(format+1))
                    {
                     case '%':
                     case '|':
                     case '{':
                     case '}':
                        displaychar(A,*(format+1),fgd,bgd,row,col);
                        format+=2;
                        break;
                     default:
                        fret=evaluatefield(format,&format,cf,*row,*col,ffgd,fbgd,&args[argctr]);
                        if(!fret){goto ARGERR;}
                        cf->limits=0;
//                        if(cfa->first==cf)
//                           {cf->limits|=HOME_FIELD;}
                        if(*fctr==0)
                           {cf->limits|=HOME_FIELD;}
                        if(*fctr==nf-1)
                           {cf->limits|=END_FIELD;}
                        if(o==0)
                           {cf->limits|=TOP_FIELD;}
                        if(o==cfa->depth-1)
                           {cf->limits|=BOTTOM_FIELD;}
                        cf->isarray=nfa+1;
                        *col=cf->w+cf->col;
                        while(*col>A->cpl-1)
                             {
//                              (*row)+=accdisarrayinc;
                              *col-=A->cpl;
                             }
                        if(sa)
                           {
                            //NB don't use # in structure arrays!
                            cf->dest=argptr+structsize;
                            argptr+=cf->argsize;
                           }
                        else
                           {
                            cf->dest=args[argctr]+(o*cf->argsize);
                            argctr+=fret;
                           }
                        cf++;
                        (*fctr)++;
                        break;
                    }
                 }
                }
             else
                {
                 displaychar(A,*format++,fgd,bgd,row,col);
                }
            }
         if(cfa->width>1){column_sort(cf,cfa->width);}
         (*row)+=accdisarrayinc;
         if(sa){argptr+=structsize;}
         else  {argctr-=cfa->width;}       //fctr+=cfa->width;
        }

    format++;                   //finally skip closing brace & adjust argptr
    *nextptr=format;
    if(sa){return argctr+1;}
    else  {return argctr+cfa->width;}

ARGERR:
    return -1;
}
#pragma breakpoints off

    UL  inaccdis;

SL  vaccept(ACCDIS* A,SL row,SL col,CH* format,SL disp,CH*args[])
{
    if((*format)&&!(isprint(*format))){brk return 1;}  //is this the sodding bug? (YES!!)
    if(inaccdis>2){return 0;} //abort accdis if called by an interrupt handler
    inaccdis++;
    CH* ptr;
    CH* ptr1;
    CH* fptr;
    CH* nptr;
    SL  argctr;          //argument array counter
    SL  fgdsav=A->fgd;
    SL  bgdsav=A->bgd;
    SL  rowsav=A->row;
    SL  colsav=A->col;
    SL  fgd=7;
    SL  bgd=0;
    SL  ffgd=7;
    SL  fbgd=0;
    SL  ret;
    SL  arraycol;
    SL  limits;
    SL  curpos;
    SL  n,m,o,sa,fret;
    SL  fctr;
    UL  ul;

    SL           arrays;
    SL           fields;
    FIELD*       field      =NULL;
    FIELD*       cf;
    FIELD_ARRAY* field_array=NULL;
    FIELD_ARRAY* cfa;
    SL           return_code;

    UL* uiptr;
    CH* dptr;

//zero critical variables
start:
    return_code=limits=curpos=fields=n=sa=m=o=arrays=0;

//Display and set up field array
    fptr=format;
//Determine number of arrays present
    arrays=count_arrays(format);
//allocate some space for FIELD_ARRAYs
    argctr=0;
    if(arrays)
        {
         field_array=(FIELD_ARRAY*)calloc(arrays,sizeof(FIELD_ARRAY));
         if(field_array==NULL){goto MEMERR;}
         //and get array sizes
         fptr=format;m=n=o=0;
         cfa=field_array;
         while(nwidth=sa=0;
             fptr++;
             if(*fptr=='{'){sa=1;fptr++;}
             if(*fptr=='%')
                 {
                  uiptr=(UL*)args+argctr;
                  cfa->depth=*uiptr;
                  argctr++;fptr++;
                 }
             else{cfa->depth=strtol(fptr,&fptr,10);}
             if(!cfa->depth)
                {(*A->putstr)(A->usr,"Expected array size after opening brace",0,0);goto escape;}
             while(*fptr!='}')
                {if(!*fptr)
                    {(*A->putstr)(A->usr,"No closing brace for array",0,0);goto escape;}
                 else if(*fptr=='%')
                    {
                     switch(*(fptr+1))
                        {
                         case '%':
                         case '|':
                         case '{':
                         case '}':
                            fptr+=2;
                            break;
                         default:
                            fptr++;
                            strtol(fptr,&fptr,10);
                            if(*fptr!='z'){cfa->width++;}
                            break;
                        }
                    }
                 fptr++;
                }
             //note zero array width is possible, to shorthand column displays
             m+=cfa->width;
             o+=cfa->width*cfa->depth;
             if(sa){argctr++;}
             else  {argctr+=cfa->width;}
             n++;
             cfa++;
            }
         }        //m=field format strings within arrays;
                  //o=total fields required for arrays;

//Determine number of fields in total
    n=count_fields(format);                      //n=field format strings
    n-=m;                                        //less those within arrays
    fields=n+o;                                  //total fields on screen
    if(fields){
               field=(FIELD*)calloc(fields,sizeof(FIELD));
               if(field==NULL){goto MEMERR;}
              }

    fptr=format;fctr=arrays=argctr=0;

//Initial display and initialise FIELDS

    cf=field;
    cfa=field_array;

    while(*fptr!=0)
        {
         if(*fptr=='%')
            {
             switch(*(fptr+1))
                {
                 case '%':
                 case '|':
                 case '{':
                 case '}':
                    displaychar(A,*(fptr+1),fgd,bgd,&row,&col);
                    fptr+=2;
                    break;
                 default:
                    fret=evaluatefield(fptr,&fptr,cf,row,col,ffgd,fbgd,(CH**)args+argctr);
                    if(!fret){goto ARGERR;}
                    else    {argctr+=fret;}
                    col=cf->w+cf->col;
                    while(col>A->cpl-1){row++;col-=A->cpl;}
//                    cf->dest=args[argctr];
                    cf->limits=0;
                    if(fctr==0)       {cf->limits|=HOME_FIELD|TOP_FIELD|BOTTOM_FIELD;}
                    if(fctr==fields-1){cf->limits|=END_FIELD |BOTTOM_FIELD|TOP_FIELD;}
                    else              {cf->limits|=TOP_FIELD|BOTTOM_FIELD;}
                    cf->isarray=0;
                    fctr++;
//                    argctr++;
                    cf++;
                    break;
                }
            }
         else if(*fptr=='|')
              {fptr++;
               if(*fptr=='p')                     //evaluate change in position
                  {fptr++;
                   m=strtol(fptr,&fptr,10);
                   row=m/100;
                   col=m%100;
                  }
               if(*fptr=='C')                     //evaluate change in column
                  {fptr++;
                   m=strtol(fptr,&fptr,10);
                   col=m%100;
                  }
               if(*fptr=='R')                     //evaluate relative change
                  {fptr++;                        //in position
                   m=strtol(fptr,&fptr,10);
                   row+=m;
                   m=strtol(fptr,&fptr,10);
                   col+=m;
                  }
               if(*fptr=='c')                     //evaluate change in colours
                  {fptr++;
                   m=strtol(fptr,&fptr,10);
                   if(m>=100){fgd=m/100;}else{fgd=0;}
                   bgd=m%100;
                  }
              }
         else if(*fptr=='{')        //array to handle
            {
             o=evaluate_array(A,fptr,&fptr,cfa,arrays,cf,fields,&fctr,(CH**)args+argctr,&row,&col,fgd,bgd,ffgd,fbgd);
             if(o==-1){goto ARGERR;}
             argctr+=o;
             cf+=cfa->width*cfa->depth;
             arrays++;                 //maybe another array to follow
             cfa++;
            }
         else
            {
             displaychar(A,*fptr++,fgd,bgd,&row,&col);
            }
        }

    if(!fields){goto escape;}   //format string is display only

    cf=field;

    UL  ltmp;
    UI  itmp;

    for(m=0;mfgd;
        bgd=cf->bgd;
        (*A->setfgd)(A->usr,fgd);
        (*A->setbgd)(A->usr,bgd);
        ptr =cf->shadow;
        ptr1=cf->dest;
        switch(cf->type)
          {
           case 's':
//               n=astrlen(ptr1);if(n>cf->w){n=cf->w;}
               n=strlen(ptr1);if(n>cf->w){n=cf->w;}
               memcpy(ptr,ptr1,n);
               break;
           case 'a':        //pascal-type string
               n=*ptr1;n&=0xff;if(n>cf->w){n=cf->w;}
               memcpy(ptr,ptr1+1,n);
               break;
           case 'S':
               memcpy(ptr,ptr1,cf->w);
               for(n=0;nw;n++)
                {
                 if(!isprint(ptr[n])){ptr[n]='.';}
                }
               break;
           case 'P':
           case 'p':
           case 'l':
           case 'i':
           case 'c':
           case 'v':
           case 'V':
           case 'Z':
           case 'd':
           case 'x':
           case 'X':
           case '&':
               numtoascii(cf);
               break;
           case 'D':
               if(cf->sign)
                    {
                     ltmp=*(UL*)cf->dest;
                    }
               else
                    {
                     ltmp=*(UM*)cf->dest;
                    }
               longtoascii(ltmp%100,ptr,2,0,1,10,0,0);
               ptr[2]='/';
               longtoascii((ltmp/100)%100,ptr+3,2,0,1,10,0,0);
               ptr[5]='/';
               longtoascii((ltmp/10000)%100,ptr+6,2,0,1,10,0,0);
               break;
           case 'M':
               if(cf->sign)
                    {
                     ltmp=*(UL*)cf->dest;
                    }
               else
                    {
                     ltmp=*(UM*)cf->dest;
                    }
               longtoascii(ltmp%100,cf->shadow,2,0,1,10,0,0);
               cf->shadow[2]='/';
               longtoascii((ltmp/100)%100,cf->shadow+3,2,0,1,10,0,0);
               cf->shadow[5]='/';
               longtoascii((ltmp/10000)%10000,cf->shadow+6,4,0,1,10,0,0);
               break;
           case 'I':
               ltmp=*(UL*)cf->dest;
               longtoascii((ltmp&0xff000000)>>24,cf->shadow,3,0,0,10,0,0);
               cf->shadow[3]='.';
               longtoascii((ltmp&0x00ff0000)>>16,cf->shadow+4,3,0,0,10,0,0);
               cf->shadow[7]='.';
               longtoascii((ltmp&0x0000ff00)>>8 ,cf->shadow+8,3,0,0,10,0,0);
               cf->shadow[11]='.';
               longtoascii((ltmp&0x000000ff)    ,cf->shadow+12,3,0,0,10,0,0);
               break;
                break;
           case 'T':
           case 't':
               if(cf->sign)
                    {
                     ltmp=*(UL*)cf->dest;
                    }
               else
                    {
                     ltmp=*(UM*)cf->dest;
                    }
               longtoascii((ltmp/1000000)%100,ptr,2,0,0,10,0,0);
               ptr[2]=':';
               longtoascii((ltmp/10000)%100,ptr+3,2,0,1,10,0,0);
               if(cf->type=='t'){break;}
               ptr[5]=':';
               longtoascii((ltmp/100)%100,ptr+6,2,0,1,10,0,0);
               break;

           case 'f':
               ltmp=*(UL*)cf->dest;
               longtoascii(ltmp>>16,ptr,4,0,1,16,0,0);
               ptr[4]=':';
               longtoascii(ltmp,    ptr+5,4,0,1,16,0,0);
               break;
           case 'F':
               ltmp=*(UL*)cf->dest;
               itmp=*(UI*)(cf->dest+4);
               longtoascii(itmp,ptr,4,0,1,16,0,0);
               ptr[4]=':';
               longtoascii(ltmp,    ptr+5,8,0,1,16,0,0);
               break;
           case 'h':
               for(n=0;nw/2;n++)
                {
                 longtoascii(ptr1[n],ptr+2*n,2,0,1,16,0,0);
                }
               break;
           case 'H':
               for(n=0;nw/3;n++)
                {
                 longtoascii(ptr1[n],ptr+3*n,2,0,1,16,0,0);
                 ptr[2+3*n]=0x20;
                }
               break;
           case '?':
                memset(ptr,'?',cf->w);
           default:
               break;
          }
        (*A->putstrn)(A->usr,ptr,cf->row,cf->col,cf->w);
        cf++;
       }

    if(A->switchframe)
        {
         (*A->switchframe)(A->usr);
        }

    if(!disp){goto escape;}
// MAIN edit loop
    dvblit();
    cf=field;                                         //start in home field
    while(1)
       {
        ret=fieldedit(A,cf,&curpos);
        switch(ret)
           {
            case ENTER:
            case NUMENTER:
                goto update;
            case (CTRL|ALT|F12):
                return_code=ret;goto escape;
            case ESC:
                return_code=-1; goto escape;
            case BADARG:
                goto ARGERR;
            case NRCUR:
            case TAB :
                cf++;curpos=0;break;
            case NLCUR:
            case BACKSPACE  :
                cf--;curpos=4000;break;
            case SHIFTTAB:
                cf--;curpos=0;break;
            case NHOME:
                cf=field;curpos=0;break;
            case NEND:
                cf=field+fields-1;curpos=0;break;
            case NDCUR:
                cf+=field_array[cf->isarray-1].width;break;
            case NUCUR:
                cf-=field_array[cf->isarray-1].width;break;
            default:
                sound_beep(500);
           }
       }

update:
        cf=field;
        for(m=0;mshadow;
             ptr1=cf->dest;
             switch(cf->type)
               {
                case 's':
                    memcpy(ptr1,ptr,cf->w);
                    break;
                case 'a':
                    memcpy(ptr1+1,ptr,cf->w);
                    break;
                case '&':
                    //after all that, accept to an address is not really valid!
                    break;
                case 'X':
                case 'l':
                    if(cf->sign)
                        {*(SL*)ptr1=strtol (ptr,&dptr,cf->base);}
                    else{*(UL*)ptr1=strtoul(ptr,&dptr,cf->base);}
                    break;
                case 'P':
                    if(cf->sign)
                        {*(UM*)ptr1=strtol (ptr,&dptr,cf->base);}
                    else{*(UM*)ptr1=strtoul(ptr,&dptr,cf->base);}
                    break;
                case 'p':
                    if(cf->sign)
                        {*(Um*)ptr1=strtol (ptr,&dptr,cf->base);}
                    else{*(Um*)ptr1=strtoul(ptr,&dptr,cf->base);}
                    break;
                case 'x':
                case 'i':
                    if(cf->sign)
                        {*(SI*)ptr1=strtol (ptr,&dptr,cf->base);}
                    else{*(UI*)ptr1=strtoul(ptr,&dptr,cf->base);}
                    break;
                case 'c':
                    *(CH*)ptr1=strtol(ptr,&dptr,cf->base);break;
                case 'd':
                    *(DB*)ptr1=atof(ptr);break;
                case 'v':
                    SL l;       //not right for IPOS yet
                    UL divisor;
                    divisor=cf->base;
                    l=cf->dec_digits;
                    while(--l)
                        {
                         divisor*=cf->base;
                        }
                    if(cf->sign&1){l=strtol(cf->shadow+1,&dptr,cf->base);}
                    else          {l=strtol(cf->shadow,&dptr,cf->base);}
                    if(*cf->shadow=='-'){l=-l;}
                    ptr=cf->shadow+cf->digits;
                    if(cf->sign){*ptr=*cf->shadow;} //shove sign or zero into
                    else        {*ptr='0';}         //point space to correctly
                    l*=divisor;                     //evaluate fractional part
                    l+=strtol(ptr,&dptr,cf->base);   //e.g.-005 or 0005
                    *(SL*)ptr1=l;break;
                case 'D':
                    ul =(UL)atol(cf->shadow);
                    ul+=(UL)atol(cf->shadow+3)*100;
                    ul+=(UL)atol(cf->shadow+6)*10000;
                    if(ul)//if non-zero date convert to Millennium format
                        { //i.e. if year < 1980, assume 21st century
                         if     ((ul/10000)<80){ul+=20000000;}
                         else                  {ul+=19000000;}
                        }
                    if(cf->sign)        //%+D format string UL date
                        {
                         *(UL*)cf->dest=(UL)ul;
                        }
                    else                //%D format string for UQ date
                        {
                         *(UM*)cf->dest=(UL)ul;
                        }
                    break;
                case 'M':
                    if(cf->sign)
                    {
                    *(UL*)cf->dest =(UL)atol(cf->shadow);
                    *(UL*)cf->dest+=(UL)atol(cf->shadow+3)*100;
                    *(UL*)cf->dest+=(UL)atol(cf->shadow+6)*10000;
                    }
                    else
                    {
                    *(UM*)cf->dest =(UL)atol(cf->shadow);
                    *(UM*)cf->dest+=(UL)atol(cf->shadow+3)*100;
                    *(UM*)cf->dest+=(UL)atol(cf->shadow+6)*10000;
                    }
                    break;
                case 't':
                    l =atoi(ptr)*100;
                    l+=atoi(ptr+3);
                    l*=10000;
                    if(cf->sign)
                        {
                         *(UL*)ptr1=l;
                        }
                    else
                        {
                         *(UM*)ptr1=l;
                        }
                    break;
                case 'f':
                    *(UL*)ptr1 =((UL)strtol(ptr,&dptr,16))<<16;
                    *(UL*)ptr1+=((UL)strtol(ptr+5,&dptr,16));
                    break;
                case 'F':
                    *(UI*)(ptr1+4)=((UL)strtol (ptr,&dptr,16));
                    *(UL*)ptr1    =((UL)strtoul(ptr+5,&dptr,16));
                    break;
                case 'h':
                    for(n=cf->w/2;n>=0;)
                     {
                      ptr[n*2]=0;n--;
                      ptr1[n]=strtoul(ptr+2*n,&dptr,cf->base);
                     }
                    break;
                case 'H':
                    for(n=0;nw/3;n++)
                     {
                      ptr1[n]=strtoul(ptr+3*n,&dptr,cf->base);
                     }
                    break;
                default:
                    break;
               }
             cf++;
            }
    return_code=0;
escape:
    cf=field;
    for(n=0;nshadow!=NULL){free(cf->shadow);}
         cf++;
        }
    if(field_array!=NULL){free(field_array);}
    if(field!=NULL){free(field);}
    (*A->setfgd)(A->usr,7);(*A->setbgd)(A->usr,0);
//    (*A->setcursiz)(A->usr,0x0607);                     //normal cursor
//    (*A->setcursiz)(A->usr,0x0000);                     //hide cursor
    A->row=row;
    A->col=col;
    inaccdis--;
    A->fgd=fgdsav;
    A->bgd=bgdsav;
//    A->row=rowsav;
//    A->col=colsav;
    accdisarrayinc=1;
    return return_code;
ARGERR:
    (*A->putstr)(A->usr,"Unknown type specifier in format string",0,0);
    return_code=-1;goto escape;
MEMERR:
    (*A->putstr)(A->usr,"Out of memory for Accept/Display",0,0);
    return_code=-1;goto escape;

}
//    KEYCALLBACK  keycallback;
//    VD*          keycallbackuserptr;

//VD  accdis_setkeycallback(VD* callbackfunc,VD* ptr)
VD  accdis_setkeycallback(KEYCALLBACK callbackfunc,VD* ptr)
{
    AA->callback=callbackfunc;
    AA->callbackuserptr=ptr;
}

//UI  getkey(UI* key,CH* c,UI row,UI col)
//{
//    while(!_bios_keybrd(_NKEYBRD_READY)){(*AA->idle)();}
//    *key=_bios_keybrd(_NKEYBRD_READ);
//    *c=*key;
//    if(keycallback!=NULL){keycallback(*key,row,col,keycallbackuserptr);}
//    return *key;
//}



//supplementary list functions

SL  selectlist (CH* str,UL recsize,UL displen,UL items,UL row,UL col,UL visibles,UL scroll)
{
    UL visibleoffset=0;
    if(visibles>items)   {visibles=items;}  //poss clip to screen here
    if(scroll>visibles)  {scroll=visibles;}
    UL current=0;
    UL key,ret;
    CH* startstring;
    CH c;

L01:
    startstring=str+(visibleoffset*recsize);
    displaylist(startstring,recsize,displen,visibles,row,col,0);
    highlightlistitem(current,startstring,recsize,displen,row,col);

    switch(key=dgetkey()&0xffff)
        {
         case ESC:     ret=-1;goto OUT;
         case ENTER:
         case NUMENTER:ret=current+visibleoffset;goto OUT;
         case NUCUR:
            if(current){current--;}
            else
                {
                 if(visibleoffset>=scroll)
                    {visibleoffset-=scroll;current+=scroll;current--;}
                 else if(visibleoffset)
                    {current=visibleoffset-1;visibleoffset=0;}
                 else{sound_beep(500);}
                }
            break;
         case NDCUR:
            if(currentvisibleoffset+visibles+scroll)
                    {visibleoffset+=scroll;current-=scroll;current++;}
                 else if((visibleoffset==items-visibles)&(current==visibles-1))
                    {sound_beep(500);}
                 else{visibleoffset=items-visibles;current=0;}
                }
            break;
         case NPGUP:
            if(visibleoffset>=scroll)
                {visibleoffset-=scroll;}
            else {goto H1;}
            break;
         case NPGDN:
            if(items-1>visibleoffset+visibles+scroll)
                 {visibleoffset+=scroll;}
            else {goto E1;}
            break;
         case NHOME:
            H1:
            if(visibleoffset|current)
                {visibleoffset=current=0;}
            else{sound_beep(500);}
            break;
         case NEND:
            E1:
            if((visibleoffset==items-visibles)&(current==visibles-1))
                {sound_beep(500);}
            else{visibleoffset=items-visibles;current=visibles-1;}
            break;
         default:
            c=key;
            if(c>=0x61&c<=0x7a){c-=0x20;}
            CH* ptr=str;
            UL n=0;UL m;
            while(n=items)
                        {m=visibleoffset+visibles-items;
                         visibleoffset-=m;current=m;
                        }
                      goto L01;
                     }
                 ptr+=recsize;
                 n++;
                }
            sound_beep(500);
        }
    goto L01;
OUT:
    unhighlightlistitem(current,startstring,recsize,displen,row,col);
    return ret;
}

VD  displaylist(CH* str,UL recsize,UL displen,UL items,UL row,UL col,UL offset)
{
    UL n;
    CH* ptr=str+(offset*recsize);

    for(n=0;ndest;
    DB* dptr;
    CH* ptr;

    switch(f->type)
        {
         case 'd':
            ptr=f->dest;
            dptr=(DB*)ptr;
            doubletoascii(dptr,f->shadow,f->w,f->dec_digits,f->sign,f->zerofill);
            return;
//            goto OUT;
//            doubletoascii(d,f->shadow,f->w,f->dec_digits,f->sign,f->zerofill);
         case '&':
            l=(SL)f->dest;break;
         case 'P':
            l=*(UM*)iptr;break;
         case 'p':
            l=(SI)*(Um*)iptr;break;
         case 'X':
         case 'l':
         case 'v':
            if(f->sign)
                {l= *(SL*)iptr;}
            else{l= *(UL*)iptr;}
            break;
         case 'x':
         case 'i':
            if(f->sign)
                {l= *(SI*)iptr;}
            else{l= *(UI*)iptr;}
            break;
         case 'c':
            if(f->sign)
                {l= *(SC*)iptr;}
            else{l= *(UC*)iptr;}
            break;
         case 'V':      //capital V type suppresses zero 0.00 display
            if(f->sign)
                {l= *(SL*)f->dest;}
            else{l= *(UL*)f->dest;}
            if(!l)
                {
                 return;
                }
            break;
         case 'Z':      //upper case Z type prints Nil for zero
            if(f->sign)
                {l= *(SL*)f->dest;}
            else{l= *(UL*)f->dest;}
            if(!l)
                {
                 memcpy(&f->shadow[f->w-4],"Nil",3);
                 return;
                }
            break;
         default:
            return;
        }
    longtoascii(l,f->shadow,f->w,f->sign,f->zerofill,f->base,f->dec_digits,0);
    if(f->type=='&'){f->shadow[1]='x';} //cobbler 0x notation for new type '&'
OUT:
    l++;
}


VD  longtoascii(UL longint,CH* buf,UL digits,UL sign,
                 UL zerofill,UL base,UL dec_digits,UL shift)
{
    UL n,minus=0,fw=digits;

    CH  fill=' ';
    if(zerofill){fill='0';}
    if(sign&2)
        {//bracketed signed number:subtract one digit for ')'
         digits--;
        }
    if(sign&4)
        {//reverse sign before display
         longint=-longint;
        }
    if(sign)
        {
         digits--;              //make room for '+','-',or '('
         if(longint&0x8000000)
            {
             longint=-longint;
             minus=1;
            }
        }

    CH  digit=0;

    while(digits)
        {
         if(longint==0)
            {
             if(digit)
                {
                 digit=fill;
                }
             else
                {
                 digit=0x30;                //force at least one zero to
                }                           //be displayed
            }
         else
            {
             digit=longint%base;
             if(digit>9){digit+=0x57;}
             else       {digit+=0x30;}
            }
         buf[digits+(sign&1)-1]=digit;
         longint/=base;
         if(dec_digits)
            {
             dec_digits--;
             digit=0;
             if(!dec_digits)
                {
                 digits--;
                 buf[digits+(sign&1)-1]='.';
                }
            }
         if(digits){digits--;}
        }
    switch(sign)
        {
         case 1:
            if(minus){digit='-';}
            else     {digit='+';}
            buf[0]=digit;
            break;
         case 3:
            if(minus)
                {
                 buf[0]   ='(';
                 buf[fw-1]=')';
                }
            else
                {
                 buf[0]   =' ';
                 buf[fw-1]=' ';
                }
            break;
        }
}

//VD  doubletoascii(DB* d,CH* buf,UL fw,UL dec_digits,UL sign,UL zerofill)
//{
//    UL  sig,lsig,shift,integer,fraction,idigits,carry;
//    UL  digits=fw-(dec_digits+1);
//    CH  fill=' ';
//    memset(buf,0x20,fw+1);
//    buf[digits]='.';
//    if(sign){digits--;}
//    UC  digit=0;
//    UC* ptr=(UC*)d;
//    SL  asign=((*(ptr+7))&0x80)>>7;
//    if(sign)
//        {
//         if(asign)
//             {buf[0]='-';}
//         else{buf[0]='+';}
//        }
//    SL  exp=   *(SL*)(ptr+4);
//        exp>>=20;
//        exp&=0x7ff;                 //mask off sign bit
//        if(!exp)
//            {                       //it's a zero
//             buf[sign+digits+1]=buf[sign+digits-1]='0';
//            }
//        else{exp-=0x3ff;
//             sig=   *(SL*)(ptr+4);
//             sig<<=12;
//             sig>>=1;               //get 20 bits from top of significand
//             sig|=0x80000000;       //add 21st hidden bit
//             lsig=   *(SL*)(ptr);
//             lsig>>=21;             //get 11 bits from bottom of significand
//             sig|=lsig;             //most significant 32 bits in sig
//
//             if(exp>=0)             //branch now to handle positive and negative
//                {                   //exponents
//                 fraction=-1;
//                 fraction<<=exp+1;
//                 fraction>>=exp+1;  //knock off top (hidden) bit
//                 fraction&=sig;
//                 fraction>>=3;
//                 shift=exp;
//                 fraction<<=shift;
//                 shift=31-exp;
//                 integer=sig>>shift;
//                }
//             else
//                {
//                 shift=-exp;
//                 integer=0;
//                 fraction=sig>>(shift+3);
//                }
//             idigits=digits;
//             if(zerofill){fill='0';}
//             while(idigits)
//                {
//                 if(integer==0)
//                    {
//                     if(digit)
//                        {
//                         digit=fill;
//                        }
//                     else
//                        {
//                         digit=0x30;        //force at least one zero to
//                        }                   //be displayed
//                    }
//                 else
//                    {
//                     digit=integer%10;
//                     digit+=0x30;
//                    }
//                 buf[idigits+sign-1]=digit;
//                 integer/=10;
//                 idigits--;
//                }
//
//             while(dec_digits)
//                {
//                 fraction*=10;
//                 digit=fraction>>28;
//                 fraction&=0xfffffff;
//                 buf[sign+1+digits++]=digit+0x30;
//                 dec_digits--;
//                }
//             if(fraction)
//                {
//                 fraction*=10;
//                 digit=fraction>>28;
//                 if(digit>=5)
//                    {
//                     carry=1;
//                     digits=fw-sign;
//                     while(digits)
//                        {
//                         buf[digits]+=carry;
//                         if(buf[digits]==0x3a)
//                            {
//                             buf[digits]=0x30;
//                             carry=1;
//                            }
//                         else
//                            {
//                             carry=0;
//                            }
//                         digits--;
//                         if(buf[digits]=='.'){digits--;}
//                        }
//                    }
//                }
//            }
//}
VD  doubletoascii(DB* d,CH* buf,UL fw,UL dec_digits,UL sign,UL zerofill)
{
    UL  sig,lsig,shift,integer,fraction,fr,idigits,carry;
    UL  digits=fw-(dec_digits+1);
    CH  fill=' ';
    memset(buf,0x20,fw+1);
    buf[digits]='.';
    if(sign){digits--;}
    UC  digit=0;
    UC* ptr=(UC*)d;
    SL  asign=((*(ptr+7))&0x80)>>7;
    if(sign)
        {
         if(asign)
             {buf[0]='-';}
         else{buf[0]='+';}
        }
    SL  exp=   *(SL*)(ptr+4);
        exp>>=20;
        exp&=0x7ff;                 //mask off sign bit
        if(!exp)
            {                       //it's a zero
             buf[sign+digits+1]='0';
             buf[sign+digits-1]='0';
            }
        else{exp-=0x3ff;
             sig=   *(SL*)(ptr+4);
             sig<<=12;
             sig>>=1;               //get 20 bits from top of significand
             sig|=0x80000000;       //add 21st hidden bit
             lsig=   *(SL*)(ptr);
             lsig>>=21;             //get 11 bits from bottom of significand
             sig|=lsig;             //most significant 32 bits in sig

             if(exp>=0)             //branch now to handle positive and negative
                {                   //exponents
                 fraction=-1;
                 fraction<<=exp+1;
                 fraction>>=exp+1;  //knock off top (hidden) bit
                 fraction&=sig;
                 fraction>>=3;
                 shift=exp;
                 fraction<<=shift;
                 shift=31-exp;
                 integer=sig>>shift;
                }
             else
                {
                 shift=-exp;
                 integer=0;
                 fraction=sig>>(shift+3);
                }
             idigits=digits;
             if(zerofill){fill='0';}
             while(idigits)
                {
                 if(integer==0)
                    {
                     if(digit)
                        {
                         digit=fill;
                        }
                     else
                        {
                         digit=0x30;        //force at least one zero to
                        }                   //be displayed
                    }
                 else
                    {
                     digit=integer%10;
                     digit+=0x30;
                    }
                 buf[idigits+sign-1]=digit;
                 integer/=10;
                 idigits--;
                }

             fr=fraction;
             while(dec_digits)
                {
                 fraction*=10;
                 digit=fraction>>28;
                 fraction&=0xfffffff;
//                 buf[sign+1+digits++]=digit+0x30;
                 buf[sign+1+digits]=digit+0x30;
                 digits++;
                 dec_digits--;
                }
             if(fraction)
                {//round fractional values up for display, e.g. 0.99999995
                 //becomes 1.00000000
                 fraction*=10;
                 digit=fraction>>28;
                 if(digit>=5)
                    {
                     carry=1;
                     digits=fw-sign;
                     while(digits)
                        {
                         if(buf[digits+sign-1]==0x20)
                            {//change leading space to zero before adding carry
                             //otherwise 9.9999995 becomes !0.0000000
                             buf[digits+sign-1]=0x30;
                            }
                         buf[digits+sign-1]+=carry;
                         if(buf[digits+sign-1]==0x3a)
                            {
                             buf[digits+sign-1]=0x30;
                             carry=1;
                            }
                         else
                            {
                             carry=0;
                             break;
                            }
                         digits--;
                         if(buf[digits+sign-1]=='.'){digits--;}
                        }
                    }
                }
            }
}
#pragma breakpoints off

//Function set for CGA, MDA and VGA text modes////////////////////////

//  mdaset(0)=MDA
//  mdaset(1)=CGA
//  mdaset(2)=VGA
//  mdaset(3)=VGA text 50-lines

VD  mdaclear(VD)
{
//    _clearscreen(_GCLEARSCREEN);
    memset16(AA->mem,0x0720,50*AA->rows);
    mdasetcurpos(AA->usr,0,0);
}
VD  mdaclear(UL rows)
{
    memset16(AA->mem,0x0720,50*rows);
    mdasetcurpos(AA->usr,0,0);
}
VD  mdacurb(UL row,UL col)
{
    mdasetcurpos(AA->usr,row,col);
}
VD  mdaread (CH* dst,UL row,UL col,UL n)
{
    CH* ptr=AA->mem+((row*AA->cpl+col)*2);
    while(n--)
        {
         *dst++=*ptr++;
         ptr++;
        }
    *dst=0;
}
VD  mdasave (CH* dst,UL row,UL col,UL n)
{
    CH* ptr=AA->mem+((row*AA->cpl+col)*2);
    while(n--)
        {
         *dst++=*ptr++;
         *dst++=*ptr++;
        }
}
VD  mdarestore (CH* src,UL row,UL col,UL n)
{
    CH* ptr=AA->mem+((row*AA->cpl+col)*2);
    while(n--)
        {
         *ptr++=*src++;
         *ptr++=*src++;
        }
}

EX  SL  clistartline;
EX  SL  cliendline;
EX  SL  clicur;

VD  mdascroll(VD* usr)
{
    CH* vptr=AA->mem+clistartline*160;
    UL  rows=cliendline-clistartline;
    memmove(vptr,vptr+160,rows*160);
    vptr=AA->mem+cliendline*160;
    memset(vptr,0,160);
}
VD  mdaprint(VD* usr,CH* str,UL row,UL col)
{
    CH* ptr=AA->mem+((row*AA->cpl+col)*2);
    CH  colour=(AA->bgd<<4)+AA->fgd;
    while(*str)
        {
         *ptr++=*str++;
         *ptr++=colour;
        }
}

VD  mdaprin(VD* usr,CH* str,UL row,UL col,UL len)
{
    CH* ptr=AA->mem+((row*AA->cpl+col)*2);
    CH  colour=(AA->bgd<<4)+AA->fgd;
    while(len--)
        {
         *ptr++=*str++;
         *ptr++=colour;
        }
}
//#pragma 7
VD  mdafgd(VD* usr,UL colour)
{
    AA->fgd=colour;return;
    if(AA==&cga){AA->fgd=colour;}
    else        {
                 switch(colour)
                    {
                     case 6: AA->fgd=7;break;
                     default:AA->fgd=7;break;
                    }
                }
//    _settextcolor(col);
}
VD  mdabgd(VD* usr,UL colour)
{
    AA->bgd=colour;return;
    if(AA==&cga){AA->bgd=colour;}
    else        {
                 switch(colour)
                    {
                     case 6: AA->bgd=15;break;
                     case 4: AA->bgd=7;AA->fgd=0;break;
                     default:AA->bgd=0; break;
                    }
                }
//    _setbkcolor(col);
}
VD  mdaputc(VD* usr,CH c,UL row,UL col)
{
    UL  n=AA->cpl*row;
    n+=col;
    n*=2;
    n+=(UL)AA->mem;
    CH* ptr=(CH*)n;
    CH  colour=(AA->bgd<<4);
    colour+=AA->fgd;
    *ptr++=c;
    *ptr=colour;
}
VD  mdasetcurpos(VD* usr,UL row,UL col)
{
    AA->row=row;
    AA->col=col;
//    row%=25;

    row*=AA->cpl;
    row+=col;
    UL  port;
    if(AA==&mda){port=0x3b4;}
    else        {port=0x3d4;}
#asm
    cli
    mov     ecx,row
    mov     edx,port
    mov     al,0x0f
    out     dx,al

    mov     al,cl
    inc     dx
    out     dx,al

    mov     al,0x0e
    dec     dx
    out     dx,al

    shr     ecx,8
    mov     al,cl
    inc     dx
    out     dx,al
    sti
#endasm
//    _settextposition(row+1,col+1);
}
VD  mdagetcurpos(UL* row,UL* col)
{
    UL port,addr;
    if(AA==&mda){port=0x3b4;}
    else        {port=0x3d4;}

asm{
    cli
    sub     eax,eax
    mov     ecx,0
    mov     edx,port
    mov     al,0x0f
    out     dx,al

    mov     al,cl
    inc     dx
    in      al,dx
    mov     cl,al

    mov     al,0x0e
    dec     dx
    out     dx,al

    inc     dx
    in      al,dx
    mov     ah,al
    mov     al,cl
    mov     addr,eax
    sti
   }

    *col=addr%AA->cpl;
    *row=addr/AA->cpl;

}
VD  mdasetcursiz(VD* usr,UL val)
{
    UL  port;
    if(AA==&mda){port=0x3b4;}
    else        {port=0x3d4;}
#asm
    cli
    mov     ecx,val
    mov     edx,port
    mov     al,0x0b
    out     dx,al

    mov     al,cl
    inc     dx
    out     dx,al

    mov     al,0x0a
    dec     dx
    out     dx,al

    shr     ecx,8
    mov     al,cl
    inc     dx
    out     dx,al
    sti
#endasm
}

//membuf ACCDIS support

//SL  ctprintf(CH* buf,CH* format,...)
//{
//    mem.mem=buf;
//    return vaccept(&mem,mem.row,mem.col,format,0,&format+1); //watch
//}
VD  memscroll(VD* usr)
{
}
VD  memprint(VD* usr,CH* str,UL row,UL col)
{
    ACCDIS* a=(ACCDIS*)usr;
    CH* ptr=a->mem+(a->bpc*(row*a->cpl+col));
    while(*str)
        {
         *ptr++=*str++;
         if(a->bpc==2){*ptr++=(a->bgd<<4)|(a->fgd&0x0f);}
        }
}
VD  memprin(VD* usr,CH* str,UL row,UL col,UL len)
{
    ACCDIS* a=(ACCDIS*)usr;
    CH* ptr=a->mem+(a->bpc*(row*a->cpl+col));
    while(len--)
        {
         *ptr++=*str++;
         if(a->bpc==2){*ptr++=(a->bgd<<4)|(a->fgd&0x0f);}
        }
}
VD  memputc(VD* usr,CH c,UL row,UL col)
{
    ACCDIS* a=(ACCDIS*)usr;
    CH* ptr=a->mem+(a->bpc*(row*a->cpl+col));
    *ptr=c;
    if(a->bpc==2){ptr++;*ptr=(a->bgd<<4)|(a->fgd&0x0f);}
}
VD  memsetcurpos(VD* usr,UL row,UL col)
{
    ACCDIS* a=(ACCDIS*)usr;
    a->row=row;
    a->col=col;
}
VD  memsetcursiz(VD* usr,UL val)
{
}
VD  memfgd(VD* usr,UL val)
{
}
VD  membgd(VD* usr,UL val)
{
}

//vmem buffer ACCDIS support

VD  vmemscroll(VD* usr)
{
}
VD  vmemprint(VD* usr,CH* str,UL row,UL col)
{
    CH* ptr=vmem.mem+((row*vmem.cpl+col)*vmem.bpc);
    CH  colour=(vmem.bgd<<4)+vmem.fgd;

    while(str)
        {
         *ptr++=*str++;
         *ptr++=colour;
        }
}
VD  vmemprin(VD* usr,CH* str,UL row,UL col,UL len)
{
    CH* ptr=vmem.mem+((row*vmem.cpl+col)*vmem.bpc);
    CH  colour=(vmem.bgd<<4)+vmem.fgd;
    while(len--)
        {
         *ptr++=*str++;
         *ptr++=colour;
        }
}
VD  vmemputc(VD* usr,CH c,UL row,UL col)
{
    CH* ptr=vmem.mem+((row*vmem.cpl+col)*vmem.bpc);
    CH  colour=(vmem.bgd<<4)+vmem.fgd;
    *ptr++=c;
    *ptr=colour;
}
VD  vmemsetcurpos(VD* usr,UL row,UL col)
{
    vmem.row=row;
    vmem.col=col;
}
VD  vmemsetcursiz(VD* usr,UL val)
{
}
VD  vmemfgd(VD* usr,UL val)
{
    vmem.fgd=val;
}
VD  vmembgd(VD* usr,UL val)
{
    vmem.bgd=val;
}