Patch.c
1 /** fMSX: portable MSX emulator ******************************/ 2 /** **/ 3 /** Patch.c **/ 4 /** **/ 5 /** This file contains implementation for the PatchZ80() **/ 6 /** function necessary for emulating MSX disk and tape IO. **/ 7 /** **/ 8 /** Copyright (C) Marat Fayzullin 1994-2005 **/ 9 /** You are not allowed to distribute this software **/ 10 /** commercially. Please, notify me, if you make any **/ 11 /** changes to this file. **/ 12 /*************************************************************/ 13 14 #include "MSX.h" 15 #include "Bootblock.h" 16 #include <stdio.h> 17 #include <string.h> 18 #include <stdlib.h> 19 #include <fcntl.h> 20 21 #include "shared.h" 22 23 24 #ifndef SEEK_CUR 25 #define SEEK_CUR 1 26 #endif 27 28 void SSlot(byte Value); /* Used to switch secondary slots */ 29 30 #ifdef FMSX 31 extern byte *RAM[],PSL[],SSLReg; 32 static byte RdZ80(WORD A) 33 { 34 if(A!=0xFFFF) return(RAM[A>>13][A&0x1FFF]); 35 else return((PSL[3]==3)? ~SSLReg:RAM[7][0x1FFF]); 36 } 37 #endif 38 39 /** PatchZ80() ***********************************************/ 40 /** Emulate BIOS calls. This function is called on an ED FE **/ 41 /** instruction to emulate disk/tape access, etc. **/ 42 /*************************************************************/ 43 void PatchZ80(Z80 *R) 44 { 45 static const byte TapeHeader[8] = { 0x1F,0xA6,0xDE,0xBA,0xCC,0x13,0x7D,0x74 }; 46 47 static const struct 48 { int Sectors;byte Heads,Names,PerTrack,PerFAT,PerCluster; } 49 Info[8] = 50 { 51 { 720,1,112,9,2,2 }, 52 { 1440,2,112,9,3,2 }, 53 { 640,1,112,8,1,2 }, 54 { 1280,2,112,8,2,2 }, 55 { 360,1, 64,9,2,1 }, 56 { 720,2,112,9,2,2 }, 57 { 320,1, 64,8,1,1 }, 58 { 640,2,112,8,1,2 } 59 }; 60 61 byte Buf[512],Count,PS,SS,N,*P; 62 int J,I,Sector; 63 WORD Addr; 64 65 switch(R->PC.W-2) 66 { 67 68 case 0x4010: 69 /** PHYDIO: Read/write sectors to disk ************************** 70 *** Input: *** 71 *** [F] CARRY=WRITE [A] Drive number (0=A:) *** 72 *** [B] Number of sectors to write [C] Media descriptor *** 73 *** [DE] Logical sector number (starts at 0) *** 74 *** [HL] Transfer address *** 75 *** Output: *** 76 *** [F] CARRY=ERROR [A] If error: errorcode *** 77 *** [B] Number of sectors remaining (not read/written) *** 78 *** Error codes in [A] can be: *** 79 *** 0 Write protected 8 Record not found *** 80 *** 2 Not ready 10 Write fault *** 81 *** 4 Data (CRC) error 12 Other errors *** 82 *** 6 Seek error *** 83 ****************************************************************/ 84 { 85 //if(Verbose&0x04) 86 // printf 87 // ( 88 // "%s DISK %c: %d sectors starting from %04Xh [buffer at %04Xh]\n", 89 // R->AF.B.l&C_FLAG? "WRITE":"READ",R->AF.B.h+'A',R->BC.B.h, 90 // R->DE.W,R->HL.W 91 // ); 92 93 R->IFF|=1; 94 Addr = R->HL.W; 95 Count = R->BC.B.h; 96 97 if(!DiskPresent(R->AF.B.h)) 98 { R->AF.W=0x0201;return; } /* No disk -> "Not ready" */ 99 if((int)(R->DE.W)+Count>Info[R->BC.B.l-0xF8].Sectors) 100 { R->AF.W=0x0801;return; } /* Wrong sector -> "Record not found" */ 101 102 /* If data does not fit into 64kB address space, trim it */ 103 if((int)(R->HL.W)+Count*512>0x10000) Count=(0x10000-R->HL.W)/512; 104 105 /* Save slot states */ 106 PS=PSLReg;SS=SSLReg; 107 108 /* Turn on RAM in all slots */ 109 OutZ80(0xA8,0xFF); 110 SSlot(0xAA); 111 112 if(R->AF.B.l&C_FLAG) 113 for(Sector=R->DE.W;Count--;Sector++) /* WRITE */ 114 { 115 for(J=0;J<512;J++) Buf[J]=RdZ80(Addr++); 116 117 if(DiskWrite(R->AF.B.h,Buf,Sector)) R->BC.B.h--; 118 else 119 { 120 R->AF.W=0x0A01; 121 SSlot(SS); 122 OutZ80(0xA8,PS); 123 return; 124 } 125 } 126 else 127 for(Sector=R->DE.W;Count--;Sector++) /* READ */ 128 { 129 if(DiskRead(R->AF.B.h,Buf,Sector)) R->BC.B.h--; 130 else 131 { 132 R->AF.W=0x0401; 133 SSlot(SS); 134 OutZ80(0xA8,PS); 135 return; 136 } 137 138 for(J=0;J<512;J++) WrZ80(Addr++,Buf[J]); 139 } 140 141 /* Restore slot states */ 142 SSlot(SS); 143 OutZ80(0xA8,PS); 144 145 /* Return "Success" */ 146 R->AF.B.l&=~C_FLAG; 147 return; 148 } 149 150 case 0x4013: 151 /** DSKCHG: Check if the disk was changed *********************** 152 *** Input: *** 153 *** [A] Drive number (0=A:) [B] Media descriptor *** 154 *** [C] Media descriptor [HL] Base address of DPB *** 155 *** Output: *** 156 *** [F] CARRY=ERROR [A] If error: errorcode (see DSKIO) *** 157 *** [B] If success: 1=Unchanged, 0=Unknown, -1=Changed *** 158 *** Note: *** 159 *** If the disk has been changed or may have been changed *** 160 *** (unknown) read the boot sector or the FAT sector for disk *** 161 *** media descriptor and transfer a new DPB as with GETDPB. *** 162 ****************************************************************/ 163 { 164 //if(Verbose&0x04) printf("CHECK DISK %c\n",R->AF.B.h+'A'); 165 166 R->IFF|=1; 167 168 /* If no disk, return "Not ready": */ 169 if(!DiskPresent(R->AF.B.h)) { R->AF.W=0x0201;return; } 170 171 /* This requires some major work to be done: */ 172 R->BC.B.h=0;R->AF.B.l&=~C_FLAG; 173 174 /* We continue with GETDPB now... */ 175 } 176 177 case 0x4016: 178 /** GETDPB: Disk format ***************************************** 179 *** Input: *** 180 *** [A] Drive number [B] 1st byte of FAT (media descriptor) *** 181 *** [C] Media descriptor [HL] Base address of DPB *** 182 *** Output: *** 183 *** [HL+1] .. [HL+18] = DPB for specified drive *** 184 *** DPB consists of: *** 185 *** Name Offset Size Description *** 186 *** MEDIA 0 1 Media type (F8..FF) *** 187 *** SECSIZ 1 2 Sector size (must be 2^n) *** 188 *** DIRMSK 3 1 (SECSIZE/32)-1 *** 189 *** DIRSHFT 4 1 Number of one bits in DIRMSK *** 190 *** CLUSMSK 5 1 (Sectors per cluster)-1 *** 191 *** CLUSSHFT 6 1 (Number of one bits in CLUSMSK)+1 *** 192 *** FIRFAT 7 2 Logical sector number of first FAT *** 193 *** FATCNT 8 1 Number of FATs *** 194 *** MAXENT A 1 Number of directory entries (max 254) *** 195 *** FIRREC B 2 Logical sector number of first data *** 196 *** MAXCLUS D 2 Number of clusters (not including *** 197 *** reserved, FAT and directory sectors)+1 *** 198 *** FATSIZ F 1 Number of sectors used *** 199 *** FIRDIR 10 2 FAT logical sector number of start of *** 200 *** directory *** 201 ****************************************************************/ 202 { 203 int BytesPerSector,SectorsPerDisk,SectorsPerFAT,ReservedSectors; 204 205 /* If no disk, return "Not ready": */ 206 if(!DiskPresent(R->AF.B.h)) { R->AF.W=0x0201;return; } 207 /* If can't read, return "Other error": */ 208 if(!DiskRead(R->AF.B.h,Buf,0)) { R->AF.W=0x0C01;return; } 209 210 BytesPerSector = (int)Buf[0x0C]*256+Buf[0x0B]; 211 SectorsPerDisk = (int)Buf[0x14]*256+Buf[0x13]; 212 SectorsPerFAT = (int)Buf[0x17]*256+Buf[0x16]; 213 ReservedSectors = (int)Buf[0x0F]*256+Buf[0x0E]; 214 215 Addr=R->HL.W+1; 216 WrZ80(Addr++,Buf[0x15]); /* Format ID [F8h-FFh] */ 217 WrZ80(Addr++,Buf[0x0B]); /* Sector size */ 218 WrZ80(Addr++,Buf[0x0C]); 219 J=(BytesPerSector>>5)-1; 220 for(I=0;J&(1<<I);I++); 221 WrZ80(Addr++,J); /* Directory mask/shft */ 222 WrZ80(Addr++,I); 223 J=Buf[0x0D]-1; 224 for(I=0;J&(1<<I);I++); 225 WrZ80(Addr++,J); /* Cluster mask/shift */ 226 WrZ80(Addr++,I+1); 227 WrZ80(Addr++,Buf[0x0E]); /* Sector # of 1st FAT */ 228 WrZ80(Addr++,Buf[0x0F]); 229 WrZ80(Addr++,Buf[0x10]); /* Number of FATs */ 230 WrZ80(Addr++,Buf[0x11]); /* Number of dirent-s */ 231 J=ReservedSectors+Buf[0x10]*SectorsPerFAT; 232 J+=32*Buf[0x11]/BytesPerSector; 233 WrZ80(Addr++,J&0xFF); /* Sector # of data */ 234 WrZ80(Addr++,(J>>8)&0xFF); 235 J=(SectorsPerDisk-J)/Buf[0x0D]; 236 WrZ80(Addr++,J&0xFF); /* Number of clusters */ 237 WrZ80(Addr++,(J>>8)&0xFF); 238 WrZ80(Addr++,Buf[0x16]); /* Sectors per FAT */ 239 J=ReservedSectors+Buf[0x10]*SectorsPerFAT; 240 WrZ80(Addr++,J&0xFF); /* Sector # of dir. */ 241 WrZ80(Addr,(J>>8)&0xFF); 242 243 /* Return success */ 244 R->AF.B.l&=~C_FLAG; 245 return; 246 } 247 248 case 0x401C: 249 /** DSKFMT: Disk format ***************************************** 250 *** Input: *** 251 *** [A] Specified choice (1-9) [D] Drive number (0=A:) *** 252 *** [HL] Begin address of work area [BC] Length of work area *** 253 *** Output: *** 254 *** [F] CARRY=ERROR *** 255 *** Notes: *** 256 *** 1) Also writes a MSX boot sector at sector 0, clears all *** 257 *** FATs (media descriptor at first byte, 0FFh at second/ *** 258 *** third byte and rest zero) and clears the directory *** 259 *** filling it with zeros. *** 260 *** 2) Error codes are: *** 261 *** 0 Write protected 10 Write fault *** 262 *** 2 Not ready 12 Bad parameter *** 263 *** 4 Data (CRC) error 14 Insufficient memory *** 264 *** 6 Seek error 16 Other errors *** 265 *** 8 Record not found *** 266 ****************************************************************/ 267 { 268 R->IFF|=1; 269 270 /* If invalid choice, return "Bad parameter": */ 271 if(!R->AF.B.h||(R->AF.B.h>2)) { R->AF.W=0x0C01;return; } 272 /* If no disk, return "Not ready": */ 273 if(!DiskPresent(R->DE.B.h)) { R->AF.W=0x0201;return; } 274 275 /* Fill bootblock with data: */ 276 P=BootBlock+3; 277 N=2-R->AF.B.h; 278 memcpy(P,"fMSXdisk",8);P+=10; /* Manufacturer's ID */ 279 *P=Info[N].PerCluster;P+=4; /* Sectors per cluster */ 280 *P++=Info[N].Names;*P++=0x00; /* Number of names */ 281 *P++=Info[N].Sectors&0xFF; /* Number of sectors */ 282 *P++=(Info[N].Sectors>>8)&0xFF; 283 *P++=N+0xF8; /* Format ID [F8h-FFh] */ 284 *P++=Info[N].PerFAT;*P++=0x00; /* Sectors per FAT */ 285 *P++=Info[N].PerTrack;*P++=0x00; /* Sectors per track */ 286 *P++=Info[N].Heads;*P=0x00; /* Number of heads */ 287 288 /* If can't write bootblock, return "Write protected": */ 289 if(!DiskWrite(R->DE.B.h,BootBlock,0)) { R->AF.W=0x0001;return; }; 290 291 /* Writing FATs: */ 292 for(Sector=1,J=0;J<2;J++) 293 { 294 Buf[0]=N+0xF8; 295 Buf[1]=Buf[2]=0xFF; 296 memset(Buf+3,0x00,509); 297 298 if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } 299 300 memset(Buf,0x00,512); 301 302 for(I=Info[N].PerFAT;I>1;I--) 303 if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } 304 } 305 306 J=Info[N].Names/16; /* Directory size */ 307 I=Info[N].Sectors-2*Info[N].PerFAT-J-1; /* Data size */ 308 309 for(memset(Buf,0x00,512);J;J--) 310 if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } 311 for(memset(Buf,0xFF,512);I;I--) 312 if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } 313 314 /* Return success */ 315 R->AF.B.l&=~C_FLAG; 316 return; 317 } 318 319 case 0x401F: 320 /** DRVOFF: Stop drives ***************************************** 321 *** Input: None *** 322 *** Output: None *** 323 ****************************************************************/ 324 return; 325 326 case 0x00E1: 327 /** TAPION: Open for read and read header *********************** 328 ****************************************************************/ 329 { 330 long Pos; 331 332 //if(Verbose&0x04) printf("TAPE: Looking for header..."); 333 334 R->AF.B.l|=C_FLAG; 335 #ifdef unused 336 if(CasStream) 337 { 338 Pos=ftell(CasStream); 339 if(Pos&7) 340 if(fseek(CasStream,8-(Pos&7),SEEK_CUR)) 341 { 342 if(Verbose&0x04) puts("FAILED"); 343 rewind(CasStream);return; 344 } 345 346 while(fread(Buf,1,8,CasStream)==8) 347 if(!memcmp(Buf,TapeHeader,8)) 348 { 349 if(Verbose&0x04) puts("OK"); 350 R->AF.B.l&=~C_FLAG;return; 351 } 352 353 rewind(CasStream); 354 } 355 356 if(Verbose&0x04) puts("FAILED"); 357 #endif 358 return; 359 } 360 361 case 0x00E4: 362 /** TAPIN: Read tape ******************************************** 363 ****************************************************************/ 364 { 365 R->AF.B.l|=C_FLAG; 366 367 #ifdef unused 368 if(CasStream) 369 { 370 J=fgetc(CasStream); 371 if(J<0) rewind(CasStream); 372 else { R->AF.B.h=J;R->AF.B.l&=~C_FLAG; } 373 } 374 #endif 375 376 return; 377 } 378 379 case 0x00E7: 380 /** TAPIOF: ***************************************************** 381 ****************************************************************/ 382 R->AF.B.l&=~C_FLAG; 383 return; 384 385 case 0x00EA: 386 /** TAPOON: ***************************************************** 387 ****************************************************************/ 388 { 389 long Pos; 390 391 R->AF.B.l|=C_FLAG; 392 393 #ifdef unused 394 if(CasStream) 395 { 396 Pos=ftell(CasStream); 397 if(Pos&7) 398 if(fseek(CasStream,8-(Pos&7),SEEK_CUR)) 399 { R->AF.B.l|=C_FLAG;return; } 400 401 fwrite(TapeHeader,1,8,CasStream); 402 R->AF.B.l&=~C_FLAG; 403 } 404 #endif 405 406 return; 407 } 408 409 case 0x00ED: 410 /** TAPOUT: Write tape ****************************************** 411 ****************************************************************/ 412 R->AF.B.l|=C_FLAG; 413 414 #ifdef unused 415 if(CasStream) 416 { 417 fputc(R->AF.B.h,CasStream); 418 R->AF.B.l&=~C_FLAG; 419 } 420 #endif 421 422 return; 423 424 case 0x00F0: 425 /** TAPOOF: ***************************************************** 426 ****************************************************************/ 427 R->AF.B.l&=~C_FLAG; 428 return; 429 430 case 0x00F3: 431 /** STMOTR: ***************************************************** 432 ****************************************************************/ 433 R->AF.B.l&=~C_FLAG; 434 return; 435 436 default: 437 //printf("Unknown BIOS trap called at PC=%04Xh\n",R->PC.W-2); 438 break; 439 } 440 }