public void Poke(ushort address, byte value) { // Timer addresses can be handled separately if (address >= Mikey.Addresses.HTIMBKUP && address <= Mikey.Addresses.TIM7CTLB) { int offset = address - Mikey.Addresses.HTIMBKUP; int index = offset >> 2; // Divide by 4 to get index of timer Timer timer = Timers[index]; switch (offset % 4) { case 0: // Backup value timer.BackupValue = value; return; case 1: // Static control StaticControlBits control = new StaticControlBits(value); timer.StaticControlBits = control; // "It is set on time out, reset with the reset timer done bit (xxx1, B6)" if (control.ResetTimerDone) { timer.DynamicControlBits.TimerDone = false; } if (control.EnableCount || control.ResetTimerDone) { timer.Start(device.SystemClock.CompatibleCycleCount); ForceTimerUpdate(); } return; case 2: // Current value timer.CurrentValue = value; ForceTimerUpdate(); return; case 3: // Dynamic control bits timer.DynamicControlBits.ByteData = value; return; } } if (address >= Mikey.Addresses.AUD0VOL && address <= Mikey.Addresses.AUD3MISC && SoundEnabled) { int offset = address - Mikey.Addresses.AUD0VOL; int index = offset >> 3; // Divide by 8 to get index of audio channel AudioChannel channel = AudioChannels[index]; // "FD20 -> FD27 Audio channel 0, links from timer 7" // "FD28 -> FD2F Audio channel 1, links from audio timer 0" // "FD30 -> FD37 Audio channel 2, links from audio timer 1" // "FD38 -> FD3F Audio channel 3, links trom audio timer 2" switch (offset % 8) { case 0: // "8 bit. 2's Complement Volume Control" channel.VolumeControl = (sbyte)value; return; case 1: // "Shift register feedback enable" channel.FeedbackEnable.ByteData = value; return; case 2: // "Audio output value" channel.OutputValue = (sbyte)value; return; case 3: // "Lower 8 Bits of Shift Register" channel.LowerShiftRegister = value; return; case 4: // "Audio Timer Backup Value" channel.BackupValue = value; return; case 5: // "Audio Control Bits" //channel.AudioControl.ByteData = value; AudioControlBits control = new AudioControlBits(value); channel.AudioControl = control; // "It is set on time out, reset with the reset timer done bit (xxx1, B6)" if (control.ResetTimerDone) { channel.DynamicControlBits.TimerDone = false; } if (control.EnableCount || control.ResetTimerDone) { channel.Start(device.SystemClock.CompatibleCycleCount); ForceTimerUpdate(); } return; case 6: // "Audio counter" channel.CurrentValue = value; ForceTimerUpdate(); return; case 7: // "Other control bits" channel.OtherControlBits = value; return; } } // Handle other addresses switch (address) { case Mikey.Addresses.MSTEREO: // "The Howard boards were not yet finished, so we went ahead and implemented this stereo on them. // This form of stereo was channel switching controlled by FD50." // TODO: Implement stereo return; case Mikey.Addresses.MPAN: // TODO: Implement panning return; case Mikey.Addresses.INTRST: // "Read is a poll, write will reset the int that corresponds to a set bit." value ^= 0xff; timerInterruptStatusRegister &= value; // When timer interrupt status register is zero, IRQ line goes back up (not-active) device.Cpu.SignalInterrupt(timerInterruptStatusRegister != 0 ? InterruptType.Irq : InterruptType.None); ForceTimerUpdate(); return; case Mikey.Addresses.INTSET: // "Read is a poll, write will set the int that corresponds to a set bit." timerInterruptStatusRegister |= value; // When timer interrupt status register is zero, IRQ line goes back up (not-active) device.Cpu.SignalInterrupt(timerInterruptStatusRegister != 0 ? InterruptType.Irq : InterruptType.None); ForceTimerUpdate(); return; case Mikey.Addresses.MIKEYSREV: // "No actual register is implemented" return; // "Also note that only the lines that are set to input are actually valid for reading." // "8 bits I/O direction corresponding to the 8 bits at FD8B 0=input, 1= output" case Mikey.Addresses.IODIR: IODIR.ByteData = value; return; // "Mikey Parallel Data(sort of a R/W) 8 bits of general purpose I/O data" case Mikey.Addresses.IODAT: IODAT.ByteData = value; // "One is that it is the data pin for the shifter that holds the cartridge address." device.Cartridge.CartAddressData(IODAT.CartAddressData); // "The other is that it controls power to the cartridge." device.CartridgePowerOn = !IODAT.CartPowerOff; // "In its current use, it is the write enable line for writeable elements in the cartridge." if (IODIR.AuxiliaryDigitalInOut == DataDirection.Output) { device.Cartridge.AuxiliaryDigitalInOut = IODAT.AuxiliaryDigitalInOut; } // "In its current use, it is the write enable line for writeable elements in the cartridge." if (IODIR.AuxiliaryDigitalInOut == DataDirection.Output) { device.Cartridge.WriteEnabled = IODAT.AuxiliaryDigitalInOut; } return; case Mikey.Addresses.SERCTL: ComLynx.SERCTL = value; return; case Mikey.Addresses.SERDAT: ComLynx.SERDAT = value; return; case Mikey.Addresses.SYSCTL1: SYSCTL1.ByteData = value; if (!SYSCTL1.Power) { device.Reset(); // TODO: Enter debug mode if configured } device.Cartridge.CartAddressStrobe(SYSCTL1.CartAddressStrobe); return; case Mikey.Addresses.SDONEACK: // "Write a '00' to SDONEACK, allowing Mikey to respond to sleep commands." // "The Suzy Done Acknowledge address must be written to prior to running the sprite engine. // This is required even prior to the first time the sprite engine is activated. // It it is not written to in the appropriate sequences, the CPU will not go to sleep // when so requested. In addition, if some software accidentally allows a Suzy operation to // complete without then following that completion with a write to SDONEACK, the CPU // will not sleep. So if sprites stop working, something may have gone wrong with your // SDONEACK software." // TODO: Implement state for Suzy Done acknowledgement if necessary return; case Mikey.Addresses.CPUSLEEP: // "A write of '0' to this address will reset the CPU bus request flip flop. // The setting of the flip flop is described in the hardware specification." // BUG: "Sleep does not work if Suzy does not have the bus." // "We assume that everyone knows about this bug and behaves accordingly, so // writing zero here must be to give Suzy access to the bus and it will start drawing // sprites and will signal when it is done." // This is implemented as a new wakeup time by calculating the number of cycles used // and skipping forward in time to that moment. ulong suzyCycles = device.Suzy.RenderSprites(); // TODO: For now use estimate of cycles for sprite drawing. // Needs to be replaced with a better calculation. See also Handy PaintSprites code. suzyCycles = 0; //1000; device.Cpu.TrySleep(suzyCycles); return; case Mikey.Addresses.DISPCTL: DISPCTL.ByteData = value; return; case Mikey.Addresses.PBKUP: // "Additionally, the magic 'P' counter has to be set to match the LCD scan rate. The formula is: // INT((((line time - .5us) / 15) * 4) -1)" PBKUP = value; return; case Mikey.Addresses.DISPADRL: // "DISPADRL (FD94) is lower 8 bits of display address with the bottom 2 bit ignored by the hardware. // The address of the upper left corner of a display buffer must always have '00' in the bottom 2 bits." VideoDisplayStartAddress.LowByte = (byte)(value & 0xFD); return; case Mikey.Addresses.DISPADRH: // "DISPADRH (FD95) is upper 8 bits of display address." VideoDisplayStartAddress.HighByte = value; return; case Mikey.Addresses.MAGRDY0: case Mikey.Addresses.MAGRDY1: case Mikey.Addresses.AUDIN: case Mikey.Addresses.MIKEYHREV: //Debug.WriteLineIf(GeneralSwitch.TraceWarning, String.Format("Mikey::Poke - Read-only address {0:X4} used (value {1:X2}).", address, value)); break; default: break; } if (address > Mikey.Addresses.BLUEREDF) { return; } // Blue and red color map if (address >= Mikey.Addresses.BLUERED0 && address <= Mikey.Addresses.BLUEREDF) { int index = address - Mikey.Addresses.BLUERED0; BlueRedColorMap[index] = value; ArgbColorMap[index] &= 0xFF00FF00; ArgbColorMap[index] |= (uint)((value & 0xF0) << 16); // Blue ArgbColorMap[index] |= (uint)((value & 0x0F) << 4); // Red return; } // Green color map if (address >= Mikey.Addresses.GREEN0) { int index = address - Mikey.Addresses.GREEN0; GreenColorMap[index] = value; ArgbColorMap[index] &= 0xFFFF00FF; ArgbColorMap[index] |= (uint)((value & 0x0F) << 12); return; } if (address >= 0xFD20 && address <= 0xFD3F) { return; } //Trace.WriteLineIf(GeneralSwitch.TraceWarning, String.Format("Mikey::Poke: Unknown address ${0:X4} specified (value={1:X2}).", address,value)); }
public Timer(byte interruptMask) : base() { InterruptMask = interruptMask; StaticControlBits = new StaticControlBits(0); }