StoicGoose.Unity/Assets/Plugins/StoicGooseUnity/StoicGoose.Core/Machines/WonderSwan.cs

335 lines
10 KiB
C#

using System.Collections.Generic;
using StoicGoose.Common.Attributes;
using StoicGoose.Core.Display;
using StoicGoose.Core.Serial;
using StoicGoose.Core.Sound;
using StoicGooseUnity;
using static StoicGoose.Common.Utilities.BitHandling;
namespace StoicGoose.Core.Machines
{
public partial class WonderSwan : MachineCommon
{
public override string Manufacturer => "Bandai";
public override string Model => "WonderSwan";
public override string InternalEepromDefaultUsername => "WONDERSWAN";
public override Dictionary<ushort, byte> InternalEepromDefaultData => new()
{
{ 0x70, 0x19 }, // Year of birth [just for fun, here set to original WS release date; new systems probably had no date set?]
{ 0x71, 0x99 }, // ""
{ 0x72, 0x03 }, // Month of birth [again, WS release for fun]
{ 0x73, 0x04 }, // Day of birth [and again]
{ 0x74, 0x00 }, // Sex [set to ?]
{ 0x75, 0x00 }, // Blood type [set to ?]
{ 0x76, 0x00 }, // Last game played, publisher ID [set to presumably none]
{ 0x77, 0x00 }, // ""
{ 0x78, 0x00 }, // Last game played, game ID [set to presumably none]
{ 0x79, 0x00 }, // ""
{ 0x7A, 0x00 }, // Swan ID (see Mama Mitte) -- TODO: set to valid/random value?
{ 0x7B, 0x00 }, // ""
{ 0x7C, 0x00 }, // Number of different games played [set to presumably none]
{ 0x7D, 0x00 }, // Number of times settings were changed [set to presumably none]
{ 0x7E, 0x00 }, // Number of times powered on [set to presumably none]
{ 0x7F, 0x00 } // ""
};
public override int InternalRamSize => 16 * 1024;
public override int InternalEepromSize => 64 * 2;
public override int InternalEepromAddressBits => 6;
public override int BootstrapRomAddress => 0xFF000;
public override int BootstrapRomSize => 0x1000;
public override void Initialize()
{
DisplayController = new AswanDisplayController(this);
SoundController = new AswanSoundController(this, 44100, 2);
base.Initialize();
}
public override void ResetRegisters()
{
isWSCOrGreater = false;
base.ResetRegisters();
}
public override void RunStep()
{
if (RunStepCallback == null || RunStepCallback.Invoke() == false)
{
HandleInterrupts();
var currentCpuClockCycles = Cpu.Step();
var displayInterrupt = DisplayController.Step(currentCpuClockCycles);
if (displayInterrupt.HasFlag(DisplayControllerCommon.DisplayInterrupts.LineCompare)) RaiseInterrupt(4);
if (displayInterrupt.HasFlag(DisplayControllerCommon.DisplayInterrupts.VBlankTimer)) RaiseInterrupt(5);
if (displayInterrupt.HasFlag(DisplayControllerCommon.DisplayInterrupts.VBlank)) RaiseInterrupt(6);
if (displayInterrupt.HasFlag(DisplayControllerCommon.DisplayInterrupts.HBlankTimer)) RaiseInterrupt(7);
SoundController.Step(currentCpuClockCycles);
if (Cartridge.Step(currentCpuClockCycles)) RaiseInterrupt(2);
else LowerInterrupt(2);
var serialInterrupt = Serial.Step();
if (serialInterrupt.HasFlag(SerialPort.SerialInterrupts.SerialSend)) RaiseInterrupt(0);
if (serialInterrupt.HasFlag(SerialPort.SerialInterrupts.SerialRecieve)) RaiseInterrupt(3);
CurrentClockCyclesInLine += currentCpuClockCycles;
cancelFrameExecution = false;
}
else
cancelFrameExecution = true;
}
public override byte ReadPort(ushort port)
{
var retVal = (byte)0;
switch (port)
{
/* Display controller, etc. (H/V timers, DISP_MODE) */
case var n when (n >= 0x00 && n < 0x40) || n == 0x60 || n == 0xA2 || (n >= 0xA4 && n <= 0xAB):
retVal = DisplayController.ReadPort(port);
break;
/* Sound controller */
case var n when n >= 0x80 && n < 0xA0:
retVal = SoundController.ReadPort(port);
break;
/* Serial port */
case var n when n == 0xB1 || n == 0xB3:
retVal = Serial.ReadPort(port);
break;
/* System controller */
case 0xA0:
/* REG_HW_FLAGS */
ChangeBit(ref retVal, 0, cartEnable);
ChangeBit(ref retVal, 1, false);
ChangeBit(ref retVal, 2, is16BitExtBus);
ChangeBit(ref retVal, 3, cartRom1CycleSpeed);
ChangeBit(ref retVal, 7, builtInSelfTestOk);
break;
case 0xB0:
/* REG_INT_BASE */
retVal = interruptBase;
retVal |= 0b11;
break;
case 0xB2:
/* REG_INT_ENABLE */
retVal = interruptEnable;
break;
case 0xB4:
/* REG_INT_STATUS */
retVal = interruptStatus;
break;
case 0xB5:
/* REG_KEYPAD */
ChangeBit(ref retVal, 4, keypadYEnable);
ChangeBit(ref retVal, 5, keypadXEnable);
ChangeBit(ref retVal, 6, keypadButtonEnable);
/* Get input from UI */
var buttonsHeld = ReceiveInput?.Invoke();
if (buttonsHeld != null)
{
if (buttonsHeld > 0)
RaiseInterrupt(1);
if (keypadYEnable)
{
if ((buttonsHeld.Value & (ushort)StoicGooseKey.Y1) > 0) ChangeBit(ref retVal, 0, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.Y2) > 0) ChangeBit(ref retVal, 1, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.Y3) > 0) ChangeBit(ref retVal, 2, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.Y4) > 0) ChangeBit(ref retVal, 3, true);
}
if (keypadXEnable)
{
if ((buttonsHeld.Value & (ushort)StoicGooseKey.X1) > 0) ChangeBit(ref retVal, 0, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.X2) > 0) ChangeBit(ref retVal, 1, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.X3) > 0) ChangeBit(ref retVal, 2, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.X4) > 0) ChangeBit(ref retVal, 3, true);
}
if (keypadButtonEnable)
{
if ((buttonsHeld.Value & (ushort)StoicGooseKey.Start) > 0) ChangeBit(ref retVal, 1, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.A) > 0) ChangeBit(ref retVal, 2, true);
if ((buttonsHeld.Value & (ushort)StoicGooseKey.B) > 0) ChangeBit(ref retVal, 3, true);
}
}
//var buttonsHeld = ReceiveInput?.Invoke().buttonsHeld;
//if (buttonsHeld != null)
//{
// if (buttonsHeld.Count > 0)
// RaiseInterrupt(1);
// if (keypadYEnable)
// {
// if (buttonsHeld.Contains("Y1")) ChangeBit(ref retVal, 0, true);
// if (buttonsHeld.Contains("Y2")) ChangeBit(ref retVal, 1, true);
// if (buttonsHeld.Contains("Y3")) ChangeBit(ref retVal, 2, true);
// if (buttonsHeld.Contains("Y4")) ChangeBit(ref retVal, 3, true);
// }
// if (keypadXEnable)
// {
// if (buttonsHeld.Contains("X1")) ChangeBit(ref retVal, 0, true);
// if (buttonsHeld.Contains("X2")) ChangeBit(ref retVal, 1, true);
// if (buttonsHeld.Contains("X3")) ChangeBit(ref retVal, 2, true);
// if (buttonsHeld.Contains("X4")) ChangeBit(ref retVal, 3, true);
// }
// if (keypadButtonEnable)
// {
// if (buttonsHeld.Contains("Start")) ChangeBit(ref retVal, 1, true);
// if (buttonsHeld.Contains("A")) ChangeBit(ref retVal, 2, true);
// if (buttonsHeld.Contains("B")) ChangeBit(ref retVal, 3, true);
// }
//}
break;
case 0xB6:
/* REG_INT_ACK */
retVal = 0x00;
break;
case 0xB7:
/* ??? */
retVal = 0x00;
break;
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBE:
/* REG_IEEP_DATA (low) */
/* REG_IEEP_DATA (high) */
/* REG_IEEP_ADDR (low) */
/* REG_IEEP_ADDR (high) */
/* REG_IEEP_CMD (write) */
retVal = InternalEeprom.ReadPort((ushort)(port - 0xBA));
break;
/* Cartridge */
case var n when n >= 0xC0 && n < 0x100:
retVal = Cartridge.ReadPort(port);
break;
/* Unmapped */
default:
retVal = 0x90;
break;
}
if (ReadPortCallback != null)
retVal = ReadPortCallback.Invoke(port, retVal);
return retVal;
}
public override void WritePort(ushort port, byte value)
{
WritePortCallback?.Invoke(port, value);
switch (port)
{
/* Display controller, etc. (H/V timers, DISP_MODE) */
case var n when (n >= 0x00 && n < 0x40) || n == 0x60 || n == 0xA2 || (n >= 0xA4 && n <= 0xAB):
DisplayController.WritePort(port, value);
break;
/* Sound controller */
case var n when n >= 0x80 && n < 0xA0:
SoundController.WritePort(port, value);
break;
/* Serial port */
case var n when n == 0xB1 || n == 0xB3:
Serial.WritePort(port, value);
break;
/* System controller */
case 0xA0:
/* REG_HW_FLAGS */
if (!cartEnable)
cartEnable = IsBitSet(value, 0);
is16BitExtBus = IsBitSet(value, 2);
cartRom1CycleSpeed = IsBitSet(value, 3);
break;
case 0xB0:
/* REG_INT_BASE */
interruptBase = (byte)(value & 0b11111000);
break;
case 0xB2:
/* REG_INT_ENABLE */
interruptEnable = value;
break;
case 0xB4:
/* REG_INT_STATUS -- read-only */
break;
case 0xB5:
/* REG_KEYPAD */
keypadYEnable = IsBitSet(value, 4);
keypadXEnable = IsBitSet(value, 5);
keypadButtonEnable = IsBitSet(value, 6);
break;
case 0xB6:
/* REG_INT_ACK */
interruptStatus &= (byte)~(value & (0b11110010 | ~interruptEnable));
break;
case 0xB7:
/* ??? */
break;
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBE:
/* REG_IEEP_DATA (low) */
/* REG_IEEP_DATA (high) */
/* REG_IEEP_ADDR (low) */
/* REG_IEEP_ADDR (high) */
/* REG_IEEP_STATUS (read) */
InternalEeprom.WritePort((ushort)(port - 0xBA), value);
break;
/* Cartridge */
case var n when n >= 0xC0 && n < 0x100:
Cartridge.WritePort(port, value);
break;
}
}
[Port("REG_INT_BASE", 0x0B0)]
[BitDescription("Interrupt base address", 3, 7)]
[Format("X4", 0)]
public override byte InterruptBase => interruptBase;
}
}