/*
 *   mops.c
 *
 *   This file is part of Emu48
 *
 *   Copyright (C) 1995 Sebastien Carlier
 *
 */
#include "pch.h"
#include "Emu48.h"
#define SXCONFIG 1

// CRC calculation
static WORD crc_table[16] =
{
	0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387,
	0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F
};
static __inline VOID CRC(BYTE nib)
{
	Chipset.crc = (WORD)((Chipset.crc>>4)^crc_table[(Chipset.crc^nib)&0xf]);
}

LPBYTE RMap[256] = {NULL,};
LPBYTE WMap[256] = {NULL,};

static __inline UINT MIN(UINT a, UINT b)
{
	return (a<b)?a:b;
}

static __inline UINT MAX(UINT a, UINT b)
{
	return (a>b)?a:b;
}

static VOID MapP0(BYTE a, BYTE b)
{
	UINT i;
	DWORD p, m;
	
	a = (BYTE)MAX(a,Chipset.P0Base);
	b = (BYTE)MIN(b,Chipset.P0End);
	m = (Chipset.Port0Size*2048)-1;
	p = ((a-Chipset.P0Base)<<12)&m;
	for (i=a; i<=b; i++)
	{
		RMap[i]=Chipset.Port0 + p;
		WMap[i]=Chipset.Port0 + p;
		p = (p+0x1000)&m;
	}
	return;
}

static VOID MapBS(BYTE a, BYTE b)
{
	UINT i;
	DWORD p, m;
	
	a = (BYTE)MAX(a,Chipset.BSBase);
	b = (BYTE)MIN(b,Chipset.BSEnd);
	m = (dwRomSize-1)&0xFF000;
	p = a*0x1000;
	for (i=a;i<=b;i++)
	{
		RMap[i]=pbyRom + p;
		WMap[i]=NULL;
		p = (p+0x1000)&m;
	}
	return;
}

static VOID MapP1(BYTE a, BYTE b)
{
	UINT i;
	DWORD p, m;
	
	if (Chipset.Port1 == NULL) return;
	a = (BYTE)MAX(a,Chipset.P1Base);
	b = (BYTE)MIN(b,Chipset.P1End);
	m = (Chipset.Port1Size*2048)-1;
	p = ((a-Chipset.P1Base)<<12)&m;
	if (Chipset.Port1_Writeable)
	{
		for (i=a; i<=b; i++)
		{
			RMap[i]=Chipset.Port1 + p;
			WMap[i]=Chipset.Port1 + p;
			p = (p+0x1000)&m;
		}
	}
	else
	{
		for (i=a; i<=b; i++)
		{
			RMap[i]=Chipset.Port1 + p;
			WMap[i]=NULL;
			p = (p+0x1000)&m;
		}
	}
	return;
}

static VOID MapP2(BYTE a, BYTE b)
{
	UINT i;
	DWORD p, m;
	LPBYTE pbyTemp;
	
	if (pbyPort2 == NULL) return;
	pbyTemp = pbyPort2 + (nPort2Bank << 18);
	a = (BYTE)MAX(a,Chipset.P2Base);
	b = (BYTE)MIN(b,Chipset.P2End);
	m = (Chipset.Port2Size*2048)-1;
	p = ((a-Chipset.P2Base)<<12)&m;
	if (bPort2Writeable)
	{
		for (i=a; i<=b; i++)
		{
			RMap[i]=pbyTemp + p;
			WMap[i]=pbyTemp + p;
			p = (p+0x1000)&m;
		}
	}
	else
	{
		for (i=a; i<=b; i++)
		{
			RMap[i]=pbyTemp + p;
			WMap[i]=NULL;
			p = (p+0x1000)&m;
		}
	}
	return;
}

static VOID MapROM(BYTE a, BYTE b)
{
	UINT i;
	DWORD p, m;

	m = (dwRomSize-1)&0xFF000;
	p = a*0x1000;
	for (i=a;i<=b;i++)
	{
		RMap[i]=pbyRom + p;
		WMap[i]=NULL;
		p = (p+0x1000)&m;
	}
	return;
}

VOID Map(BYTE a, BYTE b)
{
	
#if SXCONFIG
	if (cCurrentRomType=='S')
	{
		MapROM(a,b);
		if (Chipset.BSCfig) MapBS(a,b);
		if (Chipset.P2Cfig) MapP2(a,b);
		if (Chipset.P1Cfig) MapP1(a,b);
		if (Chipset.P0Cfig) MapP0(a,b);
		return;
	}
#endif
	MapROM(a,b);
	if (Chipset.P2Cfig) MapP2(a,b);
	if (Chipset.P1Cfig) MapP1(a,b);
	if (Chipset.BSCfig) MapBS(a,b);
	if (Chipset.P0Cfig) MapP0(a,b);
	return;
}

////////////////////////////////////////////////////////////////////////////////
//
// Bus Commands
//
////////////////////////////////////////////////////////////////////////////////

VOID Config()
{
	DWORD d = Npack(Chipset.C,5);
	BYTE  b = (BYTE)(d>>12);
	BYTE  s = (BYTE)(b^0xFF);
	
#if SXCONFIG
	if (cCurrentRomType=='S')
	{
		if (!Chipset.IOCfig)
		{
			Chipset.IOCfig=TRUE;
			Chipset.IOBase=d&0xFFFC0;
			Map(b,b);
			return;
		}
		if (!Chipset.P0Cfg2)
		{
			Chipset.P0Cfg2=TRUE;
			Chipset.P0Size=s;
			return;
		}
		if (!Chipset.P0Cfig)
		{
			Chipset.P0Cfig = TRUE;
			Chipset.P0Base = (BYTE)b;
			Chipset.P0End  = (BYTE)(b+Chipset.P0Size);
			Map(Chipset.P0Base,Chipset.P0End);
			return;
		}
		if (!Chipset.P1Cfg2)
		{
			Chipset.P1Cfg2 = TRUE;
			Chipset.P1Size = (BYTE)s;
			return;
		}
		if (!Chipset.P1Cfig)
		{
			Chipset.P1Cfig = TRUE;
			Chipset.P1Base = (BYTE)b;
			Chipset.P1End  = (BYTE)(b+Chipset.P1Size);
			Map(Chipset.P1Base,Chipset.P1End);
			return;
		}
		if (!Chipset.P2Cfg2)
		{
			Chipset.P2Cfg2 = TRUE;
			Chipset.P2Size = (BYTE)s;
			return;
		}
		if (!Chipset.P2Cfig)
		{
			Chipset.P2Cfig = TRUE;
			Chipset.P2Base = (BYTE)b;
			Chipset.P2End  = (BYTE)(b+Chipset.P2Size);
			Map(Chipset.P2Base,Chipset.P2End);
			return;
		}
		if (!Chipset.BSCfg2)
		{
			Chipset.BSCfg2=TRUE;
			Chipset.BSSize=s;
			return;
		}
		if (!Chipset.BSCfig)
		{
			Chipset.BSCfig = TRUE;
			Chipset.BSBase = (BYTE)b;
			Chipset.BSEnd  = (BYTE)(b+Chipset.BSSize);
			Map(Chipset.BSBase,Chipset.BSEnd);
			return;
		}
		return;
	}
#endif
	if (!Chipset.IOCfig)
	{
		Chipset.IOCfig=TRUE;
		Chipset.IOBase=d&0xFFFC0;
		Map(b,b);
		return;
	}
	if (!Chipset.P0Cfg2)
	{
		Chipset.P0Cfg2=TRUE;
		Chipset.P0Size=s;
		return;
	}
	if (!Chipset.P0Cfig)
	{
		Chipset.P0Cfig = TRUE;
		Chipset.P0Base = (BYTE)b;
		Chipset.P0End  = (BYTE)(b+Chipset.P0Size);
		Map(Chipset.P0Base,Chipset.P0End);
		return;
	}
	if (!Chipset.BSCfg2)
	{
		Chipset.BSCfg2=TRUE;
		Chipset.BSSize=s;
		return;
	}
	if (!Chipset.BSCfig)
	{
		Chipset.BSCfig = TRUE;
		Chipset.BSBase = (BYTE)b;
		Chipset.BSEnd  = (BYTE)(b+Chipset.BSSize);
		Map(Chipset.BSBase,Chipset.BSEnd);
		return;
	}
	if (!Chipset.P1Cfg2)
	{
		Chipset.P1Cfg2 = TRUE;
		Chipset.P1Size = (BYTE)s;
		return;
	}
	if (!Chipset.P1Cfig)
	{
		Chipset.P1Cfig = TRUE;
		Chipset.P1Base = (BYTE)b;
		Chipset.P1End  = (BYTE)(b+Chipset.P1Size);
		Map(Chipset.P1Base,Chipset.P1End);
		return;
	}
	if (!Chipset.P2Cfg2)
	{
		Chipset.P2Cfg2 = TRUE;
		Chipset.P2Size = (BYTE)s;
		return;
	}
	if (!Chipset.P2Cfig)
	{
		Chipset.P2Cfig = TRUE;
		Chipset.P2Base = (BYTE)b;
		Chipset.P2End  = (BYTE)(b+Chipset.P2Size);
		Map(Chipset.P2Base,Chipset.P2End);
		return;
	}
	return;
}

VOID Uncnfg()
{
	DWORD d=Npack(Chipset.C,5);
	BYTE  b=(BYTE)(d>>12);
	
#if SXCONFIG
	if (cCurrentRomType=='S')
	{
		if ((Chipset.IOCfig)&&(d==Chipset.IOBase)) {Chipset.IOCfig=FALSE;Chipset.IOBase=0x100000;Map(b,b);return;}
		if ((Chipset.P0Cfig)&&(b==Chipset.P0Base)) {Chipset.P0Cfig=FALSE;Chipset.P0Cfg2=FALSE;Map(Chipset.P0Base,Chipset.P0End);return;}
		if ((Chipset.P1Cfig)&&(b==Chipset.P1Base)) {Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Map(Chipset.P1Base,Chipset.P1End);return;}
		if ((Chipset.P2Cfig)&&(b==Chipset.P2Base)) {Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Map(Chipset.P2Base,Chipset.P2End);return;}
		if ((Chipset.BSCfig)&&(b==Chipset.BSBase)) {Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Map(Chipset.BSBase,Chipset.BSEnd);return;}
		return;
	}
#endif
	if ((Chipset.IOCfig)&&(d==Chipset.IOBase)) {Chipset.IOCfig=FALSE;Chipset.IOBase=0x100000;Map(b,b);return;}
	if ((Chipset.P0Cfig)&&(b==Chipset.P0Base)) {Chipset.P0Cfig=FALSE;Chipset.P0Cfg2=FALSE;Map(Chipset.P0Base,Chipset.P0End);return;}
	if ((Chipset.BSCfig)&&(b==Chipset.BSBase)) {Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Map(Chipset.BSBase,Chipset.BSEnd);return;}
	if ((Chipset.P1Cfig)&&(b==Chipset.P1Base)) {Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Map(Chipset.P1Base,Chipset.P1End);return;}
	if ((Chipset.P2Cfig)&&(b==Chipset.P2Base)) {Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Map(Chipset.P2Base,Chipset.P2End);return;}
	return;
}

VOID Reset()
{
	
	Chipset.IOCfig=FALSE;Chipset.IOBase=0x100000;
	Chipset.P0Cfig=FALSE;Chipset.P0Cfg2=FALSE;Chipset.P0Base=0;Chipset.P0Size=0;Chipset.P0End=0;
	Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Chipset.BSBase=0;Chipset.BSSize=0;Chipset.BSEnd=0;
	Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Chipset.P1Base=0;Chipset.P1Size=0;Chipset.P1End=0;
	Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Chipset.P2Base=0;Chipset.P2Size=0;Chipset.P2End=0;
	Map(0x00,0xFF);
	return;
}

VOID C_Eq_Id()
{
#if SXCONFIG
	if (cCurrentRomType=='S')
	{
		if (!Chipset.IOCfig) {Nunpack(Chipset.C,(Chipset.IOBase*0x1000)^0x00019,5);return;}
		if (!Chipset.P0Cfg2) {Nunpack(Chipset.C,(Chipset.P0Size*0x1000)^0xFF003,5);return;}
		if (!Chipset.P0Cfig) {Nunpack(Chipset.C,(Chipset.P0Base*0x1000)^0x000F4,5);return;}
		if (!Chipset.P1Cfg2) {Nunpack(Chipset.C,(Chipset.P1Size*0x1000)^0xFF005,5);return;}
		if (!Chipset.P1Cfig) {Nunpack(Chipset.C,(Chipset.P1Base*0x1000)^0x000F6,5);return;}
		if (!Chipset.P2Cfg2) {Nunpack(Chipset.C,(Chipset.P2Size*0x1000)^0xFF007,5);return;}
		if (!Chipset.P2Cfig) {Nunpack(Chipset.C,(Chipset.P2Base*0x1000)^0x000F8,5);return;}
		if (!Chipset.BSCfg2) {Nunpack(Chipset.C,(Chipset.BSSize*0x1000)^0xFF001,5);return;}
		if (!Chipset.BSCfig) {Nunpack(Chipset.C,(Chipset.BSBase*0x1000)^0x000F2,5);return;}
		memset(Chipset.C,0,5);
		return;
	}
#endif
	if (!Chipset.IOCfig) {Nunpack(Chipset.C,(Chipset.IOBase*0x1000)^0x00019,5);return;}
	if (!Chipset.P0Cfg2) {Nunpack(Chipset.C,(Chipset.P0Size*0x1000)^0xFF003,5);return;}
	if (!Chipset.P0Cfig) {Nunpack(Chipset.C,(Chipset.P0Base*0x1000)^0x000F4,5);return;}
	if (!Chipset.BSCfg2) {Nunpack(Chipset.C,(Chipset.BSSize*0x1000)^0xFF005,5);return;}
	if (!Chipset.BSCfig) {Nunpack(Chipset.C,(Chipset.BSBase*0x1000)^0x000F6,5);return;}
	if (!Chipset.P1Cfg2) {Nunpack(Chipset.C,(Chipset.P1Size*0x1000)^0xFF007,5);return;}
	if (!Chipset.P1Cfig) {Nunpack(Chipset.C,(Chipset.P1Base*0x1000)^0x000F8,5);return;}
	if (!Chipset.P2Cfg2) {Nunpack(Chipset.C,(Chipset.P2Size*0x1000)^0xFF001,5);return;}
	if (!Chipset.P2Cfig) {Nunpack(Chipset.C,(Chipset.P2Base*0x1000)^0x000F2,5);return;}
	memset(Chipset.C,0,5);
	return;
}

VOID Npeek(BYTE *a, DWORD d, UINT s)
{
	DWORD u, v;
	UINT  c;
	BYTE *p;
	
	do
	{
		u = d>>12;
		v = d&0xFFF;
		c = MIN(s,0x1000-v);
		if ((p=RMap[u]) != NULL) memcpy(a, p+v, c);
		if (s-=c) {a+=c; d+=c;}
	} while (s);
	return;
}

VOID Nread(BYTE *a, DWORD d, UINT s)
{
	DWORD u, v;
	UINT  c;
	BYTE *p;
	
	do
	{
		if ((d&0xFFFC0)==Chipset.IOBase)
		{
			v = d&0x3F;
			c = MIN(s,0x40-v);
			ReadIO(a,v,c);
		}
		else
		{
			u = d>>12;
			v = d&0xFFF;
			c = MIN(s,0x1000-v);
			if ((p=RMap[u]) != NULL)
			{
				memcpy(a, p+v, c);
				for (u=0; u<c; u++)
					CRC(a[u]);
			}
		}
		if (s-=c) {a+=c; d+=c;}
	} while (s);
	return;
}

VOID Nwrite(BYTE *a, DWORD d, UINT s)
{
	DWORD u, v;
	UINT  c;
	BYTE *p;
	
	if ((d<Chipset.end1)&&(d>=Chipset.start12))
		WriteToMainDisplay(a,d,s);
	if ((d<Chipset.end2)&&(d>=Chipset.start2))
		WriteToMenuDisplay(a,d,s);
	do
	{
		if ((d&0xFFFC0)==Chipset.IOBase)
		{
			v = d&0x3F;
			c = MIN(s,0x40-v);
			WriteIO(a, v, c);
		}
		else
		{
			u = d>>12;
			v = d&0xFFF;
			c = MIN(s,0x1000-v);
			if ((p=WMap[u]) != NULL) memcpy(p+v, a, c);
		}
		a+=c;
		d+=c;
	} while (s-=c);
	return;
}

VOID Nread2(BYTE *a, DWORD d)
{
	DWORD u, v;
	BYTE *p;
	
	if ((d&0xFFFC0)==Chipset.IOBase)
	{
		u = d&0x3F;
		if (u>0x3E) {Nread(a,d,2);return;}
		ReadIO(a,u,2);
		return;
	}
	u = d>>12;
	v = d&0xFFF;
	if (u==Chipset.BSBase) // bank switching
	{
		nPort2Bank = (v&dwPort2Mask)>>1;
		Map(Chipset.P2Base,Chipset.P2End);
		a[0] = 3;
		a[1] = 3;
		return;
	}
	if (v>0xFFE) {Nread(a,d,2);return;}
	if ((p=RMap[u]) != NULL)
	{
		*((WORD*)a) = *((WORD*)(p+v));
		CRC(a[0]);
		CRC(a[1]);
	}
	return;
}

VOID Nwrite2(BYTE *a, DWORD d)
{
	DWORD u, v;
	BYTE *p;
	
	if ((d&0xFFFC0)==Chipset.IOBase) {Nwrite(a,d,2);return;}
	u = d>>12;
	v = d&0xFFF;
	if (v>0xFFE) {Nwrite(a,d,2);return;}
	if ((d<Chipset.end1)&&(d>=Chipset.start12))
		WriteToMainDisplay(a,d,2);
	if ((d<Chipset.end2)&&(d>=Chipset.start2))
		WriteToMenuDisplay(a,d,2);
	if ((p=WMap[u]) != NULL)
	{
		*((WORD*)(p+v)) = *((WORD*)a);
	}		
	return;
}

VOID Nread5(BYTE *a, DWORD d)
{
	DWORD u, v;
	BYTE *p;
	
	if ((d&0xFFFC0)==Chipset.IOBase)
	{
		u = d&0x3F;
		if (u>0x3A) {Nread(a,d,5);return;}
		ReadIO(a,u,5);
		return;
	}
	v = d&0xFFF;
	if (v>0xFFA) {Nread(a,d,5);return;}
	u = d>>12;
	if ((p=RMap[u]) != NULL)
	{
		*((DWORD*)a) = *((DWORD*)(p+v));
		a[4] = p[v+4];
		CRC(a[0]);
		CRC(a[1]);
		CRC(a[2]);
		CRC(a[3]);
		CRC(a[4]);
	}
	return;
}

VOID Nwrite5(BYTE *a, DWORD d)
{
	DWORD u, v;
	BYTE *p;
	
	if ((d&0xFFFC0)==Chipset.IOBase) {Nwrite(a,d,5);return;}
	v = d&0xFFF;
	if (v>0xFFA) {Nwrite(a,d,5);return;}
	if ((d<Chipset.end1)&&(d>=Chipset.start12)) WriteToMainDisplay(a,d,5);
	if ((d<Chipset.end2)&&(d>=Chipset.start2)) WriteToMenuDisplay(a,d,5);
	u = d>>12;
	if ((p=WMap[u]) != NULL)
	{
		*((DWORD*)(p+v)) = *((DWORD*)a);
		p[v+4] = a[4];
	}		
}

DWORD Read5(DWORD d)
{
	BYTE p[8];
	
	Npeek(p,d,5);
	return Npack(p,5);
}

BYTE Read2(DWORD d)
{
	BYTE p[2];
	
	Npeek(p,d,2);
	return (BYTE)(p[0]|(p[1]<<4));
}

VOID Write5(DWORD d, DWORD n)
{
	BYTE p[8];
	
	Nunpack(p,n,5);
	Nwrite(p,d,5);
	return;
}

VOID ReadIO(BYTE *a, DWORD b, DWORD s)
{
	DWORD d = b;

	do
	{
		switch (d)
		{
		case 0x00: *a = (Chipset.dispon<<3)|Chipset.boffset; break;
		case 0x01: *a = Chipset.contrast&0xF; break;
		case 0x02: *a = Chipset.contrast>>4; break;
		case 0x03: *a = 0;
		case 0x04: *a = (Chipset.crc    )&0xF; break;
		case 0x05: *a = (Chipset.crc>> 4)&0xF; break;
		case 0x06: *a = (Chipset.crc>> 8)&0xF; break;
		case 0x07: *a = (Chipset.crc>>12)&0xF; break;
		case 0x08: *a = 0; break;
//		case 0x09: *a = Chipset.IORam[0x09]; break;
		case 0x0A: *a = 0; break;
//		case 0x0B: *a = Chipset.IORam[0x0B]; break;
//		case 0x0C: *a = Chipset.IORam[0x0C]; break;
//		case 0x0D: *a = Chipset.IORam[0x0D]; break;
//		case 0x0E: *a = Chipset.IORam[0x0E]; break;
		case 0x0F: *a = Chipset.cards_status; break;
//		case 0x10: *a = Chipset.IORam[0x10]; break;
//		case 0x11: *a = Chipset.IORam[0x11]; break;
		case 0x12: *a = 0; break;
		case 0x13: *a = 0; break;
		case 0x14: *a = 0; Chipset.IORam[0x11]&=0xE; break;
		case 0x15: *a = 0; Chipset.IORam[0x11]&=0xE; break;
		case 0x16: *a = 0; break;
		case 0x17: *a = 0; break;
//		case 0x18: *a = Chipset.IORam[0x18]; break;
//		case 0x19: *a = Chipset.IORam[0x19]; break;
		case 0x1A: *a = 0; break;
		case 0x1B: *a = 0; break;
		case 0x1C: *a = 0; break;
		case 0x1D: *a = 0; break;
//		case 0x1E: *a = Chipset.IORam[0x1E]; break;
//		case 0x1F: *a = Chipset.IORam[0x1F]; break;
		case 0x20: *a = 3; break;
		case 0x21: *a = 3; break;
		case 0x22: *a = 3; break;
		case 0x23: *a = 3; break;
		case 0x24: *a = 3; break;
		case 0x25: *a = 3; break;
		case 0x26: *a = 3; break;
		case 0x27: *a = 3; break;
		case 0x28:
			if (Chipset.sync)
				Chipset.sync--;
			else
				Chipset.sync=Chipset.lcounter+1;
			*a = Chipset.sync&0xF;
			break;
		case 0x29:
			if (d==b)
			{
				if (Chipset.sync)
					Chipset.sync--;
				else
					Chipset.sync=Chipset.lcounter+1;
			}
			*a = Chipset.sync&0xF;
			break;
		case 0x2A: *a = 0; break;
		case 0x2B: *a = 0; break;
		case 0x2C: *a = 0; break;
		case 0x2D: *a = 0; break;
//		case 0x2E: *a = Chipset.IORam[0x2E]; break;
//		case 0x2F: *a = Chipset.IORam[0x2F]; break;
		case 0x30: *a = 3; break;
		case 0x31: *a = 3; break;
		case 0x32: *a = 3; break;
		case 0x33: *a = 3; break;
		case 0x34: *a = 3; break;
		case 0x35: *a = 0; break;
		case 0x36: *a = 0; break;
		case 0x37: *a = ReadT1(); break;
		case 0x38: Nunpack(a, ReadT2()    , s); return;
		case 0x39: Nunpack(a, ReadT2()>> 4, s); return;
		case 0x3A: Nunpack(a, ReadT2()>> 8, s); return;
		case 0x3B: Nunpack(a, ReadT2()>>12, s); return;
		case 0x3C: Nunpack(a, ReadT2()>>16, s); return;
		case 0x3D: Nunpack(a, ReadT2()>>20, s); return;
		case 0x3E: Nunpack(a, ReadT2()>>24, s); return;
		case 0x3F: Nunpack(a, ReadT2()>>28, s); return;
		default: *a = Chipset.IORam[d];
		}
		d++; a++;
	} while (--s);
	return;
}

VOID WriteIO(BYTE *a, DWORD b, DWORD s)
{
	DWORD d=b;
	BYTE  c;
	BYTE disp=0;

	if (d>=0x38)
	{
		Nunpack(Chipset.IORam+0x38, ReadT2(), 8);
		memcpy(Chipset.IORam+d,a,s);
		SetT2(Npack(Chipset.IORam+0x38,8));
		return;
	}
	do
	{
		c = *a;
		switch (d)
		{
// 00100 =  NS:DISPIO
// 00100 @  Display bit offset and DON [DON OFF2 OFF1 OFF0]
// 00100 @  3 nibs for display offset (scrolling), DON=Display ON
		case 0x00:
			if ((c^Chipset.IORam[d])&8)
			{
				Chipset.dispon  = c>>3;
				disp|=7;
			}
			if ((c^Chipset.IORam[d])&7)
			{
				Chipset.boffset = c&7;
				disp|=3;
			}
			Chipset.IORam[d] = c;
			break;

// 00101 =  NS:CONTRLSB
// 00101 @  Contrast Control [CON3 CON2 CON1 CON0]
// 00101 @  Higher value = darker screen
		case 0x01:
			if (c!=Chipset.IORam[d])
			{
				Chipset.IORam[d]=c;
				Chipset.contrast = (Chipset.contrast&0x10)|c;
				UpdateContrast(Chipset.contrast);
				disp|=7;
			}
			break;

// 00102 =  NS:DISPTEST
// 00102 @  Display test [VDIG LID TRIM CON4] [LRT LRTD LRTC BIN]
// 00102 @  Normally zeros
		case 0x02:
			if (c!=Chipset.IORam[d])
			{
				Chipset.IORam[d]=c;
				Chipset.contrast = (Chipset.contrast&0x0f)|((c&1)<<4);
				UpdateContrast(Chipset.contrast);
				disp|=7;
			}
			break;
			
		case 0x03: Chipset.IORam[d]=c; break;

// 00104 =  HP:CRC
// 00104 @  16 bit hardware CRC (104-107) (X^16+X^12+X^5+1)
// 00104 @  crc = ( crc >> 4 ) ^ ( ( ( crc ^ nib ) & 0x000F ) * 0x1081 );
		case 0x04: Chipset.crc = (Chipset.crc&0xfff0)|(c*0x0001); break;
		case 0x05: Chipset.crc = (Chipset.crc&0xff0f)|(c*0x0010); break;
		case 0x06: Chipset.crc = (Chipset.crc&0xf0ff)|(c*0x0100); break;
		case 0x07: Chipset.crc = (Chipset.crc&0x0fff)|(c*0x1000); break;

// 00108 =  NS:POWERSTATUS
// 00108 @  Low power registers (108-109)
// 00108 @  [LB2 LB1 LB0 VLBI] (read only)       
// 00108 @  LowBat(2) LowBat(1) LowBat(S) VeryLowBat
		case 0x08: break; // read-only

// 00109 =  NS:POWERCTRL
// 00109 @  [ELBI EVLBI GRST RST] (read/write)
		case 0x09: Chipset.IORam[d]=c; break;

// 0010A =  NS:MODE
// 0010A @  Mode Register (read-only)
		case 0x0A: break; // read-only

// 0010B =  HP:ANNCTRL
// 0010B @  Annunciator control [LA4 LA3 LA2 LA1] = [ alarm alpha -> <- ]
		case 0x0B:
		case 0x0C:
		if (c!=Chipset.IORam[d])
		{
			Chipset.IORam[d] = c;
			disp|=8;
		}
		break;

// 0010D =  NS:BAU
// 0010D @  Serial baud rate [UCK BD2 BD1 BD0] (bit 3 is read-only)
// 0010D @  3 bits = {1200 1920 2400 3840 4800 7680 9600 15360}
		case 0x0D: Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); break; // bit 3 is read-only

// 0010E =  NS:CARDCTRL
// 0010E @  [ECDT RCDT SMP SWINT] (read/write)
// 0010E @  Enable Card Det., Run Card Det., Set Module Pulled, Software interrupt
		case 0x0E:
		Chipset.IORam[d]=c;
#if 1
		if ( (RMap[4]!=(pbyRom+0x4000)) && (12 != c) )
		{
			c |= 0x10;
		}
#endif
		if (c&1)
		{
			Chipset.SoftInt = TRUE;
			bInterrupt = TRUE;
		}
		if (c&2)
		{
			Chipset.HST |= 8; // MP
//			Chipset.SoftInt = TRUE;
//			bInterrupt = TRUE;
		}
		break;

// 0010F =  NS:CARDSTATUS
// 0010F @  [P2W P1W P2C P1C] (read-only) Port 2 writable .. Port 1 inserted
		case 0x0F: break; // read-only

// 00110 =  HP:IOC
// 00110 @  Serial I/O Control [SON ETBE ERBF ERBZ]
// 00110 @  Serial On, Interrupt On Recv.Buf.Empty, Full, Buzy
		case 0x10: Chipset.IORam[d]=c; break;

// 00111 =  HP:RCS
// 00111  Serial Receive Control/Status [RX RER RBZ RBF] (bit 3 is read-only)
		case 0x11: Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); break;

// 00112 =  HP:TCS
// 00112 @  Serial Transmit Control/Status [BRK LPB TBZ TBF]
		case 0x12: Chipset.IORam[d]=c; break;

// 00113 =  HP:CRER
// 00113 @  Serial Clear RER (writing anything clears RER bit)
		case 0x13: Chipset.IORam[0x11]&=0xB; Chipset.IORam[0x11]&=0xB;

// 00114 =  HP:RBR
// 00114 @  Serial Receive Buffer Register (Reading clears RBF bit)
// 00114 @  [RX RER RBZ RBF]
		case 0x14: break; // probably read-only
		case 0x15: break; // probably read-only

// 00116 =  HP:TBR
// 00116 @  Serial Transmit Buffer Register (Writing sets TBF bit)
		case 0x16: Chipset.IORam[d]=c; Chipset.IORam[0x12]|=1; break;
		case 0x17: Chipset.IORam[d]=c; Chipset.IORam[0x12]|=1; break;

// 00118 =  NS:SRR
// 00118 @  Service Request Register (read-only)
// 00118 @  [ISRQ TSRQ USRQ VSRQ] [KDN NINT2 NINT LSRQ]
		case 0x18: break; // read-only
		case 0x19: break; // read-only

// 0011A =  HP:IRC
// 0011A @  IR Control Register [IRI EIRU EIRI IRE] (bit 3 is read-only)
// 0011A @  IR Input, Enable IR UART mode, Enable IR Interrupt, IR Event
		case 0x1A: Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); break;

// 0011B =  NS:BASENIBOFF
// 0011B @  Used as addressto get BASENIB from 11F to the 5th nibble
		case 0x1B: break;

// 0011C =  NS:LCR
// 0011C @  Led Control Register [LED ELBE LBZ LBF] (Setting LED is draining)
		case 0x1C: Chipset.IORam[d]=c; break;

// 0011D =  NS:LBR
// 0011D @  Led Buffer Register [0 0 0 LBO] (bits 1-3 read zero)
		case 0x1D: Chipset.IORam[d]=c&1; break;

// 0011E =  NS:SCRATCHPAD
// 0011E @  Scratch pad
		case 0x1E: Chipset.IORam[d]=c; break;

// 0011F =  NS:BASENIB
// 0011F @  7 or F for base memory
		case 0x1F: Chipset.IORam[d]=c; break;

// 00120 =  NS:DISPADDR
// 00120 @  Display Start Address (write only)
// 00120 @  bit 0 is ignored (display must start on byte boundary)
		case 0x20:
			if (s>=5)
			{
				b = Npack(a,5)&0xFFFFE;
				if (b != Chipset.start1)
				{
					Chipset.start1 = b;
					disp|=3;
				}
				b = d;
			}
			Chipset.IORam[d]=c;
			break;
//     case 0x21: Chipset.IORam[d]=c; break;
//     case 0x22: Chipset.IORam[d]=c; break;
//     case 0x23: Chipset.IORam[d]=c; break;
//     case 0x24: Chipset.IORam[d]=c; break;

// 00125 =  NS:LINEOFFS
// 00125 @  Display Line offset (write only) (no of bytes skipped after each line)
// 00125 @  MSG sign extended
		case 0x25:
			if (s>=3)
			{
				signed short lo = (signed short)Npack(a, 3);
				if (lo&0x800) lo-=0x1000;
				if (lo==Chipset.loffset) break;
				Chipset.loffset = lo;
				disp|=3;
			}
			break;

// 00128 =  NS:LINECOUNT
// 00128 @  Display Line Counter and miscellaneous (28-29)
// 00128 @  [LC3 LC2 LC1 LC0] [DA19 M32 LC5 LC4]
// 00128 @  Line counter 6 bits -> max = 2^6-1 = 63 = disp height
// 00128 @  Normally has 55 -> Menu starts at display row 56
		case 0x28: Chipset.IORam[d]=c; break;
		case 0x29:
			Chipset.IORam[d]=c;
			d = Npack(Chipset.IORam+0x28,2)&0x3F;
			if (Chipset.lcounter != d)
			{
				Chipset.lcounter = d;
				disp|=7;
			}
			break;

		case 0x2A: break;
		case 0x2B: break;
		case 0x2C: break;
		case 0x2D: break;

// 0012E =  NS:TIMER1CTRL
// 0012E @  TIMER1 Control [SRQ WKE INT XTRA]
		case 0x2E: Chipset.IORam[d]=c&0xE; break;

// 0012F =  NS:TIMER2CTRL
// 0012F @  TIMER2 Control [SRQ WKE INT RUN]
		case 0x2F:
//			if ((Chipset.IORam[d]^c)&1)
//			// RUN changed
			Chipset.IORam[d]=c;
			if (c&1)
				StartTimers();
			else
				StopTimers();
			break;

// 00130 =  NS:MENUADDR
// 00130 @  Display Secondary Start Address (write only) (30-34)
// 00130 @  Menu Display Address, no line offsets
		case 0x30:
			if (s>=5)
			{
				b = Npack(a,5)&0xFFFFE;
				if (b != Chipset.start2)
				{
					Chipset.start2 = b;
					disp|=5;
				}
				b = d;
			}
			Chipset.IORam[d]=c;
			break;
//		case 0x31: Chipset.IORam[d]=c; break;
//		case 0x32: Chipset.IORam[d]=c; break;
//		case 0x33: Chipset.IORam[d]=c; break;
//		case 0x34: Chipset.IORam[d]=c; break;

		case 0x35: break;
		case 0x36: break;

// 00137 =  HP:TIMER1
// 00137 @  Decremented 16 times/s
		case 0x37:
			SetT1(Chipset.t1);
			break;

// 00138 =  HP:TIMER2
// 00138 @  hardware timer (38-3F), decremented 8192 times/s
		// nothing - fall through to default

		default: Chipset.IORam[d]=c;
		}
		a++; d++;
	} while (--s);
	if (disp&1) UpdateDisplayPointers();
	if (disp&2) UpdateMainDisplay();
	if (disp&4) UpdateMenuDisplay();
	if (disp&8) UpdateAnnunciators();
	return;
}
