Subject: Re: Vaxstation 4000/60 LCG and keyboard, progress report & help needed
To: None <port-vax@NetBSD.org>
From: Blaz Antonic <blaz.antonic@siol.net>
List: port-vax
Date: 01/27/2004 14:04:30
This is a multi-part message in MIME format.

--------------629765D4276C
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hello,

> I'm suprised you haven't received many replies. The only reason I hadn't
> sent you a "wow, that's cool" message and asked for a patch to try out
> was because I thought you'd get dozens of such messages. I tried to get
> the mono framebuffer working on a VS3100 a while ago, but without much
> success (console worked, but the X server code proved impenetrable to
> me). The VS3100 made way for an MV3100, but I've still got the DEC
> monitor and would love to be able to use it on my VLC.

Lol, 3 replies in 1 hour now and only one prior to that in 2 days. I
have decided to post lcg.c on the mailing list anyway, but note this:

The code is NOT final; please don't distribute it, use it for testing
only. While i don't think it can do anything harmless i'd like to point
out i can't be held responsible if it does something bad to your
computer. Again, this is a working version with some debug stuff
included.

I would really apprecite it if people could send me the result of their
"show config" command, the part that refers to LCG to be more specific.
My code currently assumes it's dealing with 8bpp 1280x1024 LCG version
which is what my 4000/60 has and what 4000/60 of a german friend of mine
who's working on Linux FB implementation for LCG has. I'd also
appreciate comments on whether the whole thing works or not on your
computer, along with computer type (VLC versus m60).

If somebody figures out how to get DEC keyboard to work on 4000/60 or
VLC please let me know.

This is a mini-HOWTO on how to get it to work:

1: place lcg.c in arch/vax/vsa directory

2: alter arch/vax/conf/files.vax ()add entry for lcg, look at smg entry
for idea on how to do that, i don't know what the parameters that come
after the full filename do so i omitted them in my version of files.vax)

3: add lcg0 at vsbus csr 0x21801000 into your kernel config file

4: alter lcg.c if you're not using 1.6 kernel, otherwise it won't
compile. The relevant parts that need changing are commented out, most
are at the very bottom of the file (and one related item is near the
top).

5: add #define NO_EXPERIMENTAL to lcg.c if you don't want LUT changing
code to be included. At the moment this code plots a bar of 256 colors
near the bottom of the screen and then changes LUT to different values;
your text will end up BLUE instead of WHITE with current settings (to
get 256 grey tones set all three color components in the loop in
lcg_attach to i instead of 0, etc.).

6: uncomment /etc/ttys entries for /dev/ttyw*. 

This should produce login prompt on ttyw0 on your monitor attached to
LCG. I remember toyin gwith ttyflags and stty on /dev/ttyw* entries
before the whole thing started to work (and works ever since, no matter
how many system resets and hardware resets i have done) but i can't
think of any explanation for that. Things that output text on other
terminal shoudl work (echo text > /dev/ttyw0, etc.).

> How did you get around the lack of documentation for the LCG? Or have
> some docs turned up recently that I missed?

Docs did surface ... sort of, but it was apparently a preliminary draft
or something, pieced together from multiple sources by multiple people
so information contained within is contradicting in places and just
plain wrong in other places, plus there are many references to
(apparently) non-existent LCG models. Roughly 50% of info i found in
there is useless so you REALLY don't want to peek at docs to figure out
how LCG works, use lcg.c code instead. With LCG i have learned to
appreciate the discoveries of pasterisation (pasteurisation ?) process,
penicilin and alike.

As for LCSPX, i havenn't seen docs for that but ragge bodged together
lcspx.c which smight almost work ... i based my code on his and spent
some time removing the bugs. Both LCG and LCSPX are used as dumb
framebuffer at the moment, lcspx.c lacks any register manipulation while
lcg.c uses some and will use more later (to detect hardware correctly).

Getting LCG HW acceleration to work looks like major pain in the ass,
even if docs are 100% accurate with regrds to it. I have never written
any HW accelerated video code in my life so it all looks like chinese to
me. I am interested  owever in finding out how on earth wscons X server
interfaces to wscons and which facilities my driver has to provide in
order for X server to work. Since i can alter palette and plot pixels
anywhere i want on the screen i already have everything i need, i just
have to figure out how it all connects with higher layers (i.e. wscons
and that with X server).

Blaz Antonic
-- 
Hi! I'm a signature virus!
Copy me into your signature to help me spread!

--------------629765D4276C
Content-Type: text/plain; charset=us-ascii; name="LCG.C"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="LCG.C"

/*	$NetBSD: lcg.c,v 1.1 2004/01/27 10:24:00 Blaz Antonic Exp $ */
/*
 * Copyright (c) 2003, 2004 Blaz Antonic
 *
 * Based on bits of ragge's code
 * Copyright (c) 1998 Ludd, University of Lule}, Sweden.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed at Ludd, University of 
 *	Lule}, Sweden and its contributors.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lcg.c,v 1.1 2004/01/27 10:24:00 Blaz Antonic Exp $");

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/kernel.h>

#include <machine/vsbus.h>
#include <machine/sid.h>
#include <machine/cpu.h>

#ifndef NO_EXPERIMENTAL
#include <machine/lcgreg.h>
#endif

#include <dev/cons.h>

#include <dev/dec/dzreg.h>
#include <dev/dec/dzvar.h>
#include <dev/dec/dzkbdvar.h>

#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wscons_callbacks.h>

#include "machine/scb.h"

#include "dzkbd.h"
#include "opt_wsfont.h"

/* Safety guard */
#ifndef FONT_QVSS8x15
#include <dev/wsfont/qvss8x15.h>
#endif

/* Screen hardware defs */
#define LCG_COLS	160	/* char width of screen */
#define LCG_ROWS	68	/* rows of char on screen */
#define LCG_CHEIGHT	15	/* lines a char consists of */
#define LCG_CWIDTH	8	/* cols a char consists of */
#define LCG_ONEROW	(LCG_COLS * LCG_CHEIGHT * LCG_CWIDTH)
#define	LCG_YSIZE	1024
#define LCG_XSIZE	1280

#define LCG_TEXT_COLOR	0xff

#define	LCG_FB_ADDR		0x21801000	/* Frame buffer */
#define LCG_FB_SIZE	(LCG_XSIZE * LCG_YSIZE)
// #define	LCG_FB_SIZE		0x00200000	/* 8MB in size */

#ifndef NO_EXPERIMENTAL
#define LCG_REG_ADDR	0x20100000	/* LCG registers */
#define LCG_REG_SIZE	0x4000		/* 16384 bytes */

#define LCG_LUT_ADDR	0x21800800	/* LUT right before onscreen FB */
#define LCG_LUT_OFFSET	0x00000800
#define LCG_LUT_SIZE	0x800		/* 2048 bytes */
#endif

static	int lcg_match(struct device *, struct cfdata *, void *);
static	void lcg_attach(struct device *, struct device *, void *);

struct	lcg_softc {
	struct	device ss_dev;
};

#if 0
CFATTACH_DECL(lcg, sizeof(struct lcg_softc),
    lcg_match, lcg_attach, NULL, NULL);
#endif

struct	cfattach lcg_ca = {
	sizeof(struct lcg_softc), lcg_match, lcg_attach,
};

static void	lcg_cursor(void *, int, int, int);
static int	lcg_mapchar(void *, int, unsigned int *);
static void	lcg_putchar(void *, int, int, u_int, long);
static void	lcg_copycols(void *, int, int, int,int);
static void	lcg_erasecols(void *, int, int, int, long);
static void	lcg_copyrows(void *, int, int, int);
static void	lcg_eraserows(void *, int, int, long);
static int	lcg_allocattr(void *, int, int, int, long *);

const struct wsdisplay_emulops lcg_emulops = {
	lcg_cursor,
	lcg_mapchar,
	lcg_putchar,
	lcg_copycols,
	lcg_erasecols,
	lcg_copyrows,
	lcg_eraserows,
	lcg_allocattr
};

const struct wsscreen_descr lcg_stdscreen = {
	"160x68", LCG_COLS, LCG_ROWS,
	&lcg_emulops,
	8, LCG_CHEIGHT,
	WSSCREEN_UNDERLINE|WSSCREEN_REVERSE,
};

const struct wsscreen_descr *_lcg_scrlist[] = {
	&lcg_stdscreen,
};

const struct wsscreen_list lcg_screenlist = {
	sizeof(_lcg_scrlist) / sizeof(struct wsscreen_descr *),
	_lcg_scrlist,
};

static	char *lcgaddr;

#ifndef NO_EXPERMINETAL
static	char *lutaddr;
static	long *regaddr;
#endif

extern struct wsdisplay_font qvss8x15;
static  u_char *qf;

#define QCHAR(c) (c < 32 ? 32 : (c > 127 ? c - 66 : c - 32))
#define QFONT(c,line)	qf[QCHAR(c) * 15 + line]
#define	LCG_ADDR(row, col, line, dot) \
	lcgaddr[(col * LCG_CWIDTH) + (row * LCG_CHEIGHT * LCG_XSIZE) + \
	    line * LCG_XSIZE + dot]


static int	lcg_ioctl(void *, u_long, caddr_t, int, struct proc *);
static paddr_t	lcg_mmap(void *, off_t, int);
static int	lcg_alloc_screen(void *, const struct wsscreen_descr *,
				      void **, int *, int *, long *);
static void	lcg_free_screen(void *, void *);
static int	lcg_show_screen(void *, void *, int,
				     void (*) (void *, int, int), void *);
static void	lcg_crsr_blink(void *);

const struct wsdisplay_accessops lcg_accessops = {
	lcg_ioctl,
	lcg_mmap,
	lcg_alloc_screen,
	lcg_free_screen,
	lcg_show_screen,
	0 /* load_font */
};

struct	lcg_screen {
	int	ss_curx;
	int	ss_cury;
	u_char	ss_image[LCG_ROWS][LCG_COLS];	/* Image of current screen */
	u_char	ss_attr[LCG_ROWS][LCG_COLS];	/* Reversed etc... */
};

static	struct lcg_screen lcg_conscreen;
static	struct lcg_screen *curscr;

static	struct callout lcg_cursor_ch = CALLOUT_INITIALIZER;

int
lcg_match(struct device *parent, struct cfdata *match, void *aux)
{
	struct vsbus_softc *sc = (void *)parent;
	struct vsbus_attach_args *va = aux;
	char *ch = (char *)va->va_addr;

	if ((vax_boardtype != VAX_BTYP_46) && (vax_boardtype != VAX_BTYP_48))
		return 0;

	*ch = 1;
	if ((*ch & 1) == 0)
		return 0;
	*ch = 0;
	if ((*ch & 1) != 0)
		return 0;

	sc->sc_mask = 0x04; /* XXX - should be generated */
	scb_fake(0x120, 0x15);
	return 20;
}

void
lcg_attach(struct device *parent, struct device *self, void *aux)
{
	struct vsbus_attach_args *va = aux;
	struct wsemuldisplaydev_attach_args aa;

#ifndef NO_EXPERIMENTAL
	int i, j, k;
	long video_conf;
#endif

	printf("\n");
	aa.console = lcgaddr != NULL;
	lcgaddr = (caddr_t)vax_map_physmem(va->va_paddr, (LCG_FB_SIZE/VAX_NBPG));
	if (lcgaddr == 0) {
		printf("%s: Couldn't allocate graphics memory.\n", self->dv_xname);
		return;
	}

#ifndef NO_EXPERIMENTAL

	lutaddr = (caddr_t)vax_map_physmem(LCG_LUT_ADDR, (LCG_LUT_SIZE/VAX_NBPG));
	regaddr = (long*)vax_map_physmem(LCG_REG_ADDR, (LCG_REG_SIZE/VAX_NBPG));

	for (i = 0; i < 256; i++)
		for (j = 0; j < 50; j++)
			for (k = 0; k < 4; k++)
				if (k != 3)
					lcgaddr[(j * LCG_XSIZE) + (i * 5) + k + (900 * LCG_XSIZE)] = i;


	for (i = 255; i >= 0; i--) {
		lutaddr[i * 8 + 0] = 0;
		lutaddr[i * 8 + 1] = i; // address
		lutaddr[i * 8 + 2] = 1;
		lutaddr[i * 8 + 3] = 0; // red
		lutaddr[i * 8 + 4] = 1;
		lutaddr[i * 8 + 5] = 0; // green
		lutaddr[i * 8 + 6] = 1;
		lutaddr[i * 8 + 7] = i; // blue
	}

	video_conf = 0;	
	video_conf |= (3 << 30);
	video_conf |= (3 << 28);
	video_conf |= (0 << 26);
	video_conf |= (0 << 25);
	video_conf |= (0 << 24);
	video_conf |= (0 << 23);
	video_conf |= (0 << 22);
	video_conf |= (0 << 16);
	video_conf |= (0 << 15);
	video_conf |= (0 << 14);
	video_conf |= (1 << 13);
	video_conf |= (1 << 12);
	video_conf |= (1 << 11);
	video_conf |= (1 << 10);
	video_conf |= (2 << 8);
	video_conf |= (1 << 6);
	video_conf |= (0 << 5);
	video_conf |= (1 << 4);
	video_conf |= (1 << 3);
	video_conf |= (0 << 2);
	video_conf |= (1 << 1);
	video_conf |= (1 << 0);

	regaddr[LCG_REG_VIDEO_CONFIG/4] = video_conf;

	/* vital !!! */
	regaddr[LCG_REG_LUT_CONSOLE_SEL/4] = 1;

	regaddr[LCG_REG_LUT_COLOR_BASE_W/4] = LCG_LUT_OFFSET;

	/* 787 x 100 worked */
	i = 0;
	while (i < 783) {
		j = 0;
		while (j < 100) j++;
		i++;
	}
	regaddr[LCG_REG_LUT_CONSOLE_SEL/4] = 0;

#if 0
	/* delay not needed, only here to see the result on color bar */
	i = 0;
	while (i < 35000) {
		j = 0;
		while (j < 1000) j++;
		i++;
	}
#endif

#endif

	curscr = &lcg_conscreen;

	aa.scrdata = &lcg_screenlist;
	aa.accessops = &lcg_accessops;
	qf = qvss8x15.data;

	/* enable software cursor */
	callout_reset(&lcg_cursor_ch, hz / 2, lcg_crsr_blink, NULL);

	config_found(self, &aa, wsemuldisplaydevprint);
}

static	char *cursor;

static	int cur_on;

static void
lcg_crsr_blink(void *arg)
{
	int dot;

	if (cur_on)
		for (dot = 0; dot < LCG_CWIDTH; dot++)
			cursor[dot] ^= LCG_TEXT_COLOR;
	
	callout_reset(&lcg_cursor_ch, hz / 2, lcg_crsr_blink, NULL);
}

void
lcg_cursor(void *id, int on, int row, int col)
{
	struct lcg_screen *ss = id;
	int dot;

	if (ss == curscr) {
		char ch = QFONT(ss->ss_image[ss->ss_cury][ss->ss_curx], 14);

		for (dot = 0; dot < LCG_CWIDTH; dot++)
			LCG_ADDR(ss->ss_cury, ss->ss_curx, LCG_CHEIGHT - 1, dot) = 
				(LCG_TEXT_COLOR * ((ch >> dot) & 1));

		cursor = &LCG_ADDR(row, col, 14, 0);

		cur_on = on;
#if 0
		if ((cur_on = on))
			for (dot = 0; dot < LCG_CWIDTH; dot++)
				cursor[dot] ^= LCG_TEXT_COLOR;
#endif

	}
	ss->ss_curx = col;
	ss->ss_cury = row;
}

int
lcg_mapchar(void *id, int uni, unsigned int *index)
{
	if (uni < 256) {
		*index = uni;
		return (5);
	}
	*index = ' ';
	return (0);
}

static void
lcg_putchar(void *id, int row, int col, u_int c, long attr)
{
	struct lcg_screen *ss = id;
	int i, j;

	c &= 0xff;

	ss->ss_image[row][col] = c;
	ss->ss_attr[row][col] = attr;
	if (ss != curscr)
		return;

	for (i = 0; i < LCG_CHEIGHT; i++) {
		unsigned char ch = QFONT(c, i);
		char dot;

		for (j = 0; j < LCG_CWIDTH; j++) {
			dot = (LCG_TEXT_COLOR * ((ch >> j) & 1));
			if (attr & WSATTR_REVERSE)
				dot = (~dot) & 0xff;
			LCG_ADDR(row, col, i, j) = dot;
		}
	}
	if (attr & WSATTR_UNDERLINE) {
		char *p = &LCG_ADDR(row, col, i, 0);
		for (i = 0; i < LCG_CWIDTH; i++)
			p[i] = ~p[i];
	}
}

/*
 * copies columns inside a row.
 */
static void
lcg_copycols(void *id, int row, int srccol, int dstcol, int ncols)
{
	struct lcg_screen *ss = id;
	int i;

	bcopy(&ss->ss_image[row][srccol], &ss->ss_image[row][dstcol], ncols);
	bcopy(&ss->ss_attr[row][srccol], &ss->ss_attr[row][dstcol], ncols);
	if (ss != curscr)
		return;
	for (i = 0; i < LCG_CHEIGHT; i++)
		memcpy(&LCG_ADDR(row, dstcol, i, 0),
		    &LCG_ADDR(row,srccol, i, 0), ncols * LCG_CWIDTH);
}

/*
 * Erases a bunch of chars inside one row.
 */
static void
lcg_erasecols(void *id, int row, int startcol, int ncols, long fillattr)
{
	struct lcg_screen *ss = id;
	int i;

	bzero(&ss->ss_image[row][startcol], ncols);
	bzero(&ss->ss_attr[row][startcol], ncols);
	if (ss != curscr)
		return;
	for (i = 0; i < LCG_CHEIGHT; i++)
		memset(&LCG_ADDR(row, startcol, i, 0), 0, ncols * LCG_CWIDTH);
}

static void
lcg_copyrows(void *id, int srcrow, int dstrow, int nrows)
{
	struct lcg_screen *ss = id;

	bcopy(&ss->ss_image[srcrow][0], &ss->ss_image[dstrow][0],
	    nrows * LCG_COLS);
	bcopy(&ss->ss_attr[srcrow][0], &ss->ss_attr[dstrow][0],
	    nrows * LCG_COLS);
	if (ss != curscr)
		return;
	memcpy(&lcgaddr[dstrow * LCG_ONEROW],
	    &lcgaddr[srcrow * LCG_ONEROW], nrows * LCG_ONEROW);
}

static void
lcg_eraserows(void *id, int startrow, int nrows, long fillattr)
{
	struct lcg_screen *ss = id;

	bzero(&ss->ss_image[startrow][0], nrows * LCG_COLS);
	bzero(&ss->ss_attr[startrow][0], nrows * LCG_COLS);
	if (ss != curscr)
		return;
	memset(&lcgaddr[startrow * LCG_ONEROW], 0, nrows * LCG_ONEROW);
}

static int
lcg_allocattr(void *id, int fg, int bg, int flags, long *attrp)
{
	*attrp = flags;
	return 0;
}

int
lcg_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct wsdisplay_fbinfo *fb = (void *)data;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_LCG;
		break;

	case WSDISPLAYIO_GINFO:
		fb->height = LCG_YSIZE;
		fb->width = LCG_XSIZE;
		fb->depth = 1;
		fb->cmsize = 2;
		break;

#if 0
	case WSDISPLAYIO_SVIDEO:
		if (*(u_int *)data == WSDISPLAYIO_VIDEO_ON) {
			curcmd = curc;
		} else {
			curc = curcmd;
			curcmd &= ~(CUR_CMD_FOPA|CUR_CMD_ENPA);
			curcmd |= CUR_CMD_FOPB;
		}
		WRITECUR(CUR_CMD, curcmd);
		break;

	case WSDISPLAYIO_GVIDEO:
		*(u_int *)data = (curcmd & CUR_CMD_FOPB ?
		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON);
		break;
#endif

	default:
		return EPASSTHROUGH;
	}
	return 0;
}

static paddr_t
lcg_mmap(void *v, off_t offset, int prot)
{
	if (offset >= LCG_FB_SIZE || offset < 0)
		return -1;
	return (LCG_FB_ADDR + offset) >> PGSHIFT;
}

int
lcg_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
    int *curxp, int *curyp, long *defattrp)
{
	*cookiep = malloc(sizeof(struct lcg_screen), M_DEVBUF, M_WAITOK);
	bzero(*cookiep, sizeof(struct lcg_screen));
	*curxp = *curyp = *defattrp = 0;
	return 0;
}

void
lcg_free_screen(void *v, void *cookie)
{
}

int
lcg_show_screen(void *v, void *cookie, int waitok,
    void (*cb)(void *, int, int), void *cbarg)
{
	struct lcg_screen *ss = cookie;
	int row, col, line, dot;

	if (ss == curscr)
		return (0);

	for (row = 0; row < LCG_ROWS; row++)
		for (col = 0; col < LCG_COLS; col++) {
			for (line = 0; line < LCG_CHEIGHT; line++) {
				u_char s, c = ss->ss_image[row][col];

				if (c < 32)
					c = 32;
				s = QFONT(c, line);
				if (ss->ss_attr[row][col] & WSATTR_REVERSE)
					s ^= 255;

				for (dot = 0; dot < LCG_CWIDTH; dot++)
					LCG_ADDR(row, col, line, dot) = 
						(LCG_TEXT_COLOR * ((s >> dot) & 1));
			}
			if (ss->ss_attr[row][col] & WSATTR_UNDERLINE)
				for (dot = 0; dot < LCG_CWIDTH; dot++)
					LCG_ADDR(row, col, LCG_CHEIGHT - 1, dot) ^= LCG_TEXT_COLOR;
		}

	cursor = &lcgaddr[(ss->ss_cury * LCG_ONEROW) + 
		((LCG_CHEIGHT - 1) * LCG_COLS * LCG_CWIDTH) +
			(ss->ss_curx * LCG_CWIDTH)];
	curscr = ss;
	return (0);
}

cons_decl(lcg);
cdev_decl(wsdisplay);

void
lcgcninit(struct consdev *cndev)
{
	/* Clear screen */
	memset(lcgaddr, 0, LCG_XSIZE * LCG_YSIZE);

	curscr = &lcg_conscreen;
	wsdisplay_cnattach(&lcg_stdscreen, &lcg_conscreen, 0, 0, 0);
	cn_tab->cn_pri = CN_INTERNAL;
	qf = qvss8x15.data;

#if NDZKBD > 0
	dzkbd_cnattach(0); /* Connect keyboard and screen together */
#endif
}

/*
 * Called very early to setup the glass tty as console.
 * Because it's called before the VM system is inited, virtual memory
 * for the framebuffer can be stolen directly without disturbing anything.
 */
void
lcgcnprobe(struct consdev *cndev)
{
	extern vaddr_t virtual_avail;
//	extern const struct cdevsw wsdisplay_cdevsw;

	if ((vax_boardtype != VAX_BTYP_46) && (vax_boardtype != VAX_BTYP_48))
		return; /* Only for 4000/60 and VLC */

	if (vax_confdata & 8)
		return; /* Diagnostic console */
	lcgaddr = (caddr_t)virtual_avail;
	virtual_avail += LCG_FB_SIZE;
#ifndef NO_EXPERIMENTAL
	virtual_avail += LCG_LUT_SIZE;
	virtual_avail += LCG_REG_SIZE;
#endif	
	ioaccess((vaddr_t)lcgaddr, LCG_FB_ADDR, (LCG_FB_SIZE/VAX_NBPG));
	cndev->cn_pri = CN_INTERNAL;
//	cndev->cn_dev = makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 0);
	cndev->cn_dev = makedev(getmajor(wsdisplayopen), 0);
}

--------------629765D4276C--