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; } }
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); }
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]); } }