/* OS- and machine-dependent stuff for IBM-PC running MS-DOS and Turbo-C * Copyright 1991 Phil Karn, KA9Q */ #include #include #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "mbuf.h" #include "proc.h" #include "iface.h" #include "internet.h" #include "session.h" #include "socket.h" #include "cmdparse.h" #include "pc.h" #define CTLC 0x3 #define DEL 0x7f static int kbchar __ARGS((void)); extern int Curdisp; extern struct proc *Display; FILE *Rawterm; unsigned _stklen = 8192; int Tick; static int32 Starttime; int32 Clock; /* This flag is set by setirq() if IRQ 8-15 is used, indicating * that the machine is a PC/AT with a second 8259 interrupt controller. * If this flag is set, the interrupt return code in pcgen.asm will * send an End of Interrupt command to the second 8259 as well as the * first. */ int Isat; static char Ttbuf[BUFSIZ]; static char Tsbuf[BUFSIZ]; static int saved_break; /* Keyboard input buffer */ #define KBSIZE 256 static struct { char buf[KBSIZE]; char *wp; char *rp; int cnt; } Keyboard; int errhandler(errval,ax,bp,si) int errval,ax,bp,si; { return 3; /* Fail the system call */ } /* Called at startup time to set up console I/O, memory heap */ void ioinit() { /* Fail all I/O errors */ harderr(errhandler); /* Save these two file table entries for something more useful */ fclose(stdaux); fclose(stdprn); setbuf(stdout,Tsbuf); Rawterm = fopen("con","wb"); setbuf(Rawterm,Ttbuf); /* this breaks tab expansion so you must use ANSI or NANSI */ ioctl(fileno(Rawterm), 1, (ioctl(fileno(Rawterm),0) & 0xff) | 0x20); saved_break = getcbrk(); setcbrk(0); Starttime = bioscnt(); /* Link timer handler into timer interrupt chain */ chtimer(btick); /* Find out what multitasker we're running under, if any */ chktasker(); /* Initialize keyboard queue */ Keyboard.rp = Keyboard.wp = Keyboard.buf; } /* Called just before exiting to restore console state */ void iostop() { struct iface *ifp,*iftmp; void (**fp)(); setbuf(Rawterm,NULLCHAR); ioctl(fileno(Rawterm), 1, ioctl(fileno(Rawterm), 0) & 0xff & ~0x20); setcbrk(saved_break); for(ifp = Ifaces;ifp != NULLIF;ifp = iftmp){ iftmp = ifp->next; if_detach(ifp); } /* Call list of shutdown functions */ for(fp = Shutdown;*fp != NULLVFP;fp++){ (**fp)(); } } /* Spawn subshell */ int doshell(argc,argv,p) int argc; char *argv[]; void *p; { char *command; int ret; if((command = getenv("COMSPEC")) == NULLCHAR) command = "/COMMAND.COM"; ret = spawnv(P_WAIT,command,argv); return ret; } /* Keyboard interrupt handler */ void kbint() { int sig = 0; int c; while((c = kbraw()) != -1 && Keyboard.cnt < KBSIZE){ sig = 1; *Keyboard.wp++ = c; if(Keyboard.wp == &Keyboard.buf[KBSIZE]) Keyboard.wp = Keyboard.buf; Keyboard.cnt++; } if(sig){ psignal(&Keyboard,0); } } static int kbchar() { char i_state; char c; i_state = dirps(); while(Keyboard.cnt == 0) pwait(&Keyboard); Keyboard.cnt--; restore(i_state); c = *Keyboard.rp++; if(Keyboard.rp == &Keyboard.buf[KBSIZE]) Keyboard.rp = Keyboard.buf; return uchar(c); } /* Flush the raw terminal output */ void rflush() { fflush(Rawterm); } /* Read characters from the keyboard, translating them to "real" ASCII. * If none are ready, block. The F-10 key is special; translate it to -2. */ int kbread() { int c; if((c = kbchar()) == 0){ /* Lead-in to a special char */ c = kbchar(); switch(c){ case 3: /* NULL (bizzare!) */ c = 0; break; case 68: /* F-10 key (used as command-mode escape) */ c = -2; break; case 83: /* DEL key */ c = 0x7f; break; default: /* Dunno what it is */ c = -1; } } return c; } /* Install hardware interrupt handler. * Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors * Note that bus line IRQ2 maps to IRQ9 on the AT */ int setirq(irq,handler) unsigned irq; INTERRUPT (*handler)(); { /* Set interrupt vector */ if(irq < 8){ setvect(8+irq,handler); } else if(irq < 16){ Isat = 1; setvect(0x70 + irq - 8,handler); } else { return -1; } return 0; } /* Return pointer to hardware interrupt handler. * Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors */ INTERRUPT (*getirq(irq))() unsigned int irq; { /* Set interrupt vector */ if(irq < 8){ return getvect(8+irq); } else if(irq < 16){ return getvect(0x70 + irq - 8); } else { return NULLVIFP; } } /* Disable hardware interrupt */ int maskoff(irq) unsigned irq; { if(irq < 8){ setbit(0x21,(char)(1<> 16; offset = l; return MK_FP(seg,offset); } #ifdef notdef /* Assembler versions in pcgen.asm */ /* Multiply a 16-bit multiplier by an arbitrary length multiplicand. * Product is left in place of the multiplicand, and the carry is * returned */ int16 longmul(multiplier,n,multiplicand) int16 multiplier; int n; /* Number of words in multiplicand[] */ register int16 *multiplicand; /* High word is in multiplicand[0] */ { register int i; unsigned long pc; int16 carry; carry = 0; multiplicand += n; for(i=n;i != 0;i--){ multiplicand--; pc = carry + (unsigned long)multiplier * *multiplicand; *multiplicand = pc; carry = pc >> 16; } return carry; } /* Divide a 16-bit divisor into an arbitrary length dividend using * long division. The quotient is returned in place of the dividend, * and the function returns the remainder. */ int16 longdiv(divisor,n,dividend) int16 divisor; int n; /* Number of words in dividend[] */ register int16 *dividend; /* High word is in dividend[0] */ { /* Before each division, remquot contains the 32-bit dividend for this * step, consisting of the 16-bit remainder from the previous division * in the high word plus the current 16-bit dividend word in the low * word. * * Immediately after the division, remquot contains the quotient * in the low word and the remainder in the high word (which is * exactly where we need it for the next division). */ unsigned long remquot; register int i; if(divisor == 0) return 0; /* Avoid divide-by-zero crash */ remquot = 0; for(i=0;i> 16; } #endif void sysreset() { void (*foo) __ARGS((void)); foo = MK_FP(0xffff,0); /* FFFF:0000 is hardware reset vector */ (*foo)(); } void newscreen(sp) struct session *sp; { if(sp != NULLSESSION) sp->screen = callocw(1,sizeof(struct screen)); } void freescreen(sp) struct session *sp; { if(sp == NULLSESSION || sp->screen == NULLSCREEN) return; if(sp->screen->save != NULLCHAR) free(sp->screen->save); free((char *)sp->screen); } /* Save specified session screen and resume console screen */ void swapscreen(old,new) struct session *old,*new; { struct text_info tr; if(old == new) return; /* Nothing to do */ fflush(Rawterm); gettextinfo(&tr); if(old != NULLSESSION){ /* Save old screen */ if(old->screen->save == NULLCHAR) old->screen->save = malloc(2*tr.screenheight*tr.screenwidth); if(old->screen->save != NULLCHAR) gettext(tr.winleft,tr.wintop,tr.winright, tr.winbottom,old->screen->save); old->screen->row = tr.cury; old->screen->col = tr.curx; } if(new != NULLSESSION){ /* Load new screen */ if(new->screen->save != NULLCHAR){ puttext(tr.winleft,tr.wintop,tr.winright, tr.winbottom,new->screen->save); gotoxy(new->screen->col,new->screen->row); /* Free the memory (saves 4K on a continuous basis) */ free(new->screen->save); new->screen->save = NULLCHAR; } else clrscr(); /* Start with a fresh slate */ } alert(Display,1); /* Wake him up */ } void display(i,v1,v2) int i; void *v1; void *v2; { int c; struct session *sp; /* This is very tricky code. Because the value of "Current" can * change any time we do a pwait, we have to be careful to detect * any change and go back and start again. */ for(;;){ sp = Current; if(sp->morewait){ pwait(&sp->row); if(sp != Current || sp->row <= 0){ /* Current changed value, or the user * hasn't really hit a key */ continue; } /* Erase the prompt */ fprintf(Rawterm,"\r \r"); } sp->morewait = 0; if((c = rrecvchar(sp->output)) == -1){ /* the alert() in swapscreen will cause this to * return -1 when current changes */ pwait(NULL); /* Prevent a nasty loop */ continue; } putc(c,Rawterm); if(sp->record != NULLFILE) putc(c,sp->record); if(sp->flowmode && c == '\n' && --sp->row <= 0){ fprintf(Rawterm,"--More--"); sp->morewait = 1; } } } /* Return time since startup in milliseconds. If the system has an * 8254 clock chip (standard on ATs and up) then resolution is improved * below 55 ms (the clock tick interval) by reading back the instantaneous * value of the counter and combining it with the global clock tick counter. * Otherwise 55 ms resolution is provided. * * Reading the 8254 is a bit tricky since a tick could occur asynchronously * between the two reads. The tick counter is examined before and after the * hardware counter is read. If the tick counter changes, try again. * Note: the hardware counter counts down from 65536. */ int32 msclock() { int32 hi; int16 lo; int16 count[4]; /* extended (48-bit) counter of timer clocks */ if(!Isat) return Clock * MSPTICK; do { hi = Clock + Tick; lo = clockbits(); } while(hi != Clock + Tick); count[0] = 0; count[1] = hi >> 16; count[2] = hi; count[3] = -lo; longmul(11,4,count); /* The ratio 11/13125 is exact */ longdiv(13125,4,count); return ((long)count[2] << 16) + count[3]; } /* Return clock in seconds */ int32 secclock() { int32 hi; int16 lo; int16 count[4]; /* extended (48-bit) counter of timer clocks */ if(!Isat) return (Clock * MSPTICK) / 1000L; do { hi = Clock + Tick; lo = clockbits(); } while(hi != Clock + Tick); count[0] = 0; count[1] = hi >> 16; count[2] = hi; count[3] = -lo; longmul(11,4,count); /* The ratio 11/13125 is exact */ longdiv(13125,4,count); longdiv(1000,4,count); /* Convert to seconds */ return ((long)count[2] << 16) + count[3]; } int doisat(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Isat,"AT/386 mode",argc,argv); } /* Directly read BIOS count of time ticks. This is used instead of * calling biostime(0,0L). The latter calls BIOS INT 1A, AH=0, * which resets the midnight overflow flag, losing days on the clock. */ long bioscnt() { int i_state; long rval; i_state = dirps(); rval = * (long far *)MK_FP(0x40,0x6c); restore(i_state); return rval; }