/* 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 "smtp.h" #include "cmdparse.h" #include "dirutil.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; int Background = 1; int Watchdog = 0; /* Watch Dog off by default */ int WDTick = 300*(1000 / MSPTICK); /* 5 minutes watchdog timer */ int WDCurr = 300*(1000 / MSPTICK); /* Initial count down timer */ /* 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 Nokeys = 0; int am_i_an_AT() { unsigned char *model_code = MK_FP(0xF000,0xFFFE); if(*model_code == 0xFC) return 1; else return 0; } 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); #ifdef MSDOS /* test to see if we're running on an AT class machine. * Set Isat flag accordingly - N1BEE */ Isat = am_i_an_AT(); #endif 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(Background) { if(!start_back()) return -1; Nokeys++; } if(argc == 1 || !stricmp(argv[1], "/c")) { if((command = getenv("COMSPEC")) == NULLCHAR) command = "COMMAND.COM"; ret = spawnvp(P_WAIT,command,argv); } else { ret = spawnvp(P_WAIT,argv[1],(argv + 1)); } if(Background) { Nokeys--; stop_back(); } return ret; } /* Spawn mailer as subshell */ int dobmail(argc,argv,p) int argc; char *argv[]; void *p; { char *command; int ret; if(Background) { if(!start_back()) return -1; Nokeys++; } if((command = getenv("MAILER")) == NULLCHAR) command = "BM.EXE"; ret = spawnvp(P_WAIT,command,argv); if(Background) { Nokeys--; stop_back(); } smtptick(NULL); /* tickle smtp to send any mail */ return ret; } /* if multitask mode is set - allow NOS and shell/mail to share system time */ dobackg(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Background,"Multitasking DOS Shell ",argc,argv); } /* if watch-dog mode is set - make NOS reboot the system if it stalls */ dowatchdog(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Watchdog,"NOS Watch Dog",argc,argv); } /* Keyboard interrupt handler */ void kbint() { int sig = 0; int c; if(Background && Nokeys) return; 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; if(Background && Nokeys) return -1; 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); } #ifdef MSDOS struct funcstr { char fkey; char alloced; char *fvalue; }; struct funcstr fkeys[] = { 15,0,NULLCHAR, /* tab + shift */ 59,1,"\033OP", /* F1 */ 60,1,"\033OQ", /* F2 */ 61,1,"\033OR", /* F3 */ 62,1,"\033OS", /* F4 */ 63,0,NULLCHAR, /* F5 */ 64,0,NULLCHAR, /* F6 */ 65,0,NULLCHAR, /* F7 */ 66,0,NULLCHAR, /* F8 */ 67,0,NULLCHAR, /* F9 */ 68,0,NULLCHAR, /* F10 */ 71,1,"\010", /* home*/ 72,1,"\033[A", /* up arrow*/ 73,1,"\025", /* pgup */ 75,1,"\033[D", /* left arrow */ 77,1,"\033[C", /* right arrow */ 79,1,"\005", /* end */ 80,1,"\033[B", /* down arrow */ 81,1,"\012", /* pgdn */ 82,1,"\001", /* ins */ 83,1,"\0177", /* del */ 84,0,NULLCHAR, /* F1 + shift*/ 85,0,NULLCHAR, /* F2 + shift*/ 86,0,NULLCHAR, /* F3 + shift*/ 87,0,NULLCHAR, /* F4 + shift*/ 88,0,NULLCHAR, /* F5 + shift*/ 89,0,NULLCHAR, /* F6 + shift*/ 90,0,NULLCHAR, /* F7 + shift*/ 91,0,NULLCHAR, /* F8 + shift*/ 92,0,NULLCHAR, /* F9 + shift*/ 93,0,NULLCHAR, /* F10 + shift*/ 94,0,NULLCHAR, /* F1 + control*/ 95,0,NULLCHAR, /* F2 + control*/ 96,0,NULLCHAR, /* F3 + control*/ 97,0,NULLCHAR, /* F4 + control*/ 98,0,NULLCHAR, /* F5 + control*/ 99,0,NULLCHAR, /* F6 + control*/ 100,0,NULLCHAR, /* F7 + control*/ 101,0,NULLCHAR, /* F8 + control*/ 102,0,NULLCHAR, /* F9 + control*/ 103,0,NULLCHAR, /* F10 + control*/ 104,0,NULLCHAR, /* F1 + alt*/ 105,0,NULLCHAR, /* F2 + alt*/ 106,0,NULLCHAR, /* F3 + alt*/ 107,0,NULLCHAR, /* F4 + alt*/ 108,0,NULLCHAR, /* F5 + alt*/ 109,0,NULLCHAR, /* F6 + alt*/ 110,0,NULLCHAR, /* F7 + alt*/ 111,0,NULLCHAR, /* F8 + alt*/ 112,0,NULLCHAR, /* F9 + alt*/ 113,0,NULLCHAR, /* F10 + alt*/ 114,0,NULLCHAR, /* PrtSc + ctl*/ 117,0,NULLCHAR, /* end + ctl */ 118,0,NULLCHAR, /* pgup + ctl */ 119,0,NULLCHAR, /* home + ctl */ 132,0,NULLCHAR, /* pgdn + ctl */ 0,0,NULLCHAR }; char Leftover = 0; char *Nextkey; #endif /* 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() { #ifndef MSDOS int c; #else int c,i,j; if((c = Leftover) != 0) { Leftover = *Nextkey++; return c; } #endif MSDOS 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) */ if(fkeys[10].fvalue == NULLCHAR){ c = -2; break; } default: /* Dunno what it is */ #ifdef MSDOS for(i=0;(j = fkeys[i].fkey) != 0;i++) if(j == c) { Nextkey = fkeys[i].fvalue; if(Nextkey == NULLCHAR) { c = -1; return c; } if((c = *Nextkey++) != 0) Leftover = *Nextkey++; else c = -1; return c; } #endif c = -1; } } return c; } #ifdef MSDOS int dofkey(argc,argv,p) int argc; char *argv[]; void *p; { int c,i,j; char *q, *r; char str[100]; if(argc == 1) { tprintf("key num key num key num key num key num\n"); tprintf("f1 59 sf1 84 cf1 94 af1 104 pgup 73\n"); tprintf("f2 60 sf2 85 cf2 95 af2 105 pgdn 81\n"); tprintf("f3 61 sf3 86 cf3 96 af3 106 home 71\n"); tprintf("f4 62 sf4 87 cf4 97 af4 107 end 79\n"); tprintf("f5 63 sf5 88 cf5 98 af5 108 arup 72\n"); tprintf("f6 64 sf6 89 cf6 99 af6 109 ardn 80\n"); tprintf("f7 65 sf7 90 cf7 100 af7 110 ar l 75\n"); tprintf("f8 66 sf8 91 cf8 101 af8 111 ar r 77\n"); tprintf("f9 67 sf9 92 cf9 102 af9 112 ins 82\n"); tprintf("f10 68 sf10 93 cf10 103 af10 113 del 83\n"); tprintf("usage: fkey [ | \"string\"]\n"); return 0; } c = atoi(argv[1]); if(c == 0 || c > 255) { tprintf("fkey number out of range.\n"); return 1; } for(j = 0;(i = fkeys[j].fkey) != 0; j++) if(i == c) break; if(i == 0){ tprintf("fkey number not found\n"); return 1; } if(argc == 2) { q = fkeys[j].fvalue; r = str; if(q == NULLCHAR) tprintf("fkey %d has no assigned value.\n",c); else { while(*q) if(*q < ' ') { /* This is ASCII dependent !! */ *r++ = '^'; *r++ = *q++ + 0x40; } else *r++ = *q++; *r = '\0'; tprintf("fkey = %s\n",str); } return 0; } if(argc == 3) { if(fkeys[j].alloced) fkeys[j].alloced = 0; else if(fkeys[j].fvalue != NULLCHAR) free(fkeys[j].fvalue); r = str; q = argv[2]; while(*q){ if(*q == '^'){ /* ^ gives control char next */ q++; if(*q == '^') { *r++ = *q++; /* No, he wants a ^ */ } else { *r++ = *q++ & 0x1f; } } else *r++ = *q++; } *r = '\0'; fkeys[j].fvalue = strdup(str); } return 0; } #endif /* 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){ if(old->split){ window(1,1,80,25); tr.winbottom = 25; } 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){ if(new->split){ window(1,1,80,23); clrscr(); } else { window(1,1,80,25); clrscr(); } 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 */ if(new->split){ new->tsavex = 1; new->tsavey = 1; new->bsavex = 1; new->bsavey = 24; window(1,1,80,25); cputs("_\b"); window(1,1,80,23); } } } 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; } if(sp->split){ if(c == 0x0a){ cputs(Eol); clreol(); } else putch(c); } else { 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); /* Make sure a tick didn't just occur */ 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); /* Make sure a tick didn't just occur */ 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); 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, * wich 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; } /* same as getenv(), but return "" instead of NULL when it does not exist */ char *getnenv (name) char *name; { char *rv; if ((rv = getenv(name)) == NULL) rv = ""; /* NULL replaced by "" */ return rv; }