////////////////////////////////////////////////////////////////////////// // Mapper020 Nintendo Disk System(FDS) // ////////////////////////////////////////////////////////////////////////// void Mapper020::Reset() { irq_type = 0; irq_enable = irq_repeat = 0; irq_counter = irq_latch = 0; irq_occur = 0; irq_transfer = 0; disk_enable = 0xFF; sound_enable = 0xFF; block_point = 0; block_mode = 0; RW_start = 0xFF; size_file_data = 0; file_amount = 0; point = 0; first_access = 0; disk_eject = 0xFF; drive_ready = 0; disk_side = 0xFF; // Eject disk = disk_w = NULL; // Mechanical sound sound_startup_flag = 0xFF; sound_startup_timer = -1; // stop sound_seekend_timer = -1; // stop MechanicalSound( MECHANICAL_SOUND_ALLSTOP ); // ::memset( DRAM, 0xFF, sizeof(DRAM) ); SetPROM_Bank( 3, DRAM+0x0000, BANKTYPE_DRAM ); SetPROM_Bank( 4, DRAM+0x2000, BANKTYPE_DRAM ); SetPROM_Bank( 5, DRAM+0x4000, BANKTYPE_DRAM ); SetPROM_Bank( 6, DRAM+0x6000, BANKTYPE_DRAM ); SetPROM_Bank( 7, nes->rom->GetDISKBIOS(), BANKTYPE_ROM ); SetCRAM_8K_Bank( 0 ); // デフォルト // nes->SetIrqType( NES::IRQ_HSYNC ); // 帰ってきたマリオブラザーズ if( nes->rom->GetMakerID() == 0x01 && nes->rom->GetGameID() == 0x4b4d4152 ) { nes->SetRenderMethod( NES::PRE_ALL_RENDER ); } // ガルフォース if( nes->rom->GetMakerID() == 0xB6 && nes->rom->GetGameID() == 0x47414C20 ) { nes->SetRenderMethod( NES::PRE_ALL_RENDER ); } // ファイアーバム if( nes->rom->GetMakerID() == 0xB6 && nes->rom->GetGameID() == 0x46424D20 ) { nes->SetRenderMethod( NES::PRE_ALL_RENDER ); } // 3Dホットラリー if( nes->rom->GetMakerID() == 0x01 && nes->rom->GetGameID() == 0x54445245 ) { nes->SetRenderMethod( NES::PRE_ALL_RENDER ); irq_type = 1; // インチキ } // タイムツイスト if( nes->rom->GetMakerID() == 0x01 && nes->rom->GetGameID() == 0x54540120 ) { nes->SetRenderMethod( NES::PRE_ALL_RENDER ); } // バイオミラクルぼくってウパ if( nes->rom->GetMakerID() == 0xA4 && nes->rom->GetGameID() == 0x424F4B20 ) { nes->SetRenderMethod( NES::PRE_ALL_RENDER ); } DEBUGOUT( "MAKER ID=%02X\n", nes->rom->GetMakerID() ); DEBUGOUT( "GAME ID=%08X\n", nes->rom->GetGameID() ); nes->apu->SelectExSound( 4 ); // ExCmdWrite( EXCMDWR_DISKINSERT, 0 ); // Disk 0, Side Aをセット disk = nes->rom->GetPROM()+16+65500*0; disk_w = nes->rom->GetDISK()+16+65500*0; disk_side = 0; disk_eject = 0xFF; drive_ready = 0; disk_mount_count = 119; nes->Command( NES::NESCMD_DISK_THROTTLE_OFF ); bDiskThrottle = FALSE; DiskThrottleTime = 0; } BYTE Mapper020::ExRead( WORD addr ) { BYTE data = addr>>8; switch( addr ) { case 0x4030: // Disk I/O status data = 0x80; data |= (irq_occur && !irq_transfer)?0x01:0x00; data |= (irq_occur && irq_transfer)?0x02:0x00; irq_occur = 0; // Clear Timer IRQ nes->cpu->ClrIRQ( IRQ_MAPPER ); // Clear Disk IRQ nes->cpu->ClrIRQ( IRQ_MAPPER2 ); break; case 0x4031: // Disk data read if( !RW_mode ) return 0xFF; first_access = 0; if( disk ) { switch( block_mode ) { case BLOCK_VOLUME_LABEL: data = disk[block_point]; if( block_point < SIZE_VOLUME_LABEL ) { block_point++; } else { data = 0; } return data; case BLOCK_FILE_AMOUNT: data = disk[ block_point + point ]; if( block_point < SIZE_FILE_AMOUNT ) { block_point++; file_amount = data; } else { data = 0; } return data; case BLOCK_FILE_HEADER: data = disk[ block_point + point ]; if( block_point == 13 ) size_file_data = data; else if( block_point == 14 ) size_file_data += ((INT)data<<8); if( block_point < SIZE_FILE_HEADER ) { block_point++; } else { data = 0; } return data; case BLOCK_FILE_DATA: data = disk[ block_point + point ]; if( block_point < size_file_data + 1 ) { block_point++; } else { data = 0; } return data; } } else { return 0xFF; } break; case 0x4032: // Disk status data = 0x40; data |= disk_eject?0x01:0x00; data |= disk_eject?0x04:0x00; data |= (!disk_eject && disk_motor_mode && !drive_reset)?0x00:0x02; break; case 0x4033: // External connector data/Battery sense data = 0x80; break; default: if( addr >= 0x4040 ) data = nes->apu->ExRead( addr ); break; } return data; } void Mapper020::ExWrite( WORD addr, BYTE data ) { switch( addr ) { case 0x4020: // IRQ latch low irq_latch = (irq_latch&0xFF00)|data; break; case 0x4021: // IRQ latch high irq_latch = (irq_latch&0x00FF)|((WORD)data<<8); break; case 0x4022: // IRQ control irq_repeat = data & 0x01; irq_enable = (data & 0x02) && (disk_enable); irq_occur = 0; if( irq_enable ) { irq_counter = irq_latch; } else { // Clear Timer IRQ nes->cpu->ClrIRQ( IRQ_MAPPER ); } break; case 0x4023: // 2C33 control disk_enable = data & 0x01; if( !disk_enable ) { irq_enable = 0; irq_occur = 0; // Clear Timer IRQ nes->cpu->ClrIRQ( IRQ_MAPPER ); // Clear Disk IRQ nes->cpu->ClrIRQ( IRQ_MAPPER2 ); } break; case 0x4024: // Data write // Clear Disk IRQ nes->cpu->ClrIRQ( IRQ_MAPPER2 ); if( RW_mode ) break; if( first_access ) { first_access = 0; break; } if( disk ) { switch( block_mode ) { case BLOCK_VOLUME_LABEL: if( block_point < SIZE_VOLUME_LABEL-1 ) { disk[ block_point ] = data; disk_w[ block_point ] = 0xFF; block_point++; } break; case BLOCK_FILE_AMOUNT: if( block_point < SIZE_FILE_AMOUNT ) { disk[ block_point + point ] = data; disk_w[ block_point + point ] = 0xFF; block_point++; } break; case BLOCK_FILE_HEADER: if( block_point < SIZE_FILE_HEADER ) { disk[ block_point + point ] = data; disk_w[ block_point + point ] = 0xFF; if( block_point == 13 ) size_file_data = data; else if( block_point == 14 ) size_file_data |= data << 8; block_point++; } break; case BLOCK_FILE_DATA: if( block_point < size_file_data+1 ) { disk[ block_point + point ] = data; disk_w[ block_point + point ] = 0xFF; block_point++; } break; } } break; case 0x4025: // Disk I/O control // 割り込み転送 irq_transfer = data & 0x80; if( !irq_transfer ) { nes->cpu->ClrIRQ( IRQ_MAPPER2 ); } if( !RW_start && (data & 0x40) ) { block_point = 0; switch( block_mode ) { case BLOCK_READY: block_mode = BLOCK_VOLUME_LABEL; point = 0; break; case BLOCK_VOLUME_LABEL: block_mode = BLOCK_FILE_AMOUNT; point += SIZE_VOLUME_LABEL; break; case BLOCK_FILE_AMOUNT: block_mode = BLOCK_FILE_HEADER; point += SIZE_FILE_AMOUNT; break; case BLOCK_FILE_HEADER: block_mode = BLOCK_FILE_DATA; point += SIZE_FILE_HEADER; break; case BLOCK_FILE_DATA: block_mode = BLOCK_FILE_HEADER; point += size_file_data+1; break; } // 最初の1回目の書き込みを無視するため first_access = 0xFF; } // 読み書きスタート RW_start = data & 0x40; // 読み書きモード RW_mode = data & 0x04; // 読み書きのリセット if( data&0x02 ) { point = 0; block_point = 0; block_mode = BLOCK_READY; RW_start = 0xFF; drive_reset = 0xFF; sound_startup_flag = 0; sound_startup_timer = -1; // stop } else { drive_reset = 0; if( !sound_startup_flag ) { MechanicalSound( MECHANICAL_SOUND_MOTOR_ON ); sound_startup_flag = 0xFF; sound_startup_timer = 40; sound_seekend_timer = 60*7; } } // ディスクモーターのコントロール disk_motor_mode = data & 0x01; if( !(data & 0x01) ) { if( sound_seekend_timer >= 0 ) { sound_seekend_timer = -1; MechanicalSound( MECHANICAL_SOUND_MOTOR_OFF ); } } // Mirror if( data&0x08 ) SetVRAM_Mirror( VRAM_HMIRROR ); else SetVRAM_Mirror( VRAM_VMIRROR ); break; case 0x4026: // External connector output/Battery sense break; default: if( addr >= 0x4040 ) nes->apu->ExWrite( addr, data ); break; } } void Mapper020::WriteLow( WORD addr, BYTE data ) { DRAM[addr-0x6000] = data; } void Mapper020::Write( WORD addr, BYTE data ) { if( addr < 0xE000 ) { DRAM[addr-0x6000] = data; } } void Mapper020::Clock( INT cycles ) { // Timer IRQ if( irq_enable ) { if( !irq_type ) { irq_counter -= cycles; } if( irq_counter <= 0 ) { //// irq_counter &= 0xFFFF; irq_counter += irq_latch; if( !irq_occur ) { irq_occur = 0xFF; if( !irq_repeat ) { irq_enable = 0; } nes->cpu->SetIRQ( IRQ_MAPPER ); } } if( irq_type ) { irq_counter -= cycles; } } } void Mapper020::HSync( INT scanline ) { // Disk IRQ if( irq_transfer ) { nes->cpu->SetIRQ( IRQ_MAPPER2 ); } } void Mapper020::VSync() { if( disk && disk_eject ) { if( disk_mount_count > 120 ) { disk_eject = 0; } else { disk_mount_count++; } } // if( disk && (disk_mount_count > 120) ) { if( disk ) { if( sound_startup_timer > 0 ) { sound_startup_timer--; } else if( sound_startup_timer == 0 ) { sound_startup_timer--; MechanicalSound( MECHANICAL_SOUND_BOOT ); } if( sound_seekend_timer > 0 ) { sound_seekend_timer--; } else if( sound_seekend_timer == 0 ) { sound_seekend_timer--; MechanicalSound( MECHANICAL_SOUND_MOTOR_OFF ); MechanicalSound( MECHANICAL_SOUND_SEEKEND ); sound_startup_flag = 0; } } if( irq_transfer || (disk && disk_mount_count < 120) ) { if( DiskThrottleTime > 2 ) { bDiskThrottle = TRUE; } else { bDiskThrottle = FALSE; DiskThrottleTime++; } } else { DiskThrottleTime = 0; bDiskThrottle = FALSE; } if( !bDiskThrottle ) { nes->Command( NES::NESCMD_DISK_THROTTLE_OFF ); } else { nes->Command( NES::NESCMD_DISK_THROTTLE_ON ); } } BYTE Mapper020::ExCmdRead( EXCMDRD cmd ) { BYTE data = 0x00; if( cmd == EXCMDRD_DISKACCESS ) { if( irq_transfer ) return 0xFF; } return data; } void Mapper020::ExCmdWrite( EXCMDWR cmd, BYTE data ) { switch( cmd ) { case EXCMDWR_NONE: break; case EXCMDWR_DISKINSERT: disk = nes->rom->GetPROM()+16+65500*data; disk_w = nes->rom->GetDISK()+16+65500*data; disk_side = data; disk_eject = 0xFF; drive_ready = 0; disk_mount_count = 0; break; case EXCMDWR_DISKEJECT: disk = NULL; // とりあえず disk_w = NULL; disk_side = 0xFF; disk_eject = 0xFF; drive_ready = 0; disk_mount_count = 0; break; } } void Mapper020::MechanicalSound( INT type ) { switch( type ) { case MECHANICAL_SOUND_BOOT: // Head start point CAM sound. if( Config.sound.bExtraSoundEnable ) DirectSound.EsfPlay( ESF_DISKSYSTEM_BOOT ); break; case MECHANICAL_SOUND_SEEKEND: if( Config.sound.bExtraSoundEnable ) DirectSound.EsfPlay( ESF_DISKSYSTEM_SEEKEND ); // Reset or Seekend sound. break; case MECHANICAL_SOUND_MOTOR_ON: if( Config.sound.bExtraSoundEnable ) DirectSound.EsfPlayLoop( ESF_DISKSYSTEM_MOTOR ); // Start Motor sound.(loop) break; case MECHANICAL_SOUND_MOTOR_OFF: DirectSound.EsfStop( ESF_DISKSYSTEM_MOTOR ); // Stop Motor sound.(loop) break; case MECHANICAL_SOUND_ALLSTOP: // Stop sound. DirectSound.EsfAllStop(); break; default: break; } } void Mapper020::SaveState( LPBYTE p ) { p[0] = irq_enable; p[1] = irq_repeat; p[2] = irq_occur; p[3] = irq_transfer; *(INT*)&p[4] = irq_counter; *(INT*)&p[8] = irq_latch; p[12] = disk_enable; p[13] = sound_enable; p[14] = RW_start; p[15] = RW_mode; p[16] = disk_motor_mode; p[17] = disk_eject; p[18] = drive_ready; p[19] = drive_reset; *(INT*)&p[20] = block_point; *(INT*)&p[24] = block_mode; *(INT*)&p[28] = size_file_data; *(INT*)&p[32] = file_amount; *(INT*)&p[36] = point; p[40] = first_access; p[41] = disk_side; p[42] = disk_mount_count; p[44] = sound_startup_flag; *(INT*)&p[48] = sound_startup_timer; *(INT*)&p[52] = sound_seekend_timer; } void Mapper020::LoadState( LPBYTE p ) { irq_enable = p[0]; irq_repeat = p[1]; irq_occur = p[2]; irq_transfer = p[3]; irq_counter = *(INT*)&p[4]; irq_latch = *(INT*)&p[8]; disk_enable = p[12]; sound_enable = p[13]; RW_start = p[14]; RW_mode = p[15]; disk_motor_mode = p[16]; disk_eject = p[17]; drive_ready = p[18]; drive_reset = p[19]; block_point = *(INT*)&p[20]; block_mode = *(INT*)&p[24]; size_file_data = *(INT*)&p[28]; file_amount = *(INT*)&p[32]; point = *(INT*)&p[36]; first_access = p[40]; disk_side = p[41]; disk_mount_count= p[42]; sound_startup_flag = p[44]; sound_startup_timer = *(INT*)&p[48]; sound_seekend_timer = *(INT*)&p[52]; if( disk_side != 0xFF ) { disk = nes->rom->GetPROM()+sizeof(NESHEADER)+65500*disk_side; disk_w = nes->rom->GetDISK()+sizeof(NESHEADER)+65500*disk_side; } else { disk = NULL; disk_w = NULL; } // DiskBios Setup(ステートで上書きされている為) SetPROM_Bank( 7, nes->rom->GetDISKBIOS(), BANKTYPE_ROM ); } void Mapper020::PPU_Latch( WORD addr ) { if(DirectInput.m_Sw[DIK_PAUSE]){ nes->Dump_CPULMEM(); nes->Dump_CPUHMEM(); nes->Dump_CRAM(); } }