335 lines
10 KiB
C#
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;
|
|
}
|
|
}
|