~kris/9p

9hist

6480bd373a04c8ebc3afd3ad7266f2a5354ec090 — David du Colombier 26 years ago a53ddff
Plan 9 from Bell Labs 2000-09-03
3 files changed, 574 insertions(+), 51 deletions(-)

M bitsy/l.s
M pc/vgas3.c
A pc/vgasavage.c
M bitsy/l.s => bitsy/l.s +37 -33
@@ 123,67 123,71 @@ TEXT _vund(SB), $-4				/* undefined */
	B	_vswitch

TEXT _vsvc(SB), $-4				/* reset or SWI or reserved */
	SUB	$12, R13
	MOVW	R14, 8(R13)
	MOVW	CPSR, R14
	MOVW	R14, 4(R13)
	MOVW	$PsrMsvc, R14
	MOVW	R14, (R13)
	MOVW	8(R13), R14
	SUB	$12, R13			/* make room for pc, psr, & type */
	MOVW	R14, 8(R13)			/* ureg->pc = interupted PC */
	MOVW	SPSR, R14			/* ureg->psr = SPSR */
	MOVW	R14, 4(R13)			/* ... */
	MOVW	$PsrMsvc, R14			/* ureg->type = PsrMsvc */
	MOVW	R14, (R13)			/* ... */
	B	_vsaveu

TEXT _vpab(SB), $-4				/* prefetch abort */
	MOVM.IA	[R0-R3], (R13)
	MOVW	$PsrMabt, R0
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$PsrMabt, R0			/* r0 = type */
	B	_vswitch

TEXT _vdab(SB), $-4				/* data abort */
	MOVM.IA	[R0-R3], (R13)
	MOVW	$(PsrMabt+1), R0
	B	_vswitch
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$(PsrMabt+1), R0		/* r0 = type */
	B	_vswitch			/* 

TEXT _virq(SB), $-4				/* IRQ */
	MOVM.IA	[R0-R3], (R13)
	MOVW	$PsrMirq, R0
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$PsrMirq, R0			/* r0 = type */
	B	_vswitch

TEXT _vfiq(SB), $-4				/* FIQ */
	MOVM.IA	[R0-R3], (R13)
	MOVW	$PsrMfiq, R0
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$PsrMfiq, R0			/* r0 = type */
	B	_vswitch

	/*
	 *  come here with type in R0 and R13 pointing above saved [r0-r3]
	 */
_vswitch:				/* switch to svc, type in R0 */
	MOVW	SPSR, R1		/* psr for ureg */
	MOVW	R14, R2			/* saved pc for ureg */
	MOVW	SPSR, R1		/* SPSR for ureg */
	MOVW	R14, R2			/* interrupted pc for ureg */
	MOVW	R13, R3			/* [R0-R3] save area */

	MOVW	CPSR, R14		/* switch */
	/* switch to svc mode, we get new R13 pointing to top of svc stack */
	MOVW	CPSR, R14
	BIC	$PsrMask, R14
	ORR	$(PsrDirq|PsrDfiq|PsrMsvc), R14
	MOVW	R14, CPSR

	MOVM.DB.W [R0-R2], (R13)	/* top of ureg */
	MOVM.IA	  (R3), [R0-R3]		/* restore [R0-R3] */
	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{pc, psr, type}; r13 points to ureg->type  */
	MOVM.IA	  (R3), [R0-R3]		/* restore [R0-R3] from previous mode's stack */
	B	_vsaveu

	/* push the registers as in ureg */
	/* 
	 *  come here with R13 pointing to ureg->type
	 */
_vsaveu:
	SUB	$4, R13			/* save link */
	MOVW	R14, (R13)
	MOVM.DB.W.S [R0-R14], (R13)	/* save svc registers */
	MOVM.DB.W.S [R0-R14], (R13)	/* save user level registers, r13 points to ureg */

	MOVW	$setR12(SB), R12	/* the SB from user mode is different */
	MOVW	R13, R0			/* argument is &ureg */

	MOVW	R13, R0			/* first arg is pointer to ureg */
	SUB	$8, R13			/* space for argument+link */
	BL	exception(SB)

_vrfe:
	ADD	$(8+4*15), R13		/* [r0-R14]+argument+link */
	MOVW	(R13), R14		/* restore link */
	MOVW	8(R13), R0		/* restore SPSR */
	MOVW	R0, SPSR
	MOVM.DB.S (R13), [R0-R14]		/* restore registers */
	ADD	$12, R13		/* skip saved link+type+SPSR */
_vrfe: 
	ADD	$(8+4*15), R13		/* r13 points to ureg->type */
	MOVW	8(R13), R14		/* restore link */
	MOVW	4(R13), R0		/* restore SPSR */
	MOVW	R0, SPSR		/* ... */
	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
	ADD	$8, R13			/* skip saved type+SPSR */
	RFE				/* MOVM.IA.S.W (R13), [R15] */

TEXT splhi(SB), $-4

M pc/vgas3.c => pc/vgas3.c +47 -18
@@ 12,6 12,28 @@
#include <cursor.h>
#include "screen.h"

enum {
	PCIS3		= 0x5333,		/* PCI VID */

	SAVAGE3D	= 0x8A20,	/* PCI DID */
	SAVAGE3DMV	= 0x8A21,
	SAVAGE4		= 0x8A22,
	SAVAGEMXMV	= 0x8C10,
	SAVAGEMX	= 0x8C11,
	SAVAGEIXMV	= 0x8C12,
	SAVAGEIX	= 0x8C13,
	SAVAGE2000	= 0x9102,

	VIRGE		= 0x5631,
	VIRGEGX2	= 0x8A10,
	VIRGEDXGX	= 0x8A01,
	VIRGEVX		= 0x883D,
	VIRGEMX		= 0x8C01,
	VIRGEMXP	= 0x8C03,

	AURORA64VPLUS	= 0x8812,
};

static int
s3pageset(VGAscr* scr, int page)
{


@@ 50,7 72,7 @@ s3page(VGAscr* scr, int page)
	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
	switch(id){

	case 0x8A10:				/* ViRGE/GX2 */
	case VIRGEGX2:
		break;

	default:


@@ 78,7 100,7 @@ s3linear(VGAscr* scr, int* size, int* align)
	mmiosize = 0;
	mmiobase = 0;
	mmioname = nil;
	if(p = pcimatch(nil, 0x5333, 0)){
	if(p = pcimatch(nil, PCIS3, 0)){
		for(i=0; i<nelem(p->mem); i++){
			if(p->mem[i].size >= *size
			&& ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0)


@@ 92,7 114,7 @@ s3linear(VGAscr* scr, int* size, int* align)
		*size = p->mem[i].size;

		id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
		if(id == 0x8A22){		/* find Savage4 mmio */
		if(id == SAVAGE4){		/* find Savage4 mmio */
			/*
			 * We could assume that the MMIO registers
			 * will be in the screen segment and just use


@@ 108,6 130,7 @@ s3linear(VGAscr* scr, int* size, int* align)
				if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){
					mmiobase = p->mem[j].bar & ~0x0F;
					mmiosize = 512*1024;
					scr->mmio = (ulong*)upamalloc(mmiobase, mmiosize, 16*1024*1024);
					mmioname = "savage4mmio";
					break;
				}


@@ 196,12 219,12 @@ s3load(VGAscr* scr, Cursor* curs)
	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
	switch(id){

	case 0x5631:				/* ViRGE */
	case 0x8A01:				/* ViRGE/[DG]X */
	case 0x8A10:				/* ViRGE/GX2 */
	case 0x883D:				/* ViRGE/VX */
	case 0x8C12:				/* Savage4/IX-MV */
	case 0x8A22:				/* Savage4 */
	case VIRGE:
	case VIRGEDXGX:
	case VIRGEGX2:
	case VIRGEVX:	
	case SAVAGEIXMV:
	case SAVAGE4:
		p += scr->storage;
		break;



@@ 244,12 267,12 @@ s3load(VGAscr* scr, Cursor* curs)

	switch(id){

	case 0x5631:				/* ViRGE */
	case 0x8A01:				/* ViRGE/[DG]X */
	case 0x8A10:				/* ViRGE/GX2 */
	case 0x883D:				/* ViRGE/VX */
	case 0x8C12:				/* Savage4/IX-MV */
	case 0x8A22:				/* Savage4 */
	case VIRGE:
	case VIRGEDXGX:
	case VIRGEGX2:
	case VIRGEVX:	
	case SAVAGEIXMV:
	case SAVAGE4:
		break;

	default:


@@ 526,9 549,11 @@ s3blank(int blank)
	vgaxo(Seqx, CursorSyncCtl, x);
}


static void
s3drawinit(VGAscr *scr)
{
	extern void savageinit(VGAscr*);	/* vgasavage.c */
	ulong id;

	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);


@@ 542,13 567,17 @@ s3drawinit(VGAscr *scr)
	 * above.
	 */
	switch(id){
	case 0x5631:				/* ViRGE */
	case 0x883D:				/* ViRGE/VX */
	case 0x8A10:				/* ViRGE/GX2 */
	case VIRGE:				/* ViRGE */
	case VIRGEVX:				/* ViRGE/VX */
	case VIRGEGX2:				/* ViRGE/GX2 */
		scr->mmio = (ulong*)(scr->aperture+0x1000000);
		scr->fill = hwfill;
		scr->scroll = hwscroll;
		/* scr->blank = hwblank; */
		break;
	case SAVAGE4:
		savageinit(scr);
		break;
	}
}


A pc/vgasavage.c => pc/vgasavage.c +490 -0
@@ 0,0 1,490 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

enum {
	PCIS3		= 0x5333,		/* PCI VID */

	SAVAGE3D	= 0x8A20,	/* PCI DID */
	SAVAGE3DMV	= 0x8A21,
	SAVAGE4		= 0x8A22,
	SAVAGEMXMV	= 0x8C10,
	SAVAGEMX	= 0x8C11,
	SAVAGEIXMV	= 0x8C12,
	SAVAGEIX	= 0x8C13,
	SAVAGE2000	= 0x9102,

	VIRGE		= 0x5631,
	VIRGEGX2	= 0x8A10,
	VIRGEDXGX	= 0x8A01,
	VIRGEVX		= 0x883D,
	VIRGEMX		= 0x8C01,
	VIRGEMXP	= 0x8C03,

	AURORA64VPLUS	= 0x8812,
};

/*
 * Savage4 et al. acceleration.
 *
 * This is based only on the Savage4 documentation.
 * It is expected to work on other Savage cards as well,
 * but has not been tried.
 * 
 * There are five ways to access the 2D graphics engine registers:
 * 	- Old MMIO non-packed format
 *	- Old MMIO packed format
 *	- New MMIO non-packed format
 *	- New MMIO packed format
 *	- Burst Command Interface (BCI)
 *
 * Of these, the manual hints that the first three are deprecated,
 * and it does not document any of those three well enough to use.
 * 
 * I have tried for many hours with no success to understand the BCI
 * interface well enough to use it.  It is not well documented, and the
 * XFree86 driver seems to completely contradict what little documentation
 * there is.
 *
 * This leaves the packed new MMIO.
 * The manual contradicts itself here, claming that the registers
 * start at 0x2008100 as well as at 0x0008100 from the base of the 
 * mmio segment.  Since the segment is only 512k, we assume that
 * the latter is the correct offset.
 *
 * According to the manual, only 16-bit reads of the 2D registers
 * are supported: 32-bit reads will return garbage in the upper word.
 * 32-bit writes must be enabled explicitly.
 * 
 * 32-bit reads of the status registers seem just fine.
 */

/* 2D graphics engine registers */
enum {
	SubsystemStatus = 0x8504,	/* Subsystem Status: read only */
	  /* read only: whether we get interrupts on various events */
	  VsyncInt		= 1<<0,		/* vertical sync */
	  GeBusyInt		= 1<<1,		/* 2D graphics engine busy */
	  BfifoFullInt	= 1<<2,		/* BIU FIFO full */
	  BfifoEmptyInt	= 1<<3,		/* BIU FIFO empty */
	  CfifoFullInt	= 1<<4,		/* command FIFO full */
	  CfifoEmptyInt	= 1<<5,		/* command FIFO empty */
	  BciInt		= 1<<6, 	/* BCI */
	  LpbInt		= 1<<7, 	/* LPB */
	  CbHiInt		= 1<<16,	/* COB upper threshold */
	  CbLoInt		= 1<<17,	/* COB lower threshold */

	SubsystemCtl 	= 0x8504,	/* Subsystem Control: write only */
	  /* clear interrupts for various events */
	  VsyncClr		= 1<<0,
	  GeBusyClr		= 1<<1,
	  BfifoFullClr	= 1<<2,
	  BfifoEmptyClr	= 1<<3,
	  CfifoFullClr	= 1<<4,
	  CfifoEmptyClr	= 1<<5,
	  BciClr		= 1<<6,
	  LpbClr		= 1<<7,
	  CbHiClr		= 1<<16,
	  CbLoClr		= 1<<17,

	  /* enable interrupts for various events */
	  VsyncEna		= 1<<8,
	  Busy2DEna		= 1<<9,
	  BfifoFullEna	= 1<<10,
	  BfifoEmptyEna	= 1<<11,
	  CfifoFullEna	= 1<<12,
	  CfifoEmptyEna	= 1<<13,
	  SubsysBciEna	= 1<<14,
	  CbHiEna		= 1<<24,
	  CbLoEna		= 1<<25,

	  /* 2D graphics engine software reset */
	  GeSoftReset	= 1<<15,

	FifoStatus		= 0x8508,	/* FIFO status: read only */
	  CwbEmpty		= 1<<0,		/* command write buffer empty */
	  CrbEmpty		= 1<<1,		/* command read buffer empty */
	  CobEmpty		= 1<<2,		/* command overflow buffer empty */
	  CfifoEmpty	= 1<<3,		/* command FIFO empty */
	  CwbFull		= 1<<8,		/* command write buffer full */
	  CrbFull		= 1<<9,		/* command read buffer full */
	  CobFull		= 1<<10,	/* command overflow buffer full */
	  CfifoFull		= 1<<11,	/* command FIFO full */

	AdvFunCtl		= 0x850C,	/* Advanced Function Control: read/write */
	  GeEna			= 1<<0,	/* enable 2D/3D engine */
	  /*
	   * according to the manual, BigPixel should be
	   * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are
	   * used to figure out bpp example.  however, it does bad things
	   * to the screen in 8bpp mode.
	   */
	  BigPixel		= 1<<2,		/* 8 or more bpp enhanced mode */
	  LaEna			= 1<<3,		/* linear addressing ena: or'ed with CR58_4 */
	  Mclk_2		= 0<<8,		/* 2D engine clock divide: MCLK/2 */
	  Mclk_4		= 1<<8,		/* " MCLK/4 */
	  Mclk			= 2<<8,		/* " MCLK */
	/* Mclk			= 3<<8,		/* " MCLK */
	  Ic33mhz		= 1<<16,	/* Internal clock 33 MHz (instead of 66) */

	WakeupReg		= 0x8510,	/* Wakeup: read/write */
	  WakeupBit		= 1<<0,	/* wake up: or'ed with 3C3_0 */

	SourceY			= 0x8100,	/* UL corner of bitblt source */
	SourceX			= 0x8102,	/* " */
	RectY			= 0x8100,	/* UL corner of rectangle fill */
	RectX			= 0x8102,	/* " */
	DestY			= 0x8108,	/* UL corner of bitblt dest */
	DestX			= 0x810A,	/* " */
	Height			= 0x8148,	/* bitblt, image xfer rectangle height */
	Width			= 0x814A, 	/* bitblt, image xfer rectangle width */

	StartY			= 0x8100,	/* Line draw: first point*/
	StartX			= 0x8102,	/* " */
	/*
	 * For line draws, the following must be programmed:
	 * axial step constant = 2*min(|dx|,|dy|)
	 * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)]
	 * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1
	 *	[sic] when start X < end X
	 * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy|
	 *  [sic] when start X >= end X
	 */
	AxialStep		= 0x8108,
	DiagonalStep	= 0x810A,
	LineError		= 0x8110,
	MinorLength		= 0x8148,	/* pixel count along minor axis */
	MajorLength		= 0x814A,	/* pixel count along major axis */

	DrawCmd			= 0x8118,	/* Drawing Command: write only */
	  CmdMagic		= 0<<1,
	  AcrossPlane	= 1<<1,		/* across the plane mode */
	  LastPixelOff	= 1<<2,		/* last pixel of line or vector draw not drawn */
	  Radial		= 1<<3,		/* enable radial direction (else axial) */
	  DoDraw		= 1<<4,		/* draw pixels (else only move current pos) */

	  DrawRight		= 1<<5,		/* axial drawing direction: left to right */
	  /* DrawLeft		= 0<<5, */
	  MajorY		= 1<<6,
	  /* MajorX		= 0<<6, */
	  DrawDown		= 1<<7,
	  /* DrawUp		= 0<<7, */
	  Degree0		= 0<<5,		/* drawing direction when Radial */
	  Degree45		= 1<<5,
		/* ... */
	  Degree315		= 7<<5,

	  UseCPUData	= 1<<8,

	  /* image write bus transfer width */
	  Bus8			= 0<<9,
	  Bus16			= 1<<9,
	  /*
	   * in Bus32 mode, doubleword bits beyond the image rect width are
	   * discarded.  each line starts on a new doubleword.
	   * Bus32AP is intended for across-the-plane mode and
	   * rounds to byte boundaries instead.
	   */
	  Bus32			= 2<<9,
	  Bus32AP		= 3<<9,

	  CmdNop		= 0<<13,	/* nop */
	  CmdLine		= 1<<13,	/* draw line */
	  CmdFill		= 2<<13,	/* fill rectangle */
	  CmdBitblt		= 6<<13,	/* bitblt */
	  CmdPatblt		= 7<<13,	/* 8x8 pattern blt */

	  SrcGBD		= 0<<16,
	  SrcPBD		= 1<<16,
	  SrcSBD		= 2<<16,

	  DstGBD		= 0<<18,
	  DstPBD		= 1<<18,
	  DstSBD		= 2<<18,

	/* color sources, controls */
	BgColor			= 0x8120,	/* Background Color: read/write */
	FgColor			= 0x8124,	/* Foreground Color: read/write */
	BitplaneWmask	= 0x8128,	/* Bitplane Write Mask: read/write */
	BitplaneRmask	= 0x812C,	/* Bitplane Read Mask: read/write */
	CmpColor		= 0x8130,	/* Color Compare: read/write */
	BgMix			= 0x8134,
	FgMix			= 0x8136,
	  MixNew		= 7,
	  SrcBg			= 0<<5,
	  SrcFg			= 1<<5,
	  SrcCPU		= 2<<5,
	  SrcDisp		= 3<<5,

	/* clipping rectangle */
	TopScissors		= 0x8138,	/* Top Scissors: write only */
	LeftScissors	= 0x813A,	/* Left Scissors: write only */
	BottomScissors	= 0x813C,	/* Bottom Scissors: write only */
	RightScissors	= 0x813E,	/* Right Scissors: write only */

	/*
	 * Registers with Magic were indirectly accessed in older modes.
	 * It is not clear whether the Magic is necessary.
	 * In the older modes, writes to these registers were pipelined,
	 * so that you had to issue an engine command and wait for engine
	 * idle before reading a write back.  It is not clear if this is
	 * still the case either.
	 */
	PixCtl			= 0x8140,	/* Pixel Control: write only */
	  PixMagic		= 0xA<<12,
	  PixMixFg		= 0<<6,		/* foreground mix register always */
	  PixMixCPU		= 2<<6,		/* CPU data determines mix register */
	  PixMixDisp	= 3<<6,		/* display data determines mix register */

	MfMisc2Ctl		= 0x8142,	/* Multifunction Control Misc. 2: write only */
	  MfMisc2Magic	= 0xD<<12,
	  DstShift		= 0,		/* 3 bits: destination base address in MB */
	  SrcShift		= 4,		/* 3 bits: source base address in MB */
	  WaitFifoEmpty	= 2<<8,		/* wait for write FIFO empty between draws */

	MfMiscCtl		= 0x8144,	/* Multifunction Control Misc: write only */
	  MfMiscMagic	= 0xE<<12,
	  UseHighBits	= 1<<4,		/* select upper 16 bits for 32-bit reg access */
	  ClipInvert	= 1<<5,		/* only touch pixels outside clip rectangle */
	  SkipSame		= 0<<6,		/* ignore pixels with color CmpColor */
	  SkipDifferent	= 1<<7,		/* ignore pixels not color CmpColor */
	  CmpEna		= 1<<8,		/* enable color compare */
	  W32Ena		= 1<<9,		/* enable 32-bit register write */
	  ClipDis		= 1<<11,	/* disable clipping */

	/*
	 * The bitmap descriptor 1 registers contain the starting
	 * address of the bitmap (in bytes).
	 * The bitmap descriptor 2 registesr contain stride (in pixels)
	 * in the lower 16 bits, depth (in bits) in the next 8 bits,
	 * and whether block write is disabled.
	 */
	GBD1			= 0x8168,	/* Global Bitmap Descriptor 1: read/write */
	GBD2			= 0x816C,	/* Global Bitmap Descriptor 2: read/write */
	  /* GBD2-only bits */
	  BDS64			= 1<<0,		/* bitmap descriptor size 64 bits */
	  GBDBciEna		= 1<<3,		/* BCI enable */
	  /* generic BD2 bits */
	  BlockWriteDis	= 1<<28,
	  StrideShift	= 0,
	  DepthShift	= 16,

	PBD1			= 0x8170,	/* Primary Bitmap Descriptor: read/write */
	PBD2			= 0x8174,
	SBD1			= 0x8178,	/* Secondary Bitmap Descriptor: read/write */
	SBD2			= 0x817C,
};

/* mastered data transfer registers */

/* configuration/status registers */
enum {
	XStatus0			= 0x48C00,	/* Status Word 0: read only */
	  /* rev. A silicon differs from rev. B; use AltStatus0 */
	  CBEMaskA		= 0x1FFFF,	/* filled command buffer entries */
	  CBEShiftA		= 0,
	  BciIdleA		= 1<<17,	/* BCI idle */
	  Ge3IdleA		= 1<<18,	/* 3D engine idle */
	  Ge2IdleA		= 1<<19,	/* 2D engine idle */
	  McpIdleA		= 1<<20,	/* motion compensation processor idle */
	  MeIdleA		= 1<<22,	/* master engine idle */
	  PfPendA		= 1<<23,	/* page flip pending */

	  CBEMaskB		= 0x1FFFFF,
	  CBEShiftB		= 0,
	  BciIdleB		= 1<<25,
	  Ge3IdleB		= 1<<26,
	  Ge2IdleB		= 1<<27,
	  McpIdleB		= 1<<28,
	  MeIdleB		= 1<<30,
	  PfPendB		= 1<<31,

	AltStatus0		= 0x48C60,	/* Alternate Status Word 0: read only */
	  CBEMask		= 0x1FFFF,
	  CBEShift		= 0,
	  /* the Savage4 manual says bits 17..23 for these, like Status0 */
	  /* empirically, they are bits 21..26 */
	  BciIdle		= 1<<21,
	  Ge3Idle		= 1<<22,
	  Ge2Idle		= 1<<23,
	  McpIdle		= 1<<24,
	  MeIdle		= 1<<25,
	  PfPend		= 1<<26,

	XStatus1			= 0x48C04,	/* Status Word 1: read only */
	  /* contains event tag 1, event tag 0, both 16 bits */

	XStatus2			= 0x48C08,	/* Status Word 2: read only */
	  ScanMask		= 0x3FF,	/* current scan line */
	  ScanShift		= 0,
	  VRTMask		= 0x7F100,	/* vert retrace count */
	  VRTShift		= 11,

	CbThresh		= 0x48C10,	/* Command Buffer Thresholds: read/write */
	CobOff			= 0x48C14,	/* Command Overflow Buffer: read/write */

	CobPtr			= 0x48C18,	/* Command Overflow Buffer Pointers: read/write */
	  CobEna		= 1<<2,		/* command overflow buffer enable */
	  CobBciEna		= 1<<3,		/* BCI function enable */
	  CbeMask		= 0xFFFF8000,	/* no. of entries in command buffer */
	  CbeShift		= 15,

	AltStatus1		= 0x48C64,	/* Alternate Status Word 1: read onnly */
	  /* contains current texture surface tag, vertex buffer tag */

};

enum {
	Maxloop = 1<<23
};

static void
savagewaitidle(VGAscr *scr)
{
	long x;
	ulong *statw;

	statw = (ulong*)((uchar*)scr->mmio+AltStatus0);

	for(x=0; x<Maxloop; x++)
		if(*statw & Ge2Idle)
			return;
}

static int
savagefill(VGAscr *scr, Rectangle r, ulong sval)
{
	uchar *mmio;

	mmio = (uchar*)scr->mmio;

	*(ulong*)(mmio+FgColor) = sval;
	*(ulong*)(mmio+BgColor) = sval;
	*(ulong*)(mmio+BgMix) = SrcFg|MixNew;
	*(ulong*)(mmio+FgMix) = SrcFg|MixNew;
	*(ushort*)(mmio+RectY) = r.min.y;
	*(ushort*)(mmio+RectX) = r.min.x;
	*(ushort*)(mmio+Width) = Dx(r)-1;
	*(ushort*)(mmio+Height) = Dy(r)-1;
	*(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown;
	savagewaitidle(scr);
	return 1;
}

static int
savagescroll(VGAscr *scr, Rectangle r, Rectangle sr)
{
	uchar *mmio;
	ulong cmd;
	Point dp, sp;

	cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD;

	if(r.min.x <= sr.min.x){
		cmd |= DrawRight;
		dp.x = r.min.x;
		sp.x = sr.min.x;
	}else{
		dp.x = r.max.x-1;
		sp.x = sr.max.x-1;
	}

	if(r.min.y <= sr.min.y){
		cmd |= DrawDown;
		dp.y = r.min.y;
		sp.y = sr.min.y;
	}else{
		dp.y = r.max.y-1;
		sp.y = sr.max.y-1;
	}

	mmio = (uchar*)scr->mmio;

	*(ushort*)(mmio+SourceX) = sp.x;
	*(ushort*)(mmio+SourceY) = sp.y;
	*(ushort*)(mmio+DestX) = dp.x;
	*(ushort*)(mmio+DestY) = dp.y;
	*(ushort*)(mmio+Width) = Dx(r)-1;
	*(ushort*)(mmio+Height) = Dy(r)-1;
	*(ulong*)(mmio+BgMix) = SrcDisp|MixNew;
	*(ulong*)(mmio+FgMix) = SrcDisp|MixNew;
	*(ulong*)(mmio+DrawCmd) = cmd;
	savagewaitidle(scr);
	return 1;
}

void
savageinit(VGAscr *scr)
{
	uchar *mmio;
	ulong bd;

	mmio = (uchar*)scr->mmio;

	if(mmio == nil) {
		print("savageinit: no mmio\n");
		return;
	}
	/* 2D graphics engine software reset */
	*(ushort*)(mmio+SubsystemCtl) = GeSoftReset;
	delay(2);
	*(ushort*)(mmio+SubsystemCtl) = 0;
	savagewaitidle(scr);

	/* disable BCI as much as possible */
	*(ushort*)(mmio+CobPtr) &= ~CobBciEna;
	*(ushort*)(mmio+GBD2) &= ~GBDBciEna;
	savagewaitidle(scr);

	/* enable 32-bit writes, disable clipping */
	*(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis;
	savagewaitidle(scr);

	/* enable all read, write planes */
	*(ulong*)(mmio+BitplaneRmask) = ~0;
	*(ulong*)(mmio+BitplaneWmask) = ~0;
	savagewaitidle(scr);

	/* turn on linear access, 2D engine */
	*(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna;
	savagewaitidle(scr);

	/* set bitmap descriptors */
	bd = (scr->gscreen->depth<<DepthShift) |
		(Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis;

	*(ulong*)(mmio+GBD1) = 0;
	*(ulong*)(mmio+GBD2) = bd | BDS64;

	*(ulong*)(mmio+PBD1) = 0;
	*(ulong*)(mmio+PBD2) = bd;

	*(ulong*)(mmio+SBD1) = 0;
	*(ulong*)(mmio+SBD2) = bd;

	/*
	 * For some reason, the GBD needs to get programmed twice,
	 * once before the PBD, SBD, and once after.
	 * This empirically makes it get set right.
	 * I would like to better understand the ugliness
	 * going on here.
	 */
	*(ulong*)(mmio+GBD1) = 0;
	*(ulong*)(mmio+GBD2) = bd;
	*(ushort*)(mmio+GBD2+2) = bd>>16;
	savagewaitidle(scr);

	scr->fill = savagefill;
	scr->scroll = savagescroll;
}