MCUME/MCUME_teensy41/teensymsx/Patch.c

441 wiersze
14 KiB
C

/** fMSX: portable MSX emulator ******************************/
/** **/
/** Patch.c **/
/** **/
/** This file contains implementation for the PatchZ80() **/
/** function necessary for emulating MSX disk and tape IO. **/
/** **/
/** Copyright (C) Marat Fayzullin 1994-2005 **/
/** You are not allowed to distribute this software **/
/** commercially. Please, notify me, if you make any **/
/** changes to this file. **/
/*************************************************************/
#include "MSX.h"
#include "Bootblock.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include "shared.h"
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
void SSlot(byte Value); /* Used to switch secondary slots */
#ifdef FMSX
extern byte *RAM[],PSL[],SSLReg;
static byte RdZ80(WORD A)
{
if(A!=0xFFFF) return(RAM[A>>13][A&0x1FFF]);
else return((PSL[3]==3)? ~SSLReg:RAM[7][0x1FFF]);
}
#endif
/** PatchZ80() ***********************************************/
/** Emulate BIOS calls. This function is called on an ED FE **/
/** instruction to emulate disk/tape access, etc. **/
/*************************************************************/
void PatchZ80(Z80 *R)
{
static const byte TapeHeader[8] = { 0x1F,0xA6,0xDE,0xBA,0xCC,0x13,0x7D,0x74 };
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,PS,SS,N,*P;
int J,I,Sector;
WORD Addr;
switch(R->PC.W-2)
{
case 0x4010:
/** 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(Verbose&0x04)
// printf
// (
// "%s DISK %c: %d sectors starting from %04Xh [buffer at %04Xh]\n",
// R->AF.B.l&C_FLAG? "WRITE":"READ",R->AF.B.h+'A',R->BC.B.h,
// R->DE.W,R->HL.W
// );
R->IFF|=1;
Addr = R->HL.W;
Count = R->BC.B.h;
if(!DiskPresent(R->AF.B.h))
{ R->AF.W=0x0201;return; } /* No disk -> "Not ready" */
if((int)(R->DE.W)+Count>Info[R->BC.B.l-0xF8].Sectors)
{ R->AF.W=0x0801;return; } /* Wrong sector -> "Record not found" */
/* If data does not fit into 64kB address space, trim it */
if((int)(R->HL.W)+Count*512>0x10000) Count=(0x10000-R->HL.W)/512;
/* Save slot states */
PS=PSLReg;SS=SSLReg;
/* Turn on RAM in all slots */
OutZ80(0xA8,0xFF);
SSlot(0xAA);
if(R->AF.B.l&C_FLAG)
for(Sector=R->DE.W;Count--;Sector++) /* WRITE */
{
for(J=0;J<512;J++) Buf[J]=RdZ80(Addr++);
if(DiskWrite(R->AF.B.h,Buf,Sector)) R->BC.B.h--;
else
{
R->AF.W=0x0A01;
SSlot(SS);
OutZ80(0xA8,PS);
return;
}
}
else
for(Sector=R->DE.W;Count--;Sector++) /* READ */
{
if(DiskRead(R->AF.B.h,Buf,Sector)) R->BC.B.h--;
else
{
R->AF.W=0x0401;
SSlot(SS);
OutZ80(0xA8,PS);
return;
}
for(J=0;J<512;J++) WrZ80(Addr++,Buf[J]);
}
/* Restore slot states */
SSlot(SS);
OutZ80(0xA8,PS);
/* Return "Success" */
R->AF.B.l&=~C_FLAG;
return;
}
case 0x4013:
/** 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. ***
****************************************************************/
{
//if(Verbose&0x04) printf("CHECK DISK %c\n",R->AF.B.h+'A');
R->IFF|=1;
/* If no disk, return "Not ready": */
if(!DiskPresent(R->AF.B.h)) { R->AF.W=0x0201;return; }
/* This requires some major work to be done: */
R->BC.B.h=0;R->AF.B.l&=~C_FLAG;
/* We continue with GETDPB now... */
}
case 0x4016:
/** 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 ***
****************************************************************/
{
int BytesPerSector,SectorsPerDisk,SectorsPerFAT,ReservedSectors;
/* If no disk, return "Not ready": */
if(!DiskPresent(R->AF.B.h)) { R->AF.W=0x0201;return; }
/* If can't read, return "Other error": */
if(!DiskRead(R->AF.B.h,Buf,0)) { R->AF.W=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=R->HL.W+1;
WrZ80(Addr++,Buf[0x15]); /* Format ID [F8h-FFh] */
WrZ80(Addr++,Buf[0x0B]); /* Sector size */
WrZ80(Addr++,Buf[0x0C]);
J=(BytesPerSector>>5)-1;
for(I=0;J&(1<<I);I++);
WrZ80(Addr++,J); /* Directory mask/shft */
WrZ80(Addr++,I);
J=Buf[0x0D]-1;
for(I=0;J&(1<<I);I++);
WrZ80(Addr++,J); /* Cluster mask/shift */
WrZ80(Addr++,I+1);
WrZ80(Addr++,Buf[0x0E]); /* Sector # of 1st FAT */
WrZ80(Addr++,Buf[0x0F]);
WrZ80(Addr++,Buf[0x10]); /* Number of FATs */
WrZ80(Addr++,Buf[0x11]); /* Number of dirent-s */
J=ReservedSectors+Buf[0x10]*SectorsPerFAT;
J+=32*Buf[0x11]/BytesPerSector;
WrZ80(Addr++,J&0xFF); /* Sector # of data */
WrZ80(Addr++,(J>>8)&0xFF);
J=(SectorsPerDisk-J)/Buf[0x0D];
WrZ80(Addr++,J&0xFF); /* Number of clusters */
WrZ80(Addr++,(J>>8)&0xFF);
WrZ80(Addr++,Buf[0x16]); /* Sectors per FAT */
J=ReservedSectors+Buf[0x10]*SectorsPerFAT;
WrZ80(Addr++,J&0xFF); /* Sector # of dir. */
WrZ80(Addr,(J>>8)&0xFF);
/* Return success */
R->AF.B.l&=~C_FLAG;
return;
}
case 0x401C:
/** 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 ***
****************************************************************/
{
R->IFF|=1;
/* If invalid choice, return "Bad parameter": */
if(!R->AF.B.h||(R->AF.B.h>2)) { R->AF.W=0x0C01;return; }
/* If no disk, return "Not ready": */
if(!DiskPresent(R->DE.B.h)) { R->AF.W=0x0201;return; }
/* Fill bootblock with data: */
P=BootBlock+3;
N=2-R->AF.B.h;
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&0xFF; /* Number of sectors */
*P++=(Info[N].Sectors>>8)&0xFF;
*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(R->DE.B.h,BootBlock,0)) { R->AF.W=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(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; }
memset(Buf,0x00,512);
for(I=Info[N].PerFAT;I>1;I--)
if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=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(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; }
for(memset(Buf,0xFF,512);I;I--)
if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; }
/* Return success */
R->AF.B.l&=~C_FLAG;
return;
}
case 0x401F:
/** DRVOFF: Stop drives *****************************************
*** Input: None ***
*** Output: None ***
****************************************************************/
return;
case 0x00E1:
/** TAPION: Open for read and read header ***********************
****************************************************************/
{
long Pos;
//if(Verbose&0x04) printf("TAPE: Looking for header...");
R->AF.B.l|=C_FLAG;
#ifdef unused
if(CasStream)
{
Pos=ftell(CasStream);
if(Pos&7)
if(fseek(CasStream,8-(Pos&7),SEEK_CUR))
{
if(Verbose&0x04) puts("FAILED");
rewind(CasStream);return;
}
while(fread(Buf,1,8,CasStream)==8)
if(!memcmp(Buf,TapeHeader,8))
{
if(Verbose&0x04) puts("OK");
R->AF.B.l&=~C_FLAG;return;
}
rewind(CasStream);
}
if(Verbose&0x04) puts("FAILED");
#endif
return;
}
case 0x00E4:
/** TAPIN: Read tape ********************************************
****************************************************************/
{
R->AF.B.l|=C_FLAG;
#ifdef unused
if(CasStream)
{
J=fgetc(CasStream);
if(J<0) rewind(CasStream);
else { R->AF.B.h=J;R->AF.B.l&=~C_FLAG; }
}
#endif
return;
}
case 0x00E7:
/** TAPIOF: *****************************************************
****************************************************************/
R->AF.B.l&=~C_FLAG;
return;
case 0x00EA:
/** TAPOON: *****************************************************
****************************************************************/
{
long Pos;
R->AF.B.l|=C_FLAG;
#ifdef unused
if(CasStream)
{
Pos=ftell(CasStream);
if(Pos&7)
if(fseek(CasStream,8-(Pos&7),SEEK_CUR))
{ R->AF.B.l|=C_FLAG;return; }
fwrite(TapeHeader,1,8,CasStream);
R->AF.B.l&=~C_FLAG;
}
#endif
return;
}
case 0x00ED:
/** TAPOUT: Write tape ******************************************
****************************************************************/
R->AF.B.l|=C_FLAG;
#ifdef unused
if(CasStream)
{
fputc(R->AF.B.h,CasStream);
R->AF.B.l&=~C_FLAG;
}
#endif
return;
case 0x00F0:
/** TAPOOF: *****************************************************
****************************************************************/
R->AF.B.l&=~C_FLAG;
return;
case 0x00F3:
/** STMOTR: *****************************************************
****************************************************************/
R->AF.B.l&=~C_FLAG;
return;
default:
//printf("Unknown BIOS trap called at PC=%04Xh\n",R->PC.W-2);
break;
}
}