diff --git a/Builder/Windows/XM8.vcxproj b/Builder/Windows/XM8.vcxproj index e0758aa..45d137d 100644 --- a/Builder/Windows/XM8.vcxproj +++ b/Builder/Windows/XM8.vcxproj @@ -366,9 +366,10 @@ - - - + + + + @@ -416,9 +417,10 @@ - - - + + + + @@ -452,4 +454,4 @@ - \ No newline at end of file + diff --git a/Builder/Windows/XM8.vcxproj.filters b/Builder/Windows/XM8.vcxproj.filters index ba82036..1085d49 100644 --- a/Builder/Windows/XM8.vcxproj.filters +++ b/Builder/Windows/XM8.vcxproj.filters @@ -105,9 +105,12 @@ ソース ファイル\ePC-8801MA\vm\pc8801 - - ソース ファイル\ePC-8801MA\vm\pc8801 - + + ソース ファイル\ePC-8801MA\vm\pc8801 + + + ソース ファイル\ePC-8801MA\vm + ソース ファイル\UI @@ -236,9 +239,12 @@ ヘッダー ファイル\ePC-8801MA\vm - - ヘッダー ファイル\ePC-8801MA\vm\pc8801 - + + ヘッダー ファイル\ePC-8801MA\vm\pc8801 + + + ヘッダー ファイル\ePC-8801MA\vm + ヘッダー ファイル\UI @@ -332,4 +338,4 @@ リソース ファイル - \ No newline at end of file + diff --git a/CMakeLists.txt b/CMakeLists.txt index 62a6b3d..375ab6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,7 @@ set(SRCS Source/ePC-8801MA/vm/i8255.cpp Source/ePC-8801MA/vm/disksub.cpp Source/ePC-8801MA/vm/pcm1bit.cpp + Source/ePC-8801MA/vm/noise.cpp Source/ePC-8801MA/vm/upd765a.cpp Source/ePC-8801MA/vm/upd1990a.cpp Source/ePC-8801MA/vm/fmsound.cpp @@ -238,4 +239,4 @@ if(ENABLE_PACKAGING) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) include(CPack) -endif() \ No newline at end of file +endif() diff --git a/Source/ePC-8801MA/vm/disk.cpp b/Source/ePC-8801MA/vm/disk.cpp index a0b8ae8..cee1ba6 100644 --- a/Source/ePC-8801MA/vm/disk.cpp +++ b/Source/ePC-8801MA/vm/disk.cpp @@ -600,8 +600,8 @@ bool DISK::make_track(int trk, int side) return true; } -bool DISK::get_sector(int trk, int side, int index) -{ +bool DISK::get_sector(int trk, int side, int index) +{ sector_size.sd = sector_num.sd = 0; sector = NULL; @@ -640,12 +640,38 @@ bool DISK::get_sector(int trk, int side, int index) data_size.read_2bytes_le_from(t + 14); t += data_size.sd + 0x10; } - set_sector_info(t); - return true; -} - -void DISK::set_sector_info(uint8 *t) -{ + set_sector_info(t); + return true; +} + +bool DISK::get_sector_info(int trk, int side, int index, uint8* c, uint8* h, uint8* r, uint8* n, bool* mfm, int* length) +{ + if(!get_sector(trk, side, index)) { + return false; + } + if(c != NULL) { + *c = id[0]; + } + if(h != NULL) { + *h = id[1]; + } + if(r != NULL) { + *r = id[2]; + } + if(n != NULL) { + *n = id[3]; + } + if(mfm != NULL) { + *mfm = (density == 0x00); + } + if(length != NULL) { + *length = sector_size.sd; + } + return true; +} + +void DISK::set_sector_info(uint8 *t) +{ // header info id[0] = t[0]; id[1] = t[1]; @@ -685,14 +711,24 @@ void DISK::set_deleted(bool value) deleted = value; } -void DISK::set_crc_error(bool value) -{ +void DISK::set_crc_error(bool value) +{ if(sector != NULL) { uint8 *t = sector - 0x10; t[8] = (t[8] & 0x0f) | (value ? 0xb0 : t[7]); // FIXME: always data crc error ? } - crc_error = value; -} + crc_error = value; +} + +void DISK::set_data_mark_missing() +{ + if(sector != NULL) { + uint8 *t = sector - 0x10; + t[8] = (t[8] & 0x0f) | 0xf0; + t[14] = t[15] = 0; + } + crc_error = false; +} bool DISK::format_track(int trk, int side) { @@ -821,8 +857,8 @@ void DISK::trim_buffer() memcpy(buffer, tmp_buffer, file_size.d); } -int DISK::get_rpm() -{ +int DISK::get_rpm() +{ if(drive_rpm != 0) { return drive_rpm; } else if(inserted) { @@ -830,10 +866,21 @@ int DISK::get_rpm() } else { return (drive_type == DRIVE_TYPE_2HD) ? 360 : 300; } -} - -int DISK::get_track_size() -{ +} + +int DISK::get_max_tracks() +{ + if(drive_type != DRIVE_TYPE_UNK) { + return (drive_type != DRIVE_TYPE_2D) ? 84 : 42; + } else if(inserted) { + return (media_type != MEDIA_TYPE_2D) ? 84 : 42; + } else { + return 84; + } +} + +int DISK::get_track_size() +{ if(inserted) { return media_type == MEDIA_TYPE_144 ? 12500 : media_type == MEDIA_TYPE_2HD ? 10410 : drive_mfm ? 6250 : 3100; } else { diff --git a/Source/ePC-8801MA/vm/disk.h b/Source/ePC-8801MA/vm/disk.h index 3a82815..535a9f7 100644 --- a/Source/ePC-8801MA/vm/disk.h +++ b/Source/ePC-8801MA/vm/disk.h @@ -170,17 +170,20 @@ class DISK bool get_track(int trk, int side); bool make_track(int trk, int side); bool get_sector(int trk, int side, int index); + bool get_sector_info(int trk, int side, int index, uint8* c, uint8* h, uint8* r, uint8* n, bool* mfm, int* length); void set_deleted(bool value); void set_crc_error(bool value); void set_data_crc_error(bool value) { set_crc_error(value); } - - bool format_track(int trk, int side); - void insert_sector(uint8 c, uint8 h, uint8 r, uint8 n, bool deleted, bool crc_error, uint8 fill_data, int length); - void sync_buffer(); - + void set_data_mark_missing(); + + bool format_track(int trk, int side); + void insert_sector(uint8 c, uint8 h, uint8 r, uint8 n, bool deleted, bool crc_error, uint8 fill_data, int length); + void sync_buffer(); + + int get_max_tracks(); int get_rpm(); int get_track_size(); double get_usec_per_track() @@ -190,6 +193,15 @@ class DISK double get_usec_per_bytes(int bytes); int get_bytes_per_usec(double usec); bool check_media_type(); + bool correct_timing() + { + // Keep current XM8 behavior: standard images use fixed timing path. + return !is_standard_image; + } + bool ignore_crc() + { + return config.ignore_crc; + } bool inserted; bool ejected; diff --git a/Source/ePC-8801MA/vm/noise.cpp b/Source/ePC-8801MA/vm/noise.cpp new file mode 100644 index 0000000..e698e8f --- /dev/null +++ b/Source/ePC-8801MA/vm/noise.cpp @@ -0,0 +1,247 @@ +/* + Skelton for retropc emulator + + Author : Takeda.Toshiya + Date : 2017.03.08- + + [ noise player ] +*/ + +#include "noise.h" + +#include + +#define EVENT_SAMPLE 0 + +#pragma pack(push, 1) +typedef struct { + char id[4]; + uint32 size; +} wav_chunk_t; + +typedef struct { + char riff[4]; + uint32 file_size; + char wave[4]; + wav_chunk_t fmt_chunk; + uint16 format_id; + uint16 channels; + uint32 sample_rate; + uint32 data_speed; + uint16 block_size; + uint16 sample_bits; +} wav_header_t; +#pragma pack(pop) + +static int noise_decibel_to_volume(int decibel) +{ + // +1 equals +0.5dB (same as fmgen/common_source_project). + const double step = 1.0592537251772889; + double factor = 1.0; + if(decibel >= 0) { + for(int i = 0; i < decibel; i++) { + factor *= step; + } + } else { + for(int i = 0; i < -decibel; i++) { + factor /= step; + } + } + return (int)(1024.0 * factor + 0.5); +} + +static int32 noise_apply_volume(int32 sample, int volume) +{ + return (sample * volume) / 1024; +} + +void NOISE::initialize() +{ + register_id = -1; + ptr = 0; + sample_l = sample_r = 0; +} + +void NOISE::release() +{ + if(buffer_l != NULL) { + free(buffer_l); + buffer_l = NULL; + } + if(buffer_r != NULL) { + free(buffer_r); + buffer_r = NULL; + } +} + +void NOISE::reset() +{ + stop(); +} + +void NOISE::event_callback(int event_id, int err) +{ + if(++ptr < samples) { + get_sample(); + } else if(loop) { + ptr = 0; + get_sample(); + } else { + stop(); + } +} + +void NOISE::mix(int32* buffer, int cnt) +{ + if(register_id != -1 && !mute) { + int32 val_l = noise_apply_volume(sample_l, volume_l); + int32 val_r = noise_apply_volume(sample_r, volume_r); + + for(int i = 0; i < cnt; i++) { + *buffer++ += val_l; // L + *buffer++ += val_r; // R + } + } +} + +void NOISE::set_volume(int ch, int decibel_l, int decibel_r) +{ + volume_l = noise_decibel_to_volume(decibel_l); + volume_r = noise_decibel_to_volume(decibel_r); +} + +bool NOISE::load_wav_file(const _TCHAR *file_name) +{ + if(samples != 0) { + // already loaded + return true; + } + FILEIO *fio = new FILEIO(); + bool result = false; + + _TCHAR path[_MAX_PATH]; + _tcscpy_s(path, array_length(path), file_name); + if(fio->Fopen(path, FILEIO_READ_BINARY)) { + wav_header_t header; + wav_chunk_t chunk; + + fio->Fread(&header, sizeof(header), 1); + + if(header.format_id == 1 && (header.sample_bits == 8 || header.sample_bits == 16)) { + if(header.fmt_chunk.size > 16) { + fio->Fseek(header.fmt_chunk.size - 16, FILEIO_SEEK_CUR); + } + while(1) { + if(fio->Fread(&chunk, sizeof(chunk), 1) != 1) { + break; + } + if(strncmp(chunk.id, "data", 4) == 0) { + break; + } + fio->Fseek(chunk.size, FILEIO_SEEK_CUR); + } + if(strncmp(chunk.id, "data", 4) == 0 && header.channels > 0 && + (samples = chunk.size / header.channels) > 0) { + if(header.sample_bits == 16) { + samples /= 2; + } + sample_rate = header.sample_rate; + + buffer_l = (int16 *)malloc(samples * sizeof(int16)); + buffer_r = (int16 *)malloc(samples * sizeof(int16)); + + for(int i = 0; i < samples; i++) { + int sample_lr[2] = {0, 0}; + for(int ch = 0; ch < header.channels; ch++) { + int16 sample = 0; + if(header.sample_bits == 16) { + union { + int16 s16; + struct { + uint8 l, h; + } b; + } pair; + pair.b.l = fio->FgetUint8(); + pair.b.h = fio->FgetUint8(); + sample = pair.s16; + } else { + sample = (int16)(fio->FgetUint8()); + sample = (sample - 128) * 256; + } + if(ch < 2) sample_lr[ch] = sample; + } + buffer_l[i] = sample_lr[0]; + buffer_r[i] = sample_lr[(header.channels > 1) ? 1 : 0]; + } + result = true; + } + } + fio->Fclose(); + } + delete fio; + + return result; +} + +void NOISE::play() +{ + if(samples > 0 && register_id == -1 && !mute) { + register_event(this, EVENT_SAMPLE, 1000000.0 / sample_rate, true, ®ister_id); + ptr = 0; + get_sample(); + } +} + +void NOISE::stop() +{ + if(samples > 0 && register_id != -1) { + cancel_event(this, register_id); + register_id = -1; + sample_l = sample_r = 0; + } +} + +void NOISE::get_sample() +{ + if(buffer_l != NULL && ptr < samples) { + sample_l = buffer_l[ptr]; + } else { + sample_l = 0; + } + if(buffer_r != NULL && ptr < samples) { + sample_r = buffer_r[ptr]; + } else { + sample_r = 0; + } +} + +#define STATE_VERSION 1 + +bool NOISE::process_state(FILEIO* state_fio, bool loading) +{ + if(loading) { + if(state_fio->FgetUint32() != STATE_VERSION) { + return false; + } + if(state_fio->FgetInt32() != this_device_id) { + return false; + } + register_id = state_fio->FgetInt32(); + ptr = state_fio->FgetInt32(); + sample_l = state_fio->FgetInt32(); + sample_r = state_fio->FgetInt32(); + loop = state_fio->FgetBool(); + mute = state_fio->FgetBool(); + } else { + state_fio->FputUint32(STATE_VERSION); + state_fio->FputInt32(this_device_id); + state_fio->FputInt32(register_id); + state_fio->FputInt32(ptr); + state_fio->FputInt32(sample_l); + state_fio->FputInt32(sample_r); + state_fio->FputBool(loop); + state_fio->FputBool(mute); + } + return true; +} + diff --git a/Source/ePC-8801MA/vm/noise.h b/Source/ePC-8801MA/vm/noise.h new file mode 100644 index 0000000..8e8fff4 --- /dev/null +++ b/Source/ePC-8801MA/vm/noise.h @@ -0,0 +1,69 @@ +/* + Skelton for retropc emulator + + Author : Takeda.Toshiya + Date : 2017.03.08- + + [ noise player ] +*/ + +#ifndef _NOISE_H_ +#define _NOISE_H_ + +#include "vm_template.h" +#include "../emu.h" +#include "device.h" + +class NOISE : public DEVICE +{ +private: + int16* buffer_l; + int16* buffer_r; + int samples; + int sample_rate; + int register_id; + int ptr; + int sample_l, sample_r; + int volume_l, volume_r; + bool loop; + bool mute; + + void get_sample(); + +public: + NOISE(VM_TEMPLATE* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) + { + buffer_l = buffer_r = NULL; + samples = 0; + volume_l = volume_r = 1024; + loop = false; + mute = false; + set_device_name(_T("Noise Player")); + } + ~NOISE() {} + + // common functions + void initialize(); + void release(); + void reset(); + void event_callback(int event_id, int err); + void mix(int32* buffer, int cnt); + void set_volume(int ch, int decibel_l, int decibel_r); + bool process_state(FILEIO* state_fio, bool loading); + + // unique functions + bool load_wav_file(const _TCHAR *file_name); + void play(); + void stop(); + void set_loop(bool value) + { + loop = value; + } + void set_mute(bool value) + { + mute = value; + } +}; + +#endif + diff --git a/Source/ePC-8801MA/vm/pc8801/pc8801.cpp b/Source/ePC-8801MA/vm/pc8801/pc8801.cpp index d2b879f..3b77d73 100644 --- a/Source/ePC-8801MA/vm/pc8801/pc8801.cpp +++ b/Source/ePC-8801MA/vm/pc8801/pc8801.cpp @@ -22,9 +22,10 @@ #include "../fmsound.h" #include "../z80.h" -#include "../disk.h" -#include "../disksub.h" -#include "../upd765a.h" +#include "../disk.h" +#include "../disksub.h" +#include "../noise.h" +#include "../upd765a.h" #ifdef USE_DEBUGGER #include "../debugger.h" @@ -72,10 +73,10 @@ VM::VM(EMU* parent_emu) : emu(parent_emu) // pc88sub->set_context_event_manager(pc88event); pc88pio_sub = new I8255(this, emu); // pc88pio_sub->set_context_event_manager(pc88event); - pc88fdc_sub = new UPD765A(this, emu); -// pc88fdc_sub->set_context_event_manager(pc88event); - pc88cpu_sub = new Z80(this, emu); -// pc88cpu_sub->set_context_event_manager(pc88event); + pc88fdc_sub = new UPD765A(this, emu); +// pc88fdc_sub->set_context_event_manager(pc88event); + pc88cpu_sub = new Z80(this, emu); +// pc88cpu_sub->set_context_event_manager(pc88event); #ifdef SUPPORT_PC88_PCG8100 pc88pit = new I8253(this, emu); @@ -87,7 +88,10 @@ VM::VM(EMU* parent_emu) : emu(parent_emu) pc88pcm2 = new PCM1BIT(this, emu); // pc88pcm->set_context_event_manager(pc88event); #endif - pc88sb2 = new FMSound(this, emu); + pc88sb2 = new FMSound(this, emu); + pc88noise_seek = new NOISE(this, emu); + pc88noise_head_down = new NOISE(this, emu); + pc88noise_head_up = new NOISE(this, emu); #ifdef SUPPORT_PC88_HIGH_CLOCK @@ -96,12 +100,15 @@ VM::VM(EMU* parent_emu) : emu(parent_emu) pc88event->set_context_cpu(pc88cpu, 3993624); #endif pc88event->set_context_cpu(pc88cpu_sub, 3993624); - pc88event->set_context_sound(pc88opn); - pc88event->set_context_sound(pc88sb2); - pc88event->set_context_sound(pc88pcm); -#ifdef SUPPORT_PC88_PCG8100 - pc88event->set_context_sound(pc88pcm0); - pc88event->set_context_sound(pc88pcm1); + pc88event->set_context_sound(pc88opn); + pc88event->set_context_sound(pc88sb2); + pc88event->set_context_sound(pc88pcm); + pc88event->set_context_sound(pc88noise_seek); + pc88event->set_context_sound(pc88noise_head_down); + pc88event->set_context_sound(pc88noise_head_up); +#ifdef SUPPORT_PC88_PCG8100 + pc88event->set_context_sound(pc88pcm0); + pc88event->set_context_sound(pc88pcm1); pc88event->set_context_sound(pc88pcm2); #endif @@ -139,13 +146,16 @@ VM::VM(EMU* parent_emu) : emu(parent_emu) pc88pio->clear_ports_by_cmdreg = true; pc88pio_sub->set_context_port_a(pc88pio, SIG_I8255_PORT_B, 0xff, 0); pc88pio_sub->set_context_port_b(pc88pio, SIG_I8255_PORT_A, 0xff, 0); - pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0x0f, 4); - pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0xf0, -4); - pc88pio_sub->clear_ports_by_cmdreg = true; - pc88fdc_sub->set_context_irq(pc88cpu_sub, SIG_CPU_IRQ, 1); - pc88cpu_sub->set_context_mem(pc88sub); - pc88cpu_sub->set_context_io(pc88sub); - pc88cpu_sub->set_context_intr(pc88sub); + pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0x0f, 4); + pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0xf0, -4); + pc88pio_sub->clear_ports_by_cmdreg = true; + pc88fdc_sub->set_context_irq(pc88cpu_sub, SIG_CPU_IRQ, 1); + pc88fdc_sub->set_context_noise_seek(pc88noise_seek); + pc88fdc_sub->set_context_noise_head_down(pc88noise_head_down); + pc88fdc_sub->set_context_noise_head_up(pc88noise_head_up); + pc88cpu_sub->set_context_mem(pc88sub); + pc88cpu_sub->set_context_io(pc88sub); + pc88cpu_sub->set_context_intr(pc88sub); #ifdef USE_DEBUGGER pc88cpu_sub->set_context_debugger(new DEBUGGER(this, emu)); #endif diff --git a/Source/ePC-8801MA/vm/pc8801/pc8801.h b/Source/ePC-8801MA/vm/pc8801/pc8801.h index a8dee3b..0a9c5d3 100644 --- a/Source/ePC-8801MA/vm/pc8801/pc8801.h +++ b/Source/ePC-8801MA/vm/pc8801/pc8801.h @@ -115,6 +115,7 @@ class DiskSub; class PC80S31K; #endif // SDL class UPD765A; +class NOISE; #ifdef SUPPORT_PC88_PCG8100 class I8253; @@ -149,6 +150,9 @@ class VM #endif // SDL I8255* pc88pio_sub; UPD765A* pc88fdc_sub; + NOISE* pc88noise_seek; + NOISE* pc88noise_head_down; + NOISE* pc88noise_head_up; Z80* pc88cpu_sub; #ifdef SUPPORT_PC88_PCG8100 diff --git a/Source/ePC-8801MA/vm/upd765a.cpp b/Source/ePC-8801MA/vm/upd765a.cpp index 6763b6b..e972e16 100644 --- a/Source/ePC-8801MA/vm/upd765a.cpp +++ b/Source/ePC-8801MA/vm/upd765a.cpp @@ -8,8 +8,9 @@ [ uPD765A ] */ -#include "upd765a.h" -#include "disk.h" +#include "upd765a.h" +#include "disk.h" +#include "noise.h" #define EVENT_PHASE 0 #define EVENT_DRQ 1 @@ -141,16 +142,37 @@ } \ } -void UPD765A::initialize() -{ - // initialize d88 handler - for(int i = 0; i < 4; i++) { - disk[i] = new DISK(emu); - } - - // initialize fdc - memset(fdc, 0, sizeof(fdc)); - memset(buffer, 0, sizeof(buffer)); +void UPD765A::initialize() +{ + // initialize d88 handler + for(int i = 0; i < 4; i++) { + disk[i] = new DISK(emu); + } + + // initialize optional drive noise players + if(d_noise_seek != NULL) { + d_noise_seek->set_device_name(_T("Noise Player (FDD Seek)")); + if(!d_noise_seek->load_wav_file(_T("FDDSEEK.WAV"))) { + if(!d_noise_seek->load_wav_file(_T("FDDSEEK1.WAV"))) { + d_noise_seek->load_wav_file(_T("SEEK.WAV")); + } + } + d_noise_seek->set_mute(false); + } + if(d_noise_head_down != NULL) { + d_noise_head_down->set_device_name(_T("Noise Player (FDD Head Load)")); + d_noise_head_down->load_wav_file(_T("HEADDOWN.WAV")); + d_noise_head_down->set_mute(false); + } + if(d_noise_head_up != NULL) { + d_noise_head_up->set_device_name(_T("Noise Player (FDD Head Unload)")); + d_noise_head_up->load_wav_file(_T("HEADUP.WAV")); + d_noise_head_up->set_mute(false); + } + + // initialize fdc + memset(fdc, 0, sizeof(fdc)); + memset(buffer, 0, sizeof(buffer)); phase = prevphase = PHASE_IDLE; status = S_RQM; @@ -446,8 +468,8 @@ uint32 UPD765A::read_signal(int ch) return stat; } -void UPD765A::event_callback(int event_id, int err) -{ +void UPD765A::event_callback(int event_id, int err) +{ #ifdef SDL request_single_exec(); #endif // SDL @@ -485,11 +507,16 @@ void UPD765A::event_callback(int event_id, int err) int drv = event_id - EVENT_SEEK; seek_id[drv] = -1; seek_event(drv); - } -} - -void UPD765A::set_irq(bool val) -{ + } +} + +void UPD765A::update_config() +{ + // reserved for optional noise device contexts +} + +void UPD765A::set_irq(bool val) +{ #ifdef _FDC_DEBUG_LOG // emu->out_debug_log("FDC: IRQ=%d\n", val ? 1 : 0); #endif @@ -682,24 +709,28 @@ void UPD765A::cmd_recalib() } } -void UPD765A::seek(int drv, int trk) -{ - // get distance - int seektime = 32 - 2 * step_rate_time; - if(disk[drv]->drive_type == DRIVE_TYPE_2HD) { - seektime /= 2; - } +void UPD765A::seek(int drv, int trk) +{ + // get distance + int prev_track = fdc[drv].track; + int seektime = 32 - 2 * step_rate_time; + if(disk[drv]->drive_type == DRIVE_TYPE_2HD) { + seektime /= 2; + } seektime = (trk == fdc[drv].track) ? 120 : seektime * abs(trk - fdc[drv].track) + 500; //usec if(drv >= MAX_DRIVE) { // invalid drive number fdc[drv].result = (drv & DRIVE_MASK) | ST0_SE | ST0_NR | ST0_AT; set_irq(true); - } else { - fdc[drv].track = trk; -#ifdef UPD765A_DONT_WAIT_SEEK - seek_event(drv); -#else + } else { + fdc[drv].track = trk; + if(prev_track != trk && d_noise_seek != NULL) { + d_noise_seek->play(); + } +#ifdef UPD765A_DONT_WAIT_SEEK + seek_event(drv); +#else if(seek_id[drv] != -1) { cancel_event(this, seek_id[drv]); } @@ -1024,42 +1055,51 @@ uint32 UPD765A::read_sector() return ST0_AT | ST1_MA; } int cy = -1; - for(int i = 0; i < secnum; i++) { - if(!disk[drv]->get_sector(trk, side, i)) { - continue; - } - cy = disk[drv]->id[0]; -#if 0 - if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] /*|| disk[drv]->id[3] != id[3]*/) { -#else - if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] || disk[drv]->id[3] != id[3]) { -#endif - continue; - } - // sector number is matched + for(int i = 0; i < secnum; i++) { + if(!disk[drv]->get_sector(trk, side, i)) { + continue; + } + if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + continue; + } + cy = disk[drv]->id[0]; +#if 0 + if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] /*|| disk[drv]->id[3] != id[3]*/) { +#else + if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] || disk[drv]->id[3] != id[3]) { +#endif + continue; + } + if(disk[drv]->sector_size.sd == 0) { + continue; + } + // sector number is matched if(disk[drv]->invalid_format) { memset(buffer, disk[drv]->drive_mfm ? 0x4e : 0xff, sizeof(buffer)); memcpy(buffer, disk[drv]->sector, disk[drv]->sector_size.sd); } else { memcpy(buffer, disk[drv]->track + disk[drv]->data_position[i], disk[drv]->get_track_size() - disk[drv]->data_position[i]); memcpy(buffer + disk[drv]->get_track_size() - disk[drv]->data_position[i], disk[drv]->track, disk[drv]->data_position[i]); - } - fdc[drv].next_trans_position = disk[drv]->data_position[i]; - - if(disk[drv]->crc_error) { - return ST0_AT | ST1_DE | ST2_DD; - } - if(disk[drv]->deleted) { - return ST2_CM; - } + } + fdc[drv].next_trans_position = disk[drv]->data_position[i]; + + if(disk[drv]->crc_error && !disk[drv]->ignore_crc()) { + return ST0_AT | ST1_DE | ST2_DD; + } + if(disk[drv]->deleted) { + return ST2_CM; + } return 0; } -#ifdef _FDC_DEBUG_LOG - emu->out_debug_log("FDC: SECTOR NOT FOUND (TRK=%d SIDE=%d ID=%2x,%2x,%2x,%2x)\n", trk, side, id[0], id[1], id[2], id[3]); -#endif - if(cy != id[0] && cy != -1) { - if(cy == 0xff) { - return ST0_AT | ST1_ND | ST2_BC; + #ifdef _FDC_DEBUG_LOG + emu->out_debug_log("FDC: SECTOR NOT FOUND (TRK=%d SIDE=%d ID=%2x,%2x,%2x,%2x)\n", trk, side, id[0], id[1], id[2], id[3]); + #endif + if(cy == -1) { + return ST0_AT | ST1_MA; + } + if(cy != id[0] && cy != -1) { + if(cy == 0xff) { + return ST0_AT | ST1_ND | ST2_BC; } else { return ST0_AT | ST1_ND | ST2_NC; } @@ -1085,19 +1125,25 @@ uint32 UPD765A::write_sector(bool deleted) return ST0_AT | ST1_MA; } int cy = -1; - for(int i = 0; i < secnum; i++) { - if(!disk[drv]->get_sector(trk, side, i)) { - continue; - } - cy = disk[drv]->id[0]; - if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] /*|| disk[drv]->id[3] != id[3]*/) { - continue; - } - // sector number is matched - int size = 0x80 << (id[3] & 7); - memcpy(disk[drv]->sector, buffer, __min(size, disk[drv]->sector_size.sd)); - disk[drv]->set_deleted(deleted); - return 0; + for(int i = 0; i < secnum; i++) { + if(!disk[drv]->get_sector(trk, side, i)) { + continue; + } + if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + continue; + } + cy = disk[drv]->id[0]; + if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] /*|| disk[drv]->id[3] != id[3]*/) { + continue; + } + if(disk[drv]->sector_size.sd == 0) { + continue; + } + // sector number is matched + int size = 0x80 << min((int)id[3], 7); + memcpy(disk[drv]->sector, buffer, __min(size, disk[drv]->sector_size.sd)); + disk[drv]->set_deleted(deleted); + return 0; } if(cy != id[0] && cy != -1) { if(cy == 0xff) { @@ -1124,21 +1170,30 @@ uint32 UPD765A::find_id() return ST0_AT | ST1_MA; } int cy = -1; - for(int i = 0; i < secnum; i++) { - if(!disk[drv]->get_sector(trk, side, i)) { - continue; - } - cy = disk[drv]->id[0]; - if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] /*|| disk[drv]->id[3] != id[3]*/) { - continue; - } - // sector number is matched - fdc[drv].next_trans_position = disk[drv]->data_position[i]; - return 0; - } - if(cy != id[0] && cy != -1) { - if(cy == 0xff) { - return ST0_AT | ST1_ND | ST2_BC; + for(int i = 0; i < secnum; i++) { + if(!disk[drv]->get_sector(trk, side, i)) { + continue; + } + if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + continue; + } + cy = disk[drv]->id[0]; + if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] /*|| disk[drv]->id[3] != id[3]*/) { + continue; + } + if(disk[drv]->sector_size.sd == 0) { + continue; + } + // sector number is matched + fdc[drv].next_trans_position = disk[drv]->data_position[i]; + return 0; + } + if(cy == -1) { + return ST0_AT | ST1_MA; + } + if(cy != id[0] && cy != -1) { + if(cy == 0xff) { + return ST0_AT | ST1_ND | ST2_BC; } else { return ST0_AT | ST1_ND | ST2_NC; } @@ -1294,26 +1349,30 @@ uint32 UPD765A::read_id() break; } } - for(int i = 0; i < secnum; i++) { - int index = (first_sector + i) % secnum; - if(disk[drv]->get_sector(trk, side, index)) { - id[0] = disk[drv]->id[0]; - id[1] = disk[drv]->id[1]; - id[2] = disk[drv]->id[2]; - id[3] = disk[drv]->id[3]; - fdc[drv].next_trans_position = disk[drv]->id_position[index] + 6; - return 0; - } - } - return ST0_AT | ST1_ND; -} - -uint32 UPD765A::write_id() -{ - int drv = hdu & DRIVE_MASK; - int trk = fdc[drv].track; - int side = (hdu >> 2) & 1; - int length = 0x80 << (id[3] & 7); + for(int i = 0; i < secnum; i++) { + int index = (first_sector + i) % secnum; + if(!disk[drv]->get_sector(trk, side, index)) { + continue; + } + if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + continue; + } + id[0] = disk[drv]->id[0]; + id[1] = disk[drv]->id[1]; + id[2] = disk[drv]->id[2]; + id[3] = disk[drv]->id[3]; + fdc[drv].next_trans_position = disk[drv]->id_position[index] + 6; + return 0; + } + return ST0_AT | ST1_MA; +} + +uint32 UPD765A::write_id() +{ + int drv = hdu & DRIVE_MASK; + int trk = fdc[drv].track; + int side = (hdu >> 2) & 1; + int length = 0x80 << min((int)id[3], 7); if((result = check_cond(true)) != 0) { return result; @@ -1464,13 +1523,13 @@ int UPD765A::get_cur_position(int drv) double UPD765A::get_usec_to_exec_phase() { int drv = hdu & DRIVE_MASK; - int trk = fdc[drv].track; - int side = (hdu >> 2) & 1; - - // XXX: this is a standard image and skew may be incorrect - if(disk[drv]->is_standard_image) { - return 100; - } + int trk = fdc[drv].track; + int side = (hdu >> 2) & 1; + + // XXX: this image may have incorrect skew, so use constant period. + if(!disk[drv]->correct_timing()) { + return 100; + } // search target sector int position = get_cur_position(drv); diff --git a/Source/ePC-8801MA/vm/upd765a.h b/Source/ePC-8801MA/vm/upd765a.h index 265794d..c69299c 100644 --- a/Source/ePC-8801MA/vm/upd765a.h +++ b/Source/ePC-8801MA/vm/upd765a.h @@ -24,18 +24,24 @@ #define SIG_UPD765A_DRQ_MASK 6 #define SIG_UPD765A_FREADY 7 -class DISK; +class DISK; +class NOISE; class UPD765A : public DEVICE { private: // output signals - outputs_t outputs_irq; - outputs_t outputs_drq; - outputs_t outputs_hdu; - outputs_t outputs_index; - - // fdc + outputs_t outputs_irq; + outputs_t outputs_drq; + outputs_t outputs_hdu; + outputs_t outputs_index; + + // drive noise (optional) + NOISE* d_noise_seek; + NOISE* d_noise_head_down; + NOISE* d_noise_head_up; + + // fdc struct { uint8 track; uint8 result; @@ -120,14 +126,18 @@ class UPD765A : public DEVICE void cmd_invalid(); public: - UPD765A(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) - { - init_output_signals(&outputs_irq); - init_output_signals(&outputs_drq); - init_output_signals(&outputs_hdu); - init_output_signals(&outputs_index); - raise_irq_when_media_changed = false; - } + UPD765A(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) + { + init_output_signals(&outputs_irq); + init_output_signals(&outputs_drq); + init_output_signals(&outputs_hdu); + init_output_signals(&outputs_index); + d_noise_seek = NULL; + d_noise_head_down = NULL; + d_noise_head_up = NULL; + raise_irq_when_media_changed = false; + set_device_name(_T("uPD765A FDC")); + } ~UPD765A() {} // common functions @@ -136,11 +146,12 @@ class UPD765A : public DEVICE void reset(); void write_io8(uint32 addr, uint32 data); uint32 read_io8(uint32 addr); - void write_dma_io8(uint32 addr, uint32 data); - uint32 read_dma_io8(uint32 addr); - void write_signal(int id, uint32 data, uint32 mask); - uint32 read_signal(int ch); + void write_dma_io8(uint32 addr, uint32 data); + uint32 read_dma_io8(uint32 addr); + void write_signal(int id, uint32 data, uint32 mask); + uint32 read_signal(int ch); void event_callback(int event_id, int err); + void update_config(); void save_state(FILEIO* state_fio); bool load_state(FILEIO* state_fio); bool process_state(FILEIO* state_fio, bool loading); @@ -154,14 +165,38 @@ class UPD765A : public DEVICE { register_output_signal(&outputs_drq, device, id, mask); } - void set_context_hdu(DEVICE* device, int id, uint32 mask) - { - register_output_signal(&outputs_hdu, device, id, mask); - } - void set_context_index(DEVICE* device, int id, uint32 mask) - { - register_output_signal(&outputs_index, device, id, mask); - } + void set_context_hdu(DEVICE* device, int id, uint32 mask) + { + register_output_signal(&outputs_hdu, device, id, mask); + } + void set_context_index(DEVICE* device, int id, uint32 mask) + { + register_output_signal(&outputs_index, device, id, mask); + } + void set_context_noise_seek(NOISE* device) + { + d_noise_seek = device; + } + NOISE* get_context_noise_seek() + { + return d_noise_seek; + } + void set_context_noise_head_down(NOISE* device) + { + d_noise_head_down = device; + } + NOISE* get_context_noise_head_down() + { + return d_noise_head_down; + } + void set_context_noise_head_up(NOISE* device) + { + d_noise_head_up = device; + } + NOISE* get_context_noise_head_up() + { + return d_noise_head_up; + } DISK* get_disk_handler(int drv) { if(drv < 4) { diff --git a/Source/ePC-8801MA/vm/vm_template.h b/Source/ePC-8801MA/vm/vm_template.h new file mode 100644 index 0000000..b59613a --- /dev/null +++ b/Source/ePC-8801MA/vm/vm_template.h @@ -0,0 +1,15 @@ +/* + Skelton for retropc emulator + + [ vm template compatibility header ] +*/ + +#ifndef _VM_TEMPLATE_H_ +#define _VM_TEMPLATE_H_ + +#include "vm.h" + +// Compatibility alias for common_source_project code. +typedef VM VM_TEMPLATE; + +#endif /* _VM_TEMPLATE_H_ */