AxibugEmuOnline/References/VirtuaNESex_src_191105/NES/Mapper/MapperFDS.cpp
2024-08-05 17:58:53 +08:00

597 lines
13 KiB
C++

//////////////////////////////////////////////////////////////////////////
// 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();
}
}