rendered paste body-------------------------------------/* LOADING OF FDS FILE */------------------------------------- fseek(fp, 0, SEEK_END); long size = ftell(fp); rewind(fp); unsigned sides = 0; u8 header[16]; fread(header, 1, 16, fp); if(header[0] == 'F' && header[1] == 'D' && header[2] == 'S' && header[3] == '\x1A') sides = header[4]; else { sides = size / 65500; rewind(fp); } // FDS transfer speed: About 96400 bits per second, i.e. 12050 bytes per second. // According to http://nesdev.parodius.com/fdstech.txt . // At CPU speed of 1789800 Hz, that translates to // about 148.53 cycles per byte. // Reading the entire disk takes about 6 seconds, they say. /* TIMING GRAPH: --MOTOR POWERON-- 150 ms 268466 This is how much the BIOS expects at least. --HEADS MOVED TO THE BEGINNING OF DISK (SCAN STARTED)-- 134.9 ms 241359 About 13000 bits of garbage data transmitted by devce 135.9 ms 243216 About 13100 bits more ignored by RAM adapter 144.2 ms 258069 About 13900 bits more before 1-bit is found = 414.9 ms 742644 TOTAL BEFORE one-bit --FIRST FILE-- 0.17 ms 297 16 bits of CRC --END OF FIRST FILE-- 10.1 ms 18121 About 976 bits of gap between files --NEXT FILE-- */ GamePak::DiskImage.resize(sides); for(unsigned side=0; side<sides; ++side) { // Read the disk side from file. std::vector<u8> Buffer(65500), &data = GamePak::DiskImage[side]; std::fread(&Buffer[0], 1, Buffer.size(), fp); // Beginning: Insert 28300 bits of gap. data.clear(); data.resize( 28300/8, u8(0) ); // Because the FDS file format does not include gaps, CRCs and block begin flags, // but yet our FDS model requires them, construct the actual bitwise disk image // that we will be using. unsigned length = 0; for(std::size_t pos=0; pos < Buffer.size(); ) { unsigned type = Buffer[pos], blocklength = 1; switch(type) { case 1: blocklength = 56; break; // Disk header case 2: blocklength = 2; break; // Number of files case 3: length = Buffer[pos+13] + 0x100*Buffer[pos+14]; blocklength = 16; break; // File header case 4: blocklength = 1+length; break; // File data } data.push_back(0x80); // Block begin mark data.insert(data.end(), &Buffer[pos], &Buffer[pos]+blocklength); data.push_back(0x55); // dummy CRC data.push_back(0xAA); // dummy CRC // After file: 976 bits of gap (480 is minimum) data.insert(data.end(), 976/8, u8(0)); pos += blocklength; } // Ensure the size is at least 65500 bytes... if(data.size() < 65500) data.resize(65500); // And insert 2000 bytes of gap in the end just in case. data.insert(data.end(), 2000, u8(0)); } fprintf(stderr, "%u sides FDS image loaded\n", sides); GamePak::mappernum = 20; // By default, side 0 is inserted. GamePak::FDS_InsertSide(0);-------------------------------------/* FDS AUDIO (EXECUTED ON EVERY CYCLE, RETURNS THE SIGNAL LEVEL OF AUDIO) */------------------------------------- int FDS_audio() { if(Snd[0].reg.FDS_SilenceChannel) return 0; auto& m = Snd[2], &w = Snd[3]; // Volume envelope tick and sweep envelope tick. // Cyc = 8 * EnvSpeedScaler * (EnvelopeSpeed+1) int scale = 8 * m.reg.FDS_EnvSpeedScaler; for(int env=0; env<2; ++env) { auto& c = Snd[env]; if(c.reg.FDS_NotEnvelopeMode) c.level = c.reg.FDS_FixedVolume; else if(!Snd[0].reg.FDS_EnvelopeDisable && c.reg.FDS_EnvelopeSpeed && count(c.wave_counter, scale * (c.reg.FDS_EnvelopeSpeed + 1))) { if(c.reg.FDS_Increase) { if(c.level < 32) ++c.level; } else { if(c.level > 0) --c.level; } } } int Freq = Snd[0].reg.FDS_Frequency; int ModFreq = Snd[1].reg.FDS_Frequency; // Modulator tick if(ModFreq && !Snd[1].reg.FDS_ModulationDisable) { if(count(m.wave_counter, 65536, ModFreq)) { int bias = s8(Snd[1].reg.FDS_SweepBias*2) / 2; int adj = s8(reg.FDS_ModulationBuffer[ (m.phase++ / 2) % 32 ]); if(adj == -4) bias = 0; else bias += adj; Snd[1].reg.FDS_SweepBias = bias; // Clamps to 7 bits. int temp = bias * std::min(32, (int) Snd[1].level); int a = 64, d = temp <= 0 ? 15 : temp < 95*32 ? a=66,-31 : 0; int temp2 = a + s8( (temp-d)/16 - a); // Note: This does not produce _exactly_ same values as the // documentation at Nesdev. Close enough. Neither does Nestopia. m.hold = Freq * temp2 / 64; } if(Freq) Freq += m.hold; } // Wavetable tick if(Freq && !m.reg.FDS_WaveTableWritable) if(count(w.wave_counter, 65536, Freq)) { int level = std::min(32, (int) Snd[0].level); level *= "\36\24\17\14"[m.reg.FDS_GlobalVolumeBits]; w.hold = reg.FDS_WaveTable[ w.phase++ % 64 ] * level / 30 / 9; } return w.hold; }-------------------------------------/* FDS TIMER AND DISK BACKGROUND TASKS (EXECUTED ON EACH CPU CYCLE) */------------------------------------- static void FDS_tick() { /* FDS TIMER DEVICE */ /* If FDS timer IRQ is enabled and the counter is nonzero, * decrement the counter. If it becomes zero, * assert the IRQ line and disable the counter. */ if(P.IRQ_Enable && P.IRQ_Counter && !--P.IRQ_Counter) { P.IRQ_Enable = false; CPU::reg.FDS_TIMER_IRQ = true; } /* FDS DISK DEVICE */ /* If there is no disk inserted, the disk system does no activity. */ if(P.FDS_CurrentDiskSide == 0xFF) { P.FDS_Delay = 1000; return; } /* If the motor is not running, do nothing else. */ if(!reg.FDS_DontStopMotor) { // When the motor is next started, it begins from the start of the disk. P.FDS_Position = 0; reg.FDS_Scanning = false; reg.FDS_GapCovered = false; P.FDS_Delay = 1/*268466*/; // 150ms delay before motor is turned on return; } // Is there some kind of delay? if(P.FDS_Delay > 0) --P.FDS_Delay; if(P.FDS_Delay) return; /* Begin scanning the disk when permitted. */ if(reg.FDS_DontScanMedia) reg.FDS_Scanning = true; if(!reg.FDS_Scanning) return; /* So the motor is on and we are scanning the disk. Which byte? */ u8& diskdata = DiskImage[P.FDS_CurrentDiskSide][P.FDS_Position]; /* Was CRC transmission enabled? If so, the next two bytes will be just that. */ if(!P.FDS_TransmittingCRC && reg.FDS_TransmitCRC) P.FDS_TransmittingCRC = 2; /* Read or write. */ if(reg.FDS_DontWrite) reg.FDS_ReadData = diskdata; else diskdata = !reg.FDS_InData ? 0x00 // Zero : P.FDS_TransmittingCRC ? 0x5A // CRC (not implemented) : reg.FDS_WriteData; // data /*bool was_crc = P.FDS_TransmittingCRC;*/ if(P.FDS_TransmittingCRC > 0) --P.FDS_TransmittingCRC; /* If we are in data, signal the CPU that we have processed of 8 bits of data */ if(reg.FDS_GapCovered) { reg.FDS_TransferComplete = true; if(reg.FDS_DiskIRQ_Enable) CPU::reg.FDS_DISK_IRQ = true; } /* Set flag if we found a nonzero bit (end of gap) */ if(diskdata) reg.FDS_GapCovered = true; /* Clear the flag if we're not allowed to detect them yet */ if(!reg.FDS_InData) reg.FDS_GapCovered = false; if(++P.FDS_Position >= (unsigned) DiskImage[P.FDS_CurrentDiskSide].size()) { // Should return to the beginning of the disk. P.FDS_Position = 0; reg.FDS_GapCovered = false; P.FDS_Delay = 900000; } else P.FDS_Delay = 149; // The time it takes to find next 8 bits. }-------------------------------------/* FDS I/O */------------------------------------- case 20: // FDS { if(addr < 0x4020) return 0x00; if(addr >= 0x4024 && addr <= 0x403F && !reg.FDS_RegEnable_Disk) return 0; if(addr < 0x4040) // 4020..403F switch( (addr & 0xFF) | (write ? 0x100 : 0x000) ) { case 0x120: // 4020: Write low 8 bits of timer IRQ reload value CPU::reg.FDS_TIMER_IRQ = false; P.IRQ_ReloadValue = (P.IRQ_ReloadValue & 0xFF00) | value; break; case 0x121: // 4021: Write high 8 bits of timer IRQ reload value CPU::reg.FDS_TIMER_IRQ = false; P.IRQ_ReloadValue = (P.IRQ_ReloadValue & 0x00FF) | (value << 8); break; case 0x122: // 4022: Reload and enable/disable timer IRQ CPU::reg.FDS_TIMER_IRQ = false; // Untrigger IRQ P.IRQ_Counter = P.IRQ_ReloadValue; P.IRQ_Enable = value & 2; break; case 0x123: // 4023: Set register write protection reg.FDS_RegEnable = value; break; case 0x124: // 4024: Set next byte to be written to disk. reg.FDS_WriteData = value; reg.FDS_TransferComplete = false; CPU::reg.FDS_DISK_IRQ = false; break; case 0x125: // 4025: Control the drive. reg.FDS_Reg4025_raw = value; // Set PPU mirroring (for some reason, horiz/vert seems to be reversed) SetMirroring( reg.FDS_PPUmirroring ? 0x1100 : 0x1010 ); break; case 0x126: // 4026: Write the bitmask that only controls what $4033 returns. reg.FDS_Reg4026 = value; break; case 0x030: // 4030: return status and confirm any IRQ. { u8 result = 0; if(CPU::reg.FDS_TIMER_IRQ) result |= 0x01; // Timer triggered if(reg.FDS_TransferComplete) result |= 0x02; // Data transferred //if(reg.FDS_CurrentDiskSide!=0xFF) result |= 0x80; // !Write protected //if(!reg.FDS_Scanning) result |= 0x40; // End of head FIXME // 0x10 = CRC test failed (never set in emulator) // Confirm any IRQ (both timer and disk) CPU::reg.FDS_TIMER_IRQ = false; CPU::reg.FDS_DISK_IRQ = false; // Reset $4030 bit 1 (& 0x02). reg.FDS_TransferComplete = false; return result; } case 0x031: // 4031: Return what was read from the disk //fprintf(stderr, "4031 read while 4032 = %02X\n", Access(0x4032,0,false)); // Confirm the FDS disk transfer IRQ CPU::reg.FDS_DISK_IRQ = false; // Reset $4030 bit 1 (& 0x02) reg.FDS_TransferComplete = false; //fprintf(stderr, "read by FDS disk at %u\n", reg.FDS_LastReadAddr+1); // Return the databyte last fetched from disk return reg.FDS_ReadData; case 0x032: { u8 result = 0; if(P.FDS_CurrentDiskSide==0xFF) result |= 0x01; // !Disk in drive if(!reg.FDS_Scanning || P.FDS_CurrentDiskSide==0xFF) result |= 0x02; // !Disk scan started if(P.FDS_CurrentDiskSide==0xFF) result |= 0x04; // !Disk writable result |= 0x40; // Constant return result; } case 0x033: // 4033: Return battery status. return 0x80 & reg.FDS_Reg4026; default: // audio? return 0; } else if(addr >= 0x8000) // PRG-RAM at 8000..DFFF; ROM at E000-EFFF { if(write && addr < 0xE000) banks[ (addr / ROM_Granularity) % ROM_Pages] [addr % ROM_Granularity] = value; // passthru } else if(addr > 0x408A) return 0; // 408B..5FFF else if(!reg.FDS_RegEnable_Sound) return 0; // 4040..408A else if(addr < 0x4080) // 4040..407F { if(write && Snd[2].reg.FDS_WaveTableWritable) reg.FDS_WaveTable[addr & 0x3F] = value & 0x3F; return reg.FDS_WaveTable[addr & 0x3F] | 0xC0; } else if(!write) return 0; else switch(addr) // 4080..408A { case 0x4088: for(unsigned a=0; a<32; ++a) reg.FDS_ModulationBuffer[a] = a < 31 ? reg.FDS_ModulationBuffer[a+1] : ((value&3) | (0x3F*(value&4))); // 0,2,4,6, -8, -6,-4,-2 return 0; default: if(addr == 0x4085) Snd[2].phase = 0; // Modulation index switch(addr & 3) { case 0: Snd[(addr/4)&3].reg.reg0 = value; break; case 1: Snd[(addr/4)&3].reg.reg1 = value; break; case 2: Snd[(addr/4)&3].reg.reg2 = value; break; case 3: Snd[(addr/4)&3].reg.reg3 = value; break; } return 0; } }-------------------------------------/* MISC. INFORMATION RELATED TO ABOVE */-------------------------------------// Integer typestypedef uint_least64_t u64;typedef uint_least32_t u32;typedef uint_least16_t u16;typedef uint_least8_t u8;typedef int_least8_t s8;typedef int_least32_t s32;typedef int_least16_t s16;// Bitfield utilitiestemplate<unsigned bitno, unsigned nbits, typename T, unsigned dim=1, unsigned index=0>struct RegBitSet{ T data[dim]; enum { mask = ((1ull << (nbits/2)) << (nbits-(nbits/2))) - 1ull }; operator T() const { return (data[index] >> bitno) & mask; } typedef RegBitSet<bitno,nbits,T,1,0> SingleElem; SingleElem& operator[] (unsigned i) { T* d = reinterpret_cast<T*>(this) + index+i; return *reinterpret_cast<SingleElem*>(d); } T operator[] (unsigned i) const { return (data[index+i] >> bitno) & mask; } template<typename T2> RegBitSet& operator=(T2 val) { data[index] = (data[index] & ~(mask << bitno)) | ((nbits > 1 ? val & mask : !!val) << bitno); return *this; } template<typename T2> RegBitSet& operator+= (T2 val) { return *this = *this + val; } template<typename T2> RegBitSet& operator|= (T2 val) { return *this = (T)*this | val; } template<typename T2> RegBitSet& operator-= (T2 val) { return *this = *this - val; } RegBitSet& operator++ () { return *this = *this + 1; } RegBitSet& operator-- () { return *this = *this - 1; } unsigned operator++ (int) { unsigned r = *this; ++*this; return r; } unsigned operator-- (int) { unsigned r = *this; --*this; return r; } bool operator! () const { return !operator T(); }};/** DATA & REGISTERS */ /* ... */ union { u32 raw[0x10]; /* SAVE */ // Common: RegBitSet<0,32, u32,0x10,0x0> mappernum; // IRQ related RegBitSet<0,32, u32,0x10,0x1> IRQ_ReloadValue; // VRC MMC3 FDS RegBitSet<0,32, u32,0x10,0x2> IRQ_Counter; // VRC MMC3 FDS RegBitSet<0,32, u32,0x10,0x3> IRQ_ScanlineCounter; // VRC RegBitSet<0,32, u32,0x10,0x4> IRQ_A12lowTime; // MMC3 RegBitSet<0, 1, u32,0x10,0x7> IRQ_Enable; // VRC MMC3 FDS RegBitSet<1, 1, u32,0x10,0x7> IRQ_AccMode; // VRC RegBitSet<2, 1, u32,0x10,0x7> IRQ_Mode; // VRC // FDS-specific: RegBitSet<0,32, u32,0x10,0x8> FDS_Position; RegBitSet<0,32, u32,0x10,0x9> FDS_CurrentDiskSide; RegBitSet<0,32, u32,0x10,0xA> FDS_Delay; RegBitSet<0,32, u32,0x10,0xB> FDS_TransmittingCRC; } P; union { u8 raw[0x70]; /* SAVE */ // FDS-specific RegBitSet<0,8,u8,0x70,0x00> FDS_WaveTable; // 00..3F RegBitSet<0,8,u8,0x70,0x40> FDS_ModulationBuffer; // 40..5F // FDS-specific RegBitSet<0,8,u8,0x70,0x60> FDS_Reg4025_raw; // $4025 RegBitSet<0,1,u8,0x70,0x60> FDS_DontStopMotor; // pin 48(-stop motor). 0 = motor stop RegBitSet<1,1,u8,0x70,0x60> FDS_DontScanMedia; // pin 49(-scan media). 0 = motor on, 1 = motor stay on RegBitSet<2,1,u8,0x70,0x60> FDS_DontWrite; // pin 50(-write). 0 = write, 1 = read RegBitSet<3,1,u8,0x70,0x60> FDS_PPUmirroring; RegBitSet<4,1,u8,0x70,0x60> FDS_TransmitCRC; RegBitSet<6,1,u8,0x70,0x60> FDS_InData; RegBitSet<7,1,u8,0x70,0x60> FDS_DiskIRQ_Enable; RegBitSet<0,8,u8,0x70,0x61> FDS_Reg4026; // $4026 RegBitSet<0,8,u8,0x70,0x62> FDS_RegEnable; RegBitSet<0,1,u8,0x70,0x62> FDS_RegEnable_Disk; RegBitSet<1,1,u8,0x70,0x62> FDS_RegEnable_Sound; RegBitSet<0,8,u8,0x70,0x63> FDS_ReadData; // $4031 RegBitSet<0,8,u8,0x70,0x64> FDS_WriteData; // $4024 // Internals: RegBitSet<0,1,u8,0x70,0x65> FDS_TransferComplete; RegBitSet<1,1,u8,0x70,0x65> FDS_Scanning; RegBitSet<2,1,u8,0x70,0x65> FDS_GapCovered; } reg; // Sound channels struct { union { u32 raw; /* SAVE */ RegBit<0,8,u32> reg0; RegBit<8,8,u32> reg1; RegBit<16,8,u32> reg2; RegBit<24,8,u32> reg3; // 4080(0:Volume), 4084(1:Sweep) RegBit<0,6,u32> FDS_EnvelopeSpeed; RegBit<0,6,u32> FDS_FixedVolume; RegBit<6,1,u32> FDS_Increase; RegBit<7,1,u32> FDS_NotEnvelopeMode; // 4085(1:) RegBit<8,7,u32> FDS_SweepBias; // 7-bit signed // 4082(1),4083,4086(1),4087 RegBit<16,12,u32> FDS_Frequency; RegBit<30, 1,u32> FDS_EnvelopeDisable; // 4083 RegBit<31, 1,u32> FDS_SilenceChannel; // 4083 RegBit<31, 1,u32> FDS_ModulationDisable; // 4087 // 4089(2) RegBit<8, 2,u32> FDS_GlobalVolumeBits; // 4089 RegBit<15, 1,u32> FDS_WaveTableWritable; // 4089 // 408A(2) RegBit<16, 8,u32> FDS_EnvSpeedScaler; // 408A } reg; union { s32 data[8]; /* SAVE */ // Internals: RegBitSet<0,32, s32,8, 0> wave_counter; RegBitSet<0,32, s32,8, 1> phase; RegBitSet<0,32, s32,8, 2> hold; RegBitSet<0,32, s32,8, 3> level; RegBitSet<0,32, s32,8, 4> level2; }; } Snd[6]; /* ... */ void FDS_InsertSide(unsigned side) { if(mappernum == 20) P.FDS_CurrentDiskSide = side; } void FDS_Eject() { if(mappernum == 20) P.FDS_CurrentDiskSide = 0xFF; }