/* Copyright 1990,1991,1992 Eric R. Smith. All rights reserved. */ /* * BIOS replacement routines */ #include "mint.h" #define UNDEF 0 /* should match definition in tty.c */ /* some key definitions */ #define CTRLALT 0xc #define DEL 0x53 /* scan code of delete key */ #define UNDO 0x61 /* scan code of undo key */ /* BIOS device definitions */ #define CONSDEV 2 #define AUXDEV 1 /* BIOS devices 0..MAX_BHANDLE-1 can be redirected to GEMDOS files */ #define MAX_BHANDLE 4 /* BIOS redirection maps */ short binput[MAX_BHANDLE] = { -3, -2, -1, -4 }; short boutput[MAX_BHANDLE] = { -3, -2, -1, -5 }; /* tty structures for the BIOS devices -- see biosfs.c */ extern struct tty con_tty, aux_tty, midi_tty; extern int tosvers; /* from main.c */ char *kbshft; /* set in main.c */ /* some BIOS vectors; note that the routines at these vectors may do nasty * things to registers! */ #define RWABS 0x476L #define MEDIACH 0x47eL #define GETBPB 0x472L /* these aren't used (yet) */ #define xconin (((long (**) P_((short)))0x53e)) #define xconout (((long (**) P_((short, short)))0x57e)) /* structure used to hold i/o buffers */ typedef struct io_rec { char *bufaddr; short buflen, head, tail, low_water, hi_water; } IOREC_T; /* variables for monitoring the keyboard */ IOREC_T *keyrec; /* keyboard i/o record pointer */ short kintr = 0; /* keyboard interrupt pending (see intr.s) */ /* Getmpb is not allowed under MiNT */ long getmpb(ptr) void *ptr; { DEBUG("failed call to Getmpb"); return -1; } /* * Note that BIOS handles 0 - MAX_BHANDLE now reference file handles; * to get the physical devices, go through u:\dev\ * * A note on translation: all of the bco[n]XXX functions have a "u" * variant that is actually what the user calls. For example, * ubconstat is the function that gets control after the user does * a Bconstat. It figures out what device or file handle is * appropriate. Typically, it will be a biosfs file handle; a * request is sent to biosfs, and biosfs in turn figures out * the "real" device and calls bconstat. */ long ubconstat(dev) int dev; { if (dev < MAX_BHANDLE) return file_instat(binput[dev]) ? -1 : 0; else return bconstat(dev); } long bconstat(dev) int dev; { if (dev == CONSDEV) { if (checkkeys()) return 0; return (keyrec->head != keyrec->tail) ? -1 : 0; } if (dev == AUXDEV && has_bconmap) dev = curproc->bconmap; return Bconstat(dev); } /* bconin: input a character */ long ubconin(dev) int dev; { if (dev < MAX_BHANDLE) return file_getchar(binput[dev], RAW); else return bconin(dev); } long bconin(dev) int dev; { IOREC_T *k; long r; short h; if (dev == CONSDEV) { k = keyrec; again: while (k->tail == k->head) { yield(); } if (checkkeys()) goto again; h = k->head + 4; if (h >= k->buflen) h = 0; r = *((long *)(k->bufaddr + h)); k->head = h; return r; } else { if (dev == AUXDEV && has_bconmap) dev = curproc->bconmap; if (dev > 0) while (!Bconstat(dev)) { yield(); } } r = Bconin(dev); return r; } /* bconout: output a character. * returns 0 for failure, nonzero for success */ long ubconout(dev, c) int dev, c; { FILEPTR *f; if (dev < MAX_BHANDLE) { f = curproc->handle[boutput[dev]]; if (!f) return 0; if (is_terminal(f)) { return tty_putchar(f, ((long)c)&0x00ff, RAW); } /* note: we're assuming sizeof(int) == 2 here! */ return (*f->dev->write)(f, ((char *)&c)+1, 1L); } else if (dev == 5) { c &= 0x00ff; f = curproc->handle[-1]; if (!f) return 0; if (is_terminal(f)) { if (c < ' ') { /* MW hack for quoted characters */ tty_putchar(f, (long)'\033', RAW); tty_putchar(f, (long)'Q', RAW); } return tty_putchar(f, ((long)c)&0x00ff, RAW); } /* note: we're assuming sizeof(int) == 2 here! */ return (*f->dev->write)(f, ((char *)&c)+1, 1L); } else return bconout(dev, c); } long bconout(dev, c) int dev,c; { int statdev; long endtime; extern long searchtime; /* in dosdir.c; updated once per second */ if (dev == AUXDEV && has_bconmap) { dev = curproc->bconmap; } /* compensate for a known BIOS bug; MIDI and IKBD are switched */ if (dev == 3) { /* MIDI */ statdev = 4; } else if (dev == 4) { statdev = 3; } else statdev = dev; /* provide a 10 second time out */ endtime = searchtime + 10; while (!Bcostat(statdev) && searchtime < endtime) { yield(); } if ( searchtime >= endtime && !Bcostat(statdev)) return 0; /* special case: many text accelerators return a bad value from * Bconout, so we ignore the returned value for the console */ if (dev != CONSDEV) { /* NOTE: if your compiler complains about the next line, then Bconout is * improperly declared in your osbind.h header file. it should be returning * a long value; some libraries incorrectly have Bconout returning void * (or cast the returned value to void) */ return Bconout(dev,c); } else { (void)Bconout(dev, c); return 1; } } /* rwabs: various disk stuff */ long rwabs(rwflag, buffer, number, recno, dev, lrecno) int rwflag, number, recno, dev; void *buffer; long lrecno; { long r; /* Note that some (most?) Rwabs device drivers don't bother saving * registers, whereas our compiler expects politeness. So we go * via callout(), which will save registers for us. */ r = callout(RWABS, rwflag, buffer, number, recno, dev, lrecno); return r; } /* setexc: set exception vector */ long setexc(number, vector) int number; long vector; { long *place; long old; extern long save_dos, save_bios, save_xbios; /* in main.c */ TRACE("Setexc %d, %lx", number, vector); place = (long *)(((long)number) << 2); if (number == 0x21) /* trap_1 */ old = save_dos; else if (number == 0x2d) /* trap_13 */ old = save_bios; else if (number == 0x2e) /* trap_14 */ old = save_xbios; else if (number == 0x101) old = (long)curproc->criticerr; /* critical error vector */ else if (number == 0x102) old = curproc->ctxt[SYSCALL].term_vec; /* GEMDOS term vector */ else old = *place; if (vector > 0) { if (number == 0x21) save_dos = vector; else if (number == 0x2d) save_bios = vector; else if (number == 0x2e) save_xbios = vector; else if (number == 0x102) curproc->ctxt[SYSCALL].term_vec = vector; else if (number == 0x101) { long mintcerr; /* * problem: lots of TSR's look for the Setexc(0x101,...) * that the AES does at startup time; so we have * to pass it along. */ mintcerr = (long) Setexc(0x101, (void *)vector); curproc->criticerr = (long (*) P_((long))) *place; *place = mintcerr; } else { /* We would do just *place = vector except that * someone else might be intercepting Setexc looking * for something in particular... */ old = (long) Setexc(number, (void *)vector); } } return old; } /* tickcal: return milliseconds per system clock tick */ long tickcal() { return (long) (*( (unsigned *) 0x0442L )); } /* getbpb: get BIOS parameter block */ long getbpb(dev) int dev; { long r; /* we can't trust the Getbpb routine to accurately save all registers, * so we do it ourselves */ r = callout(GETBPB, dev); /* * There is a bug in the TOS disk handling routines (well several actually). * If the directory size of Getbpb() is returned as zero then the drive 'dies' * and wont read any new disks even with the 'ESC' enforced disk change . This * is present even in TOS 1.6 (not sure about 1.62 though). This small routine * changes the dir size to '1' if it is zero . It may make some non-TOS disks * look a bit weird but that's better than killing the drive . */ if (r) { if ( ((short *)r)[3] == 0) /* 0 directory size? */ ((short *)r)[3] = 1; } return r; } /* bcostat: return output device status */ long ubcostat(dev) int dev; { /* the BIOS switches MIDI (3) and IKBD (4) (a bug, but it can't be corrected) */ if (dev == 4) { /* really the MIDI port */ return file_outstat(boutput[3]) ? -1 : 0; } if (dev == 3) return Bcostat(dev); if (dev < MAX_BHANDLE) return file_outstat(boutput[dev]) ? -1 : 0; else return bcostat(dev); } long bcostat(dev) int dev; { if (dev == CONSDEV) { return -1; } else if (dev == AUXDEV && has_bconmap) { dev = curproc->bconmap; } /* compensate here for the BIOS bug, so that the MIDI and IKBD files work * correctly */ else if (dev == 3) dev = 4; else if (dev == 4) dev = 3; return Bcostat(dev); } /* mediach: check for media change */ long mediach(dev) int dev; { long r; r = callout(MEDIACH, dev); return r; } /* drvmap: return drives connected to system */ long drvmap() { return *( (long *)0x4c2L ); } /* kbshift: return (and possibly change) keyboard shift key status */ long kbshift(mode) int mode; { int oldshft; oldshft = *((unsigned char *)kbshft); if (mode >= 0) *kbshft = mode; return oldshft; } /* special Bconout buffering code: * Because system call overhead is so high, programs that do output * with Bconout suffer in performance. To compensate for this, * Bconout is special-cased in syscall.s, and if possible characters * are placed in the 256 byte bconbuf buffer. This buffer is flushed * when any system call other than Bconout happens, or when a context * switch occurs. */ short bconbsiz; /* number of characters in buffer */ unsigned char bconbuf[256]; /* buffer contents */ short bconbdev; /* BIOS device for which the buffer is valid */ /* (-1 means no buffering is active) */ /* * flush pending BIOS output. Return 0 if some bytes were not successfully * written, non-zero otherwise (just like bconout) */ long bflush() /* flush bios output */ { long ret, bsiz; unsigned char *s; FILEPTR *f; short dev; short statdev; if ((dev = bconbdev) < 0) return 0; /* * Here we lock the BIOS buffering mechanism by setting bconbdev to -1 * This is necessary because if two or more programs try to do * buffered BIOS output at the same time, they can get seriously * mixed up. We unlock by setting bconbdev to 0. * * NOTE: some code (e.g. in sleep()) checks for bconbsiz != 0 in * order to see if we need to do a bflush; if one is already in * progress, it's pointless to do this, so we save a bit of * time by setting bconbsiz to 0 here. */ bconbdev = -1; bsiz = bconbsiz; bconbsiz = 0; /* BIOS handles 0..MAX_BHANDLE-1 are aliases for special GEMDOS files */ if (dev < MAX_BHANDLE || dev == 5) { if (dev == 5) f = curproc->handle[-1]; else f = curproc->handle[boutput[dev]]; if (!f) { bconbdev = 0; return 0; } if (is_terminal(f)) { s = bconbuf; if (dev == 5) { while (bsiz-- > 0) { if (*s < ' ') { /* use ESC-Q to quote control character */ (void)tty_putchar(f, (long)'\033', RAW); (void)tty_putchar(f, (long)'Q', RAW); } (void) tty_putchar(f, (long)*s++, RAW); } } else { while (bsiz-- > 0) { (void) tty_putchar(f, (long)*s++, RAW); } } ret = -1; } else { ret = (*f->dev->write)(f, (char *)bconbuf, bsiz); } bconbdev = 0; return ret; } /* Otherwise, we have a real BIOS device */ if (dev == AUXDEV && has_bconmap) { dev = curproc->bconmap; statdev = dev; } /* compensate for a known BIOS bug; MIDI and IKBD are switched */ else if (dev == 3) { /* MIDI */ statdev = 4; } else if (dev == 4) { statdev = 3; } else statdev = dev; s = bconbuf; while (bsiz-- > 0) { while (!Bcostat(statdev)) yield(); (void)Bconout(dev,*s); s++; } bconbdev = 0; return 1L; } /* initialize bios table */ #define BIOS_MAX 0x20 Func bios_tab[BIOS_MAX] = { getmpb, ubconstat, ubconin, ubconout, rwabs, setexc, tickcal, getbpb, ubcostat, mediach, drvmap, kbshift, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; short bios_max = BIOS_MAX; /* * BIOS initialization routine: gets keyboard buffer pointers, for the * interrupt routine below */ void init_bios() { keyrec = (IOREC_T *)Iorec(1); } /* * routine for checking keyboard (called by sleep() on any context * switch where a keyboard event occured). returns 1 if a special * control character was eaten, 0 if not */ int checkkeys() { char scan, ch; short shift; int sig, ret; struct tty *tty = &con_tty; extern char mshift; /* for mouse -- see biosfs.c */ static short oldktail = 0; ret = 0; mshift = kbshift(-1); while (oldktail != keyrec->tail) { /* BUG: we really should check the shift status _at the time the key was * pressed_, not now! */ sig = 0; shift = mshift; oldktail += 4; if (oldktail >= keyrec->buflen) oldktail = 0; scan = (keyrec->bufaddr + oldktail)[1]; /* function key?? */ if ( (scan >= 0x3b && scan <= 0x44) || (scan >= 0x54 && scan <= 0x5d) || scan == DEL || scan == UNDO) { if ( (shift & CTRLALT) == CTRLALT ) { oldktail = keyrec->head = keyrec->tail; do_func_key(scan); ret = 1; continue; } } /* check for special control keys, etc. */ /* BUG: this doesn't exactly match TOS' behavior, particularly for * ^S/^Q */ if ((tty->state & TS_COOKED) || (shift & CTRLALT) == CTRLALT) { ch = (keyrec->bufaddr + keyrec->tail)[3]; if (ch == UNDEF) ; /* do nothing */ else if (ch == tty->tc.t_intrc) sig = SIGINT; else if (ch == tty->tc.t_quitc) sig = SIGQUIT; else if (ch == tty->ltc.t_suspc) sig = SIGTSTP; else if (ch == tty->tc.t_stopc) { tty->state |= TS_HOLD; ret = 1; keyrec->head = oldktail; continue; } else if (ch == tty->tc.t_startc) { tty->state &= ~TS_HOLD; ret = 1; keyrec->head = oldktail; continue; } if (sig) { tty->state &= ~TS_HOLD; if (!(tty->sg.sg_flags & T_NOFLSH)) oldktail = keyrec->head = keyrec->tail; killgroup(tty->pgrp, sig); ret = 1; } else if (tty->state & TS_HOLD) { keyrec->head = oldktail; ret = 1; } } } /* has someone done select() on the keyboard?? */ if (tty->rsel && keyrec->head != keyrec->tail) wakeselect(tty->rsel); return ret; } /* do_func_key moved to debug.c */