diff --git a/disk_analyzer/analyzer.cpp b/disk_analyzer/analyzer.cpp index 67f2c80..2e72847 100644 --- a/disk_analyzer/analyzer.cpp +++ b/disk_analyzer/analyzer.cpp @@ -154,7 +154,7 @@ void cmd_read_track_pulse(size_t track_n,size_t byte_offset,size_t length) { fdc->set_pos(0); fdc->clear_wraparound(); - for(size_t i=0; iis_wraparound() == false; ++i) { + for(size_t i=0; iget_real_pos(); if(pos>fdc->get_track_length()) pos = 0; // exceptional case handling @@ -180,9 +180,24 @@ void cmd_read_track_pulse(size_t track_n,size_t byte_offset,size_t length) { std::cout << std::hex << std::setw(2) << std::setfill('0') << int(read_data); std::cout << std::dec << std::setw(10) << std::setfill(' ') << pos; std::cout << " "; - for(int j=pos; j +#include +#include +#include + +#define RESAMPLE_COUNT 12 +#define REWIND_WIDTH 2048 +// 25MHz sampling rate: 8 counts per pulse window, 16 pulse windows per byte, 8*16=128 counts per byte, 2048 counts makes 16 bytes. +// 50MHz sampling rate: 16 counts per pulse window, 16 pulse windows per byte, 8*16=128 counts per byte, 2048 counts makes 8 bytes. + +#define MB8877_STATUS_CRC_ERROR 0x08 +#define MB8877_STATUS_RECORD_NOT_FOUND 0x10 +#define MB8877_STATUS_DELETED_DATA 0x20 + +const unsigned int READ_TRACK_EX_POS=2; +const unsigned int READ_TRACK_EX_ERR=3; + + +template +static inline T Rewind(T pos) +{ + return (REWIND_WIDTHdisable_fluctuator(); + } +}; + +void disk_image_rdd::read(const std::string file_name) { + std::cout << "Reading from RDD is not supported yet." << std::endl; +} + +void disk_image_rdd::read(std::istream &ifp) { + std::cout << "Reading from RDD is not supported yet." << std::endl; +} + + +void disk_image_rdd::write(const std::string file_name) const { + std::ofstream ofp(file_name,std::ios::binary); + write(ofp); +} + +bool disk_image_rdd::write(std::ostream &ofp) const { + const size_t sector_length_table[] = { 128, 256, 512, 1024 }; + std::ios::fmtflags flags_saved = std::cout.flags(); + fdc_bitstream fdc; + + + static char padding[1024]; + for(auto &p : padding) { + p=0; + } + + + char fileID[16]={ + 'R','E','A','L','D','I','S','K','D','U','M','P',0,0,0,0 + }; + ofp.write(fileID,16); + + unsigned char beginDisk[16]={0,0,0,0,0,0,0,0}; + beginDisk[1]=0; // Version + if(m_base_prop.m_data_bit_rate == 1e6) { // 1Mbps == 2HD_MFM , 500Kbps == 2D/2DD_MFM + beginDisk[2] = 0x20; // 2HD + } else if (m_base_prop.m_number_of_tracks > 84) { + beginDisk[2] = 0x10; // 2DD + } + else { + beginDisk[2]=0; // Assume 2D. + } + beginDisk[3]=0; // Write Protected -> 1 + beginDisk[4]=0xFF; // Converted. Not directly from real device. + ofp.write((char *)beginDisk,16); + + char diskName[32]; + memset(diskName,0,32); + ofp.write(diskName,32); + + + size_t total_sector_good = 0; + size_t total_sector_bad = 0; + + fdc.set_fdc_params(m_base_prop.m_sampling_rate,m_base_prop.m_data_bit_rate); + + for (size_t track_n = 0; track_n < m_base_prop.m_number_of_tracks; track_n++) { + if(m_verbose) { + std::cout << std::setw(4) << std::dec << track_n << ":"; + } + + unsigned char beginTrack[16]={1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + beginTrack[1]=track_n/2; // Cylinder + beginTrack[2]=track_n%2; // Head + ofp.write((char *)beginTrack,16); + + size_t sector_good=0,sector_bad=0; + + for(int retry=0; retry<2; ++retry) // Try MFM, and then FM + { + const bool MFM=(0==retry ? true : false); + + bit_array mfm_trk = m_track_data[track_n]; + fdc.set_track_data(mfm_trk); + fdc.set_pos(0); + fdc.set_vfo_type(m_vfo_type); + fdc.set_vfo_gain_val(m_gain_l, m_gain_h); + std::vector id_list = fdc.read_all_idam(); + std::vector unstable_pulses=MarkUnstablePulses(fdc,id_list); + + bool leafInTheForest=CheckLeafInTheForestSignature(track_n/2,track_n%2,id_list); + + if(0==retry && 0==id_list.size()) + { + // Try again. Unformat or can be FM format. + continue; + } + + for(auto id : id_list) { + unsigned char idMark[16]={2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + idMark[1]=id.C; + idMark[2]=id.H; + idMark[3]=id.R; + idMark[4]=id.N; + idMark[5]=(id.crc_val>>8); + idMark[6]=(id.crc_val&0xFF); + if(true==id.crc_sts) { + idMark[7]|=MB8877_STATUS_CRC_ERROR; + } + + fdc.set_pos(Rewind(id.pos)); + auto read_sect = fdc.read_sector(id.C, id.R); + + if(true==read_sect.record_not_found) { + idMark[7]|=MB8877_STATUS_RECORD_NOT_FOUND; + } + + ofp.write((char *)idMark,16); + } + + for(auto id : id_list) { + unsigned char sectorHeader[16]={3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + sectorHeader[1]=id.C; + sectorHeader[2]=id.H; + sectorHeader[3]=id.R; + sectorHeader[4]=id.N; + + fdc.set_pos(Rewind(id.pos)); + auto read_sect = fdc.read_sector(id.C, id.R); + + bool corocoroTypeA=IsFM7CorocoroTypeA(fdc,id.pos,id.C,id.H,id.R,read_sect.crc_sts,read_sect.data); + bool corocoroTypeB=IsFM7CorocoroTypeB(fdc,id.pos,id.C,id.H,id.R,read_sect.crc_sts,read_sect.data); + + + if(true!=MFM) + { + sectorHeader[6]|=1; + } + if(true==corocoroTypeA || true==corocoroTypeB) + { + sectorHeader[6]|=2; + } + if(true==leafInTheForest) + { + sectorHeader[6]|=4; + } + + + if(read_sect.record_not_found == false) { + if(true==read_sect.dam_type) { + sectorHeader[5]|=MB8877_STATUS_DELETED_DATA; + } + if(true==read_sect.crc_sts) + { + sectorHeader[5]|=MB8877_STATUS_CRC_ERROR; + } + } + else + { + sectorHeader[5]|=MB8877_STATUS_RECORD_NOT_FOUND; + // Observed in ASTEKA. Data mark of C0 H0 R20 does not exist, therefore + // record not found error. At the same time, because IDMark was making + // a CRC error, so crc error flag was also set. + if(true==id.crc_sts) + { + sectorHeader[5]|=MB8877_STATUS_CRC_ERROR; + } + } + + auto start_pos=read_sect.data_pos; + auto end_pos=read_sect.data_end_pos;; + if (end_pos < start_pos) { + end_pos += mfm_trk.get_bit_length(); // wrap around correction + } + + uint64_t elapsed=end_pos-start_pos; + uint64_t microsec=elapsed*1000000LL/(uint64_t)m_base_prop.m_sampling_rate; + sectorHeader[0x0B]= microsec &0xFF; + sectorHeader[0x0C]=(microsec>> 8)&0xFF; + sectorHeader[0x0D]=(microsec>>16)&0xFF; + + if(m_verbose) { + std::cout << int(id.C) << " " << int(id.H) << " " << int(id.R) << " POS0:" << id.pos << " POS1:" << fdc.get_real_pos() << " " << microsec << "usec" << std::endl; + } + + unsigned int len=(128<<(id.N&3)); + sectorHeader[0x0E]= len &0xFF; + sectorHeader[0x0F]=(len>>8)&0xFF; + + if(true!=corocoroTypeA && true!=corocoroTypeB) + { + ofp.write((char *)sectorHeader,16); + while(read_sect.data.size()>8)&0xFF; + + while(0!=track_read.size()%16) + { + track_read.push_back(0); + } + ofp.write((char *)trackReadHeader,16); + ofp.write((char *)track_read.data(),track_read.size()); + + + if(m_verbose) { + std::cout << std::setw(4) << std::dec << (sector_good+sector_bad) << "/" << std::setw(4) << sector_bad << " "; + if(track_n % 5 == 4) { + std::cout << std::endl; + } + } + + break; // If it reaches here, don't continue. + } + + char endTrack[16]={5,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + ofp.write(endTrack,16); + } + char endFile[16]={6,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + ofp.write(endFile,16); + + if(m_verbose) { + std::cout << std::endl; + std::cout << "**TOTAL RESULT(GOOD/BAD):" << total_sector_good << " " << total_sector_bad << std::endl; + } + std::cout.flags(flags_saved); +} + +bool disk_image_rdd::IsFM7CorocoroTypeA(fdc_bitstream &fdc,uint64_t pos,unsigned char C,unsigned char H,unsigned char R,bool crc_sts,const std::vector &data) const +{ + if(true==CheckCorocoroTypeASignature(crc_sts,data)) + { + // Signature found. Now let's verify. + + std::vector > samples; + const int nRepeat=16; + + // Reduce fluctuator until first 256 bytes are all 0xE5 + for(double fluctuation=0.8; 0.2 s; + s[0]=sect.data[0x120]; + s[1]=sect.data[0x121]; + s[2]=sect.data[0x122]; + s[3]=sect.data[0x123]; + samples.push_back(s); + } + if(samples.size()==nRepeat) + { + break; + } + UPDATE_FLUCTUATION: + ; + } + + if(nRepeat!=samples.size()) + { + return false; + } + + // 4 bytes from offset $120 need to be unstable. + for(int i=1; i &data) const +{ + if(true==CheckCorocoroTypeBSignature(crc_sts,data)) + { + // Signature found. Now let's verify by fluctuating the VFO. + + const int nRepeat=16; + + // Reduce fluctuator until first 20 bytes are all 0xF7 + for(double fluctuation=0.8; 0.2 &data) const +{ + if(true==crc_sts && 1024==data.size()) + { + for(int i=0; i<256; ++i) + { + // Signature: First 256 bytes are 0xE5. + if(0xE5!=data[i]) + { + return false; + } + } + return true; + } + return false; +} +bool disk_image_rdd::CheckCorocoroTypeBSignature(bool crc_sts,const std::vector &data) const +{ + if(true==crc_sts && 128==data.size()) + { + for(int i=0; i<20; ++i) + { + // Signature: First 20 bytes are 0xF7. + if(0xF7!=data[i]) + { + return false; + } + } + + // 20 bytes from offset 24 need to be 0xF6, however, often byte[24] changes. Also byte[43] often is 0xF7. + // All 20 bytes may change to zero. + // The supplied checker will read the sector multiple times and pass if it finds 19 consecutive 0xF6 from offset 24 just once. + // So, first check 18 bytes from offset 25 are equal. + for(int i=25; i<43; ++i) + { + if(data[i]!=data[25]) + { + return false; + } + } + return true; + } + return false; +} + +bool disk_image_rdd::CheckLeafInTheForestSignature(uint8_t C,uint8_t H,const std::vector &id_list) const +{ + // Only confirmed in Thexder and Fire Crystal as of 2023/10/24. + // There is a track that has 63 sectors with all same C,H,R. + // All but two sectors are dummy. + + if(60 disk_image_rdd::MarkUnstablePulses(fdc_bitstream &fdc,const std::vector &id_list) const +{ + auto track_data=fdc.get_track_data(); + std::vector unstable; + std::vector confirmed; + + unstable.resize(track_data.get_bit_length()); + confirmed.resize(track_data.get_bit_length()); + + auto Fill=[&](size_t start_pos,size_t end_pos,bool isUnstable) + { + if(start_pos<=end_pos) + { + for(size_t i=start_pos; i fdc_bitstream::read_all_idam(void) { item.crc_val = (sect_id[4] << 8) + sect_id[5]; item.crc_sts = crc_error; item.pos = pos; + item.end_pos = m_codec.get_real_pos(); result.push_back(item); } } diff --git a/fdc_bitstream/mfm_codec.cpp b/fdc_bitstream/mfm_codec.cpp index 02c2773..12089fb 100644 --- a/fdc_bitstream/mfm_codec.cpp +++ b/fdc_bitstream/mfm_codec.cpp @@ -483,7 +483,8 @@ size_t mfm_codec::get_real_pos(void) size_t int_dist = static_cast(m_distance_to_next_pulse); if(m_track.get_stream_pos() < int_dist) { - return -1; + int_dist-=m_track.get_stream_pos(); + return m_track.size()-int_dist; } return m_track.get_stream_pos()-int_dist; diff --git a/image_converter/image_converter.cpp b/image_converter/image_converter.cpp index de6716b..437a0c9 100644 --- a/image_converter/image_converter.cpp +++ b/image_converter/image_converter.cpp @@ -24,7 +24,7 @@ void usage(std::string cmd_name) { std::cout << cmd_name << "-i input_file -o output_file [-n]" << std::endl; std::cout << "Input file : mfm, raw, d77, hfe, fdx" << std::endl; - std::cout << "Output file : mfm, raw, d77, hfe, fdx" << std::endl; + std::cout << "Output file : mfm, raw, d77, hfe, fdx, rdd" << std::endl; std::cout << "-n : Normalize pulse pitch. Get statistic data of pulse-to-pulse distance \n" " distribution in a track and align bit position with standard bit cell pitch.\n" " This will make the disk image data easy to read." << std::endl; @@ -47,7 +47,7 @@ std::string get_file_extension(std::string file_name) { } bool check_extension(std::string extension) { - std::vector allowed = { "hfe", "mfm", "raw", "d77", "fdx" }; + std::vector allowed = { "hfe", "mfm", "raw", "d77", "rdd", "fdx" }; bool res = false; for(auto it = allowed.begin(); it != allowed.end(); ++it) { if(*it == extension) res = true; @@ -61,6 +61,7 @@ disk_image* create_object_by_ext(std::string ext) { if(ext == "raw") obj = new disk_image_raw(); if(ext == "mfm") obj = new disk_image_mfm(); if(ext == "d77") obj = new disk_image_d77(); + if(ext == "rdd") obj = new disk_image_rdd(); if(ext == "fdx") obj = new disk_image_fdx(); return obj; } diff --git a/include/disk_images.h b/include/disk_images.h index 999de99..ed41c42 100644 --- a/include/disk_images.h +++ b/include/disk_images.h @@ -5,4 +5,5 @@ #include "image_raw.h" #include "image_mfm.h" #include "image_d77.h" +#include "image_rdd.h" #include "image_fdx.h" diff --git a/include/fdc_bitstream.h b/include/fdc_bitstream.h index 41f30b0..f78c89d 100644 --- a/include/fdc_bitstream.h +++ b/include/fdc_bitstream.h @@ -44,6 +44,7 @@ class fdc_bitstream { uint16_t crc_val; bool crc_sts; size_t pos; + size_t end_pos; }; /** diff --git a/include/image_rdd.h b/include/image_rdd.h new file mode 100644 index 0000000..c7630f5 --- /dev/null +++ b/include/image_rdd.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "bit_array.h" +#include "image_base.h" +#include "fdc_vfo_def.h" +#include "fdc_bitstream.h" + +class disk_image_rdd : public disk_image { +private: + size_t m_vfo_type; + double m_gain_l, m_gain_h; +public: + disk_image_rdd(void) : disk_image(), m_vfo_type(VFO_TYPE_DEFAULT), m_gain_l(VFO_GAIN_L_DEFAULT), m_gain_h(VFO_GAIN_H_DEFAULT) {}; + + void read(const std::string file_name) override; + void read(std::istream &ifp); + void write(const std::string file_name) const override; + bool write(std::ostream &ofp) const ; + void set_vfo_type(const size_t vfo_type) override { m_vfo_type = vfo_type; }; + void set_gain(double gain_l, double gain_h) override { m_gain_l = gain_l; m_gain_h = gain_h; }; + + bool IsFM7CorocoroTypeA(class fdc_bitstream &fdc,uint64_t pos,unsigned char C,unsigned char H,unsigned char R,bool crc_sts,const std::vector &data) const; + bool IsFM7CorocoroTypeB(class fdc_bitstream &fdc,uint64_t pos,unsigned char C,unsigned char H,unsigned char R,bool crc_sts,const std::vector &data) const; + + bool CheckCorocoroTypeASignature(bool crc_sts,const std::vector &data) const; + bool CheckCorocoroTypeBSignature(bool crc_sts,const std::vector &data) const; + + bool CheckLeafInTheForestSignature(uint8_t C,uint8_t H,const std::vector &id_list) const; + + std::vector MarkUnstablePulses(fdc_bitstream &fdc,const std::vector &id_list) const; +}; diff --git a/pauline2raw/pauline2raw.cpp b/pauline2raw/pauline2raw.cpp index 69f1703..cef3aa2 100644 --- a/pauline2raw/pauline2raw.cpp +++ b/pauline2raw/pauline2raw.cpp @@ -740,6 +740,45 @@ bool PaulineToRaw::ExportRaw(std::string fName,const std::vector