[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

select() for BIOS serial ports



Before you get too excited, I have to warn you that this code crashes almost
immediately when running MultiTOS. I've spent about 8 hours tracking it down
and have gotten too frustrated, so I'm sending it out in the hopes that someone
else will point out the obvious flaw....

When run without MultiTOS, it works just fine. Gory crash details to follow.
First, the concept - simply build a table of pointers to the Iorec's for each
serial port. At regular intervals (I originally thought of sticking this in
the VBL code in intr.spp) check each head & tail pointer in each iorec, and
if they're different, wake up any waiting processes. Simplicity itself. The
actual check here occurs in the sleep routine, just like the checkkeys stuff
works. I create a table of up to 4 entries (bios devices 6 to 9) to record
the relevant pointers. (Anyone writing new device drivers for the Atari ought
to do a full-blown job, like Thierry's modm0dev, so I'm not worried about
supporting new bconmap'd devices...) The table contains a pointer to the input
and output records of the port, and a pointer to the rsel and wsel entries in
the corresponding tty struct. (There's actually space to keep all this in the
tty structs themselves, if we take over some of the reserved fields, but I
decided to keep things separate for now since I only want to deal with existing
BIOS ttys, and not clutter up the tty struct with stuf that's not universally
applicable...)

Here's the code:
--- 1.2	1994/03/02 08:06:50
+++ bios.c	1994/03/03 01:50:54
@@ -23,4 +23,5 @@
 #define AUXDEV 1
 #define PRNDEV 0
+#define	SERDEV 6	/* First serial port */
 
 /* BIOS devices 0..MAX_BHANDLE-1 can be redirected to GEMDOS files */
@@ -33,4 +34,6 @@
 /* tty structures for the BIOS devices -- see biosfs.c */
 extern struct tty con_tty, aux_tty, midi_tty;
+extern struct bios_tty bttys[];
+extern short btty_max;
 
 extern int tosvers;	/* from main.c */
@@ -96,4 +99,27 @@
 }
 
+void
+checkbttys(void)
+{
+	struct bios_tty *b;
+	long *l;
+
+	for (b=bttys;b->irec;b++) {
+		if (b->irec->head != b->irec->tail) {
+			wake(IO_Q, (long)b);
+			l = b->rsel;
+			if (*l)
+				wakeselect(*l);
+		}
+		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);
+		}
+	}
+}
 
 /*
@@ -181,8 +207,17 @@
 	}
 	else {
-		if (dev == AUXDEV && has_bconmap)
-			dev = curproc->bconmap;
-
-		if (dev > 0) {
+		if (dev == AUXDEV) {
+			if (has_bconmap) {
+				dev = curproc->bconmap;
+				h = dev-SERDEV;
+			} else
+				h = 0;
+		} else
+			h = dev-SERDEV;
+
+		if (h >= 0 && h < btty_max) {
+			if (!BCONSTAT(dev))
+				sleep(IO_Q, (long)&bttys[h]);
+		} else if (dev > 0) {
 			unsigned long tick;
 
--- 1.4	1994/03/02 08:20:52
+++ biosfs.c	1994/03/03 01:11:24
@@ -147,4 +147,9 @@
 };
 
+#define	MAX_BTTY	4	/* 4 bios_tty structs */
+
+struct bios_tty bttys[MAX_BTTY];
+short	btty_max;
+
 /* Does the fcookie fc refer to the \dev\fd directory? */
 #define IS_FD_DIR(fc) ((fc)->aux == S_IFDIR)
@@ -205,12 +210,17 @@
 biosfs_init()
 {
-	struct bios_file *b;
+	struct bios_file *b, *c;
 	int majdev, mindev;
+	int i;
 
 	broot = BDEV;
 
+	c = 0L;
 	for (b = broot; b->name[0]; b++) {
 		b->next = b+1;
 
+	/* Save a pointer to the first serial port */
+		if (b->private == 6)
+			c = b;
 	/* if not a TT or Mega STE, adjust the MODEM1 device to be BIOS
 	 * device 1
@@ -234,4 +244,15 @@
 		b->next = 0;
 	}
+	/* Initialize bios_tty structures */
+	for (i=0;c && i<MAX_BTTY;c=c->next, i++) {
+		if (has_bconmap)
+			Bconmap(c->private);
+		bttys[i].irec = Iorec(0);
+		bttys[i].orec = bttys[i].irec+1;
+		bttys[i].rsel = &(c->tty->rsel);
+		bttys[i].wsel = &(c->tty->wsel);
+	}
+	btty_max = i;
+
 	defaultaux = new_fileptr();
 	defaultaux->links = 1;		/* so it never gets freed */
--- 1.3	1994/03/02 08:13:00
+++ proto.h	1994/03/03 00:13:14
@@ -1,4 +1,5 @@
 /* bios.c */
 long ARGS_ON_STACK getmpb P_((void *ptr));
+void checkbttys P_((void));
 long bconstat P_((int dev));
 long bconin P_((int dev));
--- 1.3	1994/03/02 08:21:12
+++ file.h	1994/03/02 23:51:16
@@ -502,4 +502,11 @@
 };
 
+struct bios_tty {
+	IOREC_T		*irec;		/* From XBIOS ... */
+	long		*rsel;		/* pointer to field in tty struct */
+	IOREC_T		*orec;		/* Same, for output... */
+	long		*wsel;
+};
+
 /* Dcntl constants and types */
 #define DEV_NEWTTY	0xde00

Here's the crash...
pid  0 (MiNT): Pexec(7,27,,118A000)
  ... checking for memory, creating basepage, etc.
  leaving Pexec with basepage address 1194000
  Pexec(106,GEM,BP:1194000,0)
  Checking for memory for new PROC structure
  creating environment
  creating base page
  exec_region
  MEMORY VIOLATION: type=hardware  AA=58425249 PC=112CB54 BP=112C9CA
  Operating system killed

This is a little different from my original crash; I changed a couple lines
of code. The old one had AA= (um, whatever "MiNT" is in hex.). This one is
"XBRI", dunno where the 'I' came from. Basically, the crash is in the
checkbttys routine; the memory that btty->irec points to is no longer an
Iorec. I hadn't figured on the Iorec's themselves moving around in memory
after the system started, I just figured you could change the buffer pointer
inside the record. Although I guess, since loading GEM.SYS means you're
pretty much getting a new operating system, it can put things wherever it
wants to...

The only fix I can think of right now is to compare the current contents of
the Iorec pointer to the contents on the last check, and if they differ, call
Bconmap and Iorec again to get the new value. 

[Bottom level details - the above crash is at the 6th instruction of
checkbttys:
	movel a2,sp@-
	lea _bttys,a2
	tstl a2@
	jeq Nextloop

	movel a2@,a0
	movew a0@(8),d1		<--- boom.
	cmpw a0@(6),d1
	...
So, it looks at a btty struct, sees that it has a non-null irec pointer,
and tries to get the head index out of the Iorec struct that irec points to.
But, instead of there being an Iorec there, someone has installed something
else in its place... sigh.]