Exemple #1
0
        internal override void FetchOpcode()
        {
            if (m_mode == SID2Types.sid2_env_t.sid2_envR)
            {
                base.FetchOpcode();
                return;
            }

            // Sid tunes end by wrapping the stack. For compatibility it has to be handled.
            m_sleeping |= (SIDEndian.endian_16hi8(Register_StackPointer) != SP_PAGE);
            m_sleeping |= (SIDEndian.endian_32hi16(Register_ProgramCounter) != 0);
            if (!m_sleeping)
            {
                base.FetchOpcode();
            }

            if (m_framelock == false)
            {
                int timeout = 6000000;
                m_framelock = true;
                // Simulate sidplay1 frame based execution
                while (!m_sleeping && (timeout != 0))
                {
                    base.clock();
                    timeout--;
                }
                if (timeout == 0)
                {
                    player.Reset(true);
                }
                sleep();
                m_framelock = false;
            }
        }
Exemple #2
0
        private void sid_jmp()
        {
            // For de.quippy.sidplay.sidplay compatibility, inherited from environment
            if (m_mode == SID2Types.sid2_env_t.sid2_envR)
            {
                // If a busy loop then just sleep
                if (Cycle_EffectiveAddress == instrStartPC)
                {
                    Register_ProgramCounter = SIDEndian.endian_32lo16(Register_ProgramCounter, Cycle_EffectiveAddress);
                    if (!interruptPending())
                    {
                        this.sleep();
                    }
                }
                else
                {
                    base.jmp_instr();
                }
                return;
            }

            if (player.CheckBankJump(Cycle_EffectiveAddress))
            {
                base.jmp_instr();
            }
            else
            {
                sid_rts();
            }
        }
        public void write(short addr, short data)
        {
            if (addr > 0x0f)
            {
                return;
            }

            regs[addr] = data;

            if (locked)
            {
                return; // Stop program changing time interval
            }

            // Sync up timer
            long cycles;

            cycles       = m_eventContext.getTime(m_accessClk, m_phase);
            m_accessClk += cycles;
            ta          -= (int)cycles;
            if (ta == 0)
            {
                _event();
            }

            switch (addr)
            {
            case 0x4:
                ta_latch = SIDEndian.endian_16lo8(ta_latch, data);
                break;

            case 0x5:
                ta_latch = SIDEndian.endian_16hi8(ta_latch, data);
                if ((cra & 0x01) == 0)     // Reload timer if stopped
                {
                    ta = ta_latch;
                }
                break;

            case 0x0e:
                cra = (short)(data | 0x01);
                if ((data & 0x10) != 0)
                {
                    cra &= (~0x10 & 0xff);
                    ta   = ta_latch;
                }
                m_eventContext.schedule(m_taEvent, (long)ta + 1, m_phase);
                break;

            default:
                break;
            }
        }
        private void galwayInit()
        {
            if (active)
            {
                return;
            }

            // Check all important parameters are legal
            short r = convertAddr(0x1d);

            galTones      = reg[r];
            reg[r]        = 0;
            galInitLength = reg[convertAddr(0x3d)];
            if (galInitLength == 0)
            {
                return;
            }
            galLoopWait = reg[convertAddr(0x3f)];
            if (galLoopWait == 0)
            {
                return;
            }
            galNullWait = reg[convertAddr(0x5d)];
            if (galNullWait == 0)
            {
                return;
            }

            // Load the other parameters
            r        = convertAddr(0x1e);
            address  = SIDEndian.endian_16(reg[r + 1], reg[r]);
            volShift = (short)(reg[convertAddr(0x3e)] & 0x0f);
            mode     = FM_GALWAY;
            active   = true;
            cycles   = 0;
            outputs  = 0;

            sampleLimit = 8;
            sample      = (byte)(galVolume - 8);
            galwayTonePeriod();

            // Calculate the sample offset
            m_xsid.sampleOffsetCalc();

            // Schedule a sample update
            m_context.schedule(m_xsid.xsidEvent, 0, m_phase);
            m_context.schedule(galwayEvent, cycleCount, m_phase);
        }
        /// <summary>
        /// Common address resolution procedure
        /// </summary>
        /// <param name="c64data"></param>
        /// <param name="fileOffset2"></param>
        /// <returns></returns>
        protected bool resolveAddrs(short[] c64data, int fileOffset2)
        {
            // Originally used as a first attempt at an RSID
            // style format. Now reserved for future use
            if (info.playAddr == 0xffff)
            {
                info.playAddr = 0;
            }

            // loadAddr = 0 means, the address is stored in front of the C64 data.
            if (info.loadAddr == 0)
            {
                if (info.c64dataLen < 2)
                {
                    //info.statusstring = txt_corrupt;
                    return(false);
                }
                info.loadAddr = SIDEndian.endian_16(c64data[fileOffset + 1], c64data[fileOffset + 0]);
                fileOffset   += 2;
                // c64data += 2;
                info.c64dataLen -= 2;
            }

            if (info.compatibility == SIDTUNE_COMPATIBILITY_BASIC)
            {
                if (info.initAddr != 0)
                {
                    //info.statusstring = txt_badAddr;
                    return(false);
                }
            }
            else if (info.initAddr == 0)
            {
                info.initAddr = info.loadAddr;
            }
            return(true);
        }
Exemple #6
0
        internal SidTune.LoadStatus PSID_fileSupport(Buffer_sidtt dataBuf)
        {
            short clock, compatibility;
            long  speed;
            int   bufLen = dataBuf.bufLen;

#if SIDTUNE_PSID2NG
            clock = SidTune.SIDTUNE_CLOCK_UNKNOWN;
#else
            clock = info.clockSpeed;
#endif
            compatibility = SidTune.SIDTUNE_COMPATIBILITY_C64;

            // Require minimum size to allow access to the first few bytes.
            // Require a valid ID and version number.
            PHeader pHeader = new PHeader(dataBuf.buf, 0);

            // File format check
            if (bufLen < 6)
            {
                return(SidTune.LoadStatus.LOAD_NOT_MINE);
            }
            if (SIDEndian.endian_big32((short[])pHeader.id, 0) == PSID_ID)
            {
                switch (SIDEndian.endian_big16(pHeader.version, 0))
                {
                case 1:
                    compatibility = SidTune.SIDTUNE_COMPATIBILITY_PSID;
                    // Deliberate run on
                    break;

                case 2:
                    break;

                default:
                    //info.formatstring = _sidtune_unknown_psid;
                    return(SidTune.LoadStatus.LOAD_ERROR);
                }
                //info.formatstring = _sidtune_format_psid;
            }
            else if (SIDEndian.endian_big32((short[])pHeader.id, 0) == RSID_ID)
            {
                if (SIDEndian.endian_big16(pHeader.version, 0) != 2)
                {
                    //info.formatstring = _sidtune_unknown_rsid;
                    return(SidTune.LoadStatus.LOAD_ERROR);
                }
                //info.formatstring = _sidtune_format_rsid;
                compatibility = SidTune.SIDTUNE_COMPATIBILITY_R64;
            }
            else
            {
                return(SidTune.LoadStatus.LOAD_NOT_MINE);
            }

            // Due to security concerns, input must be at least as long as version 1
            // header plus 16-bit C64 load address. That is the area which will be
            // accessed.
            if (bufLen < (PHeader.SIZE + 2))
            {
                //info.formatstring = _sidtune_truncated;
                return(SidTune.LoadStatus.LOAD_ERROR);
            }

            sidtune.fileOffset = SIDEndian.endian_big16(pHeader.data, 0);
            info.loadAddr      = SIDEndian.endian_big16(pHeader.load, 0);
            info.initAddr      = SIDEndian.endian_big16(pHeader.init, 0);
            info.playAddr      = SIDEndian.endian_big16(pHeader.play, 0);
            info.songs         = SIDEndian.endian_big16(pHeader.songs, 0);
            info.startSong     = SIDEndian.endian_big16(pHeader.start, 0);
            info.sidChipBase1  = 0xd400;
            info.sidChipBase2  = 0;
            info.compatibility = compatibility;
            speed = SIDEndian.endian_big32(pHeader.speed, 0);

            if (info.songs > SidTune.SIDTUNE_MAX_SONGS)
            {
                info.songs = SidTune.SIDTUNE_MAX_SONGS;
            }

            info.musPlayer      = false;
            info.sidModel       = SidTune.SIDTUNE_SIDMODEL_UNKNOWN;
            info.relocPages     = 0;
            info.relocStartPage = 0;
            if (SIDEndian.endian_big16(pHeader.version, 0) >= 2)
            {
                int flags = SIDEndian.endian_big16(pHeader.flags, 0);
                if ((flags & PSID_MUS) != 0)
                {
                    // MUS tunes run at any speed
                    clock          = SidTune.SIDTUNE_CLOCK_ANY;
                    info.musPlayer = true;
                }

#if SIDTUNE_PSID2NG
                // This flags is only available for the appropriate file formats
                switch (compatibility)
                {
                case SidTune.SIDTUNE_COMPATIBILITY_C64:
                    if ((flags & PSID_SPECIFIC) != 0)
                    {
                        info.compatibility = SidTune.SIDTUNE_COMPATIBILITY_PSID;
                    }
                    break;

                case SidTune.SIDTUNE_COMPATIBILITY_R64:
                    if ((flags & PSID_BASIC) != 0)
                    {
                        info.compatibility = SidTune.SIDTUNE_COMPATIBILITY_BASIC;
                    }
                    break;
                }

                if ((flags & PSID_CLOCK_PAL) != 0)
                {
                    clock |= SidTune.SIDTUNE_CLOCK_PAL;
                }
                if ((flags & PSID_CLOCK_NTSC) != 0)
                {
                    clock |= SidTune.SIDTUNE_CLOCK_NTSC;
                }
                info.clockSpeed = clock;

                info.sidModel = SidTune.SIDTUNE_SIDMODEL_UNKNOWN;
                if ((flags & PSID_SIDMODEL_6581) != 0)
                {
                    info.sidModel |= SidTune.SIDTUNE_SIDMODEL_6581;
                }
                if ((flags & PSID_SIDMODEL_8580) != 0)
                {
                    info.sidModel |= SidTune.SIDTUNE_SIDMODEL_8580;
                }

                info.relocStartPage = pHeader.relocStartPage;
                info.relocPages     = pHeader.relocPages;
#endif
            }

            // Check reserved fields to force real c64 compliance
            // as required by the RSID specification
            if (compatibility == SidTune.SIDTUNE_COMPATIBILITY_R64)
            {
                if ((info.loadAddr != 0) || (info.playAddr != 0) || (speed != 0))
                {
                    //info.formatstring = _sidtune_invalid;
                    return(SidTune.LoadStatus.LOAD_ERROR);
                }
                // Real C64 tunes appear as CIA
                speed = ~0;
            }
            // Create the speed/clock setting table.
            sidtune.convertOldStyleSpeedToTables(speed, clock);

            // Copy info strings, so they will not get lost.
            info.numberOfInfostrings = 3;

            // Name
            int i;
            for (i = 0; i < pHeader.name.Length; i++)
            {
                if (pHeader.name[i] == 0)
                {
                    break;
                }
            }
            info.infostring[0] = sidtune.infostring[0] = new string(pHeader.name, 0, Math.Min(i, _sidtune_psid_maxStrLen));

            // Author
            for (i = 0; i < pHeader.author.Length; i++)
            {
                if (pHeader.author[i] == 0)
                {
                    break;
                }
            }
            info.infostring[1] = sidtune.infostring[1] = new string(pHeader.author, 0, Math.Min(i, _sidtune_psid_maxStrLen));

            // Released
            for (i = 0; i < pHeader.released.Length; i++)
            {
                if (pHeader.released[i] == 0)
                {
                    break;
                }
            }
            info.infostring[2] = sidtune.infostring[2] = new string(pHeader.released, 0, Math.Min(i, _sidtune_psid_maxStrLen));

            return(SidTune.LoadStatus.LOAD_OK);
        }
        private void sampleInit()
        {
            if (active && (mode == FM_GALWAY))
            {
                return;
            }

            // Check all important parameters are legal
            short r = convertAddr(0x1d);

            volShift = (short)((0 - reg[r]) >> 1);
            reg[r]   = 0;

            r          = convertAddr(0x1e);
            address    = SIDEndian.endian_16(reg[r + 1], reg[r]);
            r          = convertAddr(0x3d);
            samEndAddr = SIDEndian.endian_16(reg[r + 1], reg[r]);
            if (samEndAddr <= address)
            {
                return;
            }
            samScale  = reg[convertAddr(0x5f)];
            r         = convertAddr(0x5d);
            samPeriod = SIDEndian.endian_16(reg[r + 1], reg[r]) >> samScale;
            if (samPeriod == 0)
            {
                // Stop this channel
                reg[convertAddr(0x1d)] = 0xfd;
                checkForInit();
                return;
            }

            // Load the other parameters
            samNibble     = 0;
            samRepeat     = reg[convertAddr(0x3f)];
            samOrder      = reg[convertAddr(0x7d)];
            r             = convertAddr(0x7e);
            samRepeatAddr = SIDEndian.endian_16(reg[r + 1], reg[r]);
            cycleCount    = samPeriod;

            // Support Galway Samples, but that
            // mode is setup only when a Galway
            // Noise sequence begins
            if (mode == FM_NONE)
            {
                mode = FM_HUELS;
            }

            active  = true;
            cycles  = 0;
            outputs = 0;

            sampleLimit = (short)(8 >> volShift);
            sample      = sampleCalculate();

            // Calculate the sample offset
            m_xsid.sampleOffsetCalc();

            // Schedule a sample update
            m_context.schedule(m_xsid.xsidEvent, 0, m_phase);
            m_context.schedule(sampleEvent, cycleCount, m_phase);
        }
        /// <summary>
        /// Cache the data of a single-file or two-file sidtune and its corresponding file names
        /// </summary>
        /// <param name="buf"></param>
        /// <returns></returns>
        private bool acceptSidTune(Buffer_sidtt buf)
        {
            // @FIXME@ - MUS
            if (info.numberOfInfostrings == 3)
            {
                // Add <?> (HVSC standard) to
                // missing title, author,
                // release fields
                for (int i = 0; i < 3; i++)
                {
                    if (infostring[i].Length == 0)
                    {
                        infostring[i]      = "<?>";
                        info.infostring[i] = infostring[i];
                    }
                }
            }

            // Fix bad sidtune set up.
            if (info.songs > SIDTUNE_MAX_SONGS)
            {
                info.songs = SIDTUNE_MAX_SONGS;
            }
            else if (info.songs == 0)
            {
                info.songs++;
            }
            if (info.startSong > info.songs)
            {
                info.startSong = 1;
            }
            else if (info.startSong == 0)
            {
                info.startSong++;
            }

            info.dataFileLen = buf.bufLen;
            info.c64dataLen  = buf.bufLen - fileOffset;

            // Calculate any remaining addresses and then
            // confirm all the file details are correct
            if (resolveAddrs(buf.buf, fileOffset) == false)
            {
                return(false);
            }
            if (!checkRelocInfo())
            {
                return(false);
            }
            if (!checkCompatibility())
            {
                return(false);
            }

            if (info.dataFileLen >= 2)
            {
                // We only detect an offset of two. Some position independent
                // sidtunes contain a load address of 0xE000, but are loaded
                // to 0x0FFE and call player at 0x1000.
                info.fixLoad = (SIDEndian.endian_little16(buf.buf, fileOffset) == (info.loadAddr + 2));
            }

            // Check the size of the data.
            if (info.c64dataLen > SIDTUNE_MAX_MEMORY)
            {
                //info.statusstring = txt_dataTooLong;
                return(false);
            }
            else if (info.c64dataLen == 0)
            {
                //info.statusstring = txt_empty;
                return(false);
            }

            cache.assign(buf.xferPtr(), buf.xferLen());

            //info.statusstring = txt_noErrors;
            return(true);
        }
        public void write(short addr, short data)
        {
            if (addr > 0x3f)
            {
                return;
            }

            regs[addr] = data;

            // Sync up timers
            _event();

            switch (addr)
            {
            case 0x11:     // Control register 1
            {
                raster_irq = SIDEndian.endian_16hi8(raster_irq, (short)(data >> 7));
                ctrl1      = data;
                y_scroll   = data & 7;

                if (raster_x < 11)
                {
                    break;
                }

                // In line $30, the DEN bit controls if Bad Lines can occur
                if ((raster_y == first_dma_line) && ((data & 0x10) != 0))
                {
                    bad_lines_enabled = true;
                }

                // Bad Line condition?
                bad_line = (raster_y >= first_dma_line) && (raster_y <= last_dma_line) && ((raster_y & 7) == y_scroll) && bad_lines_enabled;

                // Start bad dma line now
                if (bad_line && (raster_x < 53))
                {
                    addrctrl(false);
                }
                break;
            }

            case 0x12:     // Raster counter
                raster_irq = SIDEndian.endian_16lo8(raster_irq, data);
                break;

            case 0x17:
                sprite_expand_y |= (short)(~data & 0xff);
                break;

            case 0x19:     // IRQ flags
                idr &= (short)((~data & 0x0f) | 0x80);
                if (idr == 0x80)
                {
                    trigger(0);
                }
                break;

            case 0x1a:     // IRQ mask
                icr = (short)(data & 0x0f);
                trigger(icr & idr);
                break;
            }
        }
        public void write(short addr, short data)
        {
            long cycles;

            if (addr > 0x0f)
            {
                return;
            }

            regs[addr] = data;
            cycles     = event_context.getTime(m_accessClk, event_context.phase);

            if (cycles != 0)
            {
                m_accessClk += cycles;
                // Sync up timers
                if ((cra & 0x21) == 0x01)
                {
                    ta -= (int)cycles;
                    if (ta == 0)
                    {
                        ta_event();
                    }
                }
                if ((crb & 0x61) == 0x01)
                {
                    tb -= (int)cycles;
                    if (tb == 0)
                    {
                        tb_event();
                    }
                }
            }

            switch (addr)
            {
            case PRA:
            case DDRA:
                portA();
                break;

            case PRB:
            case DDRB:
                portB();
                break;

            case TAL:
                ta_latch = SIDEndian.endian_16lo8(ta_latch, data);
                break;

            case TAH:
                ta_latch = SIDEndian.endian_16hi8(ta_latch, data);
                if ((cra & 0x01) == 0)     // Reload timer if stopped
                {
                    ta = ta_latch;
                }
                break;

            case TBL:
                tb_latch = SIDEndian.endian_16lo8(tb_latch, data);
                break;

            case TBH:
                tb_latch = SIDEndian.endian_16hi8(tb_latch, data);
                if ((crb & 0x01) == 0)     // Reload timer if stopped
                {
                    tb = tb_latch;
                }
                break;

            // TOD implementation taken from Vice
            case TOD_HR:     // Time Of Day clock hour
                // Flip AM/PM on hour 12
                // Flip AM/PM only when writing time, not when writing alarm
                data &= 0x9f;
                if ((data & 0x1f) == 0x12 && ((crb & 0x80) == 0))
                {
                    data ^= 0x80;
                }
                // deliberate run on
                if ((crb & 0x80) != 0)
                {
                    m_todalarm[addr - TOD_TEN] = data;
                }
                else
                {
                    if (addr == TOD_TEN)
                    {
                        m_todstopped = false;
                    }
                    if (addr == TOD_HR)
                    {
                        m_todstopped = true;
                    }
                    m_todclock[addr - TOD_TEN] = data;
                }
                // check alarm
                if (!m_todstopped && !memcmp(m_todalarm, m_todclock, m_todalarm.Length))
                {
                    trigger(INTERRUPT_ALARM);
                }
                break;

            case TOD_TEN:     // Time Of Day clock 1/10 s
            case TOD_SEC:     // Time Of Day clock sec
            case TOD_MIN:     // Time Of Day clock min
                if ((crb & 0x80) != 0)
                {
                    m_todalarm[addr - TOD_TEN] = data;
                }
                else
                {
                    if (addr == TOD_TEN)
                    {
                        m_todstopped = false;
                    }
                    if (addr == TOD_HR)
                    {
                        m_todstopped = true;
                    }
                    m_todclock[addr - TOD_TEN] = data;
                }
                // check alarm
                if (!m_todstopped && !memcmp(m_todalarm, m_todclock, m_todalarm.Length))
                {
                    trigger(INTERRUPT_ALARM);
                }
                break;

            case SDR:
                if ((cra & 0x40) != 0)
                {
                    sdr_buffered = true;
                }
                break;

            case ICR:
                if ((data & 0x80) != 0)
                {
                    icr |= (short)(data & 0x1f);
                }
                else
                {
                    icr &= (short)(~data & 0xff);
                }
                trigger(idr);
                break;

            case CRA:
                // Reset the underflow flipflop for the data port
                if (((data & 1) != 0) && ((cra & 1) == 0))
                {
                    ta           = ta_latch;
                    ta_underflow = true;
                }
                cra = data;

                // Check for forced load
                if ((data & 0x10) != 0)
                {
                    cra &= (~0x10 & 0xff);
                    ta   = ta_latch;
                }

                if ((data & 0x21) == 0x01)
                {
                    // Active
                    event_context.schedule(event_ta, (long)ta + 1, m_phase);
                }
                else
                {
                    // Inactive
                    event_context.cancel(event_ta);
                }
                break;

            case CRB:
                // Reset the underflow flipflop for the data port
                if (((data & 1) != 0) && ((crb & 1) == 0))
                {
                    tb           = tb_latch;
                    tb_underflow = true;
                }
                // Check for forced load
                crb = data;
                if ((data & 0x10) != 0)
                {
                    crb &= (~0x10 & 0xff);
                    tb   = tb_latch;
                }

                if ((data & 0x61) == 0x01)
                {
                    // Active
                    event_context.schedule(event_tb, (long)tb + 1, m_phase);
                }
                else
                {
                    // Inactive
                    event_context.cancel(event_tb);
                }
                break;

            default:
                break;
            }
        }
        public short read(short addr)
        {
            long cycles;

            if (addr > 0x0f)
            {
                return(0);
            }

            bool ta_pulse = false, tb_pulse = false;

            cycles       = event_context.getTime(m_accessClk, event_context.phase);
            m_accessClk += cycles;

            // Sync up timers
            if ((cra & 0x21) == 0x01)
            {
                ta -= (int)cycles;
                if (ta == 0)
                {
                    ta_event();
                    ta_pulse = true;
                }
            }
            if ((crb & 0x61) == 0x01)
            {
                tb -= (int)cycles;
                if (tb == 0)
                {
                    tb_event();
                    tb_pulse = true;
                }
            }

            switch (addr)
            {
            case PRA:     // Simulate a serial port
                return((short)(regs[PRA] | (short)(~regs[DDRA] & 0xff)));

            case PRB:
            {
                short data = (short)(regs[PRB] | (short)(~regs[DDRB] & 0xff));
                // Timers can appear on the port
                if ((cra & 0x02) != 0)
                {
                    data &= 0xbf;
                    if ((cra & 0x04) != 0 ? ta_underflow : ta_pulse)
                    {
                        data |= 0x40;
                    }
                }
                if ((crb & 0x02) != 0)
                {
                    data &= 0x7f;
                    if ((crb & 0x04) != 0 ? tb_underflow : tb_pulse)
                    {
                        data |= 0x80;
                    }
                }
                return(data);
            }

            case TAL:
                return(SIDEndian.endian_16lo8(ta));

            case TAH:
                return(SIDEndian.endian_16hi8(ta));

            case TBL:
                return(SIDEndian.endian_16lo8(tb));

            case TBH:
                return(SIDEndian.endian_16hi8(tb));

            // TOD implementation taken from Vice
            // TOD clock is latched by reading Hours, and released
            // upon reading Tenths of Seconds. The counter itself
            // keeps ticking all the time.
            // Also note that this latching is different from the input one.
            case TOD_TEN:     // Time Of Day clock 1/10 s
            case TOD_SEC:     // Time Of Day clock sec
            case TOD_MIN:     // Time Of Day clock min
            case TOD_HR:      // Time Of Day clock hour
                if (!m_todlatched)
                {
                    for (int i = 0; i < m_todlatch.Length; i++)
                    {
                        m_todlatch[i] = m_todclock[i];
                    }
                }
                if (addr == TOD_TEN)
                {
                    m_todlatched = false;
                }
                if (addr == TOD_HR)
                {
                    m_todlatched = true;
                }
                return(m_todlatch[addr - TOD_TEN]);

            case IDR:
            {
                // Clear IRQs, and return interrupt data register
                short ret = idr;
                trigger(0);
                return(ret);
            }

            case CRA:
                return(cra);

            case CRB:
                return(crb);

            default:
                return(regs[addr]);
            }
        }