File:  [Atari MiNT] / MiNT / src / bios.c
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:58:38 2018 UTC (8 years, 1 month ago) by root
Branches: mint, MAIN
CVS tags: mint112, HEAD
MiNT 1.12

/*

Copyright 1990,1991,1992 Eric R. Smith.

Copyright 1992,1993,1994 Atari Corporation.

All rights reserved.

*/



/*

 * BIOS replacement routines

 */



#include "mint.h"

#include "xbra.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

#define PRNDEV 0

#define	SERDEV 6	/* First serial port */



/* BIOS devices 0..MAX_BHANDLE-1 can be redirected to GEMDOS files */

#define MAX_BHANDLE	4



/* BIOS redirection maps */

const short binput[MAX_BHANDLE] = { -3, -2, -1, -4 };

const 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, sccb_tty, scca_tty, ttmfp_tty;

extern struct bios_tty bttys[], midi_btty;

extern short btty_max;



extern int tosvers;	/* from main.c */



char *kbshft;		/* set in main.c */



short console_in;	/* wait condition for console input */



/* save cluster sizes of BIOS drives 0..31 (used in tosfs.c) */

unsigned short clsizb[32];



/* some BIOS vectors; note that the routines at these vectors may do nasty

 * things to registers!

 */



#define RWABS *((long *)0x476L)

#define MEDIACH *((long *)0x47eL)

#define GETBPB *((long *)0x472L)





/* these are supposed to be tables holding the addresses of the

 * first 8 BconXXX functions, but in fact only the first 5 are

 * placed here (and device 5 only has Bconout implemented; 

 * we don't use that device (raw console) anyway).

 */



#define xconstat ((long *)0x51eL)

#define xconin 	((long *)0x53eL)

#define xcostat ((long *)0x55eL)

#define xconout	((long *)0x57eL)



#if 1

/* if the system has Bconmap the ones >= 6 are in a table available

 * thru Bconmap(-2)...

 */

#define MAPTAB (bconmap2->maptab)



/* and then do BCOSTAT ourselves, the BIOS SCC ones are often broken */

#define BCOSTAT(dev) \

	(((unsigned)dev <= 4) ? ((tosvers >= 0x0102) ? \

	   (int)callout1(xcostat[dev], dev) : Bcostat(dev)) : \

	   ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \

		bcxstat(MAPTAB[dev-SERDEV].iorec+1) : Bcostat(dev)))

#define BCONOUT(dev, c) \

	(((unsigned)dev <= 4) ? ((tosvers >= 0x0102) ? \

	   callout2(xconout[dev], dev, c) : Bconout(dev, c)) : \

	   ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \

		callout2(MAPTAB[dev-SERDEV].bconout, dev, c) : Bconout(dev, c)))

#define BCONSTAT(dev) \

	(((unsigned)dev <= 4) ? ((tosvers >= 0x0102) ? \

	   (int)callout1(xconstat[dev], dev) : Bconstat(dev)) : \

	   ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \

		(int)callout1(MAPTAB[dev-SERDEV].bconstat, dev) : Bconstat(dev)))

#define BCONIN(dev) \

	(((unsigned)dev <= 4) ? ((tosvers >= 0x0102) ? \

	   callout1(xconin[dev], dev) : Bconin(dev)) : \

	   ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \

		callout1(MAPTAB[dev-SERDEV].bconin, dev) : Bconin(dev)))

#else

#define BCOSTAT(dev) \

	((tosvers > 0x0102 && (unsigned)dev <= 4) ? \

	   (int)callout1(xcostat[dev], dev) : Bcostat(dev))

#define BCONOUT(dev, c) \

	((tosvers > 0x0102 && (unsigned)dev <= 4) ? \

	   callout2(xconout[dev], dev, c) : Bconout(dev, c))

#define BCONSTAT(dev) \

	((tosvers > 0x0102 && (unsigned)dev <= 4) ? \

	   (int)callout1(xconstat[dev], dev) : Bconstat(dev))

#define BCONIN(dev) \

	((tosvers > 0x0102 && (unsigned)dev <= 4) ? \

	   callout1(xconin[dev], dev) : Bconin(dev))

#endif



/* variables for monitoring the keyboard */

IOREC_T	*keyrec;		/* keyboard i/o record pointer */

BCONMAP2_T *bconmap2;		/* bconmap struct */

short	kintr = 0;		/* keyboard interrupt pending (see intr.s) */



/* replacement *costat for BCOSTAT above */

INLINE static int bcxstat (IOREC_T *wrec)

{

	unsigned s = wrec->head - wrec->tail;

	if ((int)s <= 0)

		s += wrec->buflen;

	return s < 3 ? 0 : -1;

}



/* Getmpb is not allowed under MiNT */



long ARGS_ON_STACK

getmpb(ptr)

	void *ptr;

{

	UNUSED(ptr);



	DEBUG(("failed call to Getmpb"));

	return -1;

}



INLINE static int

isonline(b)

	struct bios_tty *b;

{

	if (b->tty == &aux_tty) {

	/* modem1 */

	/* CD is !bit 1 on the 68901 GPIP port */

		return (1 << 1) & ~*((volatile char *) 0xfffffa01L);

	} else if (b->tty == &sccb_tty) {

	/* modem2 */

	/* CD is bit 3 of read register 0 on SCC port B */

		short sr = spl7();

		unsigned char r;

		volatile char dummy;

		dummy = *((volatile char *) 0xfffffa01L);

		r = (1 << 3) & *((volatile char *) 0xffff8c85L);

		spl(sr);

		return r;

	} else if (b->tty == &scca_tty) {

	/* serial2 */

	/* like modem2, only port A */

		short sr = spl7();

		unsigned char r;

		volatile char dummy;

		dummy = *((volatile char *) 0xfffffa01L);

		r = (1 << 3) & *((volatile char *) 0xffff8c81L);

		spl(sr);

		return r;

	} else {

	/* unknown port, assume CD always on. */

		return 1;

	}

}



INLINE static int

isbrk(b)

	struct bios_tty *b;

{

	if (b->tty == &aux_tty) {

	/* modem1 */

	/* break is bit 3 in the 68901 RSR */

		return (1 << 3) & *((volatile char *) 0xfffffa2bL);

	} else if (b->tty == &sccb_tty) {

	/* modem2 */

	/* break is bit 7 of read register 0 on SCC port B */

		short sr = spl7();

		unsigned char r;

		volatile char dummy;

		dummy = *((volatile char *) 0xfffffa01L);

		r = (1 << 7) & *((volatile char *) 0xffff8c85L);

		spl(sr);

		return r;

	} else if (b->tty == &scca_tty) {

	/* serial2 */

	/* like modem2, only port A */

		short sr = spl7();

		unsigned char r;

		volatile char dummy;

		dummy = *((volatile char *) 0xfffffa01L);

		r = (1 << 7) & *((volatile char *) 0xffff8c81L);

		spl(sr);

		return r;

	} else if (b->tty == &ttmfp_tty) {

	/* serial1 */

		return (1 << 3) & *((volatile char *) 0xfffffaabL);

	} else {

	/* unknown port, cannot detect breaks... */

		return 0;

	}

}



INLINE unsigned

ionwrite(wrec)

	IOREC_T *wrec;

{

	unsigned s = wrec->head - wrec->tail;

	if ((int)s <= 0)

		s += wrec->buflen;

	if ((int)(s -= 2) < 0)

		s = 0;

	return s;

}



INLINE unsigned

ionread(irec)

	IOREC_T *irec;

{

	unsigned r = irec->tail - irec->head;

	if ((int)r < 0)

		r += irec->buflen;

	return r;

}



INLINE unsigned

btty_ionread(b)

	struct bios_tty *b;

{

	long ret = 0;

#if 1

/* try trap #1 first, to read hardware fifos too...

 * (except for modem1/serial1 which are always in one-interrupt-per-byte mode)

 */

	if (b != bttys && b->tty != &ttmfp_tty &&

	    !rsvf_ioctl (b->tosfd, &ret, FIONREAD))

		return ret;

#endif

	return ionread (b->irec);

}



#define _hz_200 (*((long *)0x4baL))



INLINE static void

checkbtty(b, sig)

	struct bios_tty *b;

	int sig;

{

	long *l;



	if (!b->irec)

		return;

	if (!b->clocal && !isonline(b)) {

		b->vticks = 0;

		b->bticks = _hz_200 + 0x80000000L;

		if (!(b->tty->state & TS_BLIND)) {

/* just lost carrier...  set TS_BLIND, let reads and writes return */

			b->tty->state |= TS_BLIND;

			iocsbrk (b->bdev, TIOCCBRK, b);

			if (sig) {

				b->orec->tail = b->orec->head;

				DEBUG(("checkbtty: bdev %d disconnect", b->bdev));

				if (!(b->tty->sg.sg_flags & T_NOFLSH))

					iread (b->bdev, (char *) NULL, 0, 1, 0);

				if (b->tty->pgrp)

/* ...and here is the long missed :) SIGHUP  */

					killgroup(b->tty->pgrp, SIGHUP, 1);

			}

			wake(IO_Q, (long)b);

			wake(IO_Q, (long)&b->tty->state);

		}

		return;

	}

	if (b->tty->state & TS_BLIND) {

/* just got carrier (or entered local mode), clear TS_BLIND and

 * wake whoever waits for it */

		b->tty->state &= ~(TS_BLIND|TS_HOLD);

		wake(IO_Q, (long)&b->tty->state);

	}

	if (sig) {

		if (b->brkint && isbrk(b)) {

			if (!b->bticks) {

/* the break should last for more than 200 ms or the equivalent of

 * 48 10-bit chars at ispeed (then its probably not line noise...)

 */

				if ((unsigned long)b->ispeed <= 2400)

					b->bticks = _hz_200+40L;

				else

					b->bticks = _hz_200+(480L*200L/(unsigned long)b->ispeed);

				if (!b->bticks)

					b->bticks = 1;

			} else if (_hz_200 - b->bticks > 0) {

/* every break only one interrupt please */

				b->bticks += 0x80000000L;

				DEBUG(("checkbtty: bdev %d break(int)", b->bdev));

				if (!(b->tty->sg.sg_flags & T_NOFLSH))

					iread (b->bdev, (char *) NULL, 0, 1, 0);

				if (b->tty->pgrp)

					killgroup(b->tty->pgrp, SIGINT, 1);

			}

		} else

			b->bticks = 0;

	}

	if (!b->vticks || _hz_200 - b->vticks > 0) {

		long r;



		if ((r = (long)b->tty->vmin - btty_ionread(b)) <= 0) {

			b->vticks = 0;

			wake(IO_Q, (long)b);

			l = b->rsel;

			if (*l)

				wakeselect(*l);

		} else if ((--r, r *= 2000L) > (unsigned long)b->ispeed) {

			b->vticks = _hz_200 + (r/(unsigned long)b->ispeed);

			if (!b->vticks)

				b->vticks = 1;

		} else

			b->vticks = 0;

	}

	if (b->tty->state & TS_HOLD)

		return;

	l = b->wsel;

	if (*l) {

		short i = b->orec->tail - b->orec->head;

		if (i < 0)

			i += b->orec->buflen;

		if (i < b->orec->hi_water)

			wakeselect(*l);

	}

}



void

checkbttys(void)

{

	struct bios_tty *b;



	for (b=bttys;b<bttys+btty_max;b++) {

		checkbtty(b, 1);

	}

	b=&midi_btty;

	if (!b->vticks || _hz_200 - b->vticks > 0) {

		long r, *l;



		if ((r = (long)b->tty->vmin - ionread(b->irec)) <= 0) {

			b->vticks = 0;

			wake(IO_Q, (long)b);

			l = b->rsel;

			if (*l)

				wakeselect(*l);

		} else if ((--r, r *= 2000L) > (unsigned long)31250) {

			b->vticks = _hz_200 + (r/(unsigned long)31250);

			if (!b->vticks)

				b->vticks = 1;

		} else

			b->vticks = 0;

	}

}



void

checkbttys_vbl(void)

{

	struct bios_tty *b;



	for (b=bttys;b<bttys+btty_max;b++) {

		if (!b->clocal && b->orec->tail != b->orec->head && !isonline(b))

			b->orec->tail = b->orec->head;

	}

}



/* check 1 tty without raising sigs, needed after turning off local mode

 * (to avoid getting SIGHUP'd immediately...)

 */



void

checkbtty_nsig(b)

	struct bios_tty *b;

{

	checkbtty(b, 0);

}



/*

 * 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.

 */



/*

 * WARNING: syscall.spp assumes that ubconstat never blocks.

 */

long ARGS_ON_STACK

ubconstat(dev)

int dev;

{

	if (dev < MAX_BHANDLE) {

		FILEPTR *f = curproc->handle[binput[dev]];

		return file_instat(f) ? -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 */

/*

 * WARNING: syscall.spp assumes that ubconin never

 * blocks if ubconstat returns non-zero.

 */

long ARGS_ON_STACK

ubconin(dev)

int dev;

{

	if (dev < MAX_BHANDLE) {

		FILEPTR *f = curproc->handle[binput[dev]];

		return file_getchar(f, 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) {

			sleep(IO_Q, (long)&console_in);

		}



		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) {

			if (has_bconmap) {

				dev = curproc->bconmap;

				h = dev-SERDEV;

			} else

				h = 0;

		} else

			h = dev-SERDEV;



		if ((unsigned)h < btty_max || dev == 3) {

			if (has_bconmap && dev != 3) {	/* help the compiler... :) */

				long *statc;



				while (!callout1(*(statc=&MAPTAB[dev-SERDEV].bconstat), dev))

					sleep(IO_Q, (long)&bttys[h]);

				return callout1(statc[1], dev);

			}

			while (!BCONSTAT(dev))

				sleep(IO_Q, (long)(dev == 3 ? &midi_btty :

								&bttys[h]));

		} else if (dev > 0) {

			unsigned long tick;



			tick = *((unsigned long *)0x4baL);

			while (!BCONSTAT(dev)) {

/* make blocking (for longer) reads eat less CPU...

 * if yield()ed > 2 seconds and still no data continue with nap

 */

			if ((*((unsigned long *)0x4baL) - tick) > 400)

				nap(60);

			else

				yield();

			}

		}

	}



	r = BCONIN(dev);



	return r;

}



/* bconout: output a character.

 * returns 0 for failure, nonzero for success

 */



long ARGS_ON_STACK

ubconout(dev, c)

int dev, c;

{

	FILEPTR *f;

	char outp;



	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);

		}

		outp = c;

		return (*f->dev->write)(f, &outp, 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! */

		outp = c;

		return (*f->dev->write)(f, &outp, 1L);

	} else

		return bconout(dev, c);

}



long

bconout(dev, c)

int dev,c;

{

	int statdev;

	long endtime;

#define curtime *((unsigned long *)0x4baL)



	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 for the printer */

	if (!BCOSTAT(statdev)) {

		if (dev != PRNDEV) {

			do {

	/* BUG: Speedo GDOS isn't re-entrant; so printer output to the

	 * serial port could cause problems

	 */

				yield();

			} while (!BCOSTAT(statdev));

		} else {

			endtime = curtime + 10*200L;

			do {

#if 0

	/* Speedo GDOS isn't re-entrant, so we can't give up CPU

	 * time here :-(

	 */

				yield();

#endif

			} while (!BCOSTAT(statdev) && curtime < endtime);

			if ( curtime >= endtime) return 0;

		}

	}



/* special case: many text accelerators return a bad value from

 * Bconout, so we ignore the returned value for the console

 * Sigh. serptch2 and hsmodem1 also screw this up, so for now let's

 * only count on it being correct for the printer.

 */

	if (dev == PRNDEV) {

/* 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 ARGS_ON_STACK

rwabs(rwflag, buffer, number, recno, dev, lrecno)

int rwflag, number, recno, dev;

void *buffer;

long lrecno;

{

	long r;

	extern PROC *dlockproc[];	/* in dosdir.c */

	extern int aliasdrv[];		/* in filesys.c */



	/* jr: inspect bit 3 of rwflag!!! */

	

	if (!(rwflag & 8) && dev >= 0 && dev < NUM_DRIVES) {

		if (aliasdrv[dev]) {

			dev = aliasdrv[dev] - 1;

		}

		if (dlockproc[dev] && dlockproc[dev] != curproc) {

			DEBUG(("Rwabs: device %c is locked", dev+'A'));

			return ELOCKED;

		}

	}



#if 0	/* commented out so that loadable file systems work :-( */

/* only the superuser can make Rwabs calls directly */



	if (curproc->in_dos || (curproc->euid == 0))

	/* 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);

	else {

		DEBUG(("Rwabs by non privileged process!"));

		r = EACCDN;

	}

#else

	r = callout(RWABS, rwflag, buffer, number, recno, dev, lrecno);

#endif

	return r;

}



/* setexc: set exception vector */



long ARGS_ON_STACK

setexc(number, vector)

int number;

long vector;

{

	long *place;

	long old;

	extern long save_dos, save_bios, save_xbios;	/* in main.c */

	extern int no_mem_prot;				/* in main.c */



	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) {

	/* validate vector; this will cause a bus error if mem

	 * protection is on and the current process doesn't have

	 * access to the memory

	 */

		if (*((long *)vector) == 0xDEADBEEFL)

			return old;



		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 ARGS_ON_STACK (*) P_((long))) *place;

			*place = mintcerr;

		}

		else {

			if (!no_mem_prot) {

			/*

			 * if memory protection is on, the vector should be

			 * pointing at supervisor or global memory

			 */

			    MEMREGION *r;



			    r = addr2region(vector);

			    if (r && get_prot_mode(r) == PROT_P) {

				DEBUG(("Changing protection to Supervisor because of Setexc"));

				mark_region(r, PROT_S);

			    }

			}

		/* 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);

		}

	}



	TRACE(("Setexc %d, %lx -> %lx", number, vector, old));

	return old;

}



/* tickcal: return milliseconds per system clock tick */



long ARGS_ON_STACK

tickcal()

{

	return (long) (*( (unsigned *) 0x0442L ));

}



/* getbpb: get BIOS parameter block */



long ARGS_ON_STACK

getbpb(dev)

int dev;

{

	long r;



/* we can't trust the Getbpb routine to accurately save all registers,

 * so we do it ourselves

 */

	r = callout1(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;



		/* jr: save cluster size in area */

		if (dev >= 0 && dev < 32)

			clsizb[dev] = ((unsigned short *)r)[2];

	}

	return r;

}



/* bcostat: return output device status */



/* WARNING: syscall.spp assumes that ubcostat never

 * blocks

 */

long ARGS_ON_STACK

ubcostat(dev)

int dev;

{

	FILEPTR *f;



/* the BIOS switches MIDI (3) and IKBD (4) (a bug, but it can't be corrected) */

	if (dev == 4) {		/* really the MIDI port */

		f = curproc->handle[boutput[3]];

		return file_outstat(f) ? -1 : 0;

	}

	if (dev == 3)

		return BCOSTAT(dev);



	if (dev < MAX_BHANDLE) {

		f = curproc->handle[boutput[dev]];

		return file_outstat(f) ? -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 ARGS_ON_STACK

mediach(dev)

int dev;

{

	long r;



	r = callout1(MEDIACH, dev);

	return r;

}



/* drvmap: return drives connected to system */



long ARGS_ON_STACK

drvmap()

{

	return *( (long *)0x4c2L );

}



/* kbshift: return (and possibly change) keyboard shift key status */

/* WARNING: syscall.spp assumes that kbshift never blocks, and never

 * calls any underlying TOS functions

 */

long ARGS_ON_STACK

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 ARGS_ON_STACK

bflush()		/* flush bios output */

{

	long ret, bsiz;

	unsigned char *s;

	FILEPTR *f;

	short dev;

	short statdev;

	long lbconbuf[256];



	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;

	if (bsiz == 0) return 0;

	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)) {

			int oldflags = f->flags;



			s = bconbuf;

/* turn off NDELAY for this write... */

			f->flags &= ~O_NDELAY;

			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 {

			    long *where, nbytes;

#if 1

			    extern FILESYS bios_filesys;



			    /* see if we can do fast RAW byte IO thru the device driver... */

			    if ((f->fc.fs != &bios_filesys ||

				    (bsiz > 1 &&

				     ((struct bios_file *)f->fc.index)->drvsize >

					    offsetof (DEVDRV, writeb))) && f->dev->writeb) {

			        struct tty *tty = (struct tty *)f->devinfo;



				tty_checkttou (f, tty);

				tty->state &= ~TS_COOKED;

				if ((ret = (*f->dev->writeb)(f, (char *)s, bsiz)) != EUNDEV) {

					f->flags = oldflags;

					bconbdev = 0;

					return ret;

				}

			    }

#endif

/* the tty_putchar should set up terminal modes correctly */

			    (void) tty_putchar(f, (long)*s++, RAW);

			    where = lbconbuf;

			    nbytes = 0;

			    while (--bsiz > 0) {

				*where++ = *s++; nbytes+=4;

			    }

			    if (nbytes)

				(*f->dev->write)(f, (char *)lbconbuf, nbytes);

			}

			ret = -1;

			f->flags = oldflags;

		} 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;

	}

	if ((ret = iwrite (dev, bconbuf, bsiz, 0, 0)) != EUNDEV) {

		bconbdev = 0;

		return ret;

	} else if (dev == 3) {		/* MIDI */

	/* compensate for a known BIOS bug; MIDI and IKBD are switched */

		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);

}



/*

 * do_bconin: try to do a bconin function quickly, without

 * blocking. If we can't do it without blocking, we return

 * 0x0123dead and the calling trap #13 code falls through

 * to the normal bconin stuff. We can't block here because

 * the trap #13 code hasn't yet saved registers or other

 * context bits, so sleep() wouldn't work properly.

 */



#define WOULDBLOCK 0x0123deadL



/* WARNING: syscall.spp assumes that do_bconin never blocks */



long ARGS_ON_STACK

do_bconin(dev)

	int dev;

{

	FILEPTR *f;

	long r, nread;

	unsigned char c;



	if (dev < MAX_BHANDLE) {

		f = curproc->handle[binput[dev]];

		if (!f) return 0;

		nread = 0;

		(void)(*f->dev->ioctl)(f, FIONREAD, &nread);

		if (!nread) return WOULDBLOCK;	/* data not ready */

		if (is_terminal(f))

			r = tty_getchar(f, RAW);

		else {

			r = (*f->dev->read)(f, (char *)&c, 1L);

			r = (r == 1) ? c : MiNTEOF;

		}

	} else {

		if (!bconstat(dev))

			r = WOULDBLOCK;

		else

			r = bconin(dev);

	}

	return r;

}



/*

 * 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);

				/* do_func_key may have read some keys */

				oldktail = keyrec->head;

				mshift = kbshift (-1);

				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, 1);

				ret = 1;

			}

			else if (tty->state & TS_HOLD) {

				keyrec->head = oldktail;

				ret = 1;

			}

		}



	}



	if (keyrec->head != keyrec->tail) {

	/* wake up any processes waiting in bconin() */

		wake(IO_Q, (long)&console_in);

	/* wake anyone that did a select() on the keyboard */

		if (tty->rsel)

			wakeselect(tty->rsel);

	}



	return ret;

}





/*

 * special vector stuff: we try to save as many vectors as possible,

 * just in case we need to restore them later

 *

 * BUG: this really should be integrated with the init_intr routine

 * in main.c

 */



#define A(x) ((long *)(long)(x))

#define L(x) (long)(x)



struct vectab {

	long *addr;

	long def_value;

} VEC[] = {

{A(0x28), 0},	/* Line A */

{A(0x2c), 0},	/* Line F */

{A(0x60), 0},	/* spurious interrupt */

{A(0x64), 0},  	/* level 1 interrupt */

{A(0x68), 0},	/* level 2 interrupt */

{A(0x6c), 0},	/* level 3 interrupt */

{A(0x70), 0},	/* level 4 interrupt */

{A(0x74), 0},	/* level 5 interrupt */

{A(0x78), 0},	/* level 6 interrupt */

{A(0x7c), 0},	/* level 7 interrupt */

{A(0x100), 0},	/* various MFP interrupts */

{A(0x104), 0},

{A(0x108), 0},

{A(0x10c), 0},

{A(0x110), 0},

{A(0x114), 0},

{A(0x118), 0},

{A(0x11c), 0},

{A(0x120), 0},

{A(0x124), 0},

{A(0x128), 0},

{A(0x12c), 0},

{A(0x130), 0},

{A(0x134), 0},

{A(0x138), 0},

{A(0x13c), 0},

{A(0x400), 0},	/* etv_timer */

{A(0x4f6), 0},  /* shell_p */



{A(0), 0}	/* special tag indicating end of list */

};



void

init_vectors() 

{

	struct vectab *v;



	for (v = VEC; v->addr; v++) {

		v->def_value = *(v->addr);

	} 

}



#if 0	/* bad code */



/* unhook a vector; if possible, do this with XBRA, but

 * if that isn't possible force the vector to have the

 * same value it had when MiNT started

 */



static void

unhook(v, where)

	struct vectab *v;

	long where;

{

	xbra_vec *xbra;

	long newval;

	int cookie;



/* to check for XBRA, we need access to the memory where the

 * vector is

 */

	cookie = prot_temp(where - 12, 16L, -1);



	if (cookie == 0)

		newval = v->def_value;

	else {

		xbra = (xbra_vec *)(where - 12);

		if (xbra->xbra_magic == XBRA_MAGIC) {

			newval = (long)xbra->next;

		} else {

			newval = v->def_value;

		}

	}

	*(v->addr) = newval;



	(void)prot_temp(where - 12, 16L, cookie);

}

#endif



/*

 * unlink_vectors(start, end): any of the "normal" system vectors

 * pointing into a freed memory region must be reset to their

 * default values, or else we'll get a memory protection violation

 * next time the vector gets called

 */



void

unlink_vectors(start, end)

	long start, end;

{

#if 0	/* this code is hosed somewhere */



	struct vectab *v;

	long where, *p;

	int i;



/* first, unhook any VBL handlers */

	i = *((short *)0x454L);	/* i = nvbls */

	p = *((long **)0x456L);	/* p = _vblqueue */

	while (i-- > 0) {

		where = *p;

		if (where >= start && where < end)

			*p = 0;

		p++;

	}



/* next, unhook various random vectors */

	for (v = VEC; v->addr; v++) {

		where = *(v->addr);

		if (where >= start && where < end) {

			unhook(v, where);

		}

	}

#else

	UNUSED(start); UNUSED(end);

#endif

}




unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.