和製MESSでMSX1 その7

オリジナリティありません。fMSXを参考にメモリマップを実装しました。色々なカートリッジが動くようになりました。

f:id:tanam:20130709233658p:image:w360

f:id:tanam:20130709233657p:image:w360

memory.cpp

/*
	ASCII MSX1 Emulator 'yaMSX1'
	Skelton for retropc emulator

	Author : tanam
	Date   : 2013.06.29-

	[ memory ]
*/

#include "memory.h"
#include "../../fileio.h"

void MEMORY::initialize()
{
	int I,J,K;
	byte *P;

	ROMData[0]=ROMData[1]=NULL;
	CCount   = 0;
	RAMPages=4;	/* Number of RAM pages    */
	RAMMask=3;

	/* Initialize ROMMasks to zeroes for now */
	ROMMask[0]=ROMMask[1]=0;

	/* Allocate 16kB for the empty space (scratch RAM) */
	if(!(EmptyRAM=(byte *)malloc(0x4000))) return;
	memset(EmptyRAM,0xff,0x4000);
	Chunks[CCount++]=EmptyRAM;

	/* Reset memory map to the empty space */
	for(I=0;I<4;I++)
		for(J=0;J<4;J++)
			for(K=0;K<8;K++)
				MemMap[I][J][K]=EmptyRAM;

	/* Allocate RAMPages*16kB for RAM */
	if(!(RAMData=(byte *)malloc(RAMPages*0x4000))) return;
	memset(RAMData,0xff,RAMPages*0x4000);
	Chunks[CCount++]=RAMData;

	P=(byte *)malloc(0x8000);
	FILEIO* fio = new FILEIO();
	if(fio->Fopen(emu->bios_path(_T("MSX1.ROM")), FILEIO_READ_BINARY)) {
		fio->Fread(P, 0x8000, 1);
		fio->Fclose();
	}
	delete fio;
	MemMap[0][0][0]=P;
	MemMap[0][0][1]=P+0x2000;
	MemMap[0][0][2]=P+0x4000;
	MemMap[0][0][3]=P+0x6000;

	for(J=0;J<4;J++)
	{
		EnWrite[J]          = 0;                           /* Write protect ON for all slots */
		PSL[J]=SSL[J]       = 0;                           /* PSL=0:0:0:0, SSL=0:0:0:0       */
		MemMap[3][2][J*2]   = RAMData+(3-J)*0x4000;        /* RAMMap=3:2:1:0 */
		MemMap[3][2][J*2+1] = MemMap[3][2][J*2]+0x2000;
		RAMMapper[J]        = 3-J;
		RAM[J*2]            = MemMap[0][0][J*2];           /* Setting RAM    */
		RAM[J*2+1]          = MemMap[0][0][J*2+1];
	}

	PSLReg=0x00;
	SSLReg=0x00;

	inserted = false;
}

void MEMORY::write_data8(uint32 addr, uint32 data)
{
	byte I,J;
	if (addr!=0xFFFF) {
		if (EnWrite[addr>>14]) RAM[addr>>13][addr&0x1FFF]=data;
		else if ((addr>0x3FFF)&&(addr<0xC000)) {
			/* J contains 16kB page number 0-3  */
			J=addr>>14;
			/* I contains slot number 0/1  */
			if(PSL[J]==1) I=0;
			else if(PSL[J]==2) I=1;
			/* If no cartridge or no mapper, exit */
			return; 
		}
	} else {
		if (PSL[3]==3) {
			if (SSLReg!=data) {
				SSLReg=data;
				for (J=0;J<4;J++,data>>=2) {
					SSL[J]=data&3;
					if (PSL[J]==3) {
						I=J<<1;
						EnWrite[J]=(SSL[J]==2)&&(MemMap[3][2][I]!=EmptyRAM);
						RAM[I]=MemMap[3][SSL[J]][I];
						RAM[I+1]=MemMap[3][SSL[J]][I+1];
					}
				}
			}
		} else if (EnWrite[3]) RAM[7][addr&0x1FFF]=data;
	}
}

uint32 MEMORY::read_data8(uint32 addr)
{
	if (addr!=0xFFFF) return(RAM[addr>>13][addr&0x1FFF]);
	else return (PSL[3]==3? ~SSLReg:RAM[7][0x1FFF]);
}

void MEMORY::write_signal(int id, uint32 data, uint32 mask)
{
	byte I,J,K,Value;
	for (J=0,PSLReg=Value=data;J<4;J++,Value>>=2) {
		PSL[J]=Value&3;I=J<<1;
		K=PSL[J]==3? SSL[J]:0;
		EnWrite[J]=(K==2)&&(MemMap[3][2][I]!=EmptyRAM);
		RAM[I]=MemMap[PSL[J]][K][I];
		RAM[I+1]=MemMap[PSL[J]][K][I+1];
	}
}

void MEMORY::open_cart(_TCHAR* file_path)
{
	int C1,C2,C3,ROM64,LastFirst;
	FILE *F;
	int Slot=0;

	/* Check slot #, try to open file */
	if(!(F=fopen(file_path,"rb"))) return;

	/* Check "AB" signature in a file */
	ROM64=LastFirst=0;
	C1=fgetc(F);
	C2=fgetc(F);

	/* Maybe this is a flat 64kB ROM? */
	if((C1!='A')||(C2!='B'))
		if(fseek(F,0x4000,SEEK_SET)>=0) {
			C1=fgetc(F);
			C2=fgetc(F);
			ROM64=(C1=='A')&&(C2=='B');
		}

	/* Maybe it is the last page that contains "AB" signature? */
	if((C1!='A')||(C2!='B'))
		if(fseek(F,-0x4000,SEEK_END)>=0) {
			C1=fgetc(F);
			C2=fgetc(F);
			LastFirst=(C1=='A')&&(C2=='B');
		}

	/* If we can't find "AB" signature, drop out */     
	if((C1!='A')||(C2!='B')) {
		fclose(F);
		return;
	}

	/* Determine file length via fseek()/ftell() */
	if (fseek(F,0,SEEK_END)>=0) C1=ftell(F);
	else {
		/* Determine file length by reading entire [GZIPped] stream */
		fseek(F,0,SEEK_SET);
		for(C1=0;(C2=fread(EmptyRAM,1,0x4000,F))==0x4000;C1+=C2);
		if(C2>=0) C1+=C2;
	}

	/* Done with the file */
	fclose(F);

	/* Length must be a multiple of 8kB */
	/* Flat 64kB ROM must be 40..64kB */
	if (C1&0x1FFF) return;
	if (ROM64&&(C1<0xA000)) return;
	if (ROM64&&(C1>0x10000)) return;

	/* Compute size in 8kB pages */
	C1>>=13;

	/* Calculate 2^n closest to number of pages */
	for(C3=1;C3<C1;C3<<=1);

	/* Assign ROMMask for MegaROMs */
	ROMMask[Slot]=!ROM64&&(C1>4)? C3-1:0x00;

	/* Allocate space for the ROM */
	ROMData[Slot]=(byte *)malloc(C3*0x2000);
	if (!ROMData[Slot]) return;
	Chunks[CCount++]=ROMData[Slot];

	/* Try loading ROM */
	FILEIO* fio = new FILEIO();
	if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
		fio->Fread(ROMData[Slot], C1*0x2000, 1);
		fio->Fclose();
	}
	delete fio;

	/* Mirror ROM if it is smaller than 2^n pages */
	if(C1<C3) memcpy(ROMData[Slot]+C1*0x2000, ROMData[Slot]+(C1-C3/2)*0x2000, (C3-C1)*0x2000); 

	/* Set memory map depending on the ROM size */
	switch (C1) {
	case 1:
		/* 8kB ROMs are mirrored 8 times: 0:0:0:0:0:0:0:0 */
		MemMap[Slot+1][0][0]=ROMData[Slot];
		MemMap[Slot+1][0][1]=ROMData[Slot];
		MemMap[Slot+1][0][2]=ROMData[Slot];
		MemMap[Slot+1][0][3]=ROMData[Slot];
		MemMap[Slot+1][0][4]=ROMData[Slot];
		MemMap[Slot+1][0][5]=ROMData[Slot];
		MemMap[Slot+1][0][6]=ROMData[Slot];
		MemMap[Slot+1][0][7]=ROMData[Slot];
		break;
	case 2:
		/* 16kB ROMs are mirrored 4 times: 0:1:0:1:0:1:0:1 */
		MemMap[Slot+1][0][0]=ROMData[Slot];
		MemMap[Slot+1][0][1]=ROMData[Slot]+0x2000;
		MemMap[Slot+1][0][2]=ROMData[Slot];
		MemMap[Slot+1][0][3]=ROMData[Slot]+0x2000;
		MemMap[Slot+1][0][4]=ROMData[Slot];
		MemMap[Slot+1][0][5]=ROMData[Slot]+0x2000;
		MemMap[Slot+1][0][6]=ROMData[Slot];
		MemMap[Slot+1][0][7]=ROMData[Slot]+0x2000;
		break;
	case 3:
	case 4:
		/* 24kB and 32kB ROMs are mirrored twice: 0:1:0:1:2:3:2:3 */
		MemMap[Slot+1][0][0]=ROMData[Slot];
		MemMap[Slot+1][0][1]=ROMData[Slot]+0x2000;
		MemMap[Slot+1][0][2]=ROMData[Slot];
		MemMap[Slot+1][0][3]=ROMData[Slot]+0x2000;
		MemMap[Slot+1][0][4]=ROMData[Slot]+0x4000;
		MemMap[Slot+1][0][5]=ROMData[Slot]+0x6000;
		MemMap[Slot+1][0][6]=ROMData[Slot]+0x4000;
		MemMap[Slot+1][0][7]=ROMData[Slot]+0x6000;
		break;
	default:
		/* 64kB ROMs are loaded to fill slot: 0:1:2:3:4:5:6:7 */
		MemMap[Slot+1][0][0]=ROMData[Slot];
		MemMap[Slot+1][0][1]=ROMData[Slot]+0x2000;
		MemMap[Slot+1][0][2]=ROMData[Slot]+0x4000;
		MemMap[Slot+1][0][3]=ROMData[Slot]+0x6000;
		MemMap[Slot+1][0][4]=ROMData[Slot]+0x8000;
		MemMap[Slot+1][0][5]=ROMData[Slot]+0xA000;
		MemMap[Slot+1][0][6]=ROMData[Slot]+0xC000;
		MemMap[Slot+1][0][7]=ROMData[Slot]+0xE000;
		break;
	}
}

void MEMORY::close_cart()
{
	int J;
	/* Free alocated memory */
	for(J=0;J<CCount;J++) free(Chunks[J]);
	initialize();
}

memory.h

/*
	ASCII MSX1 Emulator 'yaMSX1'
	Skelton for retropc emulator

	Author : tanam
	Date   : 2013.06.29-

	[ memory ]
*/

#ifndef _MEMORY_H_
#define _MEMORY_H_

#include "../vm.h"
#include "../../emu.h"
#include "../device.h"

#define SIG_MEMORY_SEL	0

class MEMORY : public DEVICE
{
private:
	/** Main hardware: CPU, RAM, VRAM, mappers *******************/
	int RAMPages;                      /* Number of RAM pages    */
	byte *RAM[8];                      /* Main RAM (8x8kB pages) */
	byte *EmptyRAM;                    /* Empty RAM page (8kB)   */
	byte *MemMap[4][4][8];   /* Memory maps [PPage][SPage][Addr] */

	byte *RAMData;                     /* RAM Mapper contents    */
	byte RAMMapper[4];                 /* RAM Mapper state       */
	byte RAMMask;                      /* RAM Mapper mask        */

	byte *ROMData[2];                  /* ROM Mapper contents    */
	byte ROMMapper[2][4];              /* ROM Mappers state      */
	byte ROMMask[2];                   /* ROM Mapper masks       */

	byte EnWrite[4];                   /* 1 if write enabled     */
	byte PSL[4],SSL[4];                /* Lists of current slots */
	byte PSLReg,SSLReg;      /* Storage for A8h port and (FFFFh) */

	byte *Chunks[256];                 /* Memory blocks to free  */
	byte CCount;                       /* Number of memory blcks */

	bool inserted;
public:
	MEMORY(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {}
	~MEMORY() {}
	
	// common functions
	void initialize();
	void write_data8(uint32 addr, uint32 data);
	uint32 read_data8(uint32 addr);
	void write_data16(uint32 addr, uint32 data) {
		write_data8(addr, data & 0xff); write_data8(addr + 1, data >> 8);
	}
	uint32 read_data16(uint32 addr) {
		return read_data8(addr) | (read_data8(addr + 1) << 8);
	}
	void write_signal(int id, uint32 data, uint32 mask);
	
	// unique functions
	void open_cart(_TCHAR* file_path);
	void close_cart();
	bool cart_inserted() {
		return inserted;
	}
};

#endif