/*	$NetBSD: bmd.c,v 1.8 2019/06/30 05:04:49 tsutsui Exp $	*/

/*
 * Copyright (c) 1992 OMRON Corporation.
 *
 * This code is derived from software contributed to Berkeley by
 * OMRON Corporation.
 *
 * 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 by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 *
 *	@(#)bmd.c	8.2 (Berkeley) 8/15/93
 */
/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * OMRON Corporation.
 *
 * 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. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 *
 *	@(#)bmd.c	8.2 (Berkeley) 8/15/93
 */
/*

 * bmd.c --- Bitmap-Display raw-level driver routines
 *
 *	by A.Fujita, SEP-09-1992
 */


#include <sys/param.h>
#include <luna68k/stand/boot/samachdep.h>
#include <machine/board.h>

/*
 *  RFCNT register
 */

union bmd_rfcnt {
	struct {
		int16_t	rfc_hcnt;
		int16_t	rfc_vcnt;
	} p;
	uint32_t u;
};

#define isprint(c)	((c) >= 0x20 && (c) < 0x7f)

/*
 *  Width & Height
 */

#define BMAP_OFFSET	8

#define PB_WIDTH	2048			/* Plane Width   (Bit) */
#define PB_HEIGHT	1024			/* Plane Hight   (Bit) */
#define PL_WIDTH	64			/* Plane Width  (long) */
#define PS_WIDTH	128			/* Plane Width  (long) */
#define P_WIDTH		256			/* Plane Width  (Byte) */

#define SB_WIDTH	1280			/* Screen Width  (Bit) */
#define SB_HEIGHT	1024			/* Screen Hight  (Bit) */
#define SL_WIDTH	40			/* Screen Width (Long) */
#define S_WIDTH		160			/* Screen Width (Byte) */

#define FB_WIDTH	12			/* Font Width    (Bit) */
#define FB_HEIGHT	20			/* Font Hight    (Bit) */


#define NEXT_LINE(addr)			(addr +  (PL_WIDTH * FB_HEIGHT))
#define SKIP_NEXT_LINE(addr)		(addr += (PL_WIDTH - SL_WIDTH))


static void	bmd_draw_char(uint8_t *, uint8_t *, int, int, int);
static void	bmd_reverse_char(uint8_t *, uint8_t *, int, int);
static void	bmd_erase_char(uint8_t *, uint8_t *, int, int);
static void	bmd_erase_screen(volatile uint32_t *);
static void	bmd_scroll_screen(volatile uint32_t *, volatile uint32_t *,
		    int, int, int, int);


struct bmd_linec {
	struct bmd_linec *bl_next;
	struct bmd_linec *bl_prev;
	int	bl_col;
	int	bl_end;
	uint8_t	bl_line[128];
};

struct bmd_softc {
	int	bc_stat;
	uint8_t *bc_raddr;
	uint8_t *bc_waddr;
	int	bc_xmin;
	int	bc_xmax;
	int	bc_ymin;
	int	bc_ymax;
	int	bc_col;
	int	bc_row;
	struct bmd_linec *bc_bl;
	char	bc_escseq[8];
	char   *bc_esc;
	void  (*bc_escape)(int);
};

#define STAT_NORMAL	0x0000
#define STAT_ESCAPE	0x0001
#define STAT_INSERT	0x0100

static struct	bmd_softc bmd_softc;
static struct	bmd_linec bmd_linec[52];

static void	bmd_escape(int);
static void	bmd_escape_0(int);
#if 0
static void	bmd_escape_1(int);
#endif


/*
 * Escape-Sequence
 */

static void
bmd_escape(int c)
{
	struct bmd_softc *bp = &bmd_softc;

	switch (c) {

	case '[':
		bp->bc_escape = bmd_escape_0;
		break;

	default:
		bp->bc_stat &= ~STAT_ESCAPE;
		bp->bc_esc = &bp->bc_escseq[0];
		bp->bc_escape = bmd_escape;
		break;
	}
}

static void
bmd_escape_0(int c)
{
	struct bmd_softc *bp = &bmd_softc;
	struct bmd_linec *bq = bp->bc_bl;

	switch (c) {

	case 'A':
		if (bp->bc_row > bp->bc_ymin) {
			bp->bc_row--;
		}
		break;

	case 'C':
		if (bq->bl_col < bp->bc_xmax - 1) {
			bq->bl_col++;
		}
		break;

	case 'K':
		if (bq->bl_col < bp->bc_xmax) {
			int col;
			for (col = bq->bl_col; col < bp->bc_xmax; col++)
				bmd_erase_char(bp->bc_raddr,
					       bp->bc_waddr,
					       col, bp->bc_row);
		}
		bq->bl_end = bq->bl_col;
		break;

	case 'H':
		bq->bl_col = bq->bl_end = bp->bc_xmin;
		bp->bc_row = bp->bc_ymin;
		break;

	default:
#if 0
		*bp->bc_esc++ = c;
		bp->bc_escape = bmd_escape_1;
		return;
#endif
		break;
	}

	bp->bc_stat &= ~STAT_ESCAPE;
	bp->bc_esc = &bp->bc_escseq[0];
	bp->bc_escape = bmd_escape;
}

#if 0
static void
bmd_escape_1(int c)
{
	struct bmd_softc *bp = &bmd_softc;
	struct bmd_linec *bq = bp->bc_bl;
	int col = 0, row = 0;
	char *p;

	switch (c) {

	case 'J':
		bp->bc_stat &= ~STAT_ESCAPE;
		bp->bc_esc = &bp->bc_escseq[0];
		bp->bc_escape = bmd_escape;
		break;

	case 'H':
		for (p = &bp->bc_escseq[0]; *p != ';'; p++)
			row = (row * 10) + (*p - 0x30);
		p++;
		for (p = &bp->bc_escseq[0]; p != bp->bc_esc; p++)
			col = (col * 10) + (*p - 0x30);

		bq->bl_col = col + bp->bc_xmin;
		bp->bc_row = row + bp->bc_ymin;

		bp->bc_stat &= ~STAT_ESCAPE;
		bp->bc_esc = &bp->bc_escseq[0];
		bp->bc_escape = bmd_escape;
		break;

	default:
		*bp->bc_esc++ = c;
		break;
	}
}
#endif

/*
 * Entry Routine
 */

void
bmdinit(void)
{
	volatile uint32_t *bmd_rfcnt = (uint32_t *)BMAP_RFCNT;
	volatile uint32_t *bmd_bmsel = (uint32_t *)BMAP_BMSEL;
	struct bmd_softc *bp = &bmd_softc;
	struct bmd_linec *bq;
	int i;
	union bmd_rfcnt rfcnt;

	/*
	 *  adjust plane position
	 */

	/* plane-0 hardware address */
	bp->bc_raddr = (uint8_t *)(BMAP_BMAP0 + BMAP_OFFSET);
	/* common bitmap hardware address */
	bp->bc_waddr = (uint8_t *)(BMAP_BMP   + BMAP_OFFSET);

	rfcnt.p.rfc_hcnt = 7;			/* shift left   16 dot */
	rfcnt.p.rfc_vcnt = -27;			/* shift down    1 dot */
	*bmd_rfcnt = rfcnt.u;

	bp->bc_stat  = STAT_NORMAL;

	bp->bc_xmin  = 8;
	bp->bc_xmax  = 96;
	bp->bc_ymin  = 2;
	bp->bc_ymax  = 48;

	bp->bc_row = bp->bc_ymin;

	for (i = bp->bc_ymin; i < bp->bc_ymax; i++) {
		bmd_linec[i].bl_next = &bmd_linec[i + 1];
		bmd_linec[i].bl_prev = &bmd_linec[i - 1];
	}
	bmd_linec[bp->bc_ymax - 1].bl_next = &bmd_linec[bp->bc_ymin];
	bmd_linec[bp->bc_ymin].bl_prev = &bmd_linec[bp->bc_ymax - 1];

	bq = bp->bc_bl = &bmd_linec[bp->bc_ymin];
	bq->bl_col = bq->bl_end = bp->bc_xmin;

	bp->bc_col = bp->bc_xmin;

	bp->bc_esc = &bp->bc_escseq[0];
	bp->bc_escape = bmd_escape;

	*bmd_bmsel = 0xff;				/* all planes */
	bmd_erase_screen((uint32_t *)bp->bc_waddr);	/* clear screen */
	*bmd_bmsel = 0x01;				/* 1 plane */

	/* turn on cursor */
	bmd_reverse_char(bp->bc_raddr,
			 bp->bc_waddr,
			 bq->bl_col, bp->bc_row);
}

void
bmdadjust(int16_t hcnt, int16_t vcnt)
{
	volatile uint32_t *bmd_rfcnt = (uint32_t *)BMAP_RFCNT;
	union bmd_rfcnt rfcnt;

	printf("bmdadjust: hcnt = %d, vcnt = %d\n", hcnt, vcnt);

	rfcnt.p.rfc_hcnt = hcnt;		/* shift left   16 dot */
	rfcnt.p.rfc_vcnt = vcnt;		/* shift down    1 dot */

	*bmd_rfcnt = rfcnt.u;
}

int
bmdputc(int c)
{
	struct bmd_softc *bp = &bmd_softc;
	struct bmd_linec *bq = bp->bc_bl;
	int i;

	c &= 0x7F;

	/* turn off cursor */
	bmd_reverse_char(bp->bc_raddr,
			 bp->bc_waddr,
			 bq->bl_col, bp->bc_row);

	/* do escape-sequence */
	if (bp->bc_stat & STAT_ESCAPE) {
		*bp->bc_esc++ = c;
		(*bp->bc_escape)(c);
		goto done;
	}

	if (isprint(c)) {
		bmd_draw_char(bp->bc_raddr, bp->bc_waddr,
			      bq->bl_col, bp->bc_row, c);
		bq->bl_col++;
		bq->bl_end++;
		if (bq->bl_col >= bp->bc_xmax) {
			bq->bl_col = bq->bl_end = bp->bc_xmin;
			bp->bc_row++;
			if (bp->bc_row >= bp->bc_ymax) {
				bmd_scroll_screen((uint32_t *)bp->bc_raddr,
						  (uint32_t *)bp->bc_waddr,
						  bp->bc_xmin, bp->bc_xmax,
						  bp->bc_ymin, bp->bc_ymax);

				bp->bc_row = bp->bc_ymax - 1;
			}
		}
	} else {
		switch (c) {
		case 0x08:				/* BS */
			if (bq->bl_col > bp->bc_xmin) {
				bq->bl_col--;
			}
			break;

		case 0x09:				/* HT */
		case 0x0B:				/* VT */
			i = ((bq->bl_col / 8) + 1) * 8;
			if (i < bp->bc_xmax) {
				bq->bl_col = bq->bl_end = i;
			}
			break;

		case 0x0A:				/* NL */
			bp->bc_row++;
			if (bp->bc_row >= bp->bc_ymax) {
				bmd_scroll_screen((uint32_t *)bp->bc_raddr,
						  (uint32_t *)bp->bc_waddr,
						  bp->bc_xmin, bp->bc_xmax,
						  bp->bc_ymin, bp->bc_ymax);

				bp->bc_row = bp->bc_ymax - 1;
			}
			break;

		case 0x0D:				/* CR */
			bq->bl_col = bp->bc_xmin;
			break;

		case 0x1B:				/* ESC */
			bp->bc_stat |= STAT_ESCAPE;
			*bp->bc_esc++ = 0x1b;
			break;

		case 0x7F:				/* DEL */
			if (bq->bl_col > bp->bc_xmin) {
				bq->bl_col--;
				bmd_erase_char(bp->bc_raddr,
					       bp->bc_waddr,
					       bq->bl_col, bp->bc_row);
			}
			break;

		default:
			break;
		}
	}

 done:
	/* turn on  cursor */
	bmd_reverse_char(bp->bc_raddr,
			 bp->bc_waddr,
			 bq->bl_col, bp->bc_row);

	return c;
}

void
bmdclear(void)
{
	struct bmd_softc *bp = &bmd_softc;
	struct bmd_linec *bq = bp->bc_bl;

	/* clear screen */
	bmd_erase_screen((uint32_t *)bp->bc_waddr);

	bq->bl_col = bq->bl_end = bp->bc_xmin;
	bp->bc_row = bp->bc_ymin;

	/* turn on cursor */
	bmd_reverse_char(bp->bc_raddr,
			 bp->bc_waddr,
			 bq->bl_col, bp->bc_row);
}


/*
 *  charactor operation routines
 */

static void
bmd_draw_char(uint8_t *raddr, uint8_t *waddr, int col, int row, int c)
{
	volatile uint16_t *p, *q;
	volatile uint32_t *lp, *lq;
	const uint16_t *fp;
	int i;

	fp = &bmdfont[c][0];

	switch (col % 4) {

	case 0:
		p = (uint16_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		q = (uint16_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		for (i = 0; i < FB_HEIGHT; i++) {
			*q = (*p & 0x000F) | (*fp & 0xFFF0);
			p += 128;
			q += 128;
			fp++;
		}
		break;

	case 1:
		lp = (uint32_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		lq = (uint32_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		for (i = 0; i < FB_HEIGHT; i++) {
			*lq = (*lp & 0xFFF000FF) |
			    ((uint32_t)(*fp & 0xFFF0) << 4);
			lp += 64;
			lq += 64;
			fp++;
		}
		break;

	case 2:
		lp = (uint32_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 2);
		lq = (uint32_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 2);
		for (i = 0; i < FB_HEIGHT; i++) {
			*lq = (*lp & 0xFF000FFF) |
			    ((uint32_t)(*fp & 0xFFF0) << 8);
			lp += 64;
			lq += 64;
			fp++;
		}
		break;

	case 3:
		p = (uint16_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 4);
		q = (uint16_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 4);
		for (i = 0; i < FB_HEIGHT; i++) {
			*q = (*p & 0xF000) | ((*fp & 0xFFF0) >> 4);
			p += 128;
			q += 128;
			fp++;
		}
		break;

	default:
		break;
	}
}

static void
bmd_reverse_char(uint8_t *raddr, uint8_t *waddr, int col, int row)
{
	volatile uint16_t *p, *q;
	volatile uint32_t *lp, *lq;
	int i;

	switch (col % 4) {

	case 0:
		p = (uint16_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		q = (uint16_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		for (i = 0; i < FB_HEIGHT; i++) {
			*q = (*p & 0x000F) | (~(*p) & 0xFFF0);
			p += 128;
			q += 128;
		}
		break;

	case 1:
		lp = (uint32_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		lq = (uint32_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6));
		for (i = 0; i < FB_HEIGHT; i++) {
			*lq = (*lp & 0xFFF000FF) | (~(*lp) & 0x000FFF00);
			lp += 64;
			lq += 64;
		}
		break;

	case 2:
		lp = (uint32_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 2);
		lq = (uint32_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 2);
		for (i = 0; i < FB_HEIGHT; i++) {
			*lq = (*lp & 0xFF000FFF) | (~(*lp) & 0x00FFF000);
			lp += 64;
			lq += 64;
		}
		break;

	case 3:
		p = (uint16_t *)(raddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 4);
		q = (uint16_t *)(waddr + ((row * FB_HEIGHT) << 8)
		    + ((col / 4) * 6) + 4);
		for (i = 0; i < FB_HEIGHT; i++) {
			*q = (*p & 0xF000) | (~(*p) & 0x0FFF);
			p += 128;
			q += 128;
		}
		break;

	default:
		break;
	}
}

static void
bmd_erase_char(uint8_t *raddr, uint8_t *waddr, int col, int row)
{

	bmd_draw_char(raddr, waddr, col, row, 0);
}


/*
 * screen operation routines
 */

static void
bmd_erase_screen(volatile uint32_t *lp)
{
	int i, j;

	for (i = 0; i < SB_HEIGHT; i++) {
		for (j = 0; j < SL_WIDTH; j++)
			*lp++ = 0;
		SKIP_NEXT_LINE(lp);
	}
}

static void
bmd_scroll_screen(volatile uint32_t *lp, volatile uint32_t *lq,
    int xmin, int xmax, int ymin, int ymax)
{
	int i, j;

	lp += ((PL_WIDTH * FB_HEIGHT) * (ymin + 1));
	lq += ((PL_WIDTH * FB_HEIGHT) *  ymin);

	for (i = 0; i < ((ymax - ymin -1) * FB_HEIGHT); i++) {
		for (j = 0; j < SL_WIDTH; j++) {
			*lq++ = *lp++;
		}
		lp += (PL_WIDTH - SL_WIDTH);
		lq += (PL_WIDTH - SL_WIDTH);
	}

	for (i = 0; i < FB_HEIGHT; i++) {
		for (j = 0; j < SL_WIDTH; j++) {
			*lq++ = 0;
		}
		lq += (PL_WIDTH - SL_WIDTH);
	}
}
