移植的基础先归档
This commit is contained in:
parent
74716f670b
commit
51336a4976
8
Assets/Iris.meta
Normal file
8
Assets/Iris.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c85535aab73500e4e93bc6a3430ff694
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Iris/Iris.CPU.meta
Normal file
8
Assets/Iris/Iris.CPU.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8eb82c88394f8a409faf989c85e64b4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
2041
Assets/Iris/Iris.CPU/ARM_Interpreter.cs
Normal file
2041
Assets/Iris/Iris.CPU/ARM_Interpreter.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Assets/Iris/Iris.CPU/ARM_Interpreter.cs.meta
Normal file
11
Assets/Iris/Iris.CPU/ARM_Interpreter.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c4deba1667702f49b08a4ccab53688e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
575
Assets/Iris/Iris.CPU/CPU_Core.cs
Normal file
575
Assets/Iris/Iris.CPU/CPU_Core.cs
Normal file
@ -0,0 +1,575 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Iris.CPU
|
||||
{
|
||||
public sealed class CPU_Core
|
||||
{
|
||||
public enum Model
|
||||
{
|
||||
ARM7TDMI,
|
||||
ARM946ES
|
||||
}
|
||||
|
||||
public delegate Byte Read8_Delegate(UInt32 address);
|
||||
public delegate UInt16 Read16_Delegate(UInt32 address);
|
||||
public delegate UInt32 Read32_Delegate(UInt32 address);
|
||||
public delegate void Write8_Delegate(UInt32 address, Byte value);
|
||||
public delegate void Write16_Delegate(UInt32 address, UInt16 value);
|
||||
public delegate void Write32_Delegate(UInt32 address, UInt32 value);
|
||||
public delegate UInt64 HandleSWI_Delegate();
|
||||
public delegate UInt64 HandleIRQ_Delegate();
|
||||
|
||||
// could have used function pointers (delegate*) for performance instead of delegates but it's less flexible (cannot use non-static function for instance)
|
||||
//public readonly struct CallbackInterface
|
||||
//(
|
||||
// Read8_Delegate read8,
|
||||
// Read16_Delegate read16,
|
||||
// Read32_Delegate read32,
|
||||
// Write8_Delegate write8,
|
||||
// Write16_Delegate write16,
|
||||
// Write32_Delegate write32,
|
||||
// HandleSWI_Delegate handleSWI,
|
||||
// HandleIRQ_Delegate handleIRQ
|
||||
//)
|
||||
//{
|
||||
// internal readonly Read8_Delegate _read8 = read8;
|
||||
// internal readonly Read16_Delegate _read16 = read16;
|
||||
// internal readonly Read32_Delegate _read32 = read32;
|
||||
// internal readonly Write8_Delegate _write8 = write8;
|
||||
// internal readonly Write16_Delegate _write16 = write16;
|
||||
// internal readonly Write32_Delegate _write32 = write32;
|
||||
// internal readonly HandleSWI_Delegate _handleSWI = handleSWI;
|
||||
// internal readonly HandleIRQ_Delegate _handleIRQ = handleIRQ;
|
||||
//}
|
||||
|
||||
public readonly struct CallbackInterface
|
||||
{
|
||||
public CallbackInterface
|
||||
(
|
||||
Read8_Delegate read8,
|
||||
Read16_Delegate read16,
|
||||
Read32_Delegate read32,
|
||||
Write8_Delegate write8,
|
||||
Write16_Delegate write16,
|
||||
Write32_Delegate write32,
|
||||
HandleSWI_Delegate handleSWI,
|
||||
HandleIRQ_Delegate handleIRQ
|
||||
)
|
||||
{
|
||||
_read8 = read8;
|
||||
_read16 = read16;
|
||||
_read32 = read32;
|
||||
_write8 = write8;
|
||||
_write16 = write16;
|
||||
_write32 = write32;
|
||||
_handleSWI = handleSWI;
|
||||
_handleIRQ = handleIRQ;
|
||||
}
|
||||
|
||||
internal readonly Read8_Delegate _read8;
|
||||
internal readonly Read16_Delegate _read16;
|
||||
internal readonly Read32_Delegate _read32;
|
||||
internal readonly Write8_Delegate _write8;
|
||||
internal readonly Write16_Delegate _write16;
|
||||
internal readonly Write32_Delegate _write32;
|
||||
internal readonly HandleSWI_Delegate _handleSWI;
|
||||
internal readonly HandleIRQ_Delegate _handleIRQ;
|
||||
}
|
||||
|
||||
public enum Signal
|
||||
{
|
||||
High,
|
||||
Low
|
||||
}
|
||||
|
||||
internal enum Flag
|
||||
{
|
||||
V = 28,
|
||||
C = 29,
|
||||
Z = 30,
|
||||
N = 31
|
||||
}
|
||||
|
||||
internal unsafe readonly struct InstructionListEntry<T>(T mask, T expected, delegate*<CPU_Core, T, UInt64> handler, List<Model> modelList)
|
||||
{
|
||||
internal readonly T _mask = mask;
|
||||
internal readonly T _expected = expected;
|
||||
internal unsafe readonly delegate*<CPU_Core, T, UInt64> _handler = handler;
|
||||
internal readonly List<Model> _modelList = modelList;
|
||||
}
|
||||
|
||||
internal unsafe readonly struct InstructionLUTEntry<T>(delegate*<CPU_Core, T, UInt64> handler)
|
||||
{
|
||||
internal unsafe readonly delegate*<CPU_Core, T, UInt64> _handler = handler;
|
||||
}
|
||||
|
||||
internal const UInt32 ModeMask = 0b1_1111;
|
||||
internal const UInt32 UserMode = 0b1_0000;
|
||||
internal const UInt32 SystemMode = 0b1_1111;
|
||||
internal const UInt32 SupervisorMode = 0b1_0011;
|
||||
internal const UInt32 AbortMode = 0b1_0111;
|
||||
internal const UInt32 UndefinedMode = 0b1_1011;
|
||||
internal const UInt32 InterruptMode = 0b1_0010;
|
||||
internal const UInt32 FastInterruptMode = 0b1_0001;
|
||||
|
||||
public const UInt32 SP = 13;
|
||||
public const UInt32 LR = 14;
|
||||
public const UInt32 PC = 15;
|
||||
|
||||
public readonly UInt32[] Reg = new UInt32[16];
|
||||
public UInt32 CPSR;
|
||||
public UInt32 SPSR;
|
||||
|
||||
public UInt32 Reg8_usr, Reg9_usr, Reg10_usr, Reg11_usr, Reg12_usr, Reg13_usr, Reg14_usr;
|
||||
public UInt32 Reg13_svc, Reg14_svc;
|
||||
public UInt32 Reg13_abt, Reg14_abt;
|
||||
public UInt32 Reg13_und, Reg14_und;
|
||||
public UInt32 Reg13_irq, Reg14_irq;
|
||||
public UInt32 Reg8_fiq, Reg9_fiq, Reg10_fiq, Reg11_fiq, Reg12_fiq, Reg13_fiq, Reg14_fiq;
|
||||
public UInt32 SPSR_svc, SPSR_abt, SPSR_und, SPSR_irq, SPSR_fiq;
|
||||
|
||||
internal readonly Model _model;
|
||||
internal readonly CallbackInterface _callbackInterface;
|
||||
|
||||
private readonly ARM_Interpreter _armInterpreter;
|
||||
private readonly THUMB_Interpreter _thumbInterpreter;
|
||||
|
||||
public UInt32 NextInstructionAddress;
|
||||
public Signal NIRQ;
|
||||
|
||||
public CPU_Core(Model model, CallbackInterface callbackInterface)
|
||||
{
|
||||
_model = model;
|
||||
_callbackInterface = callbackInterface;
|
||||
_armInterpreter = new(this);
|
||||
_thumbInterpreter = new(this);
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
Array.Clear(Reg,0,Reg.Length);
|
||||
|
||||
CPSR = 0b1_0000;
|
||||
SPSR = 0;
|
||||
|
||||
Reg8_usr = 0;
|
||||
Reg9_usr = 0;
|
||||
Reg10_usr = 0;
|
||||
Reg11_usr = 0;
|
||||
Reg12_usr = 0;
|
||||
Reg13_usr = 0;
|
||||
Reg14_usr = 0;
|
||||
|
||||
Reg13_svc = 0;
|
||||
Reg14_svc = 0;
|
||||
|
||||
Reg13_abt = 0;
|
||||
Reg14_abt = 0;
|
||||
|
||||
Reg13_und = 0;
|
||||
Reg14_und = 0;
|
||||
|
||||
Reg13_irq = 0;
|
||||
Reg14_irq = 0;
|
||||
|
||||
Reg8_fiq = 0;
|
||||
Reg9_fiq = 0;
|
||||
Reg10_fiq = 0;
|
||||
Reg11_fiq = 0;
|
||||
Reg12_fiq = 0;
|
||||
Reg13_fiq = 0;
|
||||
Reg14_fiq = 0;
|
||||
|
||||
SPSR_svc = 0;
|
||||
SPSR_abt = 0;
|
||||
SPSR_und = 0;
|
||||
SPSR_irq = 0;
|
||||
SPSR_fiq = 0;
|
||||
|
||||
NextInstructionAddress = 0;
|
||||
NIRQ = Signal.High;
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader reader)
|
||||
{
|
||||
foreach (ref UInt32 reg in Reg.AsSpan())
|
||||
reg = reader.ReadUInt32();
|
||||
|
||||
CPSR = reader.ReadUInt32();
|
||||
SPSR = reader.ReadUInt32();
|
||||
|
||||
Reg8_usr = reader.ReadUInt32();
|
||||
Reg9_usr = reader.ReadUInt32();
|
||||
Reg10_usr = reader.ReadUInt32();
|
||||
Reg11_usr = reader.ReadUInt32();
|
||||
Reg12_usr = reader.ReadUInt32();
|
||||
Reg13_usr = reader.ReadUInt32();
|
||||
Reg14_usr = reader.ReadUInt32();
|
||||
|
||||
Reg13_svc = reader.ReadUInt32();
|
||||
Reg14_svc = reader.ReadUInt32();
|
||||
|
||||
Reg13_abt = reader.ReadUInt32();
|
||||
Reg14_abt = reader.ReadUInt32();
|
||||
|
||||
Reg13_und = reader.ReadUInt32();
|
||||
Reg14_und = reader.ReadUInt32();
|
||||
|
||||
Reg13_irq = reader.ReadUInt32();
|
||||
Reg14_irq = reader.ReadUInt32();
|
||||
|
||||
Reg8_fiq = reader.ReadUInt32();
|
||||
Reg9_fiq = reader.ReadUInt32();
|
||||
Reg10_fiq = reader.ReadUInt32();
|
||||
Reg11_fiq = reader.ReadUInt32();
|
||||
Reg12_fiq = reader.ReadUInt32();
|
||||
Reg13_fiq = reader.ReadUInt32();
|
||||
Reg14_fiq = reader.ReadUInt32();
|
||||
|
||||
SPSR_svc = reader.ReadUInt32();
|
||||
SPSR_abt = reader.ReadUInt32();
|
||||
SPSR_und = reader.ReadUInt32();
|
||||
SPSR_irq = reader.ReadUInt32();
|
||||
SPSR_fiq = reader.ReadUInt32();
|
||||
|
||||
NextInstructionAddress = reader.ReadUInt32();
|
||||
NIRQ = (Signal)reader.ReadInt32();
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter writer)
|
||||
{
|
||||
foreach (UInt32 reg in Reg)
|
||||
writer.Write(reg);
|
||||
|
||||
writer.Write(CPSR);
|
||||
writer.Write(SPSR);
|
||||
|
||||
writer.Write(Reg8_usr);
|
||||
writer.Write(Reg9_usr);
|
||||
writer.Write(Reg10_usr);
|
||||
writer.Write(Reg11_usr);
|
||||
writer.Write(Reg12_usr);
|
||||
writer.Write(Reg13_usr);
|
||||
writer.Write(Reg14_usr);
|
||||
|
||||
writer.Write(Reg13_svc);
|
||||
writer.Write(Reg14_svc);
|
||||
|
||||
writer.Write(Reg13_abt);
|
||||
writer.Write(Reg14_abt);
|
||||
|
||||
writer.Write(Reg13_und);
|
||||
writer.Write(Reg14_und);
|
||||
|
||||
writer.Write(Reg13_irq);
|
||||
writer.Write(Reg14_irq);
|
||||
|
||||
writer.Write(Reg8_fiq);
|
||||
writer.Write(Reg9_fiq);
|
||||
writer.Write(Reg10_fiq);
|
||||
writer.Write(Reg11_fiq);
|
||||
writer.Write(Reg12_fiq);
|
||||
writer.Write(Reg13_fiq);
|
||||
writer.Write(Reg14_fiq);
|
||||
|
||||
writer.Write(SPSR_svc);
|
||||
writer.Write(SPSR_abt);
|
||||
writer.Write(SPSR_und);
|
||||
writer.Write(SPSR_irq);
|
||||
writer.Write(SPSR_fiq);
|
||||
|
||||
writer.Write(NextInstructionAddress);
|
||||
writer.Write((int)NIRQ);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UInt64 Step()
|
||||
{
|
||||
UInt32 i = (CPSR >> 7) & 1;
|
||||
|
||||
if ((i == 0) && (NIRQ == Signal.Low))
|
||||
return _callbackInterface._handleIRQ();
|
||||
|
||||
UInt32 t = (CPSR >> 5) & 1;
|
||||
|
||||
if (t == 0)
|
||||
return _armInterpreter.Step();
|
||||
else
|
||||
return _thumbInterpreter.Step();
|
||||
}
|
||||
|
||||
public void SetCPSR(UInt32 value)
|
||||
{
|
||||
UInt32 previousMode = CPSR & ModeMask;
|
||||
UInt32 newMode = value & ModeMask;
|
||||
|
||||
CPSR = value | 0b1_0000;
|
||||
|
||||
if (previousMode != newMode)
|
||||
{
|
||||
ref UInt32 regDataRef = ref MemoryMarshal.GetArrayDataReference(Reg);
|
||||
ref UInt32 reg8 = ref Unsafe.Add(ref regDataRef, 8);
|
||||
ref UInt32 reg9 = ref Unsafe.Add(ref regDataRef, 9);
|
||||
ref UInt32 reg10 = ref Unsafe.Add(ref regDataRef, 10);
|
||||
ref UInt32 reg11 = ref Unsafe.Add(ref regDataRef, 11);
|
||||
ref UInt32 reg12 = ref Unsafe.Add(ref regDataRef, 12);
|
||||
ref UInt32 reg13 = ref Unsafe.Add(ref regDataRef, 13);
|
||||
ref UInt32 reg14 = ref Unsafe.Add(ref regDataRef, 14);
|
||||
|
||||
// save previous mode registers
|
||||
switch (previousMode)
|
||||
{
|
||||
case UserMode:
|
||||
case SystemMode:
|
||||
Reg8_usr = reg8;
|
||||
Reg9_usr = reg9;
|
||||
Reg10_usr = reg10;
|
||||
Reg11_usr = reg11;
|
||||
Reg12_usr = reg12;
|
||||
Reg13_usr = reg13;
|
||||
Reg14_usr = reg14;
|
||||
break;
|
||||
case SupervisorMode:
|
||||
Reg8_usr = reg8;
|
||||
Reg9_usr = reg9;
|
||||
Reg10_usr = reg10;
|
||||
Reg11_usr = reg11;
|
||||
Reg12_usr = reg12;
|
||||
Reg13_svc = reg13;
|
||||
Reg14_svc = reg14;
|
||||
SPSR_svc = SPSR;
|
||||
break;
|
||||
case AbortMode:
|
||||
Reg8_usr = reg8;
|
||||
Reg9_usr = reg9;
|
||||
Reg10_usr = reg10;
|
||||
Reg11_usr = reg11;
|
||||
Reg12_usr = reg12;
|
||||
Reg13_abt = reg13;
|
||||
Reg14_abt = reg14;
|
||||
SPSR_abt = SPSR;
|
||||
break;
|
||||
case UndefinedMode:
|
||||
Reg8_usr = reg8;
|
||||
Reg9_usr = reg9;
|
||||
Reg10_usr = reg10;
|
||||
Reg11_usr = reg11;
|
||||
Reg12_usr = reg12;
|
||||
Reg13_und = reg13;
|
||||
Reg14_und = reg14;
|
||||
SPSR_und = SPSR;
|
||||
break;
|
||||
case InterruptMode:
|
||||
Reg8_usr = reg8;
|
||||
Reg9_usr = reg9;
|
||||
Reg10_usr = reg10;
|
||||
Reg11_usr = reg11;
|
||||
Reg12_usr = reg12;
|
||||
Reg13_irq = reg13;
|
||||
Reg14_irq = reg14;
|
||||
SPSR_irq = SPSR;
|
||||
break;
|
||||
case FastInterruptMode:
|
||||
Reg8_fiq = reg8;
|
||||
Reg9_fiq = reg9;
|
||||
Reg10_fiq = reg10;
|
||||
Reg11_fiq = reg11;
|
||||
Reg12_fiq = reg12;
|
||||
Reg13_fiq = reg13;
|
||||
Reg14_fiq = reg14;
|
||||
SPSR_fiq = SPSR;
|
||||
break;
|
||||
}
|
||||
|
||||
// load new mode registers
|
||||
switch (newMode)
|
||||
{
|
||||
case UserMode:
|
||||
case SystemMode:
|
||||
reg8 = Reg8_usr;
|
||||
reg9 = Reg9_usr;
|
||||
reg10 = Reg10_usr;
|
||||
reg11 = Reg11_usr;
|
||||
reg12 = Reg12_usr;
|
||||
reg13 = Reg13_usr;
|
||||
reg14 = Reg14_usr;
|
||||
break;
|
||||
case SupervisorMode:
|
||||
reg8 = Reg8_usr;
|
||||
reg9 = Reg9_usr;
|
||||
reg10 = Reg10_usr;
|
||||
reg11 = Reg11_usr;
|
||||
reg12 = Reg12_usr;
|
||||
reg13 = Reg13_svc;
|
||||
reg14 = Reg14_svc;
|
||||
SPSR = SPSR_svc;
|
||||
break;
|
||||
case AbortMode:
|
||||
reg8 = Reg8_usr;
|
||||
reg9 = Reg9_usr;
|
||||
reg10 = Reg10_usr;
|
||||
reg11 = Reg11_usr;
|
||||
reg12 = Reg12_usr;
|
||||
reg13 = Reg13_abt;
|
||||
reg14 = Reg14_abt;
|
||||
SPSR = SPSR_abt;
|
||||
break;
|
||||
case UndefinedMode:
|
||||
reg8 = Reg8_usr;
|
||||
reg9 = Reg9_usr;
|
||||
reg10 = Reg10_usr;
|
||||
reg11 = Reg11_usr;
|
||||
reg12 = Reg12_usr;
|
||||
reg13 = Reg13_und;
|
||||
reg14 = Reg14_und;
|
||||
SPSR = SPSR_und;
|
||||
break;
|
||||
case InterruptMode:
|
||||
reg8 = Reg8_usr;
|
||||
reg9 = Reg9_usr;
|
||||
reg10 = Reg10_usr;
|
||||
reg11 = Reg11_usr;
|
||||
reg12 = Reg12_usr;
|
||||
reg13 = Reg13_irq;
|
||||
reg14 = Reg14_irq;
|
||||
SPSR = SPSR_irq;
|
||||
break;
|
||||
case FastInterruptMode:
|
||||
reg8 = Reg8_fiq;
|
||||
reg9 = Reg9_fiq;
|
||||
reg10 = Reg10_fiq;
|
||||
reg11 = Reg11_fiq;
|
||||
reg12 = Reg12_fiq;
|
||||
reg13 = Reg13_fiq;
|
||||
reg14 = Reg14_fiq;
|
||||
SPSR = SPSR_fiq;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal UInt32 GetFlag(Flag flag)
|
||||
{
|
||||
return (CPSR >> (int)flag) & 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void SetFlag(Flag flag, UInt32 value)
|
||||
{
|
||||
CPSR = (CPSR & ~(1u << (int)flag)) | (value << (int)flag);
|
||||
}
|
||||
|
||||
internal bool ConditionPassed(UInt32 cond)
|
||||
{
|
||||
return cond switch
|
||||
{
|
||||
// EQ
|
||||
0b0000 => GetFlag(Flag.Z) == 1,
|
||||
// NE
|
||||
0b0001 => GetFlag(Flag.Z) == 0,
|
||||
// CS/HS
|
||||
0b0010 => GetFlag(Flag.C) == 1,
|
||||
// CC/LO
|
||||
0b0011 => GetFlag(Flag.C) == 0,
|
||||
// MI
|
||||
0b0100 => GetFlag(Flag.N) == 1,
|
||||
// PL
|
||||
0b0101 => GetFlag(Flag.N) == 0,
|
||||
// VS
|
||||
0b0110 => GetFlag(Flag.V) == 1,
|
||||
// VC
|
||||
0b0111 => GetFlag(Flag.V) == 0,
|
||||
// HI
|
||||
0b1000 => (GetFlag(Flag.C) == 1) && (GetFlag(Flag.Z) == 0),
|
||||
// LS
|
||||
0b1001 => (GetFlag(Flag.C) == 0) || (GetFlag(Flag.Z) == 1),
|
||||
// GE
|
||||
0b1010 => GetFlag(Flag.N) == GetFlag(Flag.V),
|
||||
// LT
|
||||
0b1011 => GetFlag(Flag.N) != GetFlag(Flag.V),
|
||||
// GT
|
||||
0b1100 => (GetFlag(Flag.Z) == 0) && (GetFlag(Flag.N) == GetFlag(Flag.V)),
|
||||
// LE
|
||||
0b1101 => (GetFlag(Flag.Z) == 1) || (GetFlag(Flag.N) != GetFlag(Flag.V)),
|
||||
// AL
|
||||
0b1110 => true,
|
||||
// NV
|
||||
0b1111 => false,
|
||||
// should never happen
|
||||
_ => throw new Exception($"Iris.CPU.CPU_Core: Wrong condition code {cond}"),
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static UInt32 Not(UInt32 flag)
|
||||
{
|
||||
return flag ^ 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static UInt32 CarryFrom(UInt64 result)
|
||||
{
|
||||
return (result > 0xffff_ffff) ? 1u : 0u;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static UInt32 BorrowFrom(UInt64 result)
|
||||
{
|
||||
return (result >= 0x8000_0000_0000_0000) ? 1u : 0u;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static UInt32 OverflowFrom_Addition(UInt32 leftOperand, UInt32 rightOperand, UInt32 result)
|
||||
{
|
||||
return (((leftOperand >> 31) == (rightOperand >> 31))
|
||||
&& ((leftOperand >> 31) != (result >> 31))) ? 1u : 0u;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static UInt32 OverflowFrom_Subtraction(UInt32 leftOperand, UInt32 rightOperand, UInt32 result)
|
||||
{
|
||||
return (((leftOperand >> 31) != (rightOperand >> 31))
|
||||
&& ((leftOperand >> 31) != (result >> 31))) ? 1u : 0u;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static UInt32 ArithmeticShiftRight(UInt32 value, int shiftAmount)
|
||||
{
|
||||
return (UInt32)((Int32)value >> shiftAmount);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static UInt32 SignExtend(UInt32 value, int size)
|
||||
{
|
||||
return value | ~((value & (1u << (size - 1))) - 1);
|
||||
}
|
||||
|
||||
internal static UInt64 ComputeMultiplicationCycleCount(UInt32 leftMultiplier, UInt32 rightMultiplier)
|
||||
{
|
||||
static UInt64 ComputeMultiplierCycleCount(UInt32 multiplier)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static bool CheckMultiplierAgainstMask(UInt32 multiplier, UInt32 mask)
|
||||
{
|
||||
UInt32 masked = multiplier & mask;
|
||||
return (masked == 0) || (masked == mask);
|
||||
}
|
||||
|
||||
if (CheckMultiplierAgainstMask(multiplier, 0xffff_ff00))
|
||||
return 1;
|
||||
else if (CheckMultiplierAgainstMask(multiplier, 0xffff_0000))
|
||||
return 2;
|
||||
else if (CheckMultiplierAgainstMask(multiplier, 0xff00_0000))
|
||||
return 3;
|
||||
else
|
||||
return 4;
|
||||
}
|
||||
|
||||
return Math.Max(ComputeMultiplierCycleCount(leftMultiplier), ComputeMultiplierCycleCount(rightMultiplier));
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.CPU/CPU_Core.cs.meta
Normal file
11
Assets/Iris/Iris.CPU/CPU_Core.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ad832993e3b0bc4695925ea366d4d9e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1524
Assets/Iris/Iris.CPU/THUMB_Interpreter.cs
Normal file
1524
Assets/Iris/Iris.CPU/THUMB_Interpreter.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Assets/Iris/Iris.CPU/THUMB_Interpreter.cs.meta
Normal file
11
Assets/Iris/Iris.CPU/THUMB_Interpreter.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 300995068a952f743aaddf6f4736c84c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Iris/Iris.Common.meta
Normal file
8
Assets/Iris/Iris.Common.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51d282b83f644064f8adda50edd656d7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
138
Assets/Iris/Iris.Common/Scheduler.cs
Normal file
138
Assets/Iris/Iris.Common/Scheduler.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Iris.Common
|
||||
{
|
||||
public sealed class Scheduler(int taskListSize, int scheduledTaskListSize)
|
||||
{
|
||||
public delegate void Task_Delegate(UInt64 cycleCountDelay);
|
||||
private readonly Task_Delegate[] _taskList = new Task_Delegate[taskListSize];
|
||||
|
||||
private struct ScheduledTaskListEntry
|
||||
{
|
||||
internal int _id;
|
||||
internal UInt64 _cycleCount;
|
||||
}
|
||||
|
||||
private readonly ScheduledTaskListEntry[] _scheduledTaskList = new ScheduledTaskListEntry[scheduledTaskListSize]; // sorted by _cycleCount from smallest to largest
|
||||
private int _scheduledTaskCount;
|
||||
|
||||
private UInt64 _cycleCounter;
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
_scheduledTaskCount = 0;
|
||||
_cycleCounter = 0;
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader reader)
|
||||
{
|
||||
foreach (ref ScheduledTaskListEntry entry in _scheduledTaskList.AsSpan())
|
||||
{
|
||||
entry._id = reader.ReadInt32();
|
||||
entry._cycleCount = reader.ReadUInt64();
|
||||
}
|
||||
|
||||
_scheduledTaskCount = reader.ReadInt32();
|
||||
_cycleCounter = reader.ReadUInt64();
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter writer)
|
||||
{
|
||||
foreach (ScheduledTaskListEntry entry in _scheduledTaskList)
|
||||
{
|
||||
writer.Write(entry._id);
|
||||
writer.Write(entry._cycleCount);
|
||||
}
|
||||
|
||||
writer.Write(_scheduledTaskCount);
|
||||
writer.Write(_cycleCounter);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UInt64 GetCycleCounter()
|
||||
{
|
||||
return _cycleCounter;
|
||||
}
|
||||
|
||||
public void AdvanceCycleCounter(UInt64 cycleCount)
|
||||
{
|
||||
_cycleCounter += cycleCount;
|
||||
|
||||
// process tasks
|
||||
ref readonly ScheduledTaskListEntry firstEntry = ref MemoryMarshal.GetArrayDataReference(_scheduledTaskList);
|
||||
ref Task_Delegate taskListDataRef = ref MemoryMarshal.GetArrayDataReference(_taskList);
|
||||
|
||||
while ((_scheduledTaskCount > 0) && (firstEntry._cycleCount <= _cycleCounter))
|
||||
{
|
||||
// save the task
|
||||
ScheduledTaskListEntry entry = firstEntry;
|
||||
|
||||
// remove it from the list
|
||||
--_scheduledTaskCount;
|
||||
|
||||
if (_scheduledTaskCount > 0)
|
||||
Array.Copy(_scheduledTaskList, 1, _scheduledTaskList, 0, _scheduledTaskCount);
|
||||
|
||||
// execute it
|
||||
Unsafe.Add(ref taskListDataRef, entry._id)(_cycleCounter - entry._cycleCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterTask(int id, Task_Delegate task)
|
||||
{
|
||||
_taskList[id] = task;
|
||||
}
|
||||
|
||||
public void ScheduleTask(int id, UInt64 cycleCount)
|
||||
{
|
||||
// convert cycleCount from relative to absolute
|
||||
cycleCount += _cycleCounter;
|
||||
|
||||
// get the position and reference of the new task
|
||||
// (searching is done backward because a new task is more likely to be inserted towards the end)
|
||||
int index = _scheduledTaskCount;
|
||||
ref ScheduledTaskListEntry entry = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_scheduledTaskList), _scheduledTaskCount - 1);
|
||||
|
||||
while ((index > 0) && (entry._cycleCount > cycleCount))
|
||||
{
|
||||
--index;
|
||||
entry = ref Unsafe.Subtract(ref entry, 1);
|
||||
}
|
||||
|
||||
entry = ref Unsafe.Add(ref entry, 1);
|
||||
|
||||
// insert the new task
|
||||
if (index < _scheduledTaskCount)
|
||||
Array.Copy(_scheduledTaskList, index, _scheduledTaskList, index + 1, _scheduledTaskCount - index);
|
||||
|
||||
entry._id = id;
|
||||
entry._cycleCount = cycleCount;
|
||||
|
||||
++_scheduledTaskCount;
|
||||
}
|
||||
|
||||
public void CancelTask(int id)
|
||||
{
|
||||
int index = 0;
|
||||
ref ScheduledTaskListEntry entry = ref MemoryMarshal.GetArrayDataReference(_scheduledTaskList);
|
||||
|
||||
while (index < _scheduledTaskCount)
|
||||
{
|
||||
if (entry._id == id)
|
||||
{
|
||||
--_scheduledTaskCount;
|
||||
|
||||
if (index < _scheduledTaskCount)
|
||||
Array.Copy(_scheduledTaskList, index + 1, _scheduledTaskList, index, _scheduledTaskCount - index);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
++index;
|
||||
entry = ref Unsafe.Add(ref entry, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.Common/Scheduler.cs.meta
Normal file
11
Assets/Iris/Iris.Common/Scheduler.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b12f8d8d769413c4bbeca054bf9013fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
43
Assets/Iris/Iris.Common/System.cs
Normal file
43
Assets/Iris/Iris.Common/System.cs
Normal file
@ -0,0 +1,43 @@
|
||||
namespace Iris.Common
|
||||
{
|
||||
public abstract class System : IDisposable
|
||||
{
|
||||
public delegate void PollInput_Delegate();
|
||||
public delegate void PresentFrame_Delegate(UInt16[] frameBuffer);
|
||||
|
||||
public enum Key
|
||||
{
|
||||
A,
|
||||
B,
|
||||
Select,
|
||||
Start,
|
||||
Right,
|
||||
Left,
|
||||
Up,
|
||||
Down,
|
||||
R,
|
||||
L,
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
public enum KeyStatus
|
||||
{
|
||||
Input = 0,
|
||||
NoInput = 1
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract void ResetState(bool skipIntro);
|
||||
public abstract void LoadState(BinaryReader reader);
|
||||
public abstract void SaveState(BinaryWriter writer);
|
||||
|
||||
public abstract void LoadROM(string filename);
|
||||
public abstract void SetKeyStatus(Key key, KeyStatus status);
|
||||
|
||||
public abstract bool IsRunning();
|
||||
public abstract void Run();
|
||||
public abstract void Pause();
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.Common/System.cs.meta
Normal file
11
Assets/Iris/Iris.Common/System.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d321636c09403e249a26ee8f85908765
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Iris/Iris.GBA.meta
Normal file
8
Assets/Iris/Iris.GBA.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16247def3e95ffe48b0f4909962cd91a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
117
Assets/Iris/Iris.GBA/BIOS.cs
Normal file
117
Assets/Iris/Iris.GBA/BIOS.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class BIOS : IDisposable
|
||||
{
|
||||
private const int KB = 1024;
|
||||
private const int BIOS_Size = 16 * KB;
|
||||
private readonly IntPtr _bios = Marshal.AllocHGlobal(BIOS_Size);
|
||||
|
||||
private const UInt32 BIOS_StartAddress = 0x0000_0000;
|
||||
private const UInt32 BIOS_EndAddress = 0x0000_4000;
|
||||
|
||||
private CPU.CPU_Core _cpu;
|
||||
private bool _disposed;
|
||||
|
||||
internal BIOS()
|
||||
{
|
||||
Byte[] data;
|
||||
|
||||
try
|
||||
{
|
||||
data = File.ReadAllBytes("gba_bios.bin");
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
throw new Exception("Iris.GBA.BIOS: Could not find BIOS");
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new Exception("Iris.GBA.BIOS: Could not read BIOS");
|
||||
}
|
||||
|
||||
if (data.Length != BIOS_Size)
|
||||
throw new Exception("Iris.GBA.BIOS: Wrong BIOS size");
|
||||
|
||||
Marshal.Copy(data, 0, _bios, BIOS_Size);
|
||||
}
|
||||
|
||||
~BIOS()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
Marshal.FreeHGlobal(_bios);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
internal void Initialize(CPU.CPU_Core cpu, Memory memory)
|
||||
{
|
||||
_cpu = cpu;
|
||||
|
||||
memory.Map(_bios, BIOS_Size, BIOS_StartAddress, BIOS_EndAddress, Memory.Flag.AllRead);
|
||||
}
|
||||
|
||||
internal void Reset(bool skipIntro)
|
||||
{
|
||||
if (skipIntro)
|
||||
{
|
||||
_cpu.Reg[CPU.CPU_Core.SP] = 0x300_7f00;
|
||||
_cpu.Reg[CPU.CPU_Core.LR] = 0x800_0000;
|
||||
|
||||
_cpu.CPSR = 0x1f;
|
||||
|
||||
_cpu.Reg13_svc = 0x300_7fe0;
|
||||
_cpu.Reg13_irq = 0x300_7fa0;
|
||||
|
||||
_cpu.NextInstructionAddress = 0x800_0000;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cpu.CPSR = 0xd3;
|
||||
_cpu.NextInstructionAddress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal Byte Read8(UInt32 address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal UInt16 Read16(UInt32 address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal UInt32 Read32(UInt32 address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal UInt64 HandleSWI()
|
||||
{
|
||||
_cpu.Reg14_svc = _cpu.NextInstructionAddress;
|
||||
_cpu.SPSR_svc = _cpu.CPSR;
|
||||
_cpu.SetCPSR((_cpu.CPSR & ~0xbfu) | 0x93u);
|
||||
_cpu.NextInstructionAddress = 0x08;
|
||||
return 3;
|
||||
}
|
||||
|
||||
internal UInt64 HandleIRQ()
|
||||
{
|
||||
_cpu.Reg14_irq = _cpu.NextInstructionAddress + 4;
|
||||
_cpu.SPSR_irq = _cpu.CPSR;
|
||||
_cpu.SetCPSR((_cpu.CPSR & ~0xbfu) | 0x92u);
|
||||
_cpu.NextInstructionAddress = 0x18;
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/BIOS.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/BIOS.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a44ddbc1235a2b9448cedcd04283cb60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
247
Assets/Iris/Iris.GBA/Communication.cs
Normal file
247
Assets/Iris/Iris.GBA/Communication.cs
Normal file
@ -0,0 +1,247 @@
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class Communication
|
||||
{
|
||||
internal enum Register
|
||||
{
|
||||
SIODATA0,
|
||||
SIODATA1,
|
||||
SIODATA2,
|
||||
SIODATA3,
|
||||
|
||||
SIOCNT,
|
||||
SIODATA_SEND,
|
||||
|
||||
RCNT,
|
||||
|
||||
JOYCNT,
|
||||
|
||||
JOY_RECV_L,
|
||||
JOY_RECV_H,
|
||||
|
||||
JOY_TRANS_L,
|
||||
JOY_TRANS_H,
|
||||
|
||||
JOYSTAT
|
||||
}
|
||||
|
||||
private UInt16 _SIODATA0; // SIOMULTI0 / SIODATA32_L
|
||||
private UInt16 _SIODATA1; // SIOMULTI1 / SIODATA32_H
|
||||
private UInt16 _SIODATA2; // SIOMULTI2
|
||||
private UInt16 _SIODATA3; // SIOMULTI3
|
||||
|
||||
private UInt16 _SIOCNT;
|
||||
private UInt16 _SIODATA_SEND; // SIOMLT_SEND / SIODATA_8
|
||||
|
||||
private UInt16 _RCNT;
|
||||
|
||||
private UInt16 _JOYCNT;
|
||||
|
||||
private UInt16 _JOY_RECV_L;
|
||||
private UInt16 _JOY_RECV_H;
|
||||
|
||||
private UInt16 _JOY_TRANS_L;
|
||||
private UInt16 _JOY_TRANS_H;
|
||||
|
||||
private UInt16 _JOYSTAT;
|
||||
|
||||
private InterruptControl _interruptControl;
|
||||
|
||||
internal void Initialize(InterruptControl interruptControl)
|
||||
{
|
||||
_interruptControl = interruptControl;
|
||||
}
|
||||
|
||||
internal void ResetState()
|
||||
{
|
||||
_SIODATA0 = 0;
|
||||
_SIODATA1 = 0;
|
||||
_SIODATA2 = 0;
|
||||
_SIODATA3 = 0;
|
||||
|
||||
_SIOCNT = 0;
|
||||
_SIODATA_SEND = 0;
|
||||
|
||||
_RCNT = 0;
|
||||
|
||||
_JOYCNT = 0;
|
||||
|
||||
_JOY_RECV_L = 0;
|
||||
_JOY_RECV_H = 0;
|
||||
|
||||
_JOY_TRANS_L = 0;
|
||||
_JOY_TRANS_H = 0;
|
||||
|
||||
_JOYSTAT = 0;
|
||||
}
|
||||
|
||||
internal void LoadState(BinaryReader reader)
|
||||
{
|
||||
_SIODATA0 = reader.ReadUInt16();
|
||||
_SIODATA1 = reader.ReadUInt16();
|
||||
_SIODATA2 = reader.ReadUInt16();
|
||||
_SIODATA3 = reader.ReadUInt16();
|
||||
|
||||
_SIOCNT = reader.ReadUInt16();
|
||||
_SIODATA_SEND = reader.ReadUInt16();
|
||||
|
||||
_RCNT = reader.ReadUInt16();
|
||||
|
||||
_JOYCNT = reader.ReadUInt16();
|
||||
|
||||
_JOY_RECV_L = reader.ReadUInt16();
|
||||
_JOY_RECV_H = reader.ReadUInt16();
|
||||
|
||||
_JOY_TRANS_L = reader.ReadUInt16();
|
||||
_JOY_TRANS_H = reader.ReadUInt16();
|
||||
|
||||
_JOYSTAT = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
internal void SaveState(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(_SIODATA0);
|
||||
writer.Write(_SIODATA1);
|
||||
writer.Write(_SIODATA2);
|
||||
writer.Write(_SIODATA3);
|
||||
|
||||
writer.Write(_SIOCNT);
|
||||
writer.Write(_SIODATA_SEND);
|
||||
|
||||
writer.Write(_RCNT);
|
||||
|
||||
writer.Write(_JOYCNT);
|
||||
|
||||
writer.Write(_JOY_RECV_L);
|
||||
writer.Write(_JOY_RECV_H);
|
||||
|
||||
writer.Write(_JOY_TRANS_L);
|
||||
writer.Write(_JOY_TRANS_H);
|
||||
|
||||
writer.Write(_JOYSTAT);
|
||||
}
|
||||
|
||||
internal UInt16 ReadRegister(Register register)
|
||||
{
|
||||
return register switch
|
||||
{
|
||||
Register.SIODATA0 => _SIODATA0,
|
||||
Register.SIODATA1 => _SIODATA1,
|
||||
Register.SIODATA2 => _SIODATA2,
|
||||
Register.SIODATA3 => _SIODATA3,
|
||||
|
||||
Register.SIOCNT => _SIOCNT,
|
||||
Register.SIODATA_SEND => _SIODATA_SEND,
|
||||
|
||||
Register.RCNT => _RCNT,
|
||||
|
||||
Register.JOYCNT => _JOYCNT,
|
||||
|
||||
Register.JOY_RECV_L => _JOY_RECV_L,
|
||||
Register.JOY_RECV_H => _JOY_RECV_H,
|
||||
|
||||
Register.JOY_TRANS_L => _JOY_TRANS_L,
|
||||
Register.JOY_TRANS_H => _JOY_TRANS_H,
|
||||
|
||||
Register.JOYSTAT => _JOYSTAT,
|
||||
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.Communication: Register read error"),
|
||||
};
|
||||
}
|
||||
|
||||
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case Register.SIODATA0:
|
||||
Memory.WriteRegisterHelper(ref _SIODATA0, value, mode);
|
||||
break;
|
||||
case Register.SIODATA1:
|
||||
Memory.WriteRegisterHelper(ref _SIODATA1, value, mode);
|
||||
break;
|
||||
case Register.SIODATA2:
|
||||
Memory.WriteRegisterHelper(ref _SIODATA2, value, mode);
|
||||
break;
|
||||
case Register.SIODATA3:
|
||||
Memory.WriteRegisterHelper(ref _SIODATA3, value, mode);
|
||||
break;
|
||||
|
||||
case Register.SIOCNT:
|
||||
Memory.WriteRegisterHelper(ref _SIOCNT, value, mode);
|
||||
CheckTransfer();
|
||||
break;
|
||||
case Register.SIODATA_SEND:
|
||||
Memory.WriteRegisterHelper(ref _SIODATA_SEND, value, mode);
|
||||
break;
|
||||
|
||||
case Register.RCNT:
|
||||
Memory.WriteRegisterHelper(ref _RCNT, value, mode);
|
||||
CheckTransfer();
|
||||
break;
|
||||
|
||||
case Register.JOYCNT:
|
||||
Memory.WriteRegisterHelper(ref _JOYCNT, value, mode);
|
||||
break;
|
||||
|
||||
case Register.JOY_RECV_L:
|
||||
Memory.WriteRegisterHelper(ref _JOY_RECV_L, value, mode);
|
||||
break;
|
||||
case Register.JOY_RECV_H:
|
||||
Memory.WriteRegisterHelper(ref _JOY_RECV_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.JOY_TRANS_L:
|
||||
Memory.WriteRegisterHelper(ref _JOY_TRANS_L, value, mode);
|
||||
break;
|
||||
case Register.JOY_TRANS_H:
|
||||
Memory.WriteRegisterHelper(ref _JOY_TRANS_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.JOYSTAT:
|
||||
Memory.WriteRegisterHelper(ref _JOYSTAT, value, mode);
|
||||
break;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.Communication: Register write error");
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckTransfer()
|
||||
{
|
||||
switch ((_RCNT >> 14) & 0b11)
|
||||
{
|
||||
case 0b00:
|
||||
case 0b01:
|
||||
switch ((_SIOCNT >> 12) & 0b11)
|
||||
{
|
||||
case 0b00: // 8 bits normal serial communication
|
||||
case 0b01: // 32 bits normal serial communication
|
||||
case 0b10: // 16 bits multiplayer serial communication
|
||||
if ((_SIOCNT & 0x0080) == 0x0080)
|
||||
{
|
||||
_SIOCNT = (UInt16)(_SIOCNT & ~0x0080);
|
||||
|
||||
if ((_SIOCNT & 0x4000) == 0x4000)
|
||||
_interruptControl.RequestInterrupt(InterruptControl.Interrupt.SIO);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0b11:
|
||||
Console.WriteLine("[Iris.GBA.Communication] UART communication not implemented");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0b10:
|
||||
Console.WriteLine("[Iris.GBA.Communication] General purpose communication not implemented");
|
||||
break;
|
||||
|
||||
case 0b11:
|
||||
Console.WriteLine("[Iris.GBA.Communication] JOY Bus communication not implemented");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/Communication.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/Communication.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cb1b07716db45d45b2681382a0ff8fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
469
Assets/Iris/Iris.GBA/DMA.cs
Normal file
469
Assets/Iris/Iris.GBA/DMA.cs
Normal file
@ -0,0 +1,469 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class DMA
|
||||
{
|
||||
internal enum Register
|
||||
{
|
||||
DMA0SAD_L,
|
||||
DMA0SAD_H,
|
||||
|
||||
DMA0DAD_L,
|
||||
DMA0DAD_H,
|
||||
|
||||
DMA0CNT_L,
|
||||
DMA0CNT_H,
|
||||
|
||||
DMA1SAD_L,
|
||||
DMA1SAD_H,
|
||||
|
||||
DMA1DAD_L,
|
||||
DMA1DAD_H,
|
||||
|
||||
DMA1CNT_L,
|
||||
DMA1CNT_H,
|
||||
|
||||
DMA2SAD_L,
|
||||
DMA2SAD_H,
|
||||
|
||||
DMA2DAD_L,
|
||||
DMA2DAD_H,
|
||||
|
||||
DMA2CNT_L,
|
||||
DMA2CNT_H,
|
||||
|
||||
DMA3SAD_L,
|
||||
DMA3SAD_H,
|
||||
|
||||
DMA3DAD_L,
|
||||
DMA3DAD_H,
|
||||
|
||||
DMA3CNT_L,
|
||||
DMA3CNT_H
|
||||
}
|
||||
|
||||
internal enum StartTiming
|
||||
{
|
||||
Immediate = 0b00,
|
||||
VBlank = 0b01,
|
||||
HBlank = 0b10,
|
||||
Special = 0b11
|
||||
}
|
||||
|
||||
private readonly Common.Scheduler _scheduler;
|
||||
|
||||
private InterruptControl _interruptControl;
|
||||
private Memory _memory;
|
||||
|
||||
private struct Channel
|
||||
{
|
||||
internal UInt32 _source;
|
||||
internal UInt32 _sourceReload;
|
||||
internal UInt32 _destination;
|
||||
internal UInt32 _destinationReload;
|
||||
internal UInt32 _length;
|
||||
internal UInt16 _lengthReload;
|
||||
internal UInt16 _control;
|
||||
internal bool _running;
|
||||
}
|
||||
|
||||
private Channel _channel0;
|
||||
private Channel _channel1;
|
||||
private Channel _channel2;
|
||||
private Channel _channel3;
|
||||
|
||||
private const UInt32 MaxLengthChannel0 = 0x4000;
|
||||
private const UInt32 MaxLengthChannel1 = 0x4000;
|
||||
private const UInt32 MaxLengthChannel2 = 0x4000;
|
||||
private const UInt32 MaxLengthChannel3 = 0x1_0000;
|
||||
|
||||
internal DMA(Common.Scheduler scheduler)
|
||||
{
|
||||
_scheduler = scheduler;
|
||||
|
||||
_scheduler.RegisterTask((int)GBA_System.TaskId.StartDMA_Channel0, _ => Start(ref _channel0, InterruptControl.Interrupt.DMA0, MaxLengthChannel0));
|
||||
_scheduler.RegisterTask((int)GBA_System.TaskId.StartDMA_Channel1, _ => Start(ref _channel1, InterruptControl.Interrupt.DMA1, MaxLengthChannel1));
|
||||
_scheduler.RegisterTask((int)GBA_System.TaskId.StartDMA_Channel2, _ => Start(ref _channel2, InterruptControl.Interrupt.DMA2, MaxLengthChannel2));
|
||||
_scheduler.RegisterTask((int)GBA_System.TaskId.StartDMA_Channel3, _ => Start(ref _channel3, InterruptControl.Interrupt.DMA3, MaxLengthChannel3));
|
||||
}
|
||||
|
||||
internal void Initialize(InterruptControl interruptControl, Memory memory)
|
||||
{
|
||||
_interruptControl = interruptControl;
|
||||
_memory = memory;
|
||||
}
|
||||
|
||||
internal void ResetState()
|
||||
{
|
||||
_channel0 = default;
|
||||
_channel1 = default;
|
||||
_channel2 = default;
|
||||
_channel3 = default;
|
||||
}
|
||||
|
||||
internal void LoadState(BinaryReader reader)
|
||||
{
|
||||
void LoadChannel(ref Channel channel)
|
||||
{
|
||||
channel._source = reader.ReadUInt32();
|
||||
channel._sourceReload = reader.ReadUInt32();
|
||||
channel._destination = reader.ReadUInt32();
|
||||
channel._destinationReload = reader.ReadUInt32();
|
||||
channel._length = reader.ReadUInt32();
|
||||
channel._lengthReload = reader.ReadUInt16();
|
||||
channel._control = reader.ReadUInt16();
|
||||
channel._running = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
LoadChannel(ref _channel0);
|
||||
LoadChannel(ref _channel1);
|
||||
LoadChannel(ref _channel2);
|
||||
LoadChannel(ref _channel3);
|
||||
}
|
||||
|
||||
internal void SaveState(BinaryWriter writer)
|
||||
{
|
||||
void SaveChannel(Channel channel)
|
||||
{
|
||||
writer.Write(channel._source);
|
||||
writer.Write(channel._sourceReload);
|
||||
writer.Write(channel._destination);
|
||||
writer.Write(channel._destinationReload);
|
||||
writer.Write(channel._length);
|
||||
writer.Write(channel._lengthReload);
|
||||
writer.Write(channel._control);
|
||||
writer.Write(channel._running);
|
||||
}
|
||||
|
||||
SaveChannel(_channel0);
|
||||
SaveChannel(_channel1);
|
||||
SaveChannel(_channel2);
|
||||
SaveChannel(_channel3);
|
||||
}
|
||||
|
||||
internal UInt16 ReadRegister(Register register)
|
||||
{
|
||||
return register switch
|
||||
{
|
||||
Register.DMA0CNT_H => _channel0._control,
|
||||
Register.DMA1CNT_H => _channel1._control,
|
||||
Register.DMA2CNT_H => _channel2._control,
|
||||
Register.DMA3CNT_H => _channel3._control,
|
||||
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.DMA: Register read error"),
|
||||
};
|
||||
}
|
||||
|
||||
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
||||
{
|
||||
void WriteSourceReload_Low(ref Channel channel)
|
||||
{
|
||||
UInt16 low = (UInt16)channel._sourceReload;
|
||||
Memory.WriteRegisterHelper(ref low, value, mode);
|
||||
channel._sourceReload = (channel._sourceReload & 0xffff_0000) | low;
|
||||
}
|
||||
|
||||
void WriteSourceReload_High(ref Channel channel, UInt16 mask)
|
||||
{
|
||||
UInt16 high = (UInt16)(channel._sourceReload >> 16);
|
||||
Memory.WriteRegisterHelper(ref high, (UInt16)(value & mask), mode);
|
||||
channel._sourceReload = (channel._sourceReload & 0x0000_ffff) | (UInt32)(high << 16);
|
||||
}
|
||||
|
||||
void WriteDestinationReload_Low(ref Channel channel)
|
||||
{
|
||||
UInt16 low = (UInt16)channel._destinationReload;
|
||||
Memory.WriteRegisterHelper(ref low, value, mode);
|
||||
channel._destinationReload = (channel._destinationReload & 0xffff_0000) | low;
|
||||
}
|
||||
|
||||
void WriteDestinationReload_High(ref Channel channel, UInt16 mask)
|
||||
{
|
||||
UInt16 high = (UInt16)(channel._destinationReload >> 16);
|
||||
Memory.WriteRegisterHelper(ref high, (UInt16)(value & mask), mode);
|
||||
channel._destinationReload = (channel._destinationReload & 0x0000_ffff) | (UInt32)(high << 16);
|
||||
}
|
||||
|
||||
void WriteLengthReload(ref Channel channel)
|
||||
{
|
||||
UInt16 reload = channel._lengthReload;
|
||||
Memory.WriteRegisterHelper(ref reload, value, mode);
|
||||
channel._lengthReload = reload;
|
||||
}
|
||||
|
||||
void WriteControl(ref Channel channel, GBA_System.TaskId startTaskId)
|
||||
{
|
||||
UInt16 previousControl = channel._control;
|
||||
|
||||
UInt16 newControl = channel._control;
|
||||
Memory.WriteRegisterHelper(ref newControl, value, mode);
|
||||
channel._control = newControl;
|
||||
|
||||
if ((previousControl & 0x8000) == 0)
|
||||
{
|
||||
if ((newControl & 0x8000) == 0x8000)
|
||||
_scheduler.ScheduleTask((int)startTaskId, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((newControl & 0x8000) == 0)
|
||||
{
|
||||
if (channel._running)
|
||||
channel._running = false;
|
||||
else
|
||||
_scheduler.CancelTask((int)startTaskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (register)
|
||||
{
|
||||
case Register.DMA0SAD_L:
|
||||
WriteSourceReload_Low(ref _channel0);
|
||||
break;
|
||||
case Register.DMA0SAD_H:
|
||||
WriteSourceReload_High(ref _channel0, 0x07ff);
|
||||
break;
|
||||
|
||||
case Register.DMA0DAD_L:
|
||||
WriteDestinationReload_Low(ref _channel0);
|
||||
break;
|
||||
case Register.DMA0DAD_H:
|
||||
WriteDestinationReload_High(ref _channel0, 0x07ff);
|
||||
break;
|
||||
|
||||
case Register.DMA0CNT_L:
|
||||
WriteLengthReload(ref _channel0);
|
||||
break;
|
||||
case Register.DMA0CNT_H:
|
||||
WriteControl(ref _channel0, GBA_System.TaskId.StartDMA_Channel0);
|
||||
break;
|
||||
|
||||
case Register.DMA1SAD_L:
|
||||
WriteSourceReload_Low(ref _channel1);
|
||||
break;
|
||||
case Register.DMA1SAD_H:
|
||||
WriteSourceReload_High(ref _channel1, 0x0fff);
|
||||
break;
|
||||
|
||||
case Register.DMA1DAD_L:
|
||||
WriteDestinationReload_Low(ref _channel1);
|
||||
break;
|
||||
case Register.DMA1DAD_H:
|
||||
WriteDestinationReload_High(ref _channel1, 0x07ff);
|
||||
break;
|
||||
|
||||
case Register.DMA1CNT_L:
|
||||
WriteLengthReload(ref _channel1);
|
||||
break;
|
||||
case Register.DMA1CNT_H:
|
||||
WriteControl(ref _channel1, GBA_System.TaskId.StartDMA_Channel1);
|
||||
break;
|
||||
|
||||
case Register.DMA2SAD_L:
|
||||
WriteSourceReload_Low(ref _channel2);
|
||||
break;
|
||||
case Register.DMA2SAD_H:
|
||||
WriteSourceReload_High(ref _channel2, 0x0fff);
|
||||
break;
|
||||
|
||||
case Register.DMA2DAD_L:
|
||||
WriteDestinationReload_Low(ref _channel2);
|
||||
break;
|
||||
case Register.DMA2DAD_H:
|
||||
WriteDestinationReload_High(ref _channel2, 0x07ff);
|
||||
break;
|
||||
|
||||
case Register.DMA2CNT_L:
|
||||
WriteLengthReload(ref _channel2);
|
||||
break;
|
||||
case Register.DMA2CNT_H:
|
||||
WriteControl(ref _channel2, GBA_System.TaskId.StartDMA_Channel2);
|
||||
break;
|
||||
|
||||
case Register.DMA3SAD_L:
|
||||
WriteSourceReload_Low(ref _channel3);
|
||||
break;
|
||||
case Register.DMA3SAD_H:
|
||||
WriteSourceReload_High(ref _channel3, 0x0fff);
|
||||
break;
|
||||
|
||||
case Register.DMA3DAD_L:
|
||||
WriteDestinationReload_Low(ref _channel3);
|
||||
break;
|
||||
case Register.DMA3DAD_H:
|
||||
WriteDestinationReload_High(ref _channel3, 0x0fff);
|
||||
break;
|
||||
|
||||
case Register.DMA3CNT_L:
|
||||
WriteLengthReload(ref _channel3);
|
||||
break;
|
||||
case Register.DMA3CNT_H:
|
||||
WriteControl(ref _channel3, GBA_System.TaskId.StartDMA_Channel3);
|
||||
break;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.DMA: Register write error");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void PerformVBlankTransfers()
|
||||
{
|
||||
if (_channel0._running && (((_channel0._control >> 12) & 0b11) == (int)StartTiming.VBlank))
|
||||
PerformTransfer(ref _channel0, InterruptControl.Interrupt.DMA0, MaxLengthChannel0);
|
||||
|
||||
if (_channel1._running && (((_channel1._control >> 12) & 0b11) == (int)StartTiming.VBlank))
|
||||
PerformTransfer(ref _channel1, InterruptControl.Interrupt.DMA1, MaxLengthChannel1);
|
||||
|
||||
if (_channel2._running && (((_channel2._control >> 12) & 0b11) == (int)StartTiming.VBlank))
|
||||
PerformTransfer(ref _channel2, InterruptControl.Interrupt.DMA2, MaxLengthChannel2);
|
||||
|
||||
if (_channel3._running && (((_channel3._control >> 12) & 0b11) == (int)StartTiming.VBlank))
|
||||
PerformTransfer(ref _channel3, InterruptControl.Interrupt.DMA3, MaxLengthChannel3);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void PerformHBlankTransfers()
|
||||
{
|
||||
if (_channel0._running && (((_channel0._control >> 12) & 0b11) == (int)StartTiming.HBlank))
|
||||
PerformTransfer(ref _channel0, InterruptControl.Interrupt.DMA0, MaxLengthChannel0);
|
||||
|
||||
if (_channel1._running && (((_channel1._control >> 12) & 0b11) == (int)StartTiming.HBlank))
|
||||
PerformTransfer(ref _channel1, InterruptControl.Interrupt.DMA1, MaxLengthChannel1);
|
||||
|
||||
if (_channel2._running && (((_channel2._control >> 12) & 0b11) == (int)StartTiming.HBlank))
|
||||
PerformTransfer(ref _channel2, InterruptControl.Interrupt.DMA2, MaxLengthChannel2);
|
||||
|
||||
if (_channel3._running && (((_channel3._control >> 12) & 0b11) == (int)StartTiming.HBlank))
|
||||
PerformTransfer(ref _channel3, InterruptControl.Interrupt.DMA3, MaxLengthChannel3);
|
||||
}
|
||||
|
||||
internal void PerformVideoTransfer(bool disable)
|
||||
{
|
||||
if (_channel3._running && (((_channel3._control >> 12) & 0b11) == (int)StartTiming.Special))
|
||||
{
|
||||
PerformTransfer(ref _channel3, InterruptControl.Interrupt.DMA3, MaxLengthChannel3);
|
||||
|
||||
if (disable)
|
||||
{
|
||||
_channel3._control = (UInt16)(_channel3._control & ~0x8000);
|
||||
_channel3._running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Start(ref Channel channel, InterruptControl.Interrupt interrupt, UInt32 maxLength)
|
||||
{
|
||||
channel._source = channel._sourceReload;
|
||||
channel._destination = channel._destinationReload;
|
||||
channel._length = (channel._lengthReload == 0) ? maxLength : channel._lengthReload;
|
||||
channel._running = true;
|
||||
|
||||
if (((channel._control >> 12) & 0b11) == (int)StartTiming.Immediate)
|
||||
PerformTransfer(ref channel, interrupt, maxLength);
|
||||
}
|
||||
|
||||
private void PerformTransfer(ref Channel channel, InterruptControl.Interrupt interrupt, UInt32 maxLength)
|
||||
{
|
||||
UInt16 sourceAddressControlFlag = (UInt16)((channel._control >> 7) & 0b11);
|
||||
UInt16 destinationAddressControlFlag = (UInt16)((channel._control >> 5) & 0b11);
|
||||
|
||||
int GetSourceIncrement(int dataUnitSize)
|
||||
{
|
||||
return sourceAddressControlFlag switch
|
||||
{
|
||||
// increment
|
||||
0b00 => dataUnitSize,
|
||||
// decrement
|
||||
0b01 => -dataUnitSize,
|
||||
// fixed
|
||||
0b10 => 0,
|
||||
// prohibited
|
||||
0b11 => 0,
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.DMA: Wrong source address control flag"),
|
||||
};
|
||||
}
|
||||
|
||||
(int destinationIncrement, bool reloadDestination) GetDestinationIncrement(int dataUnitSize)
|
||||
{
|
||||
return destinationAddressControlFlag switch
|
||||
{
|
||||
// increment
|
||||
0b00 => (dataUnitSize, false),
|
||||
// decrement
|
||||
0b01 => (-dataUnitSize, false),
|
||||
// fixed
|
||||
0b10 => (0, false),
|
||||
// increment+reload
|
||||
0b11 => (dataUnitSize, true),
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.DMA: Wrong destination address control flag"),
|
||||
};
|
||||
}
|
||||
|
||||
bool reloadDestination;
|
||||
|
||||
// 16 bits
|
||||
if ((channel._control & 0x0400) == 0)
|
||||
{
|
||||
const int DataUnitSize = 2;
|
||||
|
||||
int sourceIncrement = GetSourceIncrement(DataUnitSize);
|
||||
(int destinationIncrement, reloadDestination) = GetDestinationIncrement(DataUnitSize);
|
||||
|
||||
for (; channel._length > 0; --channel._length)
|
||||
{
|
||||
_memory.Write16(channel._destination, _memory.Read16(channel._source));
|
||||
|
||||
channel._source = (UInt32)(channel._source + sourceIncrement);
|
||||
channel._destination = (UInt32)(channel._destination + destinationIncrement);
|
||||
|
||||
_scheduler.AdvanceCycleCounter(2);
|
||||
}
|
||||
}
|
||||
|
||||
// 32 bits
|
||||
else
|
||||
{
|
||||
const int DataUnitSize = 4;
|
||||
|
||||
int sourceIncrement = GetSourceIncrement(DataUnitSize);
|
||||
(int destinationIncrement, reloadDestination) = GetDestinationIncrement(DataUnitSize);
|
||||
|
||||
for (; channel._length > 0; --channel._length)
|
||||
{
|
||||
_memory.Write32(channel._destination, _memory.Read32(channel._source));
|
||||
|
||||
channel._source = (UInt32)(channel._source + sourceIncrement);
|
||||
channel._destination = (UInt32)(channel._destination + destinationIncrement);
|
||||
|
||||
_scheduler.AdvanceCycleCounter(2);
|
||||
}
|
||||
}
|
||||
|
||||
if ((channel._control & 0x4000) == 0x4000)
|
||||
_interruptControl.RequestInterrupt(interrupt);
|
||||
|
||||
// Repeat off
|
||||
if ((channel._control & 0x0200) == 0)
|
||||
{
|
||||
channel._control = (UInt16)(channel._control & ~0x8000);
|
||||
channel._running = false;
|
||||
}
|
||||
|
||||
// Repeat on
|
||||
else
|
||||
{
|
||||
if (reloadDestination)
|
||||
channel._destination = channel._destinationReload;
|
||||
|
||||
channel._length = (channel._lengthReload == 0) ? maxLength : channel._lengthReload;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/DMA.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/DMA.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dcd66b296ec4434b836a1075fb8a12a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
178
Assets/Iris/Iris.GBA/GBA_System.cs
Normal file
178
Assets/Iris/Iris.GBA/GBA_System.cs
Normal file
@ -0,0 +1,178 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Iris.GBA
|
||||
{
|
||||
public sealed class GBA_System : Common.System
|
||||
{
|
||||
internal enum TaskId
|
||||
{
|
||||
// ---- Timer ----
|
||||
StartTimer_Channel0,
|
||||
StartTimer_Channel1,
|
||||
StartTimer_Channel2,
|
||||
StartTimer_Channel3,
|
||||
|
||||
HandleTimerOverflow_Channel0,
|
||||
HandleTimerOverflow_Channel1,
|
||||
HandleTimerOverflow_Channel2,
|
||||
HandleTimerOverflow_Channel3,
|
||||
|
||||
// ---- DMA ----
|
||||
StartDMA_Channel0,
|
||||
StartDMA_Channel1,
|
||||
StartDMA_Channel2,
|
||||
StartDMA_Channel3,
|
||||
|
||||
// ---- KeyInput ----
|
||||
CheckKeyInterrupt,
|
||||
|
||||
// ---- Video ----
|
||||
StartHBlank,
|
||||
StartScanline
|
||||
}
|
||||
|
||||
private static readonly int s_taskIdCount = Enum.GetNames(typeof(TaskId)).Length;
|
||||
private readonly Common.Scheduler _scheduler = new(s_taskIdCount, 2 * s_taskIdCount);
|
||||
|
||||
private readonly CPU.CPU_Core _cpu;
|
||||
private readonly Communication _communication = new();
|
||||
private readonly Timer _timer;
|
||||
private readonly Sound _sound = new();
|
||||
private readonly DMA _dma;
|
||||
private readonly KeyInput _keyInput;
|
||||
private readonly SystemControl _systemControl = new();
|
||||
private readonly InterruptControl _interruptControl = new();
|
||||
private readonly Memory _memory = new();
|
||||
private readonly Video _video;
|
||||
private readonly BIOS _bios = new();
|
||||
|
||||
private string _romHash;
|
||||
private bool _running;
|
||||
|
||||
private const string StateSaveMagic = "IRISGBA";
|
||||
private const int StateSaveVersion = 1;
|
||||
|
||||
public GBA_System(PollInput_Delegate pollInputCallback, PresentFrame_Delegate presentFrameCallback)
|
||||
{
|
||||
CPU.CPU_Core.CallbackInterface cpuCallbackInterface = new(_memory.Read8, _memory.Read16, _memory.Read32, _memory.Write8, _memory.Write16, _memory.Write32, _bios.HandleSWI, _bios.HandleIRQ);
|
||||
|
||||
_cpu = new(CPU.CPU_Core.Model.ARM7TDMI, cpuCallbackInterface);
|
||||
_timer = new(_scheduler);
|
||||
_dma = new(_scheduler);
|
||||
_keyInput = new(_scheduler, pollInputCallback);
|
||||
_video = new(_scheduler, presentFrameCallback);
|
||||
|
||||
_communication.Initialize(_interruptControl);
|
||||
_timer.Initialize(_interruptControl);
|
||||
_dma.Initialize(_interruptControl, _memory);
|
||||
_keyInput.Initialize(_interruptControl);
|
||||
_interruptControl.Initialize(_cpu);
|
||||
_memory.Initialize(_communication, _timer, _sound, _dma, _keyInput, _systemControl, _interruptControl, _video, _bios);
|
||||
_video.Initialize(_dma, _interruptControl, _memory);
|
||||
_bios.Initialize(_cpu, _memory);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_memory.Dispose();
|
||||
_video.Dispose();
|
||||
_bios.Dispose();
|
||||
}
|
||||
|
||||
public override void ResetState(bool skipIntro)
|
||||
{
|
||||
_scheduler.ResetState(); // This has to be done first
|
||||
|
||||
_cpu.ResetState();
|
||||
_communication.ResetState();
|
||||
_timer.ResetState();
|
||||
_sound.ResetState();
|
||||
_dma.ResetState();
|
||||
_keyInput.ResetState();
|
||||
_systemControl.ResetState();
|
||||
_interruptControl.ResetState();
|
||||
_memory.ResetState();
|
||||
_video.ResetState();
|
||||
|
||||
_bios.Reset(skipIntro); // This has to be done last
|
||||
}
|
||||
|
||||
public override void LoadState(BinaryReader reader)
|
||||
{
|
||||
if (reader.ReadString() != StateSaveMagic)
|
||||
throw new Exception("Iris.GBA.GBA_System: Wrong state save magic");
|
||||
|
||||
if (reader.ReadInt32() != StateSaveVersion)
|
||||
throw new Exception("Iris.GBA.GBA_System: Wrong state save version");
|
||||
|
||||
if (reader.ReadString() != _romHash)
|
||||
throw new Exception("Iris.GBA.GBA_System: Wrong ROM hash");
|
||||
|
||||
_scheduler.LoadState(reader);
|
||||
_cpu.LoadState(reader);
|
||||
_communication.LoadState(reader);
|
||||
_timer.LoadState(reader);
|
||||
_sound.LoadState(reader);
|
||||
_dma.LoadState(reader);
|
||||
_keyInput.LoadState(reader);
|
||||
_systemControl.LoadState(reader);
|
||||
_interruptControl.LoadState(reader);
|
||||
_memory.LoadState(reader);
|
||||
_video.LoadState(reader);
|
||||
}
|
||||
|
||||
public override void SaveState(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(StateSaveMagic);
|
||||
writer.Write(StateSaveVersion);
|
||||
writer.Write(_romHash);
|
||||
|
||||
_scheduler.SaveState(writer);
|
||||
_cpu.SaveState(writer);
|
||||
_communication.SaveState(writer);
|
||||
_timer.SaveState(writer);
|
||||
_sound.SaveState(writer);
|
||||
_dma.SaveState(writer);
|
||||
_keyInput.SaveState(writer);
|
||||
_systemControl.SaveState(writer);
|
||||
_interruptControl.SaveState(writer);
|
||||
_memory.SaveState(writer);
|
||||
_video.SaveState(writer);
|
||||
}
|
||||
|
||||
public override void LoadROM(string filename)
|
||||
{
|
||||
_memory.LoadROM(filename);
|
||||
|
||||
using HashAlgorithm hashAlgorithm = SHA512.Create();
|
||||
using FileStream fileStream = File.OpenRead(filename);
|
||||
_romHash = BitConverter.ToString(hashAlgorithm.ComputeHash(fileStream));
|
||||
}
|
||||
|
||||
public override void SetKeyStatus(Key key, KeyStatus status)
|
||||
{
|
||||
_keyInput.SetKeyStatus(key, status);
|
||||
}
|
||||
|
||||
public override bool IsRunning()
|
||||
{
|
||||
return _running;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
_running = true;
|
||||
|
||||
while (_running)
|
||||
{
|
||||
UInt64 cycleCount = _cpu.Step();
|
||||
_scheduler.AdvanceCycleCounter(cycleCount);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Pause()
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/GBA_System.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/GBA_System.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bc3a1e87e31e9a4abef36e543dab2c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
125
Assets/Iris/Iris.GBA/InterruptControl.cs
Normal file
125
Assets/Iris/Iris.GBA/InterruptControl.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class InterruptControl
|
||||
{
|
||||
internal enum Register
|
||||
{
|
||||
IE,
|
||||
IF,
|
||||
IME
|
||||
}
|
||||
|
||||
internal enum Interrupt
|
||||
{
|
||||
VBlank = 1 << 0,
|
||||
HBlank = 1 << 1,
|
||||
VCountMatch = 1 << 2,
|
||||
Timer0 = 1 << 3,
|
||||
Timer1 = 1 << 4,
|
||||
Timer2 = 1 << 5,
|
||||
Timer3 = 1 << 6,
|
||||
SIO = 1 << 7,
|
||||
DMA0 = 1 << 8,
|
||||
DMA1 = 1 << 9,
|
||||
DMA2 = 1 << 10,
|
||||
DMA3 = 1 << 11,
|
||||
Key = 1 << 12,
|
||||
//GamePak = 1 << 13
|
||||
}
|
||||
|
||||
private UInt16 _IE;
|
||||
private UInt16 _IF;
|
||||
private UInt16 _IME;
|
||||
|
||||
private CPU.CPU_Core _cpu;
|
||||
|
||||
internal void Initialize(CPU.CPU_Core cpu)
|
||||
{
|
||||
_cpu = cpu;
|
||||
}
|
||||
|
||||
internal void ResetState()
|
||||
{
|
||||
_IE = 0;
|
||||
_IF = 0;
|
||||
_IME = 0;
|
||||
}
|
||||
|
||||
internal void LoadState(BinaryReader reader)
|
||||
{
|
||||
_IE = reader.ReadUInt16();
|
||||
_IF = reader.ReadUInt16();
|
||||
_IME = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
internal void SaveState(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(_IE);
|
||||
writer.Write(_IF);
|
||||
writer.Write(_IME);
|
||||
}
|
||||
|
||||
internal UInt16 ReadRegister(Register register)
|
||||
{
|
||||
return register switch
|
||||
{
|
||||
Register.IE => _IE,
|
||||
Register.IF => _IF,
|
||||
Register.IME => _IME,
|
||||
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.InterruptControl: Register read error"),
|
||||
};
|
||||
}
|
||||
|
||||
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case Register.IE:
|
||||
Memory.WriteRegisterHelper(ref _IE, value, mode);
|
||||
break;
|
||||
|
||||
case Register.IF:
|
||||
switch (mode)
|
||||
{
|
||||
case Memory.RegisterWriteMode.LowByte:
|
||||
_IF &= (UInt16)~value;
|
||||
break;
|
||||
case Memory.RegisterWriteMode.HighByte:
|
||||
_IF &= (UInt16)~(value << 8);
|
||||
break;
|
||||
case Memory.RegisterWriteMode.HalfWord:
|
||||
_IF &= (UInt16)~value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Register.IME:
|
||||
Memory.WriteRegisterHelper(ref _IME, value, mode);
|
||||
break;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.InterruptControl: Register write error");
|
||||
}
|
||||
|
||||
CheckInterrupts();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void RequestInterrupt(Interrupt interrupt)
|
||||
{
|
||||
_IF |= (UInt16)interrupt;
|
||||
CheckInterrupts();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void CheckInterrupts()
|
||||
{
|
||||
_cpu.NIRQ = ((_IME == 0) || ((_IE & _IF) == 0)) ? CPU.CPU_Core.Signal.High : CPU.CPU_Core.Signal.Low;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/InterruptControl.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/InterruptControl.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c58052e2ebb4549418cb2d58b101d5ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
177
Assets/Iris/Iris.GBA/KeyInput.cs
Normal file
177
Assets/Iris/Iris.GBA/KeyInput.cs
Normal file
@ -0,0 +1,177 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class KeyInput
|
||||
{
|
||||
internal enum Register
|
||||
{
|
||||
KEYINPUT,
|
||||
KEYCNT
|
||||
}
|
||||
|
||||
private UInt16 _KEYINPUT;
|
||||
private UInt16 _KEYCNT;
|
||||
|
||||
private readonly Common.Scheduler _scheduler;
|
||||
private readonly Common.System.PollInput_Delegate _pollInputCallback;
|
||||
|
||||
private InterruptControl _interruptControl;
|
||||
|
||||
private const UInt64 CheckInterruptCycleCount = 280_896; // once per frame
|
||||
private bool _checkingInterrupt;
|
||||
|
||||
internal KeyInput(Common.Scheduler scheduler, Common.System.PollInput_Delegate pollInputCallback)
|
||||
{
|
||||
_scheduler = scheduler;
|
||||
_pollInputCallback = pollInputCallback;
|
||||
|
||||
_scheduler.RegisterTask((int)GBA_System.TaskId.CheckKeyInterrupt, CheckInterrupt);
|
||||
}
|
||||
|
||||
internal void Initialize(InterruptControl interruptControl)
|
||||
{
|
||||
_interruptControl = interruptControl;
|
||||
}
|
||||
|
||||
internal void ResetState()
|
||||
{
|
||||
_KEYINPUT = 0x03ff;
|
||||
_KEYCNT = 0;
|
||||
}
|
||||
|
||||
internal void LoadState(BinaryReader reader)
|
||||
{
|
||||
_KEYINPUT = reader.ReadUInt16();
|
||||
_KEYCNT = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
internal void SaveState(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(_KEYINPUT);
|
||||
writer.Write(_KEYCNT);
|
||||
}
|
||||
|
||||
internal UInt16 ReadRegister(Register register)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case Register.KEYINPUT:
|
||||
_pollInputCallback();
|
||||
|
||||
if ((_KEYCNT & 0x4000) == 0x4000)
|
||||
CheckInterrupt();
|
||||
|
||||
return _KEYINPUT;
|
||||
|
||||
case Register.KEYCNT:
|
||||
return _KEYCNT;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.KeyInput: Register read error");
|
||||
}
|
||||
}
|
||||
|
||||
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case Register.KEYCNT:
|
||||
Memory.WriteRegisterHelper(ref _KEYCNT, value, mode);
|
||||
|
||||
if ((_KEYCNT & 0x4000) == 0x4000)
|
||||
{
|
||||
_pollInputCallback();
|
||||
CheckInterrupt();
|
||||
|
||||
if (!_checkingInterrupt)
|
||||
{
|
||||
_checkingInterrupt = true;
|
||||
_scheduler.ScheduleTask((int)GBA_System.TaskId.CheckKeyInterrupt, CheckInterruptCycleCount);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.KeyInput: Register write error");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void SetKeyStatus(Common.System.Key key, Common.System.KeyStatus status)
|
||||
{
|
||||
int pos;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case Common.System.Key.A:
|
||||
pos = 0;
|
||||
break;
|
||||
case Common.System.Key.B:
|
||||
pos = 1;
|
||||
break;
|
||||
case Common.System.Key.Select:
|
||||
pos = 2;
|
||||
break;
|
||||
case Common.System.Key.Start:
|
||||
pos = 3;
|
||||
break;
|
||||
case Common.System.Key.Right:
|
||||
pos = 4;
|
||||
break;
|
||||
case Common.System.Key.Left:
|
||||
pos = 5;
|
||||
break;
|
||||
case Common.System.Key.Up:
|
||||
pos = 6;
|
||||
break;
|
||||
case Common.System.Key.Down:
|
||||
pos = 7;
|
||||
break;
|
||||
case Common.System.Key.R:
|
||||
pos = 8;
|
||||
break;
|
||||
case Common.System.Key.L:
|
||||
pos = 9;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
_KEYINPUT = (UInt16)((_KEYINPUT & ~(1 << pos)) | ((int)status << pos));
|
||||
}
|
||||
|
||||
private void CheckInterrupt(UInt64 cycleCountDelay)
|
||||
{
|
||||
if ((_KEYCNT & 0x4000) == 0)
|
||||
{
|
||||
_checkingInterrupt = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_pollInputCallback();
|
||||
CheckInterrupt();
|
||||
|
||||
_scheduler.ScheduleTask((int)GBA_System.TaskId.CheckKeyInterrupt, CheckInterruptCycleCount - cycleCountDelay);
|
||||
}
|
||||
|
||||
private void CheckInterrupt()
|
||||
{
|
||||
UInt16 mask = (UInt16)(_KEYCNT & 0x03ff);
|
||||
|
||||
if ((_KEYCNT & 0x8000) == 0)
|
||||
{
|
||||
if ((~_KEYINPUT & mask) != 0)
|
||||
_interruptControl.RequestInterrupt(InterruptControl.Interrupt.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: figure out what happens when mask == 0
|
||||
if ((~_KEYINPUT & mask) == mask)
|
||||
_interruptControl.RequestInterrupt(InterruptControl.Interrupt.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/KeyInput.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/KeyInput.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee2423bcb9113dc4580cb958b890e91d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
2215
Assets/Iris/Iris.GBA/Memory.cs
Normal file
2215
Assets/Iris/Iris.GBA/Memory.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Assets/Iris/Iris.GBA/Memory.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/Memory.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c63b68705a3d713459aaacd1a2506ed8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
348
Assets/Iris/Iris.GBA/Sound.cs
Normal file
348
Assets/Iris/Iris.GBA/Sound.cs
Normal file
@ -0,0 +1,348 @@
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class Sound
|
||||
{
|
||||
internal enum Register
|
||||
{
|
||||
SOUND1CNT_L,
|
||||
SOUND1CNT_H,
|
||||
SOUND1CNT_X,
|
||||
|
||||
SOUND2CNT_L,
|
||||
SOUND2CNT_H,
|
||||
|
||||
SOUND3CNT_L,
|
||||
SOUND3CNT_H,
|
||||
SOUND3CNT_X,
|
||||
|
||||
SOUND4CNT_L,
|
||||
SOUND4CNT_H,
|
||||
|
||||
SOUNDCNT_L,
|
||||
SOUNDCNT_H,
|
||||
SOUNDCNT_X,
|
||||
|
||||
SOUNDBIAS,
|
||||
|
||||
WAVE_RAM0_L,
|
||||
WAVE_RAM0_H,
|
||||
|
||||
WAVE_RAM1_L,
|
||||
WAVE_RAM1_H,
|
||||
|
||||
WAVE_RAM2_L,
|
||||
WAVE_RAM2_H,
|
||||
|
||||
WAVE_RAM3_L,
|
||||
WAVE_RAM3_H,
|
||||
|
||||
FIFO_A_L,
|
||||
FIFO_A_H,
|
||||
|
||||
FIFO_B_L,
|
||||
FIFO_B_H
|
||||
}
|
||||
|
||||
private UInt16 _SOUND1CNT_L;
|
||||
private UInt16 _SOUND1CNT_H;
|
||||
private UInt16 _SOUND1CNT_X;
|
||||
|
||||
private UInt16 _SOUND2CNT_L;
|
||||
private UInt16 _SOUND2CNT_H;
|
||||
|
||||
private UInt16 _SOUND3CNT_L;
|
||||
private UInt16 _SOUND3CNT_H;
|
||||
private UInt16 _SOUND3CNT_X;
|
||||
|
||||
private UInt16 _SOUND4CNT_L;
|
||||
private UInt16 _SOUND4CNT_H;
|
||||
|
||||
private UInt16 _SOUNDCNT_L;
|
||||
private UInt16 _SOUNDCNT_H;
|
||||
private UInt16 _SOUNDCNT_X;
|
||||
|
||||
private UInt16 _SOUNDBIAS;
|
||||
|
||||
private UInt16 _WAVE_RAM0_L;
|
||||
private UInt16 _WAVE_RAM0_H;
|
||||
|
||||
private UInt16 _WAVE_RAM1_L;
|
||||
private UInt16 _WAVE_RAM1_H;
|
||||
|
||||
private UInt16 _WAVE_RAM2_L;
|
||||
private UInt16 _WAVE_RAM2_H;
|
||||
|
||||
private UInt16 _WAVE_RAM3_L;
|
||||
private UInt16 _WAVE_RAM3_H;
|
||||
|
||||
private UInt16 _FIFO_A_L;
|
||||
private UInt16 _FIFO_A_H;
|
||||
|
||||
private UInt16 _FIFO_B_L;
|
||||
private UInt16 _FIFO_B_H;
|
||||
|
||||
internal void ResetState()
|
||||
{
|
||||
_SOUND1CNT_L = 0;
|
||||
_SOUND1CNT_H = 0;
|
||||
_SOUND1CNT_X = 0;
|
||||
|
||||
_SOUND2CNT_L = 0;
|
||||
_SOUND2CNT_H = 0;
|
||||
|
||||
_SOUND3CNT_L = 0;
|
||||
_SOUND3CNT_H = 0;
|
||||
_SOUND3CNT_X = 0;
|
||||
|
||||
_SOUND4CNT_L = 0;
|
||||
_SOUND4CNT_H = 0;
|
||||
|
||||
_SOUNDCNT_L = 0;
|
||||
_SOUNDCNT_H = 0;
|
||||
_SOUNDCNT_X = 0;
|
||||
|
||||
_SOUNDBIAS = 0;
|
||||
|
||||
_WAVE_RAM0_L = 0;
|
||||
_WAVE_RAM0_H = 0;
|
||||
|
||||
_WAVE_RAM1_L = 0;
|
||||
_WAVE_RAM1_H = 0;
|
||||
|
||||
_WAVE_RAM2_L = 0;
|
||||
_WAVE_RAM2_H = 0;
|
||||
|
||||
_WAVE_RAM3_L = 0;
|
||||
_WAVE_RAM3_H = 0;
|
||||
|
||||
_FIFO_A_L = 0;
|
||||
_FIFO_A_H = 0;
|
||||
|
||||
_FIFO_B_L = 0;
|
||||
_FIFO_B_H = 0;
|
||||
}
|
||||
|
||||
internal void LoadState(BinaryReader reader)
|
||||
{
|
||||
_SOUND1CNT_L = reader.ReadUInt16();
|
||||
_SOUND1CNT_H = reader.ReadUInt16();
|
||||
_SOUND1CNT_X = reader.ReadUInt16();
|
||||
|
||||
_SOUND2CNT_L = reader.ReadUInt16();
|
||||
_SOUND2CNT_H = reader.ReadUInt16();
|
||||
|
||||
_SOUND3CNT_L = reader.ReadUInt16();
|
||||
_SOUND3CNT_H = reader.ReadUInt16();
|
||||
_SOUND3CNT_X = reader.ReadUInt16();
|
||||
|
||||
_SOUND4CNT_L = reader.ReadUInt16();
|
||||
_SOUND4CNT_H = reader.ReadUInt16();
|
||||
|
||||
_SOUNDCNT_L = reader.ReadUInt16();
|
||||
_SOUNDCNT_H = reader.ReadUInt16();
|
||||
_SOUNDCNT_X = reader.ReadUInt16();
|
||||
|
||||
_SOUNDBIAS = reader.ReadUInt16();
|
||||
|
||||
_WAVE_RAM0_L = reader.ReadUInt16();
|
||||
_WAVE_RAM0_H = reader.ReadUInt16();
|
||||
|
||||
_WAVE_RAM1_L = reader.ReadUInt16();
|
||||
_WAVE_RAM1_H = reader.ReadUInt16();
|
||||
|
||||
_WAVE_RAM2_L = reader.ReadUInt16();
|
||||
_WAVE_RAM2_H = reader.ReadUInt16();
|
||||
|
||||
_WAVE_RAM3_L = reader.ReadUInt16();
|
||||
_WAVE_RAM3_H = reader.ReadUInt16();
|
||||
|
||||
_FIFO_A_L = reader.ReadUInt16();
|
||||
_FIFO_A_H = reader.ReadUInt16();
|
||||
|
||||
_FIFO_B_L = reader.ReadUInt16();
|
||||
_FIFO_B_H = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
internal void SaveState(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(_SOUND1CNT_L);
|
||||
writer.Write(_SOUND1CNT_H);
|
||||
writer.Write(_SOUND1CNT_X);
|
||||
|
||||
writer.Write(_SOUND2CNT_L);
|
||||
writer.Write(_SOUND2CNT_H);
|
||||
|
||||
writer.Write(_SOUND3CNT_L);
|
||||
writer.Write(_SOUND3CNT_H);
|
||||
writer.Write(_SOUND3CNT_X);
|
||||
|
||||
writer.Write(_SOUND4CNT_L);
|
||||
writer.Write(_SOUND4CNT_H);
|
||||
|
||||
writer.Write(_SOUNDCNT_L);
|
||||
writer.Write(_SOUNDCNT_H);
|
||||
writer.Write(_SOUNDCNT_X);
|
||||
|
||||
writer.Write(_SOUNDBIAS);
|
||||
|
||||
writer.Write(_WAVE_RAM0_L);
|
||||
writer.Write(_WAVE_RAM0_H);
|
||||
|
||||
writer.Write(_WAVE_RAM1_L);
|
||||
writer.Write(_WAVE_RAM1_H);
|
||||
|
||||
writer.Write(_WAVE_RAM2_L);
|
||||
writer.Write(_WAVE_RAM2_H);
|
||||
|
||||
writer.Write(_WAVE_RAM3_L);
|
||||
writer.Write(_WAVE_RAM3_H);
|
||||
|
||||
writer.Write(_FIFO_A_L);
|
||||
writer.Write(_FIFO_A_H);
|
||||
|
||||
writer.Write(_FIFO_B_L);
|
||||
writer.Write(_FIFO_B_H);
|
||||
}
|
||||
|
||||
internal UInt16 ReadRegister(Register register)
|
||||
{
|
||||
return register switch
|
||||
{
|
||||
Register.SOUND1CNT_L => _SOUND1CNT_L,
|
||||
Register.SOUND1CNT_H => _SOUND1CNT_H,
|
||||
Register.SOUND1CNT_X => _SOUND1CNT_X,
|
||||
|
||||
Register.SOUND2CNT_L => _SOUND2CNT_L,
|
||||
Register.SOUND2CNT_H => _SOUND2CNT_H,
|
||||
|
||||
Register.SOUND3CNT_L => _SOUND3CNT_L,
|
||||
Register.SOUND3CNT_H => _SOUND3CNT_H,
|
||||
Register.SOUND3CNT_X => _SOUND3CNT_X,
|
||||
|
||||
Register.SOUND4CNT_L => _SOUND4CNT_L,
|
||||
Register.SOUND4CNT_H => _SOUND4CNT_H,
|
||||
|
||||
Register.SOUNDCNT_L => _SOUNDCNT_L,
|
||||
Register.SOUNDCNT_H => _SOUNDCNT_H,
|
||||
Register.SOUNDCNT_X => _SOUNDCNT_X,
|
||||
|
||||
Register.SOUNDBIAS => _SOUNDBIAS,
|
||||
|
||||
Register.WAVE_RAM0_L => _WAVE_RAM0_L,
|
||||
Register.WAVE_RAM0_H => _WAVE_RAM0_H,
|
||||
|
||||
Register.WAVE_RAM1_L => _WAVE_RAM1_L,
|
||||
Register.WAVE_RAM1_H => _WAVE_RAM1_H,
|
||||
|
||||
Register.WAVE_RAM2_L => _WAVE_RAM2_L,
|
||||
Register.WAVE_RAM2_H => _WAVE_RAM2_H,
|
||||
|
||||
Register.WAVE_RAM3_L => _WAVE_RAM3_L,
|
||||
Register.WAVE_RAM3_H => _WAVE_RAM3_H,
|
||||
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.Sound: Register read error"),
|
||||
};
|
||||
}
|
||||
|
||||
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case Register.SOUND1CNT_L:
|
||||
Memory.WriteRegisterHelper(ref _SOUND1CNT_L, value, mode);
|
||||
break;
|
||||
case Register.SOUND1CNT_H:
|
||||
Memory.WriteRegisterHelper(ref _SOUND1CNT_H, value, mode);
|
||||
break;
|
||||
case Register.SOUND1CNT_X:
|
||||
Memory.WriteRegisterHelper(ref _SOUND1CNT_X, value, mode);
|
||||
break;
|
||||
|
||||
case Register.SOUND2CNT_L:
|
||||
Memory.WriteRegisterHelper(ref _SOUND2CNT_L, value, mode);
|
||||
break;
|
||||
case Register.SOUND2CNT_H:
|
||||
Memory.WriteRegisterHelper(ref _SOUND2CNT_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.SOUND3CNT_L:
|
||||
Memory.WriteRegisterHelper(ref _SOUND3CNT_L, value, mode);
|
||||
break;
|
||||
case Register.SOUND3CNT_H:
|
||||
Memory.WriteRegisterHelper(ref _SOUND3CNT_H, value, mode);
|
||||
break;
|
||||
case Register.SOUND3CNT_X:
|
||||
Memory.WriteRegisterHelper(ref _SOUND3CNT_X, value, mode);
|
||||
break;
|
||||
|
||||
case Register.SOUND4CNT_L:
|
||||
Memory.WriteRegisterHelper(ref _SOUND4CNT_L, value, mode);
|
||||
break;
|
||||
case Register.SOUND4CNT_H:
|
||||
Memory.WriteRegisterHelper(ref _SOUND4CNT_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.SOUNDCNT_L:
|
||||
Memory.WriteRegisterHelper(ref _SOUNDCNT_L, value, mode);
|
||||
break;
|
||||
case Register.SOUNDCNT_H:
|
||||
Memory.WriteRegisterHelper(ref _SOUNDCNT_H, value, mode);
|
||||
break;
|
||||
case Register.SOUNDCNT_X:
|
||||
Memory.WriteRegisterHelper(ref _SOUNDCNT_X, value, mode);
|
||||
break;
|
||||
|
||||
case Register.SOUNDBIAS:
|
||||
Memory.WriteRegisterHelper(ref _SOUNDBIAS, value, mode);
|
||||
break;
|
||||
|
||||
case Register.WAVE_RAM0_L:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM0_L, value, mode);
|
||||
break;
|
||||
case Register.WAVE_RAM0_H:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM0_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.WAVE_RAM1_L:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM1_L, value, mode);
|
||||
break;
|
||||
case Register.WAVE_RAM1_H:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM1_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.WAVE_RAM2_L:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM2_L, value, mode);
|
||||
break;
|
||||
case Register.WAVE_RAM2_H:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM2_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.WAVE_RAM3_L:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM3_L, value, mode);
|
||||
break;
|
||||
case Register.WAVE_RAM3_H:
|
||||
Memory.WriteRegisterHelper(ref _WAVE_RAM3_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.FIFO_A_L:
|
||||
Memory.WriteRegisterHelper(ref _FIFO_A_L, value, mode);
|
||||
break;
|
||||
case Register.FIFO_A_H:
|
||||
Memory.WriteRegisterHelper(ref _FIFO_A_H, value, mode);
|
||||
break;
|
||||
|
||||
case Register.FIFO_B_L:
|
||||
Memory.WriteRegisterHelper(ref _FIFO_B_L, value, mode);
|
||||
break;
|
||||
case Register.FIFO_B_H:
|
||||
Memory.WriteRegisterHelper(ref _FIFO_B_H, value, mode);
|
||||
break;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.Sound: Register write error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/Sound.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/Sound.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e76b919919204d4db34341937e30bc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
61
Assets/Iris/Iris.GBA/SystemControl.cs
Normal file
61
Assets/Iris/Iris.GBA/SystemControl.cs
Normal file
@ -0,0 +1,61 @@
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class SystemControl
|
||||
{
|
||||
internal enum Register
|
||||
{
|
||||
WAITCNT,
|
||||
SYSCNT_UND0 // undocumented - Post Boot Flag (POSTFLG) & Power Down Control (HALTCNT)
|
||||
}
|
||||
|
||||
private UInt16 _WAITCNT;
|
||||
private UInt16 _SYSCNT_UND0;
|
||||
|
||||
internal void ResetState()
|
||||
{
|
||||
_WAITCNT = 0;
|
||||
_SYSCNT_UND0 = 0;
|
||||
}
|
||||
|
||||
internal void LoadState(BinaryReader reader)
|
||||
{
|
||||
_WAITCNT = reader.ReadUInt16();
|
||||
_SYSCNT_UND0 = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
internal void SaveState(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(_WAITCNT);
|
||||
writer.Write(_SYSCNT_UND0);
|
||||
}
|
||||
|
||||
internal UInt16 ReadRegister(Register register)
|
||||
{
|
||||
return register switch
|
||||
{
|
||||
Register.WAITCNT => _WAITCNT,
|
||||
Register.SYSCNT_UND0 => _SYSCNT_UND0,
|
||||
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.SystemControl: Register read error"),
|
||||
};
|
||||
}
|
||||
|
||||
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case Register.WAITCNT:
|
||||
Memory.WriteRegisterHelper(ref _WAITCNT, value, mode);
|
||||
break;
|
||||
case Register.SYSCNT_UND0:
|
||||
Memory.WriteRegisterHelper(ref _SYSCNT_UND0, value, mode);
|
||||
break;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.SystemControl: Register write error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/SystemControl.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/SystemControl.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24d9a1588f50c654691f136b394d3d31
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
353
Assets/Iris/Iris.GBA/Timer.cs
Normal file
353
Assets/Iris/Iris.GBA/Timer.cs
Normal file
@ -0,0 +1,353 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Iris.GBA
|
||||
{
|
||||
internal sealed class Timer
|
||||
{
|
||||
internal enum Register
|
||||
{
|
||||
TM0CNT_L,
|
||||
TM0CNT_H,
|
||||
|
||||
TM1CNT_L,
|
||||
TM1CNT_H,
|
||||
|
||||
TM2CNT_L,
|
||||
TM2CNT_H,
|
||||
|
||||
TM3CNT_L,
|
||||
TM3CNT_H
|
||||
}
|
||||
|
||||
private readonly Common.Scheduler _scheduler;
|
||||
|
||||
private InterruptControl _interruptControl;
|
||||
|
||||
private struct Channel(GBA_System.TaskId startTaskId, GBA_System.TaskId handleOverflowTaskId, InterruptControl.Interrupt interrupt)
|
||||
{
|
||||
internal UInt16 _counter;
|
||||
internal UInt16 _reload;
|
||||
internal UInt16 _control;
|
||||
internal UInt64 _cycleCount; // only used in non-cascading mode
|
||||
internal bool _running;
|
||||
|
||||
internal readonly GBA_System.TaskId _startTaskId = startTaskId;
|
||||
internal readonly GBA_System.TaskId _handleOverflowTaskId = handleOverflowTaskId;
|
||||
internal readonly InterruptControl.Interrupt _interrupt = interrupt;
|
||||
}
|
||||
|
||||
private readonly Channel[] _channels;
|
||||
|
||||
internal Timer(Common.Scheduler scheduler)
|
||||
{
|
||||
_scheduler = scheduler;
|
||||
|
||||
_channels =
|
||||
[
|
||||
new(GBA_System.TaskId.StartTimer_Channel0, GBA_System.TaskId.HandleTimerOverflow_Channel0, InterruptControl.Interrupt.Timer0),
|
||||
new(GBA_System.TaskId.StartTimer_Channel1, GBA_System.TaskId.HandleTimerOverflow_Channel1, InterruptControl.Interrupt.Timer1),
|
||||
new(GBA_System.TaskId.StartTimer_Channel2, GBA_System.TaskId.HandleTimerOverflow_Channel2, InterruptControl.Interrupt.Timer2),
|
||||
new(GBA_System.TaskId.StartTimer_Channel3, GBA_System.TaskId.HandleTimerOverflow_Channel3, InterruptControl.Interrupt.Timer3)
|
||||
];
|
||||
|
||||
for (int channelIndex = 0; channelIndex < 4; ++channelIndex)
|
||||
{
|
||||
int channelIndexCopy = channelIndex;
|
||||
_scheduler.RegisterTask((int)_channels[channelIndex]._startTaskId, cycleCountDelay => Start(channelIndexCopy, cycleCountDelay));
|
||||
_scheduler.RegisterTask((int)_channels[channelIndex]._handleOverflowTaskId, cycleCountDelay => HandleOverflow(channelIndexCopy, cycleCountDelay));
|
||||
}
|
||||
}
|
||||
|
||||
internal void Initialize(InterruptControl interruptControl)
|
||||
{
|
||||
_interruptControl = interruptControl;
|
||||
}
|
||||
|
||||
internal void ResetState()
|
||||
{
|
||||
foreach (ref Channel channel in _channels.AsSpan())
|
||||
{
|
||||
channel._counter = 0;
|
||||
channel._reload = 0;
|
||||
channel._control = 0;
|
||||
channel._cycleCount = 0;
|
||||
channel._running = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void LoadState(BinaryReader reader)
|
||||
{
|
||||
foreach (ref Channel channel in _channels.AsSpan())
|
||||
{
|
||||
channel._counter = reader.ReadUInt16();
|
||||
channel._reload = reader.ReadUInt16();
|
||||
channel._control = reader.ReadUInt16();
|
||||
channel._cycleCount = reader.ReadUInt64();
|
||||
channel._running = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
internal void SaveState(BinaryWriter writer)
|
||||
{
|
||||
foreach (Channel channel in _channels)
|
||||
{
|
||||
writer.Write(channel._counter);
|
||||
writer.Write(channel._reload);
|
||||
writer.Write(channel._control);
|
||||
writer.Write(channel._cycleCount);
|
||||
writer.Write(channel._running);
|
||||
}
|
||||
}
|
||||
|
||||
internal UInt16 ReadRegister(Register register)
|
||||
{
|
||||
UInt16 ReadCounter(int channelIndex)
|
||||
{
|
||||
ref Channel channel = ref _channels[channelIndex];
|
||||
|
||||
if (channel._running && (((channel._control & 0x0004) == 0) || (channelIndex == 0)))
|
||||
UpdateCounter(ref channel, channel._control);
|
||||
|
||||
return channel._counter;
|
||||
}
|
||||
|
||||
return register switch
|
||||
{
|
||||
Register.TM0CNT_L => ReadCounter(0),
|
||||
Register.TM0CNT_H => _channels[0]._control,
|
||||
|
||||
Register.TM1CNT_L => ReadCounter(1),
|
||||
Register.TM1CNT_H => _channels[1]._control,
|
||||
|
||||
Register.TM2CNT_L => ReadCounter(2),
|
||||
Register.TM2CNT_H => _channels[2]._control,
|
||||
|
||||
Register.TM3CNT_L => ReadCounter(3),
|
||||
Register.TM3CNT_H => _channels[3]._control,
|
||||
|
||||
// should never happen
|
||||
_ => throw new Exception("Iris.GBA.Timer: Register read error"),
|
||||
};
|
||||
}
|
||||
|
||||
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
||||
{
|
||||
void WriteReload(ref Channel channel)
|
||||
{
|
||||
UInt16 reload = channel._reload;
|
||||
Memory.WriteRegisterHelper(ref reload, value, mode);
|
||||
channel._reload = reload;
|
||||
}
|
||||
|
||||
void WriteControl(int channelIndex)
|
||||
{
|
||||
ref Channel channel = ref _channels[channelIndex];
|
||||
|
||||
UInt16 previousControl = channel._control;
|
||||
|
||||
UInt16 newControl = channel._control;
|
||||
Memory.WriteRegisterHelper(ref newControl, value, mode);
|
||||
channel._control = newControl;
|
||||
|
||||
CheckControl(ref channel, channelIndex, previousControl, newControl);
|
||||
}
|
||||
|
||||
switch (register)
|
||||
{
|
||||
case Register.TM0CNT_L:
|
||||
WriteReload(ref _channels[0]);
|
||||
break;
|
||||
case Register.TM0CNT_H:
|
||||
WriteControl(0);
|
||||
break;
|
||||
|
||||
case Register.TM1CNT_L:
|
||||
WriteReload(ref _channels[1]);
|
||||
break;
|
||||
case Register.TM1CNT_H:
|
||||
WriteControl(1);
|
||||
break;
|
||||
|
||||
case Register.TM2CNT_L:
|
||||
WriteReload(ref _channels[2]);
|
||||
break;
|
||||
case Register.TM2CNT_H:
|
||||
WriteControl(2);
|
||||
break;
|
||||
|
||||
case Register.TM3CNT_L:
|
||||
WriteReload(ref _channels[3]);
|
||||
break;
|
||||
case Register.TM3CNT_H:
|
||||
WriteControl(3);
|
||||
break;
|
||||
|
||||
// should never happen
|
||||
default:
|
||||
throw new Exception("Iris.GBA.Timer: Register write error");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void CheckControl(ref Channel channel, int channelIndex, UInt16 previousControl, UInt16 newControl)
|
||||
{
|
||||
if ((previousControl & 0x0080) == 0)
|
||||
{
|
||||
if ((newControl & 0x0080) == 0x0080)
|
||||
_scheduler.ScheduleTask((int)channel._startTaskId, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((newControl & 0x0080) == 0)
|
||||
{
|
||||
if (channel._running)
|
||||
{
|
||||
if (((previousControl & 0x0004) == 0) || (channelIndex == 0))
|
||||
{
|
||||
UpdateCounter(ref channel, previousControl);
|
||||
|
||||
_scheduler.CancelTask((int)channel._handleOverflowTaskId);
|
||||
}
|
||||
|
||||
channel._running = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_scheduler.CancelTask((int)channel._startTaskId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (channel._running)
|
||||
{
|
||||
if (channelIndex == 0)
|
||||
{
|
||||
if ((previousControl & 0b11) != (newControl & 0b11))
|
||||
{
|
||||
UpdateCounter(ref channel, previousControl);
|
||||
|
||||
_scheduler.CancelTask((int)channel._handleOverflowTaskId);
|
||||
_scheduler.ScheduleTask((int)channel._handleOverflowTaskId, ComputeCycleCountUntilOverflow(ref channel));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((previousControl & 0x0004) == 0)
|
||||
{
|
||||
if ((newControl & 0x0004) == 0)
|
||||
{
|
||||
if ((previousControl & 0b11) != (newControl & 0b11))
|
||||
{
|
||||
UpdateCounter(ref channel, previousControl);
|
||||
|
||||
_scheduler.CancelTask((int)channel._handleOverflowTaskId);
|
||||
_scheduler.ScheduleTask((int)channel._handleOverflowTaskId, ComputeCycleCountUntilOverflow(ref channel));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCounter(ref channel, previousControl);
|
||||
|
||||
_scheduler.CancelTask((int)channel._handleOverflowTaskId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((newControl & 0x0004) == 0)
|
||||
{
|
||||
channel._cycleCount = _scheduler.GetCycleCounter();
|
||||
|
||||
_scheduler.ScheduleTask((int)channel._handleOverflowTaskId, ComputeCycleCountUntilOverflow(ref channel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCounter(ref Channel channel, UInt16 control)
|
||||
{
|
||||
UInt64 currentCycleCount = _scheduler.GetCycleCounter();
|
||||
UInt64 cycleCountDelta = currentCycleCount - channel._cycleCount;
|
||||
UInt64 prescaler = GetPrescaler(control);
|
||||
|
||||
channel._counter += (UInt16)(cycleCountDelta / prescaler);
|
||||
channel._cycleCount = currentCycleCount - (UInt16)(cycleCountDelta % prescaler);
|
||||
}
|
||||
|
||||
private void Start(int channelIndex, UInt64 cycleCountDelay)
|
||||
{
|
||||
ref Channel channel = ref _channels[channelIndex];
|
||||
|
||||
channel._counter = channel._reload;
|
||||
channel._running = true;
|
||||
|
||||
if (((channel._control & 0x0004) == 0) || (channelIndex == 0))
|
||||
{
|
||||
channel._cycleCount = _scheduler.GetCycleCounter() - cycleCountDelay;
|
||||
|
||||
_scheduler.ScheduleTask((int)channel._handleOverflowTaskId, ComputeCycleCountUntilOverflow(ref channel) - cycleCountDelay);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOverflow(int channelIndex, UInt64 cycleCountDelay)
|
||||
{
|
||||
ref Channel channel = ref _channels[channelIndex];
|
||||
|
||||
channel._counter = channel._reload;
|
||||
channel._cycleCount = _scheduler.GetCycleCounter() - cycleCountDelay;
|
||||
|
||||
_scheduler.ScheduleTask((int)channel._handleOverflowTaskId, ComputeCycleCountUntilOverflow(ref channel) - cycleCountDelay);
|
||||
|
||||
if ((channel._control & 0x0040) == 0x0040)
|
||||
_interruptControl.RequestInterrupt(channel._interrupt);
|
||||
|
||||
CascadeOverflow(channelIndex);
|
||||
}
|
||||
|
||||
private void CascadeOverflow(int channelIndex)
|
||||
{
|
||||
if (channelIndex == 3)
|
||||
return;
|
||||
|
||||
++channelIndex;
|
||||
|
||||
ref Channel channel = ref _channels[channelIndex];
|
||||
|
||||
if (!channel._running || ((channel._control & 0x0004) != 0x0004))
|
||||
return;
|
||||
|
||||
if (channel._counter == 0xffff)
|
||||
{
|
||||
channel._counter = channel._reload;
|
||||
|
||||
if ((channel._control & 0x0040) == 0x0040)
|
||||
_interruptControl.RequestInterrupt(channel._interrupt);
|
||||
|
||||
CascadeOverflow(channelIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
++channel._counter;
|
||||
}
|
||||
}
|
||||
|
||||
private static UInt64 ComputeCycleCountUntilOverflow(ref readonly Channel channel)
|
||||
{
|
||||
return (0x1_0000u - channel._counter) * GetPrescaler(channel._control);
|
||||
}
|
||||
|
||||
private static UInt64 GetPrescaler(UInt16 control)
|
||||
{
|
||||
return (control & 0b11) switch
|
||||
{
|
||||
0b00 => 1,
|
||||
0b01 => 64,
|
||||
0b10 => 256,
|
||||
0b11 => 1024,
|
||||
_ => 0, // cannot happen
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.GBA/Timer.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/Timer.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1c87077f94b1c547b5ef23240dca9cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1346
Assets/Iris/Iris.GBA/Video.cs
Normal file
1346
Assets/Iris/Iris.GBA/Video.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Assets/Iris/Iris.GBA/Video.cs.meta
Normal file
11
Assets/Iris/Iris.GBA/Video.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c10b6bffb02e139458b969dbf72fc69b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Iris/Iris.NDS.meta
Normal file
8
Assets/Iris/Iris.NDS.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89f540181c8a126469d24bee76e92113
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
25
Assets/Iris/Iris.NDS/BIOS.cs
Normal file
25
Assets/Iris/Iris.NDS/BIOS.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace Iris.NDS
|
||||
{
|
||||
public sealed partial class NDS_System
|
||||
{
|
||||
private void BIOS_Reset()
|
||||
{
|
||||
const UInt32 ROMAddress = 0x0800_0000;
|
||||
|
||||
// TODO
|
||||
|
||||
_cpu.Reg[CPU.CPU_Core.PC] = ROMAddress;
|
||||
_cpu.NextInstructionAddress = ROMAddress;
|
||||
}
|
||||
|
||||
private UInt64 HandleSWI()
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.BIOS: HandleSWI unimplemented");
|
||||
}
|
||||
|
||||
private UInt64 HandleIRQ()
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.BIOS: HandleIRQ unimplemented");
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.NDS/BIOS.cs.meta
Normal file
11
Assets/Iris/Iris.NDS/BIOS.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65bfe93914138fb498b88080b1e38337
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
44
Assets/Iris/Iris.NDS/Memory.cs
Normal file
44
Assets/Iris/Iris.NDS/Memory.cs
Normal file
@ -0,0 +1,44 @@
|
||||
namespace Iris.NDS
|
||||
{
|
||||
public sealed partial class NDS_System
|
||||
{
|
||||
private const int KB = 1024;
|
||||
|
||||
private Byte[]? _ROM;
|
||||
|
||||
public override void LoadROM(string filename)
|
||||
{
|
||||
_ROM = File.ReadAllBytes(filename);
|
||||
}
|
||||
|
||||
private Byte ReadMemory8(UInt32 address)
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.Memory: ReadMemory8 unimplemented");
|
||||
}
|
||||
|
||||
private UInt16 ReadMemory16(UInt32 address)
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.Memory: ReadMemory16 unimplemented");
|
||||
}
|
||||
|
||||
private UInt32 ReadMemory32(UInt32 address)
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.Memory: ReadMemory32 unimplemented");
|
||||
}
|
||||
|
||||
private void WriteMemory8(UInt32 address, Byte value)
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.Memory: ReadMemory32 unimplemented");
|
||||
}
|
||||
|
||||
private void WriteMemory16(UInt32 address, UInt16 value)
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.Memory: ReadMemory32 unimplemented");
|
||||
}
|
||||
|
||||
private void WriteMemory32(UInt32 address, UInt32 value)
|
||||
{
|
||||
throw new NotImplementedException("Iris.NDS.Core.Memory: ReadMemory32 unimplemented");
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.NDS/Memory.cs.meta
Normal file
11
Assets/Iris/Iris.NDS/Memory.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 901a653d16f65ca4b906d7d0fb660a03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
73
Assets/Iris/Iris.NDS/NDS_System.cs
Normal file
73
Assets/Iris/Iris.NDS/NDS_System.cs
Normal file
@ -0,0 +1,73 @@
|
||||
namespace Iris.NDS
|
||||
{
|
||||
public sealed partial class NDS_System : Common.System
|
||||
{
|
||||
private readonly CPU.CPU_Core _cpu;
|
||||
private readonly PPU _ppu;
|
||||
|
||||
private bool _running;
|
||||
private bool _disposed;
|
||||
|
||||
public NDS_System(PollInput_Delegate pollInputCallback, PresentFrame_Delegate presentFrameCallback)
|
||||
{
|
||||
CPU.CPU_Core.CallbackInterface cpuCallbackInterface = new(ReadMemory8, ReadMemory16, ReadMemory32, WriteMemory8, WriteMemory16, WriteMemory32, HandleSWI, HandleIRQ);
|
||||
_cpu = new(CPU.CPU_Core.Model.ARM946ES, cpuCallbackInterface);
|
||||
_ppu = new(presentFrameCallback);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
// TODO
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public override void ResetState(bool skipIntro)
|
||||
{
|
||||
BIOS_Reset();
|
||||
|
||||
_cpu.NIRQ = CPU.CPU_Core.Signal.High;
|
||||
}
|
||||
|
||||
public override void LoadState(BinaryReader reader)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public override void SaveState(BinaryWriter writer)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public override bool IsRunning()
|
||||
{
|
||||
return _running;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
_running = true;
|
||||
|
||||
while (_running)
|
||||
{
|
||||
UInt64 cycles = _cpu.Step();
|
||||
|
||||
for (UInt64 i = 0; i < cycles; ++i)
|
||||
_ppu.Step();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Pause()
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
|
||||
public override void SetKeyStatus(Key key, KeyStatus status)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.NDS/NDS_System.cs.meta
Normal file
11
Assets/Iris/Iris.NDS/NDS_System.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2233aca5b62bca346b553db0b0fcc482
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
19
Assets/Iris/Iris.NDS/PPU.cs
Normal file
19
Assets/Iris/Iris.NDS/PPU.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace Iris.NDS
|
||||
{
|
||||
public sealed class PPU
|
||||
{
|
||||
private const int KB = 1024;
|
||||
|
||||
private readonly Common.System.PresentFrame_Delegate _presentFrameCallback;
|
||||
|
||||
internal PPU(Common.System.PresentFrame_Delegate presentFrameCallback)
|
||||
{
|
||||
_presentFrameCallback = presentFrameCallback;
|
||||
}
|
||||
|
||||
internal void Step()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Iris/Iris.NDS/PPU.cs.meta
Normal file
11
Assets/Iris/Iris.NDS/PPU.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd011144397045948876bf23745e4a4c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Scenes.meta
Normal file
8
Assets/Scenes.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ea315d0fd7389c41b19996891e99ae3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
267
Assets/Scenes/SampleScene.unity
Normal file
267
Assets/Scenes/SampleScene.unity
Normal file
@ -0,0 +1,267 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 9
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 705507994}
|
||||
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 12
|
||||
m_GIWorkflowMode: 1
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_FinalGather: 0
|
||||
m_FinalGatherFiltering: 1
|
||||
m_FinalGatherRayCount: 256
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 500
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 500
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 2
|
||||
m_PVRDenoiserTypeDirect: 0
|
||||
m_PVRDenoiserTypeIndirect: 0
|
||||
m_PVRDenoiserTypeAO: 0
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 0
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 2
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
accuratePlacement: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &705507993
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 705507995}
|
||||
- component: {fileID: 705507994}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &705507994
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 705507993}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 8
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_Lightmapping: 1
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
--- !u!4 &705507995
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 705507993}
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!1 &963194225
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 963194228}
|
||||
- component: {fileID: 963194227}
|
||||
- component: {fileID: 963194226}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &963194226
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 963194225}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &963194227
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 963194225}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_GateFitMode: 2
|
||||
m_FocalLength: 50
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &963194228
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 963194225}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
7
Assets/Scenes/SampleScene.unity.meta
Normal file
7
Assets/Scenes/SampleScene.unity.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9fc0d4010bbf28b4594072e72b8655ab
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Reference in New Issue
Block a user