[comp.os.minix] Problems with Jim Paradis' tty driver

ast@cs.vu.nl (Andy Tanenbaum) (01/15/88)

I installed the new tty driver on a Zenith Z-248 (AT clone) using a Paradise
Systems Hi Res Graphics Card.  This board is compatible with IBM mono and
has a Hercules compatible mode.  When the system came up, a few lines
appeared, then blank lines, then more text, etc.  The cursor always
seemed to work.  The same binary ran perfectly on an XT with a cheap CGA
card except for some snow.  Standard MINIX runs perfectly on the Paradise
card.  I would definitely like to get the new tty driver fully debugged.

If any one has any observations, comments etc., please post them.  If you
have tried the new driver, please post your experience, describing the
hardware you used.  Maybe we can see some pattern.

Many people seem to have recently discovered the V1.2 does not work with
the EGA card.  I posted an improved driver that does software scrolling
for EGA cards some months ago.  Anyone who can't get it from the archive
can get it from me directly.  Hopefully the new driver will make this
version obsolete, but we are not there yet.

Andy Tanenbaum (ast@cs.vu.nl)

housel@ea.ecn.purdue.edu (Peter S. Housel) (01/17/88)

	Here's a patch for Jim Paradis' new tty driver to allow it to work
on HGC cards (mine anyway). Apply this diff to console.c:
-----------------------------cut----------------------------------------
530,531c530,531
< 		oque[oque_last].address = cur_start +
< 				( ((crow * NCOLS) + ccol) << 1);
---
> 		oque[oque_last].address = (cur_start +
> 				( ((crow * NCOLS) + ccol) << 1)) & SEG_MASK;
542,545c542,545
< 		    (int)(cur_start + (((crow * NCOLS) + ccol) << 1)),
< 		    (int)ch);
< 		put_byte(VIDEO_SEG,
< 		    (int)(cur_start + (((crow * NCOLS) + ccol) << 1)) + 1,
---
> 		    (int)(cur_start + (((crow * NCOLS) + ccol) << 1)) & SEG_MASK,
> 		    (int)ch);
> 		put_byte(VIDEO_SEG,
> 		    (int)((cur_start + (((crow * NCOLS) + ccol) << 1)) + 1) & SEG_MASK,
-----------------------------cut----------------------------------------
	Also, if you're going to run without CHEAP_CGA defined, you
need to add the following (fairly obvious) routine to klib88.s:

-----------------------------cut----------------------------------------
.globl _put_byte
|*===========================================================================*
|*				put_byte				     *
|*===========================================================================*
| This routine is used to stuff a byte anywhere in memory.
| The call is:
|     c = put_byte(seg, off, byte)
| where
|     'seg' is the value to put in es
|     'off' is the offset from the es value
|     'byte' is the byte to be placed
_put_byte:
	push bp			| save bp
	mov bp,sp		| we need to access parameters
	push es			| save es
	mov es,4(bp)		| load es with segment value
	mov bx,6(bp)		| load bx with offset from segment
	mov ax,8(bp)
	seg es			| go get the byte
	movb (bx),al		| al = byte
	pop es			| restore es
	pop bp			| restore bp
	ret			| return to caller
-----------------------------cut----------------------------------------

	I haven't had such good luck with the driver itself. Running
the "t.c" program, and configuring rs232.c to work at 1200 baud, it misses
about three of every five characters.

	-Peter S. Housel-
	housel@ei.ecn.purdue.edu
	...!pur-ee!housel

paradis@encore.UUCP (Jim Paradis) (01/18/88)

In article <1509@ea.ecn.purdue.edu> housel@ea.ecn.purdue.edu.UUCP (Peter S. Housel) writes:
>
>	I haven't had such good luck with the driver itself. Running
>the "t.c" program, and configuring rs232.c to work at 1200 baud, it misses
>about three of every five characters.

Did you read the section of the "README" file on the subject of handling
additional characters on an interrupt?  I don't know what kind of machine
you're running, but you might want to get rid of the "<< 1" in the latency
calculation.  Alternately, you might try tweaking MAX_CHARS_PER_INT up or
down.  Basically, if you wait too long to try and get the next character,
you might wind up missing some... and three out of five sounds about
right for that.

Let me know if this helps...

   +----------------+  Jim Paradis                  linus--+
+--+-------------+  |  Encore Computer Corp.       necntc--|
|  | E N C O R E |  |  257 Cedar Hill St.           ihnp4--+-encore!paradis
|  +-------------+--+  Marlboro MA 01752           decvax--|
+----------------+     (617) 460-0500             talcott--+
...coated with sesame seeds, and garnished with lark's vomit!

brucee@runx.ips.oz (Bruce Evans) (01/19/88)

Here are some patches to help get Jim Paradis' tty driver running on
Minix. I am running Minix on a standard PC with a Hercules graphics
card and a nonstandard cross-development environment, and this setup
exposed a few problems.

(1) tty.c uses the wrong syntax to call pointers to functions. This was a
minor problem since my Xenix compiler complained immediately. Apparently
the Minix compiler generates the correct code from the wrong syntax. It
probably needs fixing. The patches to tty.c cover this in great detail
(there are lots of pointers).

(2) console.c doesn't use SEG_MASK in all cases. Apparently it doesn't matter
on a CGA or EGA, but I have an HGA. Hmmm, recent postings say the EGA
doesn't really work. Hope this is the only problem. I hate to say this, but
Minix should start using the BIOS to handle screen initialisation. There
are too many hardware variations.

(3) ser_putc and ser_bufin need to lock out interrupts. This was a real
problem to debug since it is time-dependent. Must be rare on an AT.
What happened was the output ready flag would be FALSE when read at the start
of ser_putc but an output interrupt would set it to TRUE and ser_putc
would then put a character in the buffer. Then ser_oflush would loop forever.
I didn't actually see any problems from ser_bufin but the same lock seems
necessary.  See patches to rs232.c. I also tried implementing DTR/RTS flow
control on input, shown in the diffs. Didn't help at all, because my PC is
swamped on single-character input. Output flow control should work, except
there seems to be no good way to wait for an external device in Minix (fs
tends to block. Comments, Andy?).

(4) The new drivers pushed my kernel over 64K, mainly because of a debugger
already in it, so I had to use separate I & D. That (the 64K) broke build.
Worse, the get_tot_mem routine scribbles on the memory above 128K, including
(now) part of init. I am posting a rewrite of get_tot_mem separately.

After this exercise I'm not very impressed with having all drivers linked
directly to the kernel. It would be nice to keep the old ones around
until the new ones are debugged. Also, I slightly prefer the old console
driver. Now the arrow keys don't produce ANSI escape sequences. Actually,
I hate the ANSI protocol, but it seems to be necessary as a standard.
I've been thinking about a scheme where every non-control, non-shift (etc)
key returns itself (together with usual control/shift encoding) plus an extra
flags byte containing ALL special keys. Much like the standard PC scheme
except the flags byte is dynamic and the scan code is not avaiable (but
various 1's on the keyboard would be distinguished by the flags byte). A
fancy combination like CTRL-ALT-RIGHTSHIFT ESC - would just return ESC
(even PC BIOS doesn't return) and the application could decide what to
with the control flags. Would need another fancy ioctl ...

General ideas the speed problem.

I only run Minix on a standard slow PC. It doesn't get close to keeping up
even at 2400 Baud (when Xenix sends "\n$ " for a prompt, the " " is
consistently missed). I traced the output interrupt routine in detail. It
takes about 218 instructions, compared with a similar routine I wrote for
a PC terminal program, all in assembler, which took 47. It is unnecessary
to call save and restore for the output interrupt, could just push the
registers C uses. That would save 50 or so instructions. However, the input
interrupt is the real problem. It must be taking over 1000 instructions,
mainly message passing.  The only thing I can think of is to accumulate input
in timed chunks. Actually 1000 is not so many. I also traced the instructions
to write 1 character to the (old) console and was underwhelmed when the
count was over 4700 or 20 msec (The exact count depends on the compiler. I
don't know what it is for standard Minix.)

Beep needs rewriting as Jim says. Suggest starting the beep as now but leaving
interrupts on and stopping it with an alarm.

Ser_oflush really shouldn't do a busy wait, it should give up control if the
queue is large (though with external flow control, even 1 character could
take a long time to flush).

Ser_doint can't get by forever with the clever trick of waiting around a
while for the next character to arrive. Interrupts are disabled in it,
so interrupts on other serial lines will be missed for sure. Sending a
message to wait would be no use since messages take about the same time as
the delay between the interrupts.

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by brucee on Mon Jan 18 00:49:13 GMT+11:00 1988
# Contents:  console.c.diff rs232.diff tty.c.diff
 
echo x - console.c.diff
sed 's/^@//' > "console.c.diff" <<'@//E*O*F console.c.diff//'
45a46
> #undef CHEAP_CGA			/* otherwise MONO uses some slow CHEAP_CGA code */
410c411
< 
---
> 	int vidoff;
530,531c531,532
< 		oque[oque_last].address = cur_start +
< 				( ((crow * NCOLS) + ccol) << 1);
---
> 		oque[oque_last].address = (cur_start +
> 				(((crow * NCOLS) + ccol)) << 1) & SEG_MASK;
541,546c542,544
< 		put_byte(VIDEO_SEG,
< 		    (int)(cur_start + (((crow * NCOLS) + ccol) << 1)),
< 		    (int)ch);
< 		put_byte(VIDEO_SEG,
< 		    (int)(cur_start + (((crow * NCOLS) + ccol) << 1)) + 1,
< 		    (int)attrib);
---
> 		vidoff = (cur_start + (((crow * NCOLS) + ccol) << 1)) & SEG_MASK;
> 		put_byte(VIDEO_SEG, vidoff, (int)ch);
> 		put_byte(VIDEO_SEG, vidoff + 1, (int)attrib);
@//E*O*F console.c.diff//
chmod u=rw,g=,o= console.c.diff
 
echo x - rs232.diff
sed 's/^@//' > "rs232.diff" <<'@//E*O*F rs232.diff//'
243a244
> 	lock();
256c257
< 
---
> 	restore();
457a459
> 	int nchars;
476a479,484
>     nchars = rs->rs_ilast - rs->rs_ifirst;
>     if(nchars < 0) nchars += CIBUFSZ;
> 	if(nchars == (3 * CIBUFSZ) / 4)
> 		/* high water 3/4 full, drop RTS & DTR */
>     	port_out(MOD_CTL_REG(rs->rs_line), MC_OUT2);
> 
562,563c570
< 
< 
---
> 	lock();
567,568c574,577
<     if(maxchars < nchars) nchars = maxchars;
< 
---
> 	if(nchars >= CIBUFSZ/4 && nchars - maxchars < CIBUFSZ/4)
> 		/* low water 1/4 full, ready for more */
>     	port_out(MOD_CTL_REG(rs->rs_line), MC_OUT2 | MC_RTS | MC_DTR);
>     if(maxchars < nchars) nchars = maxchars;
575,578c584,586
<     return(nchars);
< }
< 
< 
---
> 	restore();
>     return(nchars);
> }
@//E*O*F rs232.diff//
chmod u=rw,g=,o= rs232.diff
 
echo x - tty.c.diff
sed 's/^@//' > "tty.c.diff" <<'@//E*O*F tty.c.diff//'
202,205c202,205
<     (tty_list[0].tt_dev_init)(0, 0);
< 
<     for(i = 1; i <= NUM_SERIAL_DEV; i++) {
<         (tty_list[1].tt_dev_init)(i, i - 1);
---
>     (*tty_list[0].tt_dev_init)(0, 0);
> 
>     for(i = 1; i <= NUM_SERIAL_DEV; i++) {
>         (*tty_list[1].tt_dev_init)(i, i - 1);
236c236
<   nchars = (tp->tt_dev_bufin)(tp->tt_minor, cbuf, 132);
---
>   nchars = (*tp->tt_dev_bufin)(tp->tt_minor, cbuf, 132);
252c252
< 		(tp->tt_dev_oflush)(tp->tt_minor);
---
> 		(*tp->tt_dev_oflush)(tp->tt_minor);
276c276
< 	    (tp->tt_dev_oflush)(tp->tt_minor);
---
> 	    (*tp->tt_dev_oflush)(tp->tt_minor);
298c298
< 		    (tp->tt_dev_oflush)(tp->tt_minor);
---
> 		    (*tp->tt_dev_oflush)(tp->tt_minor);
315c315
< 	    (tp->tt_dev_oflush)(tp->tt_minor);
---
> 	    (*tp->tt_dev_oflush)(tp->tt_minor);
346c346
< 	    (tp->tt_dev_oflush)(tp->tt_minor);
---
> 	    (*tp->tt_dev_oflush)(tp->tt_minor);
388,389c388,389
< 	    (tp->tt_dev_putc)(tp->tt_minor, '\r');
< 	    (tp->tt_dev_putc)(tp->tt_minor, '\n');
---
> 	    (*tp->tt_dev_putc)(tp->tt_minor, '\r');
> 	    (*tp->tt_dev_putc)(tp->tt_minor, '\n');
394,395c394,395
< 		    (tp->tt_dev_putc)(tp->tt_minor, '\r');
< 		    (tp->tt_dev_putc)(tp->tt_minor, '\n');
---
> 		    (*tp->tt_dev_putc)(tp->tt_minor, '\r');
> 		    (*tp->tt_dev_putc)(tp->tt_minor, '\n');
402c402
< 	    (tp->tt_dev_oflush)(tp->tt_minor);
---
> 	    (*tp->tt_dev_oflush)(tp->tt_minor);
415,417c415,417
< 		(tp->tt_dev_putc)(tp->tt_minor, '\r');
< 		(tp->tt_dev_putc)(tp->tt_minor, '\n');
< 		(tp->tt_dev_oflush)(tp->tt_minor);
---
> 		(*tp->tt_dev_putc)(tp->tt_minor, '\r');
> 		(*tp->tt_dev_putc)(tp->tt_minor, '\n');
> 		(*tp->tt_dev_oflush)(tp->tt_minor);
423,424c423,424
< 		(tp->tt_dev_putc)(tp->tt_minor, ch);
< 		(tp->tt_dev_oflush)(tp->tt_minor);
---
> 		(*tp->tt_dev_putc)(tp->tt_minor, ch);
> 		(*tp->tt_dev_oflush)(tp->tt_minor);
434c434
< 		(tp->tt_dev_oflush)(tp->tt_minor);
---
> 		(*tp->tt_dev_oflush)(tp->tt_minor);
442c442
< 		(tp->tt_dev_oflush)(tp->tt_minor);
---
> 		(*tp->tt_dev_oflush)(tp->tt_minor);
515,517c515,517
< 		(tp->tt_dev_putc)(tp->tt_minor, 8);
< 		(tp->tt_dev_putc)(tp->tt_minor, ' ');
< 		(tp->tt_dev_putc)(tp->tt_minor, 8);
---
> 		(*tp->tt_dev_putc)(tp->tt_minor, 8);
> 		(*tp->tt_dev_putc)(tp->tt_minor, ' ');
> 		(*tp->tt_dev_putc)(tp->tt_minor, 8);
748c748
<     (tp->tt_dev_oflush)(tp->tt_minor);
---
>     (*tp->tt_dev_oflush)(tp->tt_minor);
801,803c801,803
< 	        (tp->tt_dev_putc)(tp->tt_minor, '\r');
< 	    }
< 	    (tp->tt_dev_putc)(tp->tt_minor, tty_sbuf[i]);
---
> 	        (*tp->tt_dev_putc)(tp->tt_minor, '\r');
> 	    }
> 	    (*tp->tt_dev_putc)(tp->tt_minor, tty_sbuf[i]);
1080c1080
< 	    tp->tt_dev_fcon(tp->tt_minor);
---
> 	    (*tp->tt_dev_fcon)(tp->tt_minor);
1130c1130
< 	tp->tt_dev_fcoff(tp->tt_minor);
---
> 	(*tp->tt_dev_fcoff)(tp->tt_minor);
1180c1180
< 	tp->tt_dev_fcoff(tp->tt_minor);
---
> 	(*tp->tt_dev_fcoff)(tp->tt_minor);
1205,1209c1205,1209
< 	(tp->tt_dev_putc)(tp->tt_minor, ch);
<     }
<     else {
< 	(tp->tt_dev_putc)(tp->tt_minor, '^');
< 	(tp->tt_dev_putc)(tp->tt_minor, ch + '@');
---
> 	(*tp->tt_dev_putc)(tp->tt_minor, ch);
>     }
>     else {
> 	(*tp->tt_dev_putc)(tp->tt_minor, '^');
> 	(*tp->tt_dev_putc)(tp->tt_minor, ch + '@');
@//E*O*F tty.c.diff//
chmod u=rw,g=,o= tty.c.diff
 
exit 0

Bruce Evans (brucee@runx.ips.oz)