All pastes #2128359 Raw Edit

Famicom Disk System emu. WIP2 by

public cpp v1 · immutable
#2128359 ·published 2012-03-15 12:41 UTC
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(); }};// Utility function for soundtemplate<typename T>bool count(T& v, int reset)    { return --v < 0 ? (v = reset),true : false; }template<typename T>bool count(T& v, int reset, int n_at_time)    { return (v -= n_at_time) < 0 ? (v += reset),true : false; }/** 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;    }