//com.ctp Copyright (C) 1989-2004 I.Pedley (CTPP) Wed 16-Feb-2000 at 21:23:44

#include {time.htm}
#include {conio.htm}
#include {string.htm}
#include {pci.htm}
#include {dev.htm}
#include {com.htm}

#include {stdlib.htm}
#include {gl.htm}
#include {msg.htm}

#include {accdis.htm}
#include {svga.htm}
#include {vesa.htm}
#include {vga.htm}

    UL  comaddr[]={0x3f8,0x2f8,0x3e8,0x2e8};

VD  setCOMbaud(UL port,UL baudrate)
{
    UL  a,b,c=inp(port+1);

    outp(port+3,0x80);           //set DLAB on
    outp(port  ,baudrate&0xff);
    msecdelay(1);
    outp(port+1,baudrate>>8);
    outp(port+3,3);     //8 data bits
    outp(port+1,0);
    a=inp(port+5);
    b=inp(port+4);
    msecdelay(20);
    outp(port+1,c);
}
UL  getCOMbaud(UL port)
{
    UL  baudrate;
    UL  portbits=inp(port+3);
    outp(port+3,0x80);           //set DLAB on
    baudrate=inpw(port);
    outp(port+3,portbits);
    return baudrate;
}

UL  serialoutchar(UL port,UL c)
{//output a character to a serial port, using PIO
    UL t=uptime;
    outp(port,c);
    while(!(inp(port+5)&0x20))
        {
         if(uptime>t+50){return 0;}
        }
    return 1;
}
UL  serialoutstr(UL port,CH* str)
{
    while(*str)
        {
         if(!serialoutchar(port,*str)){return 0;}
         str++;
        }
    return 1;
}
UL  serialinchar(UL port)
{//input a character to a serial port, using PIO
    UL t=0;
    while(!(inp(port+5)&1))
        {
         t++;
         if(t==500000){return 0;}
        }
    return inp(port);
}
UL  serialinstr(UL port,CH* str,UL maxlen)
{
    UL  n=0;
    while(*str++=serialinchar(port))
        {
         n++;
         if(n==maxlen){break;}
        }
    *str=0;
    return n;
}
VD  setISR(UL port)
{
    outp(port+1,0x0d);
}
VD  clrISR(UL port)
{
    outp(port+1,0x00);
}
VD  setISR(DEV* d)
{
//    UL  n=d->svctr;
    outp(d->iores->start+1,0x0d);
//    while(n==d->svctr);
}
VD  xmit(DEV* d,UC c)
{
    UL  n=d->svctr;
    outp(d->iores->start,c);
    while(inp(d->iores->start+5)&0x20);
}
VD  clrISR(DEV* d)
{
    outp(d->iores->start+1,0x00);
}
VD  setCOMbaud(DEV* d,UL baudrate)
{
    UL  port=d->iores->start;
    clrISR(d);
    outp(port+3,0x80);           //set DLAB on
    outp(port,baudrate&0xff);
    outp(port+1,baudrate>>8);
    outp(port+3,3);     //8 data bits
    outp(port+1,0);
    setISR(d);
}
VD  resetCOM(DEV* d)
{
    UL  port=d->iores->start;
    setISR(d);
    outp(d->iores->start+4,0x00);
    if(inp(port+2)&4)
       {while(inp(port+5)&1){inp(port);}
        if((inp(port+2)&0xc0)==0xc0){outp(port+2,0x00);outp(port+2,0xc1);}
//        eoiIRQ(d->irqres->irqno);
       }
    msecdelay(50);
    outp(d->iores->start+4,0x0b);//msecdelay(20);
}
//VD  serialintson(UL port)
//{//switch serial port receiver interrupts on
//    setISR(port);msecdelay(20);
//    if(inp(port+4)){outp(port+4,0);msecdelay(100);} //GPO2 off forces reset
//    outp(port+4,0x0b);   //GPO2 on to connect IRQ
//    inp(port);
//    clrISR(port);
//    setISR(port);
////    while(inp(port+5)&1){inp(port);}
//}
VD  serialintson(UL port)
{//switch serial port receiver interrupts on
    setISR(port);msecdelay(20);
    if(inp(port+4)){outp(port+4,0);msecdelay(100);} //GPO2 off forces reset
    outp(port+4,0x0b);   //GPO2 on to connect IRQ
    inp(port);
    clrISR(port);
    setISR(port);
//    while(inp(port+5)&1){inp(port);}
}
VD  serialintsoff(UL port)
{//switch all serial port interrupts off using interrupt enable register only
    clrISR(port);
    outp(port+4,0x00);   //knock GPO2 off
}

//at-a-glance divisors for the 12 standard baud rates (1.8432MHz clock)
    UL  bauddvsr[12]={0x0417, 0x0300, 0x0180, 0x00c0,
                      0x0060, 0x0030, 0x0018, 0x000c,
                      0x0006, 0x0003, 0x0002, 0x0001};
    CH* baudstr[12]= {"110",  "150",  "300",  "600",
                      "1200", "2400", "4800", "9600",
                      "19200","38400","57600","115200"};

UL  parsebaud(CH* str)
{
    UL  n;
    for(n=0;n<12;n++)
        {
         if(!stricmp(str,baudstr[n])){return bauddvsr[n];}
        }
    return 0;
}
CH* getbaudstr(UL dvsr)
{
    UL  n;
    for(n=0;n<12;n++)
        {
         if(dvsr==bauddvsr[n]){return baudstr[n];}
        }
    return "unset";
}
#pragma breakpoints on
SL  detectCOMport(DEV* d)
{//called from DM_IDENT, must use d->sv0 to locate port!
    UL  port=d->sv0;
    UL  n;
    for(n=0;n<8;n++){if(inp(port+n)!=0xff){n=10;}}
    if(n==8){return 1;}     //ports all read 0xff, no serial port here
    UL tmp=inp(port+7);
    outp(port+7,0xaa);
    n=inp(port+7);
    if(n!=0xaa){return 1;}  //scratchpad is not read/write, no serial port here
    outp(port+7,tmp);
    outp(port+1,0);
    return 0;
}
SL  detectFSdig(DEV* d)
{
//special case for Fujitsu Stylistic 1000 inbuilt digitiser
//needed because this device doesn't appear in PnP info, or in
//BIOS data area
    UL  port=d->iores->start;
//Fujitsu stylistic-specifics
    if(inp(port+1)!=1){return 1;}
    if(getCOMbaud(port)!=BAUD19200){return 1;}
    d->flg|=DF_FSDIG;
//    DEV* sub=initdev(dpCOM,dtFSDIG,0,d,0,0);
//    if(sub)//..initialise downline interrupt signalling
//        {d->flg|=DF_CHILDHANDLER;}
    return 0;
}
CH* strstr(CH* str1,CH* str2)
{
    CH* ptr=strchr(str1,*str2);
    if(ptr)
        {
         if(!memcmp(ptr,str2,strlen(str2))){return ptr;}
        }
    return NULL;
}

VD  secpoll(UL hsec);

SL  echochar(DEV* d,UL c,UL delay)
{
    outp(d->iores->start,c);msecdelay(delay);
    UC  c1;
    if((readbuf(d,&c1,1)==1)&&(c==c1)){return 0;}
    return 1;
}



SL  detectMDM(DEV* d)
{
//    resetCOM(d);
//    setbaudrate(d,BAUD1200);
    UL sts=inp(d->iores->start+6);
//    if((sts&0x30)!=0x30){return 1;} //no Data Set Ready and Clear to Send
    if(!(sts&0x10)){return 1;}  //no Clear to Send


//    msecdelay(150);
//    outp(d->iores->start+2,0);
    if(echochar(d,'A', 100)){return 1;}
    if(echochar(d,'T', 100)){return 1;}
    if(echochar(d,'\r',100)){return 1;}
//    outp(d->iores->start+2,0x81);

//    serialoutstr(d->iores->start,"AT\r");
    SL  n;CH* ptr;
    for(n=0;n<5;n++)
        {
         msecdelay(100);
//         if(ptr=memchr(d->buf,'A',d->buflen))
//            {if(!memcmp(ptr,"AT",6)){return 0;}}
         if(strstr((CH*)d->buf,"AT"))
            {if(strstr((CH*)d->buf,"OK")){return 0;}}
        }
    return 1;
}
SL  detectGPS(DEV* d)
{
//    resetCOM(d);
    setbaudrate(d,BAUD4800);
//    secpoll(25);
////    msecdelay(10);
//    UL  port=d->iores->start;
//    if(inp(port+2)&4)
//       {while(inp(port+5)&1){inp(port);}    //clear FIFO overrun
//        outp(port+2,0x81);inp(port+6);}
////    d->bufin=d->bufout=0;
//    setISR(d);
//    msecdelay(10);
//    secpoll(50);
//    if(!d->bufin){return 1;}
    UL n;
    UC* ptr;
    for(n=0;n<20;n++)
        {
         secpoll(10);
//         msecdelay(10);
//         if(strstr((CH*)d->buf,"$GP")){return 0;}
         if(ptr=memchr(d->buf,'$',d->buflen))
            {if(!memcmp(ptr,"$GP",3)){return 0;}else{*ptr=0;}}
//         if(d->bufin&&(n>5)){break;}
        }
    setbaudrate(d,BAUD9600);
    for(n=0;n<15;n++)
        {
         secpoll(10);
//         msecdelay(10);
//         if(strstr((CH*)d->buf,"$GP")){return 0;}
         if(ptr=memchr(d->buf,'$',d->buflen))
            {if(!memcmp(ptr,"$GP",3)){return 0;}else{*ptr=0;}}
        }
    return 1;
}
SL  detectexternalCOMdevice(DEV* d)
{
    DEV* sub;
    CH* ptr;
//first wait for PnP device to stop sending info
    UL  n,m;
    do
        {
         n=d->bufin;        //wait up to half a second for device to
         for(m=0;m<25;m++)  //start sending PnP info
            {
//             msecdelay(10); //breakout every 10msec for quicker devices
             secpoll(2);    //breakout every 10msec for quicker devices
             if(n!=d->bufin){break;}
            }
//         if(d->bufin>200)
//            {//continuous stream COM device connected maybe GPS
////             outp(d->iores->start+1,0);
//             TRYGPS:
//             if(!detectGPS(d))
//                {
//                 sub=initdev(dpCOM,dtGPS,0,d,0,0);
//                 return 0;
//                }
//             else {return 1;}
//            }
        }
    while(n!=d->bufin);
    if(d->bufin)
        {
         d->usr=calloc(1,d->bufin+4);
         if(d->usr){*(UL*)d->usr=d->bufin;memcpy(d->usr+4,d->buf,d->bufin);}
         if(d->buf[0]=='M')
            {//brk       dev com2

             sub=initdev(dpCOM,dtMSM,0,d,0,0);
//             if(sub)//..initialise downline interrupt signalling
//                {d->flg|=DF_CHILDHANDLER;}
             return 0;
            }
         else // if(d->buf[0]=='T')
            {
             ptr=strchr((CH*)d->buf,0x28);       //search for PnP info block
             if(ptr)
                {
                 ptr+=3;    //skip 0x28 and PnP version
                 if(!memcmp(ptr,"KYE0100",7)){sub=initdev(dpCOM,dtKYE0100,0,d,0,0);return 0;}
                 if(!memcmp(ptr,"WAC0405",7)){sub=initdev(dpCOM,dtWAC0405,0,d,0,0);return 0;}
                }
            }

        }
    else
        {
         if(!detectMDM(d))
            {
             sub=initdev(dpCOM,dtMDM,0,d,0,0);
             return 0;
            }
        }
//    secpoll(50);
//    if(!d->bufin){return 1;}
    if(!detectGPS(d))
        {
         sub=initdev(dpCOM,dtGPS,0,d,0,0);
         return 0;
        }

    return 1;
}

//embryo DRVs for COM device

//simple drivers are almost identical, (indeed 16550 handler works fine for
//8250), but two handlers are presented here to show how to use DM_IDENT to
//discriminate between chipsets

SL  baud(UL argc,CH* argv[]);
SL  com1(UL argc,CH* argv[]);

SL  comsvc(DEV* d,UL service)
{
//common code
    UL  port=d->iores->start;
switch(service)
    {
     case DM_TRIGGER:
        UL  q=inp(port+2)&0x0f;
        UL  p=inp(port+6);
//        catdisp(45,40,"Com svtrg=%02X %02X   %8l         ",&q,&p,&d->svctr);
//        if(!q){q=inp(port+6);return q|0x100;}
        return (p<<8)+q;
//        if(q&1){brk return 0;}
//        return inp(port+2)&0x0f;
//        return inp(port+2)&4;
     case DM_PREINIT:   //setups before interrupts are running
        detectFSdig(d);
        return 0;
     case DM_INIT:
        initbuf(d,256,1);
        if(d->flg&DF_FSDIG){return 0;}
        d->flg|=DF_POLL|DF_COMMANDS;d->pollfreq=20;
        setCOMbaud(d,BAUD1200);
        outp(port+3,2);         //set 1200,n,7,1
        resetCOM(d);
//        serialintson(port);
//        if(inp(port+2)&4)
//           {while(inp(port+5)&1){inp(port);}
//            if((inp(port+2)&0xc0)==0xc0){outp(port+2,0x81);}}
        detectexternalCOMdevice(d);
        return 0;
     case DM_TERM:
        if(d->flg&DF_FSDIG){return 0;}
        if(d->usr){free(d->usr);d->usr=0;}
        serialintsoff(port);
        termbuf(d);
        return 0;
     case DM_RESET:
        outp(port+4,0);      //GPO2 off forces reset
        msecdelay(20);
        outp(port+4,0x0b);   //GPO2 on to connect IRQ
        return 0;
     case DM_HALT:
        if(d->flg&DF_FSDIG){return 0;}
        clrISR(d);
        return 0;
     case DM_RESUME:
        if(d->flg&DF_FSDIG){return 0;}
        setISR(d);
        return 0;
     case DM_CLIDISPINFO:
        CH* str=getbaudstr(d);
        clidisplay("Baud rate for %sc1400 is %sc1400",d->name,str);
        if(d->usr)
            {
             clidisplay("PnP buffer, length %4l bytes:",d->usr);
             UL  n,m=*(UL*)d->usr;
             for(n=0;nusr+4+n*16,d->usr+4+n*16);}
            }
        return 0;
     //device-specific section//////////////////
     case DM_GETBAUDRATE:
        return getCOMbaud(port);
     case DM_SETBAUDRATE:
        setCOMbaud(d,d->sv0);return 0;
     case DM_GETCOMMAND:
        if(!stricmp((CH*)d->svbuf,"baud")){d->svbuf=(UC*)baud;return 0;}
        if(!stricmp((CH*)d->svbuf,"com1")){d->svbuf=(UC*)com1;return 0;}
        return 1;
    }
}

SL  catdisp(UL row,UL col,CH* format,...);

SL  comsvc0(DEV* d,UL service)
{
//8250/16450 UART services
    UL  port=d->iores->start;

switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        if(detectCOMport(d)){return 1;}
        port=d->sv0;
        outp(port+2,0x81);           //attempt to set fifo on, threshold 8 bytes
        if((inp(port+2)&0xc0)==0xc0){return 1;} //its a 16550 UART
        return 0;       //no fifo, we can use this driver
     case DM_HANDLER:
        switch(d->svtrg&7)
            {
             case 4:
                UC c=d->sv0=inp(port);
                writebuf(d,&c,1);
                if(d->skt&&d->skt->drv)
                    {
                     d->skt->svtrg=d->svtrg;
                     d->skt->sv0  =d->sv0;
                     (*d->skt->drv->svc)(d->skt,DM_CHILDHANDLER);
                     d->skt->svctr++;
                    }
//                catdisp(46,40,"Com svtrg=%02X            ",&d->svtrg);
                break;
             case 1:
                //not quite sure why this is, but after GPO2 reset
                //I sometimes see d->svtrg=1, and this seems to require
                //a data input to clear the device
                //you see d->svtrg=1 means NO interrupt is pending
                //so I can't understand how this handler can be entered
                //on this condition
                inp(port);
                break;
            }
        return 0;
     default:
        return comsvc(d,service);
    }
}

    UL  resets;
SL  comsvc1(DEV* d,UL service)
{
//16550 UART services
    UL  port=d->iores->start;
    UL  n,c,q;

switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        if(detectCOMport(d)){return 1;}
        port=d->sv0;
        outp(port+2,0x81);           //attempt to set fifo on, threshold 8 bytes
        if((inp(port+2)&0xc0)!=0xc0){return 1;} //its not a 16550 UART
        return 0;       //got fifo, we can use this driver
     case DM_HANDLER:
        switch(d->svtrg&0x0f)
            {
             case 0x01:inp(port);
                n=d->svtrg;break;
             case 0x06://overrun,parity,frame error or break
                if(inp(port+5)&2){outp(port+2,0);outp(port+2,0x81);}
                break;
             case 0x0c:
             case 0x04:
//                c=d->sv0=inp(port);
//                writebuf(d,(UC*)&c,1);
//                if(d->skt&&d->skt->drv)
//                    {
//                     d->skt->svtrg=d->svtrg;
//                     d->skt->sv0  =d->sv0;
//                     (*d->skt->drv->svc)(d->skt,DM_CHILDHANDLER);
//                     d->skt->svctr++;
//                    }
////                catdisp(46,40,"Com svtrg=%02X            ",&d->svtrg);
//                break;
                UL fifo=0;
                do {
                    c=d->sv0=inp(port);
                    writebuf(d,(UC*)&c,1);
                    //downline hardware interrupt signal mechanism
                    if(d->skt&&d->skt->drv)
                        {
                         d->skt->svtrg=d->svtrg;
                         d->skt->sv0  =d->sv0;
                         (*d->skt->drv->svc)(d->skt,DM_CHILDHANDLER);
                         d->skt->svctr++;
                        }
//                    if(++fifo==16){break;}
                   }
                while(inp(port+5)&1);
                break;
             case 2:
                n=d->svtrg;
                break;
            }
//        catdisp(46,40,"Com svtrg=%04X   %8l         ",&d->svtrg,&d->svctr);
//        *(UL*)0xfec00040=0x24;
//        *(UL*)0xfee000b0=0x24;
        return 0;
     case DM_POLL:
        if(inp(port+2)&4)
            {
             outp(port+2,0);                    //reset FIFO
             while(inp(port+5)&1){inp(port);}   //clear FIFO overrun
             UL irqno=irq0(d)&0xff;
             if(irqno==-1){irqno=d->irqres->irqno;}
             eoiIRQ(irqno);
             outp(port+2,0xc1);                 //restart FIFO
             d->bufin=d->bufout=0;              //reset buffer
            }
////        if(inp(port+2)&4)
////           {outp(port+2,0x00);outp(port+2,0x81);} //reset FIFO
        break;
     default:
        return comsvc(d,service);
    }
}

//type-safe com DEV functions
UL  getbaudrate(DEV* d)
{
    return (*d->drv->svc)(d,DM_GETBAUDRATE);
}
CH* getbaudstr(DEV* d)
{
    UL dvsr=(*d->drv->svc)(d,DM_GETBAUDRATE);
    return getbaudstr(dvsr);
}
UL  setbaudrate(DEV* d,UL dvsr)
{
    d->sv0=dvsr;
    return (*d->drv->svc)(d,DM_SETBAUDRATE);
}

SL  dclick(PTR* p,UL btn);
VD  mpost(PTR* p,SL type);

//SL  dclick(CURSOR* p,UL btn)
//{
//    MSG m;prevmsg(&m);
//    SL  diff=uptime-m.uptime;
//    if(diff<25)
//        {
//         switch(btn)
//            {
//             case msLEFT:  if(m.type==mtMLup){return 1;}break;
//             case msRIGHT: if(m.type==mtMRup){return 1;}break;
//             case msCENTRE:if(m.type==mtMCup){return 1;}break;
//            }
//        }
//    return 0;
//}
//
//EX  UL  kbdstate;
//
//VD  mpost(CURSOR* p,SL type)
//{
////    if(msgflags&msgPS2MOUSE)
//        {
//         qmsg(type  ,p->posx,p->posy,p->press,kbdstate,p->dx,p->dy,0);
//        }
//}

VD  queuemessages(PTR* p)
{
    SL  btn;
    btn=(p->status^p->olds)&msLEFT;
    if(btn)
        {//left button status has changed
         if(p->status&msLEFT){mpost(p,mtML);if(dclick(p,msLEFT)){mpost(p,mtML2);}}
         else                {mpost(p,mtMLup);}
        }
    btn=(p->status^p->olds)&msRIGHT;
    if(btn)
        {//right button has changed
         if(p->status&msRIGHT){mpost(p,mtMR);if(dclick(p,msRIGHT)){mpost(p,mtMR2);}}
         else                 {mpost(p,mtMRup);}
        }
    btn=(p->status^p->olds)&msCENTRE;
    if(btn)
        {
         //centre button status has changed
         if(p->status&msCENTRE){mpost(p,mtMC);if(dclick(p,msCENTRE)){mpost(p,mtMC2);}}
         else                  {mpost(p,mtMCup);}
        }

    //do extra message when two buttons are down at the same time
    btn=(p->status^p->olds)&7;
    switch(btn)
        {
         case msLEFT|msRIGHT:  mpost(p,mtMLR);break;
         case msLEFT|msCENTRE: mpost(p,mtMLC);break;
         case msRIGHT|msCENTRE:mpost(p,mtMRC);break;
        }
    //send a pressure change message if necessary
    if(p->press!=p->oldp){mpost(p,mtMPC);}

    //send a mouse moved message if necessary
//    if((p->posx!=p->oldx)||(p->posy!=p->oldy)){mpost(p,mtMM);}
    if(memcmp(&p->posx,&p->oldx,8)){mpost(p,mtMM);}

    //fill in old state variables
//    p->olds=p->status;
//    p->oldx=p->posx;
//    p->oldy=p->posy;
//    p->oldp=p->press;
//    memcpy(&p->olds,&p->status,16);
}

//raw decoders and device drivers for various pointing devices follow....
//calling mousevent() in os/ps2 posts pointer messages to the system queue
//on creation all pointing devices are bound to boot VGA device
//the idea is: simply set PTR->VGA to switch pointer to a different screen
VD    mouseevent(DEV* d,DEV* vga,PTR* p); //now in os/ps2
DEV*  findbootmonitor(VD);
VD  accelerate(SL* dx,SL* dy);

VD  msmouserawdecode(DEV* d,PTR* p)
{
//decode raw device stream into PTR->status,PTR->dx,PTR->dy

//    DISP* disp=(DISP*)scr->usr;
//    CURSOR* p=&disp->c;

//standard MS mouse 3-byte encoding is:
//
// bits:  7      6      5      4      3     2     1     0
//byte0   0      1    right  left  
//                     btn    btn
//byte1   0      0   <--dX: left=negative,right=positive----->
//byte2   0      0   <--dY:   up=negative, down=positive----->

    SL  st=d->buf[0];
    SC  x=(d->buf[1]+((d->buf[0]&0x03)<<6));
    SC  y=(d->buf[2]+((d->buf[0]&0x0c)<<4));

    SL  dx=x;
    SL  dy=y;

    if(p->flags&mfACCEL){accelerate(&dx,&dy);}

    //swap dx,dy using p->dx,p->dy as placeholders
    if(p->flags&mfSWXY)
        {p->dx=dy*p->xdir;p->dy=dx*p->ydir;}
    else{p->dx=dx*p->xdir;p->dy=dy*p->ydir;}

    //set mouse button status flags
    if(p->flags&mfSWLR)
        {
         if(st&0x20){p->status|=msRIGHT;}
         else       {p->status&=~msRIGHT;}
         if(st&0x10){p->status|=msLEFT;}
         else       {p->status&=~msLEFT;}
       }
    else
       {
        if(st&0x10){p->status|=msRIGHT;}
        else       {p->status&=~msRIGHT;}
        if(st&0x20){p->status|=msLEFT;}
        else       {p->status&=~msLEFT;}
       }
}

SL  msmsvc(DEV* d,UL service)
{
//Microsoft serial mouse

    DEV*   scr=NULL;
    DISP* disp=NULL;
    PTR*   ptr=(PTR*)d->usr;
    if(ptr)
        {
         scr=ptr->s;
         if(scr)
            {
             disp=(DISP*)scr->usr;
            }
        }

switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;               //COM DEV decides is this device is present
     case DM_PREINIT:
        return 0;               //device MUST support DM_PREINIT
     case DM_INIT:
        initbuf(d,3,1);
        d->usr=(PTR*)calloc(sizeof(PTR),1);
        if(d->usr)
            {
             ptr=(PTR*)d->usr;
             ptr->xdir=ptr->ydir=1;//ptr->ydir=-1;
             ptr->xratio=ptr->yratio=5;
             d->sv0=(UL)finddevice("cga");
             (*d->drv->svc)(d,DM_SETDISPLAY);
             ptr->flags|=mfACCEL;
             ptr->vga=findbootmonitor();
            }
        return 0;
     case DM_SETDISPLAY:
        scr=(DEV*)d->sv0;
        if(scr)
            {
             ptr->s=scr;
             disp=(DISP*)scr->usr;
             ptr->cx=(disp->w/2)-1;
             ptr->cy=(disp->h/2)-1;
             ptr->mickx=0;
             ptr->micky=0;
             ptr->flags=mfACCEL;
             disp->c.disp=0;
             return 0;
            }
        return 1;
     case DM_TERM:
        free(d->usr);
        termbuf(d);
        return 0;
     case DM_CHILDHANDLER:
        if(!d->bufin)   //resynch, if ! inbyte&0x40 on byte 0 of the three
            {
             if(d->sv0&0x40){writebuf(d,(UC*)&d->sv0,1);}
             else return 0;
            }
        else {writebuf(d,(UC*)&d->sv0,1);} //writebuf wraps bufin to 0 after 3 bytes
        if(!d->bufin)
            {//we have a sequence of three bytes in the buffer now
//             clidisplay("%3H",d->buf);
             if(ptr)
                {
                 msmouserawdecode(d,ptr);
                 mouseevent      (d,ptr->vga,ptr);
//                 catdisp(48,40,"st=%02X,x=%4l,y=%4l",&disp->c.status,&disp->c.posx,&disp->c.posy);
                }
            }
        return 0;
     case DM_CLIDISPINFO:
//      clidisplay("st=%02X,x=%4l,y=%4l",&disp->c.status,&disp->c.posx,&disp->c.posy);
        return 0;
    }
}
VD  fsdigrawdecode(DEV* d,PTR* p)
{
//decode raw device stream into CURSOR->status,CURSOR->dx,CURSOR->dy

//Fujitsu Stylistic 5-byte encoding is:
//
// bits:  7      6      5      4      3     2     1     0
//byte0   1      0   proximity 0      0     0   right  left
//                     flag                      btn    btn
//byte1 and 2   16-bit X co-ord   lsb, msb
//byte3 and 4   16-bit y co-ord   lsb, msb

    SL  st=d->buf[0];

//    SL  dx=*(UI*)&d->buf[1];
//    SL  dy=*(UI*)&d->buf[3];
    SL  dx=((SL)(d->buf[2])<<7)+d->buf[1];
    SL  dy=((SL)(d->buf[4])<<7)+d->buf[3];

    //swap dx,dy using p->dx,p->dy as placeholders
    if(p->flags&mfSWXY)
        {p->dx=dy;p->dy=dx;}
    else{p->dx=dx;p->dy=dy;}

    //set mouse button status flags
    if(p->flags&mfSWLR)
        {
         if(st&0x01){p->status|=msRIGHT;}
         else       {p->status&=~msRIGHT;}
         if(st&0x02){p->status|=msLEFT;}
         else       {p->status&=~msLEFT;}
       }
    else
       {
        if(st&0x02){p->status|=msRIGHT;}
        else       {p->status&=~msRIGHT;}
        if(st&0x01){p->status|=msLEFT;}
        else       {p->status&=~msLEFT;}
       }
}

SL  fsdigsvc(DEV* d,UL service)
{
//Fujitsu Stylistic digitiser
    DEV*   scr=NULL;
    DISP* disp=NULL;
    PTR*   ptr=(PTR*)d->usr;
    if(ptr)
        {
         scr=ptr->s;
         if(scr)
            {
             disp=(DISP*)scr->usr;
            }
        }

switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;               //COM DEV decides is this device is present
     case DM_PREINIT:
        return 0;               //device MUST support DM_PREINIT
     case DM_INIT:
        initbuf(d,5,1);
        d->usr=(PTR*)calloc(sizeof(PTR),1);
        if(d->usr)
            {
             ptr=(PTR*)d->usr;
             ptr->xdir=ptr->ydir=1;//ptr->ydir=-1;
             ptr->xratio=ptr->yratio=1;
             d->sv0=(UL)finddevice("cga");
             (*d->drv->svc)(d,DM_SETDISPLAY);
             ptr->vga=findbootmonitor();
            }
        return 0;
     case DM_SETDISPLAY:
        scr=(DEV*)d->sv0;
        if(scr)
            {
             ptr->s=scr;
             disp=(DISP*)scr->usr;
             ptr->cx=(disp->w/2)-1;
             ptr->cy=(disp->h/2)-1;
             ptr->mickx=0;
             ptr->micky=0;
             ptr->flags=mfDIG;
//             ptr->t=220;      //Fujitsu Stylistic 1000
//             ptr->l=0;
//             ptr->b=5000;
//             ptr->r=6400;
             ptr->t=240;      //Fujitsu Stylistic 2300
             ptr->l=0;
             ptr->b=5400;
             ptr->r=6800;
             ptr->ydir=-1;
             disp->c.disp=0;
            }
        return 0;
     case DM_TERM:
        free(d->usr);
        termbuf(d);
        return 0;
     case DM_CHILDHANDLER:
        if(!d->bufin)   //resynch, if ! inbyte&0x80 on byte 0 of the five
            {
             if(d->sv0&0x80){writebuf(d,(UC*)&d->sv0,1);}
             else return 0;
            }
        else {writebuf(d,(UC*)&d->sv0,1);} //wraps bufin to 0 after 5 bytes
        if(!d->bufin)
            {//we have a sequence of five bytes in the buffer now
//             clidisplay("%5H",d->buf);
             if(ptr)
                {
                 fsdigrawdecode  (d,ptr);
                 mouseevent      (d,ptr->vga,ptr);
//                 catdisp(48,40,"st=%02X,x=%5l,y=%5l",&disp->c.status,&disp->c.posx,&disp->c.posy);
//                 catdisp(48,20,"st=%02X,x=%5i,y=%5i sx=%5l,sy=%5l",&disp->c.status,d->buf+1,d->buf+3,&disp->c.posx,&disp->c.posy);
                 SL  dx=((SL)(d->buf[2])<<7)+d->buf[1];
                 SL  dy=((SL)(d->buf[4])<<7)+d->buf[3];
//                 catdisp(48,20,"st=%02X,x=%5l,y=%5l sx=%5l,sy=%5l",&disp->c.status,&dx,&dy,&disp->c.posx,&disp->c.posy);
                }
            }
        return 0;
     case DM_CLIDISPINFO:
        return 0;
    }
}
VD  kyerawdecode(DEV* d,PTR* p)
{
//decode raw device stream into PTR->status,PTR->dx,PTR->dy

//Kye/Genius 5-byte encoding is:
//
// bits:  7      6      5      4      3     2     1     0
//byte0   1  proximity  0      1      1     0   right  left
//             flag                              btn    btn
//byte1 and 2   16-bit X co-ord   lsb, msb
//byte3 and 4   16-bit y co-ord   lsb, msb

    SL  st=d->buf[0];

//    SL  dx=*(UI*)&d->buf[1];
//    SL  dy=*(UI*)&d->buf[3];
    SL  dx=((SL)(d->buf[2])<<7)+d->buf[1];
    SL  dy=((SL)(d->buf[4])<<7)+d->buf[3];

    //swap dx,dy using p->dx,p->dy as placeholders
    if(p->flags&mfSWXY)
        {p->dx=dy;p->dy=dx;}
    else{p->dx=dx;p->dy=dy;}

    //set mouse button status flags
    if(p->flags&mfSWLR)
        {
         if(st&0x01){p->status|=msRIGHT;}
         else       {p->status&=~msRIGHT;}
         if(st&0x02){p->status|=msLEFT;}
         else       {p->status&=~msLEFT;}
       }
    else
       {
        if(st&0x02){p->status|=msRIGHT;}
        else       {p->status&=~msRIGHT;}
        if(st&0x01){p->status|=msLEFT;}
        else       {p->status&=~msLEFT;}
       }
}
SL  kyesvc(DEV* d,UL service)
{
//Kye0100/Genius EasyPen digitiser, 5-byte decode, almost identical to fsdig

    DEV*   scr=NULL;
    DISP* disp=NULL;
    PTR*   ptr=(PTR*)d->usr;
    if(ptr)
        {
         scr=ptr->s;
         if(scr)
            {
             disp=(DISP*)scr->usr;
            }
        }

switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;               //COM DEV decides is this device is present
     case DM_PREINIT:
        return 0;               //device MUST support DM_PREINIT
     case DM_INIT:
        initbuf(d,5,1);
        d->usr=(PTR*)calloc(sizeof(PTR),1);
        if(d->usr)
            {
             setbaudrate(d->ifc,BAUD9600);
             ptr=(PTR*)d->usr;
             ptr->xdir=ptr->ydir=1;//ptr->ydir=-1;
             ptr->xratio=ptr->yratio=1;
             d->sv0=(UL)finddevice("cga");
             (*d->drv->svc)(d,DM_SETDISPLAY);
            }
        return 0;
     case DM_SETDISPLAY:
        scr=(DEV*)d->sv0;
        if(scr)
            {
             ptr->s=scr;
             disp=(DISP*)scr->usr;
             ptr->cx=(disp->w/2)-1;
             ptr->cy=(disp->h/2)-1;
             ptr->mickx=0;
             ptr->micky=0;
             ptr->flags=mfDIG;
             ptr->t=000;
             ptr->l=000;
             ptr->b=1500;
             ptr->r=2000;
             ptr->ydir=-1;
             disp->c.disp=0;
            }
        return 0;
     case DM_TERM:
        free(d->usr);
        termbuf(d);
        return 0;
     case DM_CHILDHANDLER:
        if(!d->bufin)   //resynch, if ! inbyte&0x80 on byte 0 of the five
            {
             if(d->sv0&0x80){writebuf(d,(UC*)&d->sv0,1);}
             else return 0;
            }
        else {writebuf(d,(UC*)&d->sv0,1);} //wraps bufin to 0 after 5 bytes
        if(!d->bufin)
            {//we have a sequence of five bytes in the buffer now
//             clidisplay("%5H",d->buf);
             if(ptr)
                {
                 kyerawdecode(d,ptr);
                 mouseevent  (d,ptr->vga,ptr);
                 SL  dx=((SL)(d->buf[2])<<7)+d->buf[1];
                 SL  dy=((SL)(d->buf[4])<<7)+d->buf[3];
//                 catdisp(48,20,"st=%02X,x=%5li,y=%5l sx=%5l,sy=%5l",&disp->c.status,&dx,&dy,&disp->c.posx,&disp->c.posy);
                }
            }
        return 0;
     case DM_CLIDISPINFO:
        return 0;
    }
}
VD  wacom7rawdecode(DEV* d,PTR* p)
{
//decode raw device stream into PTR->status,PTR->dx,PTR->dy

//Wacom 7-byte encoding is:
//
// bits:  7      6      5      4      3      2     1     0
//byte0   1  proximity  1      0  right|left 0     0     0
//             flag           btn    btn
//byte1 and 2   16-bit X co-ord   msb, lsb
//byte3   0      0      0    right  left     0     0     0
//                            btn    btn
//byte4 and 5   16-bit y co-ord   msb, lsb
//byte6   0    <7-bit signed pressure sense  -64 to +63 --->
                //also reads zero as pen leaves proximity

    SL  st=d->buf[3];

    SL  dx=*(Um*)&d->buf[1];
    SL  dy=*(Um*)&d->buf[4];


    //swap dx,dy using p->dx,p->dy as placeholders
    if(p->flags&mfSWXY)
        {p->dx=dy;p->dy=dx;}
    else{p->dx=dx;p->dy=dy;}

    //set mouse button status flags
    if(p->flags&mfSWLR)
        {
         if(st&0x08){p->status|=msRIGHT;}
         else       {p->status&=~msRIGHT;}
         if(st&0x10){p->status|=msLEFT;}
         else       {p->status&=~msLEFT;}
       }
    else
       {
        if(st&0x10){p->status|=msRIGHT;}
        else       {p->status&=~msRIGHT;}
        if(st&0x08){p->status|=msLEFT;}
        else       {p->status&=~msLEFT;}
       }

    SC  pr=d->buf[6]|0x80;
    p->press=pr;

}
SL  wacsvc(DEV* d,UL service)
{
//Wacom0405 digitiser, uses Fujitsu Stylistic 5-byte decode

    DEV*   scr=NULL;
    DISP* disp=NULL;
    PTR*   ptr=(PTR*)d->usr;
    if(ptr)
        {
         scr=ptr->s;
         if(scr)
            {
             disp=(DISP*)scr->usr;
            }
        }

switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;               //COM DEV decides is this device is present
     case DM_PREINIT:
        return 0;               //device MUST support DM_PREINIT
     case DM_INIT:
        initbuf(d,7,1);
        d->usr=(PTR*)calloc(sizeof(PTR),1);
        if(d->usr)
            {
             setbaudrate(d->ifc,BAUD9600);
             ptr=(PTR*)d->usr;
             ptr->xdir=ptr->ydir=1;//ptr->ydir=-1;
             ptr->xratio=ptr->yratio=1;
             d->sv0=(UL)finddevice("cga");
             (*d->drv->svc)(d,DM_SETDISPLAY);
             ptr->vga=findbootmonitor();
            }
        return 0;
     case DM_SETDISPLAY:
        scr=(DEV*)d->sv0;
        if(scr)
            {
             ptr->s=scr;
             disp=(DISP*)scr->usr;
             ptr->cx=(disp->w/2)-1;
             ptr->cy=(disp->h/2)-1;
             ptr->mickx=0;
             ptr->micky=0;
             ptr->flags=mfDIG;
             ptr->t=000;
             ptr->l=000;
             ptr->b=7490;
             ptr->r=10030;
//             ptr->ydir=-1;
             disp->c.disp=0;
            }
        return 0;
     case DM_TERM:
        free(d->usr);
        termbuf(d);
        return 0;
     case DM_CHILDHANDLER:
        if(!d->bufin)   //resynch, if ! inbyte&0x80 on byte 0 of the seven
            {
             if(d->sv0&0x80){writebuf(d,(UC*)&d->sv0,1);}
             else return 0;
            }
        else {writebuf(d,(UC*)&d->sv0,1);} //wraps bufin to 0 after 7 bytes
        if(!d->bufin)
            {//we have a sequence of five bytes in the buffer now
//             clidisplay("%7H",d->buf);
             if(ptr)
                {
                 wacom7rawdecode(d,ptr);
                 mouseevent     (d,ptr->vga,ptr);
        //         catdisp(48,20,"st=%02X,x=%5p,y=%5p sx=%5l,sy=%5l",&p->status,d->buf+1,d->buf+4,&p->posx,&p->posy);
                }
            }
        return 0;
     case DM_CLIDISPINFO:
        return 0;
    }
}

SL  pensvc0(DEV* d,UL service)
{
//8250/16450 UART services
    UL  port=d->iores->start;

switch(service)
    {//Mandatory message support//////////////////
     case DM_INIT:
        if(d->sts&sfRUNNING){return 0;}
        initbuf(d,256,1);
//        serialintson(port);
        outp(port+1,1);
        d->flg|=DF_FSDIG|DF_POLL;
        DEV* sub=initdev(dpCOM,dtFSDIG,0,d,0,0);
        return 0;
     case DM_PREINIT:
        return 0;
     case DM_IDENT:
        if(detectCOMport(d)){return 1;}
        port=d->sv0;
        outp(port+2,0x81);           //attempt to set fifo on, threshold 8 bytes
        if((inp(port+2)&0xc0)==0xc0){brk return 1;} //16550 UART on a digitiser?
        return 0;       //no fifo, we can use this driver
     case DM_HANDLER:
        switch(d->svtrg&7)
            {
             case 6://overrun,parity,frame error or break
//                if(inp(port+5)&2){outp(port+2,0);outp(port+2,0x81);}
                if(inp(port+5)&2){inp(port);}
                break;
             case 4:
                UC c=d->sv0=inp(port);
                writebuf(d,&c,1);
                if(d->skt&&d->skt->drv)
                    {
                     d->skt->svtrg=d->svtrg;
                     d->skt->sv0  =d->sv0;
                     (*d->skt->drv->svc)(d->skt,DM_CHILDHANDLER);
                     d->skt->svctr++;
                    }
//                catdisp(46,40,"Com svtrg=%02X            ",&d->svtrg);
                break;
             case 1:
                //not quite sure why this is, but after GPO2 reset
                //I sometimes see d->svtrg=1, and this seems to require
                //a data input to clear the device
                //you see d->svtrg=1 means NO interrupt is pending
                //so I can't understand how this handler can be entered
                //on this condition
                inp(port);
                break;
            }
        return 0;
     case DM_POLL:
        if(inp(port+5)&2){inp(port);}
        return 0;
     default:
        return comsvc(d,service);
    }
}
SL  mdmsvc  (DEV* d,UL service)
{
//MDM serial modem handler

    UL  n,ck,ck1,ck2;
switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;               //COM DEV decides is this device is present
     case DM_PREINIT:
        return 0;               //device MUST support DM_PREINIT
     case DM_INIT:
        initbuf(d,256,1);
        return 0;
     case DM_TERM:
//        free(d->usr);
        termbuf(d);
        return 0;
     case DM_CHILDHANDLER:
        if(d->sv0==0x0a)
            {
             d->buf[d->bufin]=0;
             clidisplay((CH*)d->buf);
             if(d->notify){(*d->notify)(d);}
             d->bufin=0;
            }
        writebuf(d,(UC*)&d->sv0,1);
        return 0;
     case DM_CLIDISPINFO:
        return 0;
    }
}
SL  baud(UL argc,CH* argv[])
{
    if(argc<2)
        {
         clidisplay("Usage:BAUD  ");return 1;
        }
    DEV* d=finddevice(argv[1]);
    if(!d)
        {
         clidisplay("Device %sc1500 not found",argv[1]);return 1;
        }

    UL  br;
    if(d->drv)
        {
         if(d->drv->svc)
            {
             if(argc<3)
                {
                 br=getbaudrate(d);
                 clidisplay("Baud rate for <%sc1500> is %s",d->name,getbaudstr(br));
                }
             else
                {
                 br=parsebaud(argv[2]);
                 if(br){return setbaudrate(d,br);}
                }
            }
        }
    return 1;
}
SL  com1(UL argc,CH* argv[])
{
//#pragma breakpoint

    DEV* d=finddevice("com2");

    CH msg[512];
    if(d)
        {
         strcpy(msg,argv[1]);
         strcat(msg,"\r");
         if(!serialoutstr(d->iores->start,msg))
            {
             brk return 1;
            }
        }

    return 0;
}
//in st4110.htm
SL  st4110svc   (DEV* d,UL service);
SL  st4110digsvc(DEV* d,UL service);
//in gps.htm
SL  gpssvc  (DEV* d,UL service);

//these comdrv0 and comdrv1 could easily be merged into one, but serve to
//show how DM_IDENT can be used to load the correct driver for similar
//devices with different chipsets
//ST4110 digitiser shows how to substitute specials for
//the standard drivers - DRV table is searched backwards
//so st41drv is tried before comdrv0
    DRV comdrv1={0,0x7000000,0x7000000,comsvc1,"16550 UART"};
    DRV comdrv0={0,0x7000000,0x7000000,comsvc0,"8250/16450 UART"};
    DRV st41drv1={dpCOM,dtST4110,dcPEN,st4110digsvc,"ST4110 pen"};
    DRV st41drv={0,0x7000000,0x7000000,st4110svc,"ST4110"};
    DRV msmdrv  ={dpCOM,dtMSM  ,dcPEN,msmsvc,   "MS mouse"};
    DRV pendrv  ={    0,0x9010000,0x9010000,pensvc0, "Digitiser"};
    DRV kyedrv  ={dpCOM,dtKYE0100,dcPEN,kyesvc, "Genius EasyPen"};
    DRV fsdigdrv={dpCOM,dtFSDIG,dcPEN,fsdigsvc, "Pen mouse"};
    DRV wacdrv  ={dpCOM,dtWAC0405,dcPEN,wacsvc, "Wacom Tablet"};
//    DRV mdmdrv  ={    0,0x7800001,0x7800001,comsvc1, "Modem"};
    DRV gpsdrv  ={dpCOM,dtGPS    ,dcGPS    ,gpssvc , "GPS"};
    DRV mdmdrv  ={dpCOM,dtMDM    ,dcMDM    ,mdmsvc , "AT Modem"};


SL  main(UL argc,CH* argv[])
{
    SL  n,m,o;
    return 0;
}

SL  term(VD)
{
    return 0;
}