Welcome to Data Crystal's new home! Data Crystal is now part of the TCRF family (sort of).
The wiki has recently moved; please report any issues in Discord. Pardon the dust.

Talk:Video (PC8801)

From Data Crystal
Jump to navigation Jump to search

m88 Source

// ---------------------------------------------------------------------------
//	M88 - PC-88 Emulator.
//	Copyright (C) cisc 1998.
// ---------------------------------------------------------------------------
//  ‰æ–ʐ§Œä‚ƃOƒ‰ƒtƒBƒbƒNƒX‚̃Gƒ~ƒ…ƒŒ[ƒVƒ‡ƒ“
// ---------------------------------------------------------------------------
//	’ŽF	
//	fullline = false Žž‚Ì”ñ•`‰æƒ‰ƒCƒ“‚̐F‚ª palette 0 ˆË‘¶
//

#include <stdio.h>
#include <string.h>
#include "Screen.h"
// #include "Screen_i.h"

static const bool fullline = true;

#ifndef RELEASEBUILD
//	#define SCREEN_LOGGING
#endif

#ifdef SCREEN_LOGGING
	#define LOG0(a)			if (fp) fprintf(fp, a)
	#define LOG1(a, b)		if (fp) fprintf(fp, a, b)
	#define LOG2(a, b, c)	if (fp) fprintf(fp, a, b, c)
	#define LOG3(a, b, c, d) if (fp) fprintf(fp, a, b, c, d)
	static FILE* fp;
#else
	#define LOG0(a)
	#define LOG1(a, b)
	#define LOG2(a, b, c)
	#define LOG3(a, b, c, d)
#endif

// ---------------------------------------------------------------------------
// \’z/Á–Å
// ---------------------------------------------------------------------------

Screen::Screen()
{
#ifdef SCREEN_LOGGING
	fp = fopen("screen.log", "w");
#endif
	CreateTable();
	
	for (int i=0; i<8; i++)
		green[i] = red[i] = blue[i]  = 0;
	
	line400 = false;
	port32 = 0;

	LOG0("Initial ");
	Reset();
}

Screen::~Screen()
{
#ifdef SCREEN_LOGGING
	if (fp)	fclose(fp);
#endif
}

// ---------------------------------------------------------------------------
//	‰Šú‰»
// ---------------------------------------------------------------------------

void Screen::Reset()
{
	displaytext = true;			// ƒeƒLƒXƒg‚ð•\Ž¦
	selectedplane = 3;			// RAM ‚ð‘I‘ð

//	nreads = nwrites = 0;
	SelectAccessFunc();			// ƒƒ‚ƒŠƒAƒNƒZƒXŠÖ”‚ðÝ’è
	LOG0("Reset!\n");

	bgred = bggreen = bgblue = 0;
}

void Screen::Init()
{
	displaygraphics = 7;
	InitPalette();
}

// ---------------------------------------------------------------------------
//	ƒpƒŒƒbƒg‚ð‰Šú‰»
// ---------------------------------------------------------------------------

void Screen::InitPalette()
{
	int i;

	if (color)
	{
		LOG2("mode: color %s\ttext %s\n", displaygraphics ? "enabled" : "disabled", displaytext ? "on" : "off");
		int j = displaytext ? 0x40 : 0x80;

		for (i=0; i<j; i++)				// ƒOƒ‰ƒtƒBƒbƒNƒX•”
		{
			SetPalette(i, red[i & displaygraphics],
				          green[i & displaygraphics],
						  blue[i & displaygraphics] );
//			SetPalette(i, i & 2 ? 7 : 0, i & 4 ? 7 : 0, i & 1 ? 7 : 0);
		}
		if (!(port30 & 2))
		{
			for (;i<0x80; i++)
			{
#if 1
				SetPalette(i, (i & 0x10) ? 7 : 0, (i & 0x20) ? 7 : 0,  (i & 0x08) ? 7 : 0);
#else		// ƒeƒLƒXƒg”¼“§–¾
				SetPalette(i, 
					((i & 0x10 ? 7 : 0) + red[i & displaygraphics]) / 2,
					((i & 0x20 ? 7 : 0) + green[i & displaygraphics]) / 2,
					((i & 0x08 ? 7 : 0) + blue[i & displaygraphics]) / 2 );
#endif
			}
		}
		else
		{
			for (;i<0x80; i++)
				SetPalette(i, 7, 7, 7);
		}
	}
	else
	{
		LOG3("mode: mono  %s(%d)\ttext %s\n", 
			displaygraphics ? "enabled" : "disabled", displayplane,
			displaytext ? "on" : "off");
		if (!(port30 & 2))
		{
			if (port32 & 0x20)
			{
				for (i=0; i<0x80; i++)
				{
					if (((i & 0x40) && displaytext) || (i & displayplane & displaygraphics))
						SetPalette(i, red[(i>>3) & 7], green[(i>>3) & 7], blue[(i>>3) & 7]);
					else
						SetPalette(i, bgred, bggreen, bgblue);
				}
			}
			else
			{
				for (i=0; i<0x80; i++)
				{
					if (((i & 0x40) && displaytext) || (i & displayplane & displaygraphics))
						SetPalette(i, i & 16 ? 7 : 0, i & 32 ? 7 : 0, i & 8 ? 7 : 0);
					else
						SetPalette(i, bgred, bggreen, bgblue);
				}
			}
		}
		else
		{
			for (i=0; i<0x80; i++)
			{
				if (((i & 0x40) && displaytext) || (i & displayplane & displaygraphics))
					SetPalette(i, 7, 7, 7);
				else
					SetPalette(i, bgred, bggreen, bgblue);
			}
		}
	}
}

// ---------------------------------------------------------------------------
//	Out 52
// ---------------------------------------------------------------------------

void Screen::Out52(uint data)
{
	if (!(port32 & 0x20))
	{
		bgblue   = (data & 0x08) ? 7 : 0;
		bgred    = (data & 0x10) ? 7 : 0;
		bggreen  = (data & 0x20) ? 7 : 0;
		LOG1("bgpalette(d) = %3x\n", bggreen*256+bgred*16+bgblue);
		if (!color)
			reinitpalette = true;
	}
}

// ---------------------------------------------------------------------------
//	Out 53
// ---------------------------------------------------------------------------

void Screen::Out53(uint data)
{
	LOG1("port 53: %.2x\n", data);
	uint8 dpp = displayplane;
	bool dtp = displaytext;
	displayplane = ((~data >> 1) & 7);
	displaytext  = !(data & 1);
#ifndef RELEASEBUILD
//	displaytext  = true;
#endif
	if (dpp != displayplane || dtp != displaytext)
		reinitpalette = true;
}

// ---------------------------------------------------------------------------
//	Out 54
// ---------------------------------------------------------------------------

void Screen::Out54(uint data)
{ 
	if (port32 & 0x20)
	{
		if (data & 0x80)
		{
			// BG Color
			if (data & 0x40)
				bggreen = data & 7;
			else
				bgblue = data & 7, bgred = (data / 8) & 7;
			LOG1("bgpalette(a) = %3x\n", bggreen*256+bgred*16+bgblue);
		}
		else
		{
			if (data & 0x40)
				green[0] = data & 7;
			else
				blue[0] = data & 7, red[0] = (data/8) & 7;
			LOG1("palette(a) 0 = %3x\n", green[0]*256+red[0]*16+blue[0]);
		}
	}
	else
	{
		blue[0]  = (data & 1) ? 7 : 0;
		red[0]   = (data & 2) ? 7 : 0;
		green[0] = (data & 4) ? 7 : 0;
		LOG1("palette(d) 0 = %3x\n", green[0]*256+red[0]*16+blue[0]);
	}
	reinitpalette = true;
}

// ---------------------------------------------------------------------------
//	Out 55 - 5b
// ---------------------------------------------------------------------------

void Screen::Out55to5b(uint port, uint data)
{
	LOG2("port %2x: %.2x\t\t", port, data);
	uint num = port - 0x54;
	if (port32 & 0x20)
	{
		if (data & 0x40)
			green[num] = data & 7;
		else
			blue[num] = data & 7, red[num] = (data/8) & 7;
		LOG2("palette(a) %d = %3x\n", num, green[num]*256+red[num]*16+blue[num]);
	}
	else
	{
		blue[num]  = (data & 1) ? 7 : 0;
		red[num]   = (data & 2) ? 7 : 0;
		green[num] = (data & 4) ? 7 : 0;
		LOG2("palette(d) %d = %3x\n", num, green[num]*256+red[num]*16+blue[num]);
	}
	reinitpalette = true;
}

// ---------------------------------------------------------------------------
//	Memory R/W ŠÖ”‚Ì‘I‘ð
// ---------------------------------------------------------------------------

void Screen::SelectAccessFunc()
{
	static const Reader readfunc[4] = 
	{
		&Screen::ReadPlane0, &Screen::ReadPlane1, 
		&Screen::ReadPlane2, &Screen::Readdummy
	};
	static const Writer writefunc[4] = 
	{
		&Screen::WritePlane0, &Screen::WritePlane1, 
		&Screen::WritePlane2, &Screen::Writedummy
	};
	static const Writer aluwrite[4] = 
	{
		&Screen::WriteALUSet,	&Screen::WriteALURGB,
		&Screen::WriteALUB,		&Screen::WriteALUR,
	};

//	uint8 access;
	if (port32 & 0x40)
	{
		if (port35 & 0x80)
		{
			reader = &Screen::ReadALU;
			writer = aluwrite[ (port35 >> 4) & 3 ];
//			access = 'A';
		}
		else
		{
			reader = &Screen::Readdummy;
			writer = &Screen::Writedummy;
//			access = ' ';
		}
	}
	else
	{
		reader = readfunc[ selectedplane ];
		writer = writefunc[ selectedplane ];
//		access = selectedplane == 3 ? ' ' : selectedplane + '0';
	}

//	LOG3("r:%8d w:%8d (%c) ", nreads, nwrites, access);
//	nreads = nwrites = 0;
}

// ---------------------------------------------------------------------------
//	•ÏXˆÊ’u‚Ì‹L˜^
// ---------------------------------------------------------------------------

inline void Screen::SetDirtyFlag(uint addr)
{
	dirtyline[addr >> 4] = true;
}

// ---------------------------------------------------------------------------
//	GVRAM planer access
// ---------------------------------------------------------------------------

uint8 Screen::ReadPlane0(uint addr)
{
	return gvram[addr].byte[0];
}

uint8 Screen::ReadPlane1(uint addr)
{
	return gvram[addr].byte[1];
}

uint8 Screen::ReadPlane2(uint addr)
{
	return gvram[addr].byte[2];
}

void Screen::WritePlane0(uint addr, uint data)
{
	gvram[addr].byte[0] = data;
	SetDirtyFlag(addr);
}

void Screen::WritePlane1(uint addr, uint data)
{
	gvram[addr].byte[1] = data;
	SetDirtyFlag(addr);
}

void Screen::WritePlane2(uint addr, uint data)
{
	gvram[addr].byte[2] = data;
	SetDirtyFlag(addr);
}

// ---------------------------------------------------------------------------
//	GVRAM access via ALU
// ---------------------------------------------------------------------------

void Screen::WriteALUSet(uint addr, uint d)
{
	quadbyte q;
	q.pack = ExpandTable[d & 0xff];
	gvram[addr].pack = ((gvram[addr].pack & ~(q.pack & maskr.pack)) | (q.pack & masks.pack)) ^ (q.pack & maski.pack);
	SetDirtyFlag(addr);
}

void Screen::WriteALURGB(uint addr, uint d)
{
	gvram[addr] = alureg;
	SetDirtyFlag(addr);
}

void Screen::WriteALUR(uint addr, uint d)
{
	gvram[addr].byte[1] = alureg.byte[0];
	SetDirtyFlag(addr);
}

void Screen::WriteALUB(uint addr, uint d)
{
	gvram[addr].byte[0] = alureg.byte[1];
	SetDirtyFlag(addr);
}

uint8 Screen::ReadALU(uint addr)
{
	quadbyte q;
	alureg = gvram[addr];
	q.pack = alureg.pack ^ aluread.pack;
	return ~(q.byte[0] | q.byte[1] | q.byte[2]);
}

// ---------------------------------------------------------------------------
//	Memory R/W dummy
// ---------------------------------------------------------------------------

void Screen::Writedummy(uint, uint)
{

}

uint8 Screen::Readdummy(uint)
{
	return 0xff;
}

// ---------------------------------------------------------------------------
//	Table ì¬
// ---------------------------------------------------------------------------

packed Screen::ExpandTable[256] = { 1 };
packed Screen::ByteExpandTable[1 << sizeof(packed)];

void Screen::CreateTable()
{
	if (ExpandTable[0])
	{
		int i;
		for (i=0; i<0x100; i++)
			ExpandTable[i] = PACK(i);

		for (i=0; i<(1 << sizeof(packed)); i++)
		{
			packed p=0;
			for (int j=0; j<sizeof(packed); j++)
			{
#ifdef ENDIAN_IS_BIG
				p = (p << 8) | (((1 << (sizeof(packed)-j)) & i) ? 7 : 0);
#else
				p = (p << 8) | (((1 << j) & i) ? 7 : 0);
#endif
			}
			ByteExpandTable[i] = p;
		}
	}
}


// ---------------------------------------------------------------------------
//	‰æ–ʍXV
//	image	“WŠJæ bitmap (8bps)
//	bpl		bitmap ‚Ì• (640 ˆÈãA‘½•ª 4 bytes aligned ‚¶‚á‚È‚¢‚Æ‘Ê–Ú‚¾‚ÆŽv‚¤)
//	region	update ‚µ‚½—̈æ‚ð—^‚¦‚邽‚ß‚Ì pointer (int 2 ŒÂ•ª)
//			[0] ŠJŽn (400 ˆÈã‚̏ꍇ‚͍XV—̈æ‚È‚µ‚̈Ӗ¡)
//			[1] I—¹ (region[0] < region[1])
//
void Screen::UpdateScreen(uint8* image, int bpl, int* region)
{
	bool updateall = false;
	if (reinitpalette)
	{
		reinitpalette = false;
		updateall = true;
		InitPalette();
	}
	
	if (shouldredraw)
	{
		shouldredraw = false;
		updateall = true;
		Redraw(image, bpl);
	}

	if (line400)
		UpdateScreen400(image, bpl, region);
	else
		UpdateScreen200(image, bpl, region);

	if (updateall)
	{
		region[0] = 0;
		region[1] = 399;
	}
}

// ---------------------------------------------------------------------------
//	‰æ–ʍXV—pƒ}ƒNƒ
//	packed.‚Ì‘å‚«‚³‚Ɉˑ¶
// ---------------------------------------------------------------------------

#define WRITE0(s, d)	dest[d] = \
		(dest[d] & ~PACK(GVRAM_BIT)) \
		| (ByteExpandTable[src[s].byte[0] >> 4] & PACK(GVRAM0_SET)) \
		| (ByteExpandTable[src[s].byte[1] >> 4] & PACK(GVRAM1_SET)) \
		| (ByteExpandTable[src[s].byte[2] >> 4] & PACK(GVRAM2_SET))

#define WRITE1(s, d)	dest[d] = \
		(dest[d] & ~PACK(GVRAM_BIT)) \
		| (ByteExpandTable[src[s].byte[0] & 15] & PACK(GVRAM0_SET)) \
		| (ByteExpandTable[src[s].byte[1] & 15] & PACK(GVRAM1_SET)) \
		| (ByteExpandTable[src[s].byte[2] & 15] & PACK(GVRAM2_SET))

#define WRITE0F(s, d)	dest[d] = *((packed*)(((uint8*)(dest+d))+bpl)) = \
		(dest[d] & ~PACK(GVRAM_BIT)) \
		| (ByteExpandTable[src[s].byte[0] >> 4] & PACK(GVRAM0_SET)) \
		| (ByteExpandTable[src[s].byte[1] >> 4] & PACK(GVRAM1_SET)) \
		| (ByteExpandTable[src[s].byte[2] >> 4] & PACK(GVRAM2_SET))

#define WRITE1F(s, d)	dest[d] = *((packed*)(((uint8*)(dest+d))+bpl)) = \
		(dest[d] & ~PACK(GVRAM_BIT)) \
		| (ByteExpandTable[src[s].byte[0] & 15] & PACK(GVRAM0_SET)) \
		| (ByteExpandTable[src[s].byte[1] & 15] & PACK(GVRAM1_SET)) \
		| (ByteExpandTable[src[s].byte[2] & 15] & PACK(GVRAM2_SET))

#define WRITE400(d, s, b)	\
	(d)[0] = ((d)[0] & 0xf8f8f8f8) | ByteExpandTable[(s)->byte[b] >> 4]; \
	(d)[1] = ((d)[1] & 0xf8f8f8f8) | ByteExpandTable[(s)->byte[b] & 15];


// ---------------------------------------------------------------------------
//	‰æ–ʍXV (200 line mode)
// ---------------------------------------------------------------------------

void Screen::UpdateScreen200(uint8* image, int bpl, int* region)
{
	int y;
	for (y=0; y<1000; y+=sizeof(packed))						// Å‰‚ɍXV‚·‚郉ƒCƒ“‚ð’T‚·
	{
		if (*(packed*)(&dirtyline[y]))
			break;
	}
	if (y < 1000)
	{
		y /= 5;
		region[0] = y * 2;
		
		int lasty = -1;
		uint8* d = image + y * bpl * 2;
		volatile bool* dline = dirtyline + y * 5;

		if (!fullline)
		{
			quadbyte* src = gvram + y * 80;

			for (; y<200; y++, d += bpl*2)
			{
				packed* dest = (packed*) d;

				for (int x=0; x<5; x++, dline++, src += 16, dest += 32)
				{
					if (*dline)
					{
						*dline = 0;
						lasty = y;
						for (int j=0; j<16; j+=4)
						{
							WRITE0(j,   j*2  ); WRITE1(j,   j*2+1);
							WRITE0(j+1, j*2+2); WRITE1(j+1, j*2+3);
							WRITE0(j+2, j*2+4); WRITE1(j+2, j*2+5);
							WRITE0(j+3, j*2+6); WRITE1(j+3, j*2+7);
						}
					}
				}
			}
		}
		else		// ‚±‚Ì’†Š‡ŒÊ“à‚̓}ƒNƒˆÈŠO“¯‚¶
		{
			quadbyte* src = gvram + y * 80;

			for (; y<200; y++, d += bpl*2)
			{
				packed* dest = (packed*) d;

				for (int x=0; x<5; x++, dline++, src += 16, dest += 32)
				{
					if (*dline)
					{
						*dline = 0;
						lasty = y;
						for (int j=0; j<16; j+=4)
						{
							WRITE0F(j,   j*2  ); WRITE1F(j,   j*2+1);
							WRITE0F(j+1, j*2+2); WRITE1F(j+1, j*2+3);
							WRITE0F(j+2, j*2+4); WRITE1F(j+2, j*2+5);
							WRITE0F(j+3, j*2+6); WRITE1F(j+3, j*2+7);
						}
					}
				}
			}
		}
		region[1] = lasty * 2 + 1;
	}
	else
	{
		region[0] = 400;
		region[1] = -1;
	}
}

// ---------------------------------------------------------------------------
//	‰æ–ʍXV (400 line mode)
// ---------------------------------------------------------------------------

void Screen::UpdateScreen400(uint8* image, int bpl, int* region)
{
	int y;
	for (y=0; y<1000; y+=sizeof(packed))						// Å‰‚ɍXV‚·‚郉ƒCƒ“‚ð’T‚·
	{
		if (*(packed*)(&dirtyline[y]))
			break;
	}
	if (y < 1000)
	{
		y /= 5;

		region[0] = y;
		
		int lasty = -1000;
		
		volatile bool* dline = dirtyline + y * 5;
		uint8* d0 = image + y * bpl;
		uint8* d1 = d0 + 200 * bpl;
		quadbyte* src = gvram + y * 80;
		
		for (; y<200; y++, d0+=bpl, d1+=bpl)
		{
			packed* dest0 = (packed*) d0;
			packed* dest1 = (packed*) d1;

			for (int x=0; x<5; x++)
			{
				if (*dline)
				{
					*dline=0;
					lasty = y;
					for (int j=0; j<16; j++)
					{
						WRITE400(dest0+j*2, src+j, 0);
						WRITE400(dest1+j*2, src+j, 1);
					}
				}
				dline++;
				src += 16; dest0 += 32; dest1 += 32;
			}
		}
		region[1] = lasty + 200;
	}
	else
	{
		region[0] = 400;
		region[1] = -1;
	}
}

// ---------------------------------------------------------------------------
//	‰æ–ʍĕ`‰æ
// ---------------------------------------------------------------------------

void Screen::Redraw(uint8* image, int bpl)
{
	int i;

	if (!(fullline || line400))			// ƒOƒ‰ƒtƒBƒbƒNƒX‚̃‰ƒCƒ“ŠÔ‚ðÁ‹Ž
	{
		uint8* dest = image + bpl;

		for (i=1; i<400; i+=2, dest+=bpl*2)
		{
			packed* d = (packed*) dest;
			for (int j=0; j<640/8/sizeof(packed); j++)
			{
				d[0] &= ~PACK(GVRAM_BIT); d[1] &= ~PACK(GVRAM_BIT);
				d[2] &= ~PACK(GVRAM_BIT); d[3] &= ~PACK(GVRAM_BIT);
				d[4] &= ~PACK(GVRAM_BIT); d[5] &= ~PACK(GVRAM_BIT);
				d[6] &= ~PACK(GVRAM_BIT); d[7] &= ~PACK(GVRAM_BIT);
				d += 8;
			}
		}
	}

	for (i=0; i<1024; i++)
		dirtyline[i] = true;
}