ePX-7を使ってます!でもPX-7持ってません(笑)
http://homepage3.nifty.com/takeda-toshiya/msx/
FD1を実装しましたので、CART#1で以下の改造したDISK.ROMを読み込ませます。
http://www.geocities.jp/parallel_computer_inc/android.html
/** Places in DiskROM to be patched with ED FE C9 ************/
uint16 DiskPatches[] = { 0x4010,0x4013,0x4016,0x401C,0x401F,0 };
fMSXを参考にZ80未定義命令を使った、かなりトリッキーな実装です。
px7.rc
POPUP "Laser Disc" BEGIN MENUITEM "Insert", ID_OPEN_LASER_DISC MENUITEM "Eject", ID_CLOSE_LASER_DISC MENUITEM SEPARATOR MENUITEM "Recent", ID_RECENT_LASER_DISC END POPUP "FD1" BEGIN MENUITEM "Insert", ID_OPEN_FD1 MENUITEM "Eject", ID_CLOSE_FD1 MENUITEM SEPARATOR MENUITEM "Recent", ID_RECENT_FD1 END
resource.h
// PIONEER PX-7 #ifdef _PX7 #define MENU_POS_CONTROL 0 #define MENU_POS_CART1 1 #define MENU_POS_CART2 2 #define MENU_POS_TAPE 3 #define MENU_POS_LASER_DISC 4 #define MENU_POS_FD1 5 #define MENU_POS_SCREEN 6 #define MENU_POS_SOUND 7 #endif
msx1.cpp
bool VM::laser_disc_inserted() { return ldp->disc_inserted(); } extern byte ChangeDisk(byte ID,char *Name); bool status=false; void VM::open_disk(int drv, _TCHAR* file_path, int offset) { ChangeDisk(drv, file_path); status=true; } void VM::close_disk(int drv) { ChangeDisk(drv, NULL); status=false; } bool VM::disk_inserted(int drv) { return status; }
msx1.h
/* ASCII MSX1 Emulator 'yaMSX1' Pioneer PX-7 Emulator 'ePX-7' Author : tanam Date : 2013.06.29- modified by Takeda.Toshiya, umaiboux [ virtual machine ] */ #ifndef _MSX1_H_ #define _MSX1_H_ #define DEVICE_NAME "PIONEER PX-7" #define CONFIG_NAME "px7" // device informations for virtual machine #define FRAMES_PER_SEC 60 #define LINES_PER_FRAME 262 #define CPU_CLOCKS 3579545 //#define SCREEN_WIDTH 256 //#define SCREEN_HEIGHT 192 #define SCREEN_WIDTH 512 #define SCREEN_HEIGHT 384 #define TMS9918A_VRAM_SIZE 0x4000 #define TMS9918A_LIMIT_SPRITES #define TMS9918A_SUPER_IMPOSE #define HAS_AY_3_8910 // for Flappy Limited '85 #define YM2203_PORT_MODE 0x80 // device informations for win32 #define USE_CART1 #define USE_CART2 #define USE_TAPE #define USE_LASER_DISC #define USE_FD1 #define USE_ALT_F10_KEY #define USE_AUTO_KEY 6 #define USE_AUTO_KEY_RELEASE 10 #include "../../common.h" class EMU; class DEVICE; class EVENT; class DATAREC; class I8255; class IO; class LD700; class NOT; class YM2203; class PCM1BIT; class TMS9918A; class Z80; class JOYSTICK; class KEYBOARD; class MEMORY; class SLOT_CART; class SLOT_MAIN; class SLOT_SUB; class VM { protected: EMU* emu; // devices EVENT* event; DATAREC* drec; I8255* pio; IO* io; LD700* ldp; NOT* not; YM2203* psg; PCM1BIT* pcm; TMS9918A* vdp; Z80* cpu; JOYSTICK* joystick; KEYBOARD* keyboard; MEMORY* memory; SLOT_MAIN *slot0; SLOT_CART *slot1; SLOT_SUB *slot2; SLOT_CART *slot3; public: // ---------------------------------------- // initialize // ---------------------------------------- VM(EMU* parent_emu); ~VM(); // ---------------------------------------- // for emulation class // ---------------------------------------- // drive virtual machine void reset(); void run(); // draw screen void draw_screen(); // sound generation void initialize_sound(int rate, int samples); uint16* create_sound(int* extra_frames); int sound_buffer_ptr(); void movie_sound_callback(uint8 *buffer, long size); // user interface void open_cart(int drv, _TCHAR* file_path); void close_cart(int drv); bool cart_inserted(int drv); void play_tape(_TCHAR* file_path); void rec_tape(_TCHAR* file_path); void close_tape(); bool tape_inserted(); void open_laser_disc(_TCHAR* file_path); void close_laser_disc(); bool laser_disc_inserted(); void open_disk(int drv, _TCHAR* file_path, int offset); void close_disk(int drv); bool disk_inserted(int drv); bool now_skip(); void update_config(); // ---------------------------------------- // for each device // ---------------------------------------- // devices DEVICE* get_device(int id); DEVICE* dummy; DEVICE* first_device; DEVICE* last_device; }; #endif
z80.cpp
case 0xbb: OTDR(); break; /* OTDR */ default: #ifdef _CPU_DEBUG_LOG if(debug_count) { emu->out_debug(_T("%4x\tDB ED\n"), prevpc); } #endif #ifdef _PX7 uint8 DiskPresent(uint8 ID); byte DiskRead(byte ID,byte *Buf, int N); byte DiskWrite(byte ID,byte *Buf,int N); #define C_FLAG 0x01 /* 1: Carry/Borrow occured */ static byte BootBlock[] = { 0xEB,0xFE,0x90,0x56,0x46,0x42,0x2D,0x31,0x39,0x38,0x39,0x00,0x02,0x02,0x01,0x00, 0x02,0x70,0x00,0xA0,0x05,0xF9,0x03,0x00,0x09,0x00,0x02,0x00,0x00,0x00,0xD0,0xED, 0x53,0x58,0xC0,0x32,0xC2,0xC0,0x36,0x55,0x23,0x36,0xC0,0x31,0x1F,0xF5,0x11,0x9D, 0xC0,0x0E,0x0F,0xCD,0x7D,0xF3,0x3C,0x28,0x28,0x11,0x00,0x01,0x0E,0x1A,0xCD,0x7D, 0xF3,0x21,0x01,0x00,0x22,0xAB,0xC0,0x21,0x00,0x3F,0x11,0x9D,0xC0,0x0E,0x27,0xCD, 0x7D,0xF3,0xC3,0x00,0x01,0x57,0xC0,0xCD,0x00,0x00,0x79,0xE6,0xFE,0xFE,0x02,0x20, 0x07,0x3A,0xC2,0xC0,0xA7,0xCA,0x22,0x40,0x11,0x77,0xC0,0x0E,0x09,0xCD,0x7D,0xF3, 0x0E,0x07,0xCD,0x7D,0xF3,0x18,0xB4,0x42,0x6F,0x6F,0x74,0x20,0x65,0x72,0x72,0x6F, 0x72,0x0D,0x0A,0x50,0x72,0x65,0x73,0x73,0x20,0x61,0x6E,0x79,0x20,0x6B,0x65,0x79, 0x20,0x66,0x6F,0x72,0x20,0x72,0x65,0x74,0x72,0x79,0x0D,0x0A,0x24,0x00,0x4D,0x53, 0x58,0x44,0x4F,0x53,0x20,0x20,0x53,0x59,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF3,0x2A, 0x51,0xF3,0x11,0x00,0x01,0x19,0x01,0x00,0x01,0x11,0x00,0xC1,0xED,0xB0,0x3A,0xEE, 0xC0,0x47,0x11,0xEF,0xC0,0x21,0x00,0x00,0xCD,0x51,0x52,0xF3,0x76,0xC9,0x18,0x64, 0x3A,0xAF,0x80,0xF9,0xCA,0x6D,0x48,0xD3,0xA5,0x0C,0x8C,0x2F,0x9C,0xCB,0xE9,0x89, 0xD2,0x00,0x32,0x26,0x40,0x94,0x61,0x19,0x20,0xE6,0x80,0x6D,0x8A,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; static const struct { int Sectors;byte Heads,Names,PerTrack,PerFAT,PerCluster; } Info[8] = { { 720,1,112,9,2,2 }, { 1440,2,112,9,3,2 }, { 640,1,112,8,1,2 }, { 1280,2,112,8,2,2 }, { 360,1, 64,9,2,1 }, { 720,2,112,9,2,2 }, { 320,1, 64,8,1,1 }, { 640,2,112,8,1,2 } }; byte Buf[512],Count,N,*P; int Sector, i, j; uint16 Addr; /** PHYDIO: Read/write sectors to disk ************************** *** Input: *** *** [F] CARRY=WRITE [A] Drive number (0=A:) *** *** [B] Number of sectors to write [C] Media descriptor *** *** [DE] Logical sector number (starts at 0) *** *** [HL] Transfer address *** *** Output: *** *** [F] CARRY=ERROR [A] If error: errorcode *** *** [B] Number of sectors remaining (not read/written) *** *** Error codes in [A] can be: *** *** 0 Write protected 8 Record not found *** *** 2 Not ready 10 Write fault *** *** 4 Data (CRC) error 12 Other errors *** *** 6 Seek error *** ****************************************************************/ if (prevpc==0x4010) { iff1|=1; /// R->IFF|=1; Addr = HL; /// R->HL.W; Count = B; /// R->BC.B.h; if (!DiskPresent(A)) { AF=0x0201; ///R->AF.W=0x0201; return; } // No disk -> "Not ready" if (DE+Count>Info[C-0xF8].Sectors) { AF=0x0801; return; } // Wrong sector -> "Record not found" // If data does not fit into 64kB address space, trim it if (HL+Count*512>0x10000) Count=(0x10000-HL)/512; // Save slot states // Turn on RAM in all slots if (F&C_FLAG) for (Sector=DE;Count--;Sector++) { // WRITE for (j=0;j<512;j++) Buf[j]=RM8(Addr++); if (DiskWrite(A,Buf,Sector)) B--; else { AF=0x0A01; return; } } else for(Sector=DE;Count--;Sector++) { // READ if (DiskRead(A,Buf,Sector)) { B--; } else { AF=0x0401; return; } for(j=0;j<512;j++) WM8(Addr++,Buf[j]); } F&=~C_FLAG; return; /** DSKCHG: Check if the disk was changed *********************** *** Input: *** *** [A] Drive number (0=A:) [B] Media descriptor *** *** [C] Media descriptor [HL] Base address of DPB *** *** Output: *** *** [F] CARRY=ERROR [A] If error: errorcode (see DSKIO) *** *** [B] If success: 1=Unchanged, 0=Unknown, -1=Changed *** *** Note: *** *** If the disk has been changed or may have been changed *** *** (unknown) read the boot sector or the FAT sector for disk *** *** media descriptor and transfer a new DPB as with GETDPB. *** ****************************************************************/ } else if (prevpc==0x4013) { iff1|=1; // If no disk, return "Not ready": if(!DiskPresent(A)) { AF=0x0201; return; } // This requires some major work to be done: B=0; F &= ~C_FLAG; // We continue with GETDPB now... /** GETDPB: Disk format ***************************************** *** Input: *** *** [A] Drive number [B] 1st byte of FAT (media descriptor) *** *** [C] Media descriptor [HL] Base address of DPB *** *** Output: *** *** [HL+1] .. [HL+18] = DPB for specified drive *** *** DPB consists of: *** *** Name Offset Size Description *** *** MEDIA 0 1 Media type (F8..FF) *** *** SECSIZ 1 2 Sector size (must be 2^n) *** *** DIRMSK 3 1 (SECSIZE/32)-1 *** *** DIRSHFT 4 1 Number of one bits in DIRMSK *** *** CLUSMSK 5 1 (Sectors per cluster)-1 *** *** CLUSSHFT 6 1 (Number of one bits in CLUSMSK)+1 *** *** FIRFAT 7 2 Logical sector number of first FAT *** *** FATCNT 8 1 Number of FATs *** *** MAXENT A 1 Number of directory entries (max 254) *** *** FIRREC B 2 Logical sector number of first data *** *** MAXCLUS D 2 Number of clusters (not including *** *** reserved, FAT and directory sectors)+1 *** *** FATSIZ F 1 Number of sectors used *** *** FIRDIR 10 2 FAT logical sector number of start of *** *** directory *** ****************************************************************/ } else if (prevpc==0x4016) { int BytesPerSector,SectorsPerDisk,SectorsPerFAT,ReservedSectors; // If no disk, return "Not ready": if (!DiskPresent(A)) { AF=0x0201; return; } // If can't read, return "Other error": if (!DiskRead(A, Buf , 0)) { AF=0x0C01; return; } BytesPerSector = (int)Buf[0x0C]*256+Buf[0x0B]; SectorsPerDisk = (int)Buf[0x14]*256+Buf[0x13]; SectorsPerFAT = (int)Buf[0x17]*256+Buf[0x16]; ReservedSectors = (int)Buf[0x0F]*256+Buf[0x0E]; Addr=HL+1; WM8(Addr++,Buf[0x15]); // Format ID [F8h-FFh] WM8(Addr++,Buf[0x0B]); // Sector size WM8(Addr++,Buf[0x0C]); j=(BytesPerSector>>5)-1; for(i=0;j&(1<<i);i++); WM8(Addr++,j); // Directory mask/shft WM8(Addr++,i); j=Buf[0x0D]-1; for(i=0;j&(1<<i);i++); WM8(Addr++,j); // Cluster mask/shift WM8(Addr++,i+1); WM8(Addr++,Buf[0x0E]); // Sector # of 1st FAT WM8(Addr++,Buf[0x0F]); WM8(Addr++,Buf[0x10]); // Number of FATs WM8(Addr++,Buf[0x11]); // Number of dirent-s j=ReservedSectors+Buf[0x10]*SectorsPerFAT; j+=32*Buf[0x11]/BytesPerSector; WM8(Addr++,j&0xFF); // Sector # of data WM8(Addr++,(j>>8)&0xFF); j=(SectorsPerDisk-j)/Buf[0x0D]; WM8(Addr++,j&0xFF); // Number of clusters WM8(Addr++,(j>>8)&0xFF); WM8(Addr++,Buf[0x16]); // Sectors per FAT j=ReservedSectors+Buf[0x10]*SectorsPerFAT; WM8(Addr++,j&0xFF); // Sector # of dir. WM8(Addr,(j>>8)&0xFF); // Return success F&=~C_FLAG; return; /** DSKFMT: Disk format ***************************************** *** Input: *** *** [A] Specified choice (1-9) [D] Drive number (0=A:) *** *** [HL] Begin address of work area [BC] Length of work area *** *** Output: *** *** [F] CARRY=ERROR *** *** Notes: *** *** 1) Also writes a MSX boot sector at sector 0, clears all *** *** FATs (media descriptor at first byte, 0FFh at second/ *** *** third byte and rest zero) and clears the directory *** *** filling it with zeros. *** *** 2) Error codes are: *** *** 0 Write protected 10 Write fault *** *** 2 Not ready 12 Bad parameter *** *** 4 Data (CRC) error 14 Insufficient memory *** *** 6 Seek error 16 Other errors *** *** 8 Record not found *** ****************************************************************/ } else if (prevpc==0x401c) { iff1|=1; // If invalid choice, return "Bad parameter": if (!A || A>2) { AF=0x0C01; return; } // If no disk, return "Not ready": if (!DiskPresent(D)) { AF=0x0201; return; } // Fill bootblock with data: P=BootBlock+3; N=2-A; memcpy(P,"fMSXdisk",8);P+=10; // Manufacturer's ID *P=Info[N].PerCluster;P+=4; // Sectors per cluster *P++=Info[N].Names;*P++=0x00; // Number of names *P++=Info[N].Sectors // Number of sectors *P++=(Info[N].Sectors>>8) *P++=N+0xF8; // Format ID [F8h-FFh] *P++=Info[N].PerFAT;*P++=0x00; // Sectors per FAT *P++=Info[N].PerTrack;*P++=0x00; // Sectors per track *P++=Info[N].Heads;*P=0x00; // Number of heads // If can't write bootblock, return "Write protected": if (!DiskWrite(D, BootBlock, 0)) { AF=0x0001; return; } // Writing FATs: for (Sector=1,j=0;j<2;j++) { Buf[0]=N+0xF8; Buf[1]=Buf[2]=0xFF; memset(Buf+3,0x00,509); if (!DiskWrite(D, Buf, Sector++)) { AF=0x0A01; return; } memset(Buf,0x00,512); for (i=Info[N].PerFAT;i>1;i--) if (!DiskWrite(D, Buf, Sector++)) { AF=0x0A01; return; } } j=Info[N].Names/16; // Directory size i=Info[N].Sectors-2*Info[N].PerFAT-j-1; // Data size for (memset(Buf,0x00,512);j;j--) if (!DiskWrite(D, Buf, Sector++)) { AF=0x0A01; return; } for (memset(Buf,0xFF,512);I;I--) if (!DiskWrite(D, Buf, Sector++)) { AF=0x0A01; return; } // Return success F&=~C_FLAG; return; /*** DRVOFF: Stop drives ***************************************** *** Input: None *** *** Output: None *** ****************************************************************/ } else if (prevpc==0x401f) { return; } break; } } #define MAXDRIVES 2 #define O_BINARY 0 static FILE *Drives[2]={NULL,NULL}; /* Disk image files */ static int RdOnly[2]; /* 1 = read-only */ /** ChangeDisk() *********************************************/ /** Change disk image in a given drive. Closes current disk **/ /** image if Name=0 was given. Returns 1 on success or 0 on **/ /** failure. **/ /*************************************************************/ byte ChangeDisk(byte ID,char *Name) { /* We only have MAXDRIVES drives */ if (ID >= MAXDRIVES) return(0); /* Close previous disk image */ if (Drives[ID] != NULL) { fclose(Drives[ID]); Drives[ID] = NULL; } if (Name == NULL) return (0); /* If no disk image given, consider drive empty */ if (!Name) return(1); /* Open new disk image */ Drives[ID]=fopen(Name,"r+b"); RdOnly[ID]=0; /* If failed to open for writing, open read-only */ if (Drives[ID] == NULL) { Drives[ID]=fopen(Name,"rb"); RdOnly[ID]=1; } /* Return operation result */ return (Drives[ID] != NULL); } /** DiskPresent() ********************************************/ /** Return 1 if disk drive with a given ID is present. **/ /*************************************************************/ uint8 DiskPresent(uint8 ID) { return((ID<MAXDRIVES)&&(Drives[ID] != NULL)); } /** DiskRead() ***********************************************/ /** Read requested sector from the drive into a buffer. **/ /*************************************************************/ byte DiskRead(byte ID,byte *Buf, int N) { if ((ID<MAXDRIVES) && (Drives[ID] != NULL)) if (0 == fseek(Drives[ID], N*512L, SEEK_SET)) { int tmp=fread(Buf, 1, 512, Drives[ID]); return (tmp==512); } return (0); } /** DiskWrite() **********************************************/ /** Write contents of the buffer into a given sector of the **/ /** disk. **/ /*************************************************************/ byte DiskWrite(byte ID,byte *Buf,int N) { if ((ID<MAXDRIVES) && (Drives[ID] != NULL) && !RdOnly[ID]) if (0 == fseek(Drives[ID], N*512L, SEEK_SET)) return(fwrite(Buf, 1, 512, Drives[ID]) == 512); return (0); } #else OP(code); break; } } #endif void Z80::OP(uint8 code) { prevpc = PC - 1; icount -= cc_op[code];