Yet Another ASCII MSX1 Emulator その4

引き続きDISK関連を実装しました。しかし、RS232C関連はMSX1だと何に使えばいいんだろうか。

f:id:tanam:20131011021351p:image:w360

f:id:tanam:20131011032434p:image:w360

f:id:tanam:20131011021350p:image:w360

fMSXを参考にZ80未定義命令を使った、かなりトリッキーな実装です。

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 _MSX1
#include "./msx1/memory.cpp"
#else
		OP(code);
		break;
	}
}
#endif

(省略)

memory.cpp

#ifndef _Z80_H_
/*
	ASCII MSX1 Emulator 'yaMSX1'
	Skelton for retropc emulator

	Author : tanam
	Date   : 2013.06.29-

	[ memory ]
*/

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

/** Places in DiskROM to be patched with ED FE C9 ************/
uint16 DiskPatches[] = { 0x4010,0x4013,0x4016,0x401C,0x401F,0 };

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

	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();
	}
	MemMap[0][0][0]=P;
	MemMap[0][0][1]=P+0x2000;
	MemMap[0][0][2]=P+0x4000;
	MemMap[0][0][3]=P+0x6000;

	/* Try loading DiskROM */
	Q=(byte *)malloc(0x4000);
    if(fio->Fopen(emu->bios_path(_T("DISK.ROM")), FILEIO_READ_BINARY)) {
		fio->Fread(Q, 0x4000, 1);
		fio->Fclose();
		MemMap[3][1][2]=Q;
		MemMap[3][1][3]=Q+0x2000;
		/* Apply patches to BDOS */
	    for (J=0;DiskPatches[J];J++) {
			R=MemMap[3][1][2]+DiskPatches[J]-0x4000;
			R[0]=0xED;R[1]=0xFE;R[2]=0xC9;
		}
   	}
	delete fio;

	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;
	PS=0;
	SS=0;
	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==0x4011) {
		// Save slot states
		PS=PSLReg;SS=SSLReg;

		// Turn on RAM in all slots
		write_signal(0xA8,0xFF,0xFF);
		SSLReg=0xAA;
	}
	if (SS!=0 && PS !=0) {
		// Restore slot states
		SSLReg=SS;
		write_signal(0xA8,PS,0xFF);
		SS=0;
		PS=0;
	}
	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;
	}
	inserted = true;
}

void MEMORY::close_cart()
{
	int J;
	/* Free alocated memory */
	for(J=0;J<CCount;J++) free(Chunks[J]);
	if (P) free(P);
	if (Q) free(Q);
	initialize();
}
#else
		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);
}
#endif

未実装な機能(実装予定)