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