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