// MemMapGenerator
//
// This is a little prog that will generate the data
// to go into an EEPROM, forming an address decoder
// for my DIY6502.
//
// The EEPROM will ALWAYS be active and outputting - its
// job isn't to be addressable, it's doing the addressing!
// 
// The EEPROM will use the address lines to select which
// (if any) device should be selected. The devices it can
// select will be wired to pins DEVICE0 - DEVICE7.
//
// I probably won't bother using this EEPROM to select the
// RAM, as I've already hard-wired the RAM to be selected
// when A15 is low. But this EEPROM does need to be aware
// of when A15 is low, so it knows not to select anything
// else.
//


// details about the EEPROM we're populating ...
static const int kEEPROMAddressLines = 13; // 8KB EEPROM has lines A0 to A12 inclusive
static const int kComputerAddressLinesToIgnore = 16 - kEEPROMAddressLines; // for an 8K EEPROM we want to ignore A0, A1 and A2

static const char kFilenameLower[] = { "eeprom0-7.rom" };
static const char kFilenameUpper[] = { "eeprom8-15.rom" };

// Constants to describe which digital output from the EEPROM selects which device

static const int kDevice_None = 0;
static const int kDevice00 = 1 << 0;
static const int kDevice01 = 1 << 1;
static const int kDevice02 = 1 << 2;
static const int kDevice03 = 1 << 3;
static const int kDevice04 = 1 << 4;
static const int kDevice05 = 1 << 5;
static const int kDevice06 = 1 << 6;
static const int kDevice07 = 1 << 7;
// Eight of these if using one EEPROM, or 16 if using two.


// Now we provide a list of ranges, and what device they should select.
// As soon as a line is matched, then the search ends. A device can be referenced multiple times.
// Devices can share ranges (though I can't think of a reason why).
//
// Just for reference, he's how the Beeb does it:
//      0x0000 - 0x7FFF is the RAM
//      0x8000 - 0xBFFF is the sideways ROM
//      0xC000 - 0xFFFF is the MOS ROM, apart from these exceptions:
//      0xFC00 - 0xFCFF is FRED
//      0xFD00 - 0xFDFF is JIM
//      0xFE00 - 0xFEFF is SHIELA
//
// And from JGH's compilation of peripherals:
//     0xFC80 - 0xFC87 - LCD Display Control
//     0xFE00 - 0xFE07 - 6845 CTRC Video controller
//     0xFE08 - 0xFE0F - 6850 ACIA Serial controller
//     0xFE10 - 0xFE17 - Serial ULA
//     0xFE40 - 0xFE5F - System VIA
//     0xFE60 - 0xFE7F - User VIA

// My current memory map:
//   0000 to 7FFF - RAM
//   8000 to FFFF - device 0 - the ROM, except for:
//   FE80 to FE8F - device 1 - the Hitachi LCD screen
//   FE90 to FE9F - device 2
//   FEA0 to FEAF - device 3 - currently the 6551 ACIA serial port
//   FEB0 to FEBF - device 4 - currently 6522 "userport"
//   FEC0 to FECF - device 5
//   FED0 to FEDF - device 6
//   FEE0 to FEEF - device 7

struct RangeEntry
{
	int iStart;
	int iEnd;
	int iDeviceToSelect; // bit pattern of devices that should be selected (normally you'd only do one!)
};

static const RangeEntry g_kDeviceRange[] =
{
	{0xFE80, 0xFE8F, kDevice01},
	{0xFE90, 0xFE9F, kDevice02},
	{0xFEA0, 0xFEAF, kDevice03},
	{0xFEB0, 0xFEBF, kDevice04},
	{0xFEC0, 0xFECF, kDevice05},
	{0xFED0, 0xFEDF, kDevice06},
	{0xFEE0, 0xFEEF, kDevice07},
	// page FF is needed for OS hookups, so I'm placing this lot just before.
	// Need to be careful about where I put Oddmon.
	{0x8000, 0xFFFF, kDevice00}, // anywhere NOT covered by one of the above must go to the EEPROM!
	{0x0000, 0x7FFF, kDevice_None}, // RAM here!
	{0x0000, 0x0000, -1}, // end of list
};

// If a device is active-low, then we toggle those bits so it's dealt with by the EEPROM,
// rather than having to add extra physical NOT gates.
static const int kActiveLowDevices = kDevice00 | kDevice02 | kDevice03 | kDevice04 | kDevice05 | kDevice06 | kDevice07;
// (most things are active-low ... but the LCD isn't.)

int ConvertComputerAddressToEEPROM(int iComputerAddress)
{
	return  iComputerAddress >> kComputerAddressLinesToIgnore;
}

int ConvertEEPROMAddressToComputer(int iEEPROMAddress)
{
	return (iEEPROMAddress << kComputerAddressLinesToIgnore);
}

int FindDevicesForThisAddress(int iComputerAddr)
{
	for (int i = 0; g_kDeviceRange[i].iDeviceToSelect != -1; ++i)
	{
		if (g_kDeviceRange[i].iEnd < iComputerAddr) continue;
		if (g_kDeviceRange[i].iStart > iComputerAddr) continue;
		return g_kDeviceRange[i].iDeviceToSelect;
	}

	return 0; // which is BAD, because that means that for the passed address, NO devices were selected!
}


int main()
{
	const int kEEPROMSizeInBytes = 1 << kEEPROMAddressLines;

	int iLastDevices = 0xFFFFFF; // invalid

	FILE* lowerh = fopen(kFilenameLower, "wb");
	FILE* upperh = fopen(kFilenameUpper, "wb");

	for (int iEEPROMAddr = 0; iEEPROMAddr < kEEPROMSizeInBytes; ++iEEPROMAddr)
	{
		int iComputerAddr = ConvertEEPROMAddressToComputer(iEEPROMAddr);

		int iDevices = FindDevicesForThisAddress(iComputerAddr);

		if (iDevices != iLastDevices)
		{
			printf("+0x%04x : 0x%04x :", iEEPROMAddr, iComputerAddr);
			if (iDevices == 0) printf(" None!\n");
			else
			{
				if (iDevices & kDevice00) printf(" Device00");
				if (iDevices & kDevice01) printf(" Device01");
				if (iDevices & kDevice02) printf(" Device02");
				if (iDevices & kDevice03) printf(" Device03");
				if (iDevices & kDevice04) printf(" Device04");
				if (iDevices & kDevice05) printf(" Device05");
				if (iDevices & kDevice06) printf(" Device06");
				if (iDevices & kDevice07) printf(" Device07");
				if (iDevices == 0)        printf(" None");
				printf("\n");
			}

			iLastDevices = iDevices;
		}

		iDevices ^= kActiveLowDevices; // invert any outputs that are active-low
		
		if (lowerh)
		{
			// write lowest eight bits to the low file ...
			fputc((char)iDevices & 0xFF, lowerh);
		}
		
		if (upperh)
		{
			// write upper eight bits to the high file ...
			fputc((char)(iDevices & 0xFF00) >> 8, upperh);
		}
	}

	if (lowerh)
	{
		fclose(lowerh);
	}

	if (upperh)
	{
		fclose(upperh);
	}

    return 0;
}
