[unix-pc.sources] Patch to wind.o to stop cursor from blinking

mdapoz@hybrid.UUCP (Mark Dapoz) (09/26/89)

Here's a quick program I wrote to patch the wind.o device driver of the
UNIX PC so that the cursor will become a solid block instead of the usual
(and annoying in my opinion) blinking block.  The patch is known to work
with the wind.o driver supplied with the 3.51 version of unix and may or
may not work with older versions.  This patch is supplied as is and I don't
guarantee that there aren't any side effects, since modifying device drivers
without source is tricky at best.  I suggest you read the ReadMe file if you
have any doubt about what exactly it is I'm doing to the driver.  I've been 
running with the patch applied for about a week now and I haven't seen any 
problems yet so it's probably safe to apply it :-)

  Mark Dapoz  (mdapoz@hybrid.UUCP)  ...uunet!mnetor!hybrid!mdapoz

I remind you that humans are only a tiny minority in this galaxy.
	   -- Spock, "The Apple," stardate 3715.6.

--- cut here ------ cut here ------ cut here ------ cut here ------ cut here ---

# 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 mdapoz@hybrid on Mon Sep 25 23:24:30 EDT 1989
# Contents:  noblink/ noblink/noblink.c noblink/Makefile noblink/ReadMe
 
echo mkdir - noblink
mkdir noblink
chmod u=rwx,g=rx,o=rx noblink
 
echo x - noblink/noblink.c
sed 's/^@//' > "noblink/noblink.c" <<'@//E*O*F noblink/noblink.c//'
/*
   noblink.c - modify the standard AT&T UNIX PC wind.o device driver so that
	       the cursor remains on all the time.

	       NOTE: this is known to work with version 3.51 and may not work
	       with earlier (or later if we're lucky enough :-) versions,
	       although there's a good chance it will.

	       USE AT YOUR OWN RISK!!! I take no responsibility for anything
	       that may happen if you use this program.  I haven't seen any
	       side effects from the program but that doesn't mean that it
	       can't happen!

	       Mark Dapoz 89/09/25
	       mdapoz@hybrid.UUCP
*/

#include <stdio.h>
#include <fcntl.h>
#include <nlist.h>
#include <filehdr.h>
#include <scnhdr.h>
#include <aouthdr.h>

#define	CCBLINK_MAGIC	0x24	/* magical offset from ccblink label */
#define	WUNLOCK_MAGIC	0x6a	/* magical offset from wunlock label */

#define	WIND_DRIVER	"./wind.o"

main(argc, argv)
int argc;
char **argv;
{
	struct nlist nl[3];	/* lookup for driver routines */
	struct filehdr fh;	/* COFF standard file header */
	struct scnhdr *shdr;	/* COFF section headers */
	struct scnhdr *hdr_ptr;	/* COFF pointer to section header */
	struct aouthdr ohdr;	/* COFF optional header */
	char buff[32];		/* small buffer for double checking code */
	int fd,i,j;
	int nmods=0;		/* number of successful mods */
				/* what code we should see before modifying */
	static char ccblink_code[] = {0x67,0x2e,0x42,0x97,0x4e,0xba,0xfe,0xb0,
				      0x74,0x0f};
	static char wunlock_code[] = {0x67,0x0a,0x42,0x39,0x00,0x05,0x97,0xa0,
				      0x4e,0xba};
				/* the code to put on top */
	static char ccblink_mod[] = {0x60};	/* 68000 BRA instruction */
	static char wunlock_mod[] = {0x4e,0x71};/* 68000 NOP instruction */

			/* make sure the user understands the circumstances */
	if (getopt(argc, argv, "y") != 'y')
	    disclaimer();
	nl[0].n_name = "ccblink";	/* routine which causes blinking */
	nl[1].n_name = "wunlock";	/* also causes blinking at end */
	nl[2].n_name = "";
	if (nlist(WIND_DRIVER, nl) < 0) { /* locate it in the object file */
	    perror(argv[0]);
	    exit(1);
	}
#ifdef DEBUG
	printf("%s: n_value=0x%x n_scnum=0x%x n_type=0x%x\n",
		nl[0].n_name, nl[0].n_value, nl[0].n_scnum, nl[0].n_type);
	printf("%s: n_value=0x%x n_scnum=0x%x n_type=0x%x\n",
		nl[1].n_name, nl[1].n_value, nl[1].n_scnum, nl[1].n_type);
#endif
	if ((fd=open(WIND_DRIVER, O_RDWR)) < 0) {
	    perror(argv[0]);
	    exit(1);
	}
	if (read(fd, &fh, FILHSZ) < 0) { /* get the standard COFF header*/
	    perror(argv[0]);
	    close(fd);
	    exit(1);
	}
#ifdef DEBUG
	printf("f_magic=0x%x f_nscns=0x%x f_opthdr=0x%x\n",
		fh.f_magic, fh.f_nscns, fh.f_opthdr);
#endif
			/* allocate room for the section headers */
	if ((shdr=(struct scnhdr *)malloc(SCNHSZ*fh.f_nscns)) == NULL) {
	    perror(argv[0]);
	    close(fd);
	    exit(1);
	}
	if (fh.f_opthdr)  /* read any optional headers which may be present */
	    if (read(fd, &ohdr, sizeof(AOUTHDR)) < 0) {
		perror(argv[0]);
		close(fd);
		exit(1);
	    }
			/* read the COFF section headers */
	if (read(fd, shdr, SCNHSZ*fh.f_nscns) < 0) {
	    perror(argv[0]);
	    close(fd);
	    exit(1);
	}
				/* run through all the sections */
	for (i=0,hdr_ptr=shdr; i < fh.f_nscns; i++,hdr_ptr++) {
#ifdef DEBUG
	    printf("s_name=%s s_size=0x%x s_scnptr=0x%x s_vaddr=0x%x\n",
	    	hdr_ptr->s_name, hdr_ptr->s_size, hdr_ptr->s_scnptr,
		hdr_ptr->s_vaddr);
#endif
	    if (i+1 == nl[0].n_scnum) { /* look for  "ccblink" routine */
		if (strcmp(hdr_ptr->s_name, _TEXT)) {
		    printf("'ccblink' routine not in a .text section\n");
		    continue;
		}
					/* jump to magical place (Mr Rogers?)*/
		if (lseek(fd, hdr_ptr->s_scnptr-hdr_ptr->s_vaddr+nl[0].n_value+
			CCBLINK_MAGIC, 0) < 0) {
		    perror(argv[0]);
		    break;
		}
					/* read in code from offset */
		if (read(fd, buff, 32) < 0) {
		    perror(argv[0]);
		    close(fd);
		    exit(1);
		}
#ifdef DEBUG
		printf("ccblink mod =0x%x\n", buff[0]);
#endif
		for (j=0; j < 10; j++)
		    if (buff[i] != ccblink_code[i]) {
			printf("ccblink routine not as expected, aborting!\n");
			exit(1);
		    }
					/* jump to magical place again */
		if (lseek(fd, hdr_ptr->s_scnptr-hdr_ptr->s_vaddr+nl[0].n_value+
			CCBLINK_MAGIC, 0) < 0) {
		    perror(argv[0]);
		    break;
		}
					/* make the code modification */
		if (write(fd, ccblink_mod, 1) < 0) {
		    perror(argv[0]);
		    close(fd);
		    exit(1);
		}
		nmods++;		/* indicate successful modification */
	    }
	    if (i+1 == nl[1].n_scnum) { /* look for  "wunlock" routine */
		if (strcmp(hdr_ptr->s_name, _TEXT)) {
		    printf("'wunlock' routine not in a .text section\n");
		    continue;
		}
					/* jump to magical place (Mr Rogers?)*/
		if (lseek(fd, hdr_ptr->s_scnptr-hdr_ptr->s_vaddr+nl[1].n_value+
			WUNLOCK_MAGIC, 0) < 0) {
		    perror(argv[0]);
		    break;
		}
					/* read in code from offset */
		if (read(fd, buff, 32) < 0) {
		    perror(argv[0]);
		    close(fd);
		    exit(1);
		}
#ifdef DEBUG
		printf("wunlock mod =0x%x\n", buff[0]);
#endif
		for (j=0; j < 10; j++)
		    if (buff[i] != wunlock_code[i]) {
			printf("wunlock routine not as expected, aborting!\n");
			exit(1);
		    }
					/* jump to magical place again */
		if (lseek(fd, hdr_ptr->s_scnptr-hdr_ptr->s_vaddr+nl[1].n_value+
			WUNLOCK_MAGIC, 0) < 0) {
		    perror(argv[0]);
		    break;
		}
					/* make the code modification */
		if (write(fd, wunlock_mod, 2) < 0) {
		    perror(argv[0]);
		    close(fd);
		    exit(1);
		}
		nmods++;		/* indicate successful modification */
	    }
	}
	if (nmods == 2)
	    printf("Driver successfully modified\n");
	else
	    printf("Driver modification failed\n");
	free(shdr);	/* be nice and return allocated memory */
	close(fd);	/* and close up the file */
}

disclaimer()
{
	fprintf(stderr, "NOTE: this program will attempt to modify your window device driver and leave\n");
	fprintf(stderr, "behind a patched copy which produces a steady solid block cursor instead of the\n");
	fprintf(stderr, "blinking cursor.  Since this involves patching the binary for the device driver\n");
	fprintf(stderr, "the author does NOT assume any responsibily for problems which may occur.\n");
	fprintf(stderr, "It's HIGHLY recommended you have a floppy unix ready for backout purposes.\n");
	fprintf(stderr, "\nUSE AT YOUR OWN RISK!!!!\n");
	fprintf(stderr, "\nPlease enter 'noblink -y' to indicate you understand this.\n");
	exit(0);
}
@//E*O*F noblink/noblink.c//
chmod u=rw,g=r,o=r noblink/noblink.c
 
echo x - noblink/Makefile
sed 's/^@//' > "noblink/Makefile" <<'@//E*O*F noblink/Makefile//'
CC=cc
CFLAGS=

all: noblink
	/bin/cp /etc/lddrv/wind.o ./wind.o
	@./noblink
	@/bin/echo 
	@/bin/echo "When the driver is successfully modified, enter 'make install' to install"
	@/bin/echo "the modified driver (you will need to be 'root' to do this)."

noblink: noblink.o
	$(CC) $(LDFLAGS) noblink.o -o noblink

install:
	/bin/ln /etc/lddrv/wind.o /etc/lddrv/wind.o.orig
	/bin/rm /etc/lddrv/wind.o
	/bin/cp ./wind.o /etc/lddrv/wind.o
	-/bin/rm /etc/lddrv/wind
	@/bin/echo 
	@/bin/echo "The new driver has been installed.  A copy of the old one can be found in"
	@/bin/echo "/etc/lddrv/wind.o.orig."
	@/bin/echo 
	@/bin/echo "You will need to reboot your system to pick up the new device driver."

clean: 
	/bin/rm noblink.o noblink wind.o

clobber: clean
@//E*O*F noblink/Makefile//
chmod u=rw,g=r,o=r noblink/Makefile
 
echo x - noblink/ReadMe
sed 's/^@//' > "noblink/ReadMe" <<'@//E*O*F noblink/ReadMe//'

NoBlink - a program to stop the blinking of the cursor on the console of the
	  UNIX PC

This is a quick description of what the noblink program really does.  Since this
program modifies a device driver, you should be somewhat competent in how the
UNIX PC manages loadable device drivers.  If you don't know what a device 
driver is, stop here, go no further, don't pass go and don't collect $200. It's
very easy to get yourself into a bind (no pun intended :-) when moving device 
drivers around, so you should at least know how to boot off a floppy unix and 
be able to restore the original driver in case of problems.  Enough warnings,
on to the real stuff.

If you were to disassemble the wind.o device driver, you would find that there 
are two routines which primarily control the blinking of the cursor.  The
first routine "ccblink" is an interrupt driven routine called by the timeout()
device driver call.  This routine is called twice per second and controls the
blink rate of the cursor.  Below is the disassembled routine:

	global	ccblink
ccblink:
	link	%fp,&-12
	movm.l	&0x4,-4(%fp)	#	 %d2
	mov.l	&30,%d2		# blink rate in 60 Hz (ie. 1/2 second)
	jsr	spl6
	mov.l	%d0,-8(%fp)
	tst.w	winlock
	bne	TS321
	tst.b	ccvis
#	beq	TS322		# **modified** branch always
	bra	TS322		# skip blanking the cursor
	clr.l	(%sp)
	jsr	ccset
	mov.l	&15,%d2

TS321:
	mov.l	-8(%fp),(%sp)
	jsr	splx
	mov.l	%d2,(%sp)
	clr.l	-(%sp)
	mov.l	ccblink,-(%sp)
	jsr	timeout
	add.w	&8,%sp
	movm.l	-4(%fp),&0x4	#	 %d2
	unlk	%fp
	rts

TS322:
	jsr	ccon
	bra	TS321


You'll notice that there is a variable called ccvis which indicates if the
cursor is visible or not.  This variable is controlled by the ccset routine
(not provided here) which does the actual clearing/displaying of the cursor.
By modifying "ccblink", we can prevent the cursor from being blanked by
always making the test of ccvis succeed.  This will catch about 90% of the
blanking of the cursor.  The remainder of the blanking takes place in the
"wunlock" routine.  I beleive this routine is used for locking updates to
the screen within the device driver.  The disassembled code for this routine
is as follows:

	global	wunlock
wunlock:
	link	%fp,&-4
	tst.w	winlock
	bne	TS128
	mov.l	RS2,(%sp)
	jsr	panic

TS128:
	tst.l	scrticks
	beq	TS129
	mov.l	lbolt,%d0
	sub.l	ktime,%d0
	cmp.l	%d0,scrticks
	ble	TS129
	tst.b	scrsav
	bne	TS130
	jsr	savscrn
	bra	TS130

TS129:
	cmp.l	winscreen+0x90004,&0x420000
	beq	TS130
	jsr	usavscrn

TS130:
	tst.b	omsvis
	beq	TS131
	mov.l	&1,%d0
	mov.l	%d0,(%sp)
	jsr	msset+0x832ea

TS131:
	tst.b	forceon
#	beq	TS132		# **modified**
	nop			# force cursor redisplay on every entry
	clr.b	forceon
	jsr	ccon

TS132:
	clr.w	winlock
	mov.l	winlock,(%sp)
	jsr	wakeup
	unlk	%fp
	rts

You'll notice that there is an additional variable called forceon which controls
whether or not "wunlock" causes the cursor to be redisplayed.  By modifying
the result of the comparison of this variable, we can cause the cursor to be
redisplayed whenever the screen is unlocked.  This provides us with a full
cursor for the remaining 10% of the time.

One small note should be made now.  You'll probably notice that the cursor tends
to flicker every now and then.  This can be attributed to the fact that several
areas of the screen must continually be updated; the top status line which
contains the clock is one such area.  When these areas are updated, the cursor
must be "moved" to the area being written to, and as a result you notice a
temporary flicker of the cursor.  You'll also notice this behaviour on most
ascii terminals.

	Mark Dapoz
	...uunet!mnetor!hybrid!mdapoz
@//E*O*F noblink/ReadMe//
chmod u=rw,g=r,o=r noblink/ReadMe
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    202    814   6413 noblink.c
     28     95    755 Makefile
    124    593   3624 ReadMe
    354   1502  10792 total
!!!
wc  noblink/noblink.c noblink/Makefile noblink/ReadMe | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
-- 
  Mark Dapoz  (mdapoz@hybrid.UUCP)  ...uunet!mnetor!hybrid!mdapoz

I remind you that humans are only a tiny minority in this galaxy.
	   -- Spock, "The Apple," stardate 3715.6.