aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-02-23 21:59:38 -0300
committergdkchan <gab.dark.100@gmail.com>2018-02-23 21:59:38 -0300
commit3936c934482a587635bc5a1e47962551aeb53aeb (patch)
tree7df4ab78fe30ea27edcda9e54a32a8d132717225
parent2ed733b1d5addad027f48acfdd407e64b71427fc (diff)
Map heap on heap base region, fix for thread start on homebrew, add FCVTMU and FCVTPU (general) instructions, fix FMOV (higher 64 bits) encodings, improve emit code for FCVT* (general) instructions
-rw-r--r--ChocolArm64/AOpCodeTable.cs6
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdCvt.cs105
-rw-r--r--Ryujinx.Core/Loaders/Executable.cs3
-rw-r--r--Ryujinx.Core/Loaders/Executables/IExecutable.cs8
-rw-r--r--Ryujinx.Core/Loaders/Executables/Nro.cs4
-rw-r--r--Ryujinx.Core/Loaders/Executables/Nso.cs4
-rw-r--r--Ryujinx.Core/OsHle/Horizon.cs16
-rw-r--r--Ryujinx.Core/OsHle/MemoryRegions.cs13
-rw-r--r--Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs2
-rw-r--r--Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs13
-rw-r--r--Ryujinx.Core/OsHle/Process.cs19
-rw-r--r--Ryujinx.Core/OsHle/Svc/SvcSystem.cs8
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs5
-rw-r--r--Ryujinx/Ui/Program.cs4
14 files changed, 109 insertions, 101 deletions
diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs
index d4d6dd7c..b8fe278d 100644
--- a/ChocolArm64/AOpCodeTable.cs
+++ b/ChocolArm64/AOpCodeTable.cs
@@ -156,7 +156,9 @@ namespace ChocolArm64
Set("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
+ Set("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
+ Set("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt));
Set("0>0011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd));
@@ -179,8 +181,8 @@ namespace ChocolArm64
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
Set("x00111100x100110000000xxxxxxxxxx", AInstEmit.Fmov_Ftoi, typeof(AOpCodeSimdCvt));
Set("x00111100x100111000000xxxxxxxxxx", AInstEmit.Fmov_Itof, typeof(AOpCodeSimdCvt));
- Set("x00111100x101110000000xxxxxxxxxx", AInstEmit.Fmov_Ftoi1, typeof(AOpCodeSimdCvt));
- Set("x00111100x101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
+ Set("1001111010101110000000xxxxxxxxxx", AInstEmit.Fmov_Ftoi1, typeof(AOpCodeSimdCvt));
+ Set("1001111010101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
index 00c2fe9b..688f05a2 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
@@ -23,52 +23,62 @@ namespace ChocolArm64.Instruction
public static void Fcvtas_Gp(AILEmitterCtx Context)
{
- Fcvta__Gp(Context, Signed: true);
+ EmitFcvt_s_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero));
}
public static void Fcvtau_Gp(AILEmitterCtx Context)
{
- Fcvta__Gp(Context, Signed: false);
+ EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero));
}
public static void Fcvtms_Gp(AILEmitterCtx Context)
{
- EmitFcvt_s_Gp(Context, nameof(Math.Floor));
+ EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
+ }
+
+ public static void Fcvtmu_Gp(AILEmitterCtx Context)
+ {
+ EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
}
public static void Fcvtps_Gp(AILEmitterCtx Context)
{
- EmitFcvt_s_Gp(Context, nameof(Math.Ceiling));
+ EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling)));
+ }
+
+ public static void Fcvtpu_Gp(AILEmitterCtx Context)
+ {
+ EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling)));
}
public static void Fcvtzs_Gp(AILEmitterCtx Context)
{
- EmitFcvtz__Gp(Context, Signed: true);
+ EmitFcvt_s_Gp(Context, () => { });
}
public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context)
{
- EmitFcvtz__Gp_Fix(Context, Signed: true);
+ EmitFcvtzs_Gp_Fix(Context);
}
public static void Fcvtzs_V(AILEmitterCtx Context)
{
- EmitVectorFcvt(Context, Signed: true);
+ EmitVectorFcvtzs(Context);
}
public static void Fcvtzu_Gp(AILEmitterCtx Context)
{
- EmitFcvtz__Gp(Context, Signed: false);
+ EmitFcvt_u_Gp(Context, () => { });
}
public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context)
{
- EmitFcvtz__Gp_Fix(Context, Signed: false);
+ EmitFcvtzu_Gp_Fix(Context);
}
public static void Fcvtzu_V(AILEmitterCtx Context)
{
- EmitVectorFcvt(Context, Signed: false);
+ EmitVectorFcvtzu(Context);
}
public static void Scvtf_Gp(AILEmitterCtx Context)
@@ -165,13 +175,23 @@ namespace ChocolArm64.Instruction
}
}
- private static void Fcvta__Gp(AILEmitterCtx Context, bool Signed)
+ private static void EmitFcvt_s_Gp(AILEmitterCtx Context, Action Emit)
+ {
+ EmitFcvt___Gp(Context, Emit, true);
+ }
+
+ private static void EmitFcvt_u_Gp(AILEmitterCtx Context, Action Emit)
+ {
+ EmitFcvt___Gp(Context, Emit, false);
+ }
+
+ private static void EmitFcvt___Gp(AILEmitterCtx Context, Action Emit, bool Signed)
{
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
- EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
+ Emit();
if (Signed)
{
@@ -190,45 +210,14 @@ namespace ChocolArm64.Instruction
Context.EmitStintzr(Op.Rd);
}
- private static void EmitFcvt_s_Gp(AILEmitterCtx Context, string Name)
+ private static void EmitFcvtzs_Gp_Fix(AILEmitterCtx Context)
{
- AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
-
- EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
-
- EmitUnaryMathCall(Context, Name);
-
- EmitScalarFcvts(Context, Op.Size, 0);
-
- if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
- {
- Context.Emit(OpCodes.Conv_U8);
- }
-
- Context.EmitStintzr(Op.Rd);
+ EmitFcvtz__Gp_Fix(Context, true);
}
- private static void EmitFcvtz__Gp(AILEmitterCtx Context, bool Signed)
+ private static void EmitFcvtzu_Gp_Fix(AILEmitterCtx Context)
{
- AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
-
- EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
-
- if (Signed)
- {
- EmitScalarFcvts(Context, Op.Size, 0);
- }
- else
- {
- EmitScalarFcvtu(Context, Op.Size, 0);
- }
-
- if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
- {
- Context.Emit(OpCodes.Conv_U8);
- }
-
- Context.EmitStintzr(Op.Rd);
+ EmitFcvtz__Gp_Fix(Context, false);
}
private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed)
@@ -254,6 +243,16 @@ namespace ChocolArm64.Instruction
Context.EmitStintzr(Op.Rd);
}
+ private static void EmitVectorScvtf(AILEmitterCtx Context)
+ {
+ EmitVectorCvtf(Context, true);
+ }
+
+ private static void EmitVectorUcvtf(AILEmitterCtx Context)
+ {
+ EmitVectorCvtf(Context, false);
+ }
+
private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@@ -289,7 +288,17 @@ namespace ChocolArm64.Instruction
}
}
- private static void EmitVectorFcvt(AILEmitterCtx Context, bool Signed)
+ private static void EmitVectorFcvtzs(AILEmitterCtx Context)
+ {
+ EmitVectorFcvtz(Context, true);
+ }
+
+ private static void EmitVectorFcvtzu(AILEmitterCtx Context)
+ {
+ EmitVectorFcvtz(Context, false);
+ }
+
+ private static void EmitVectorFcvtz(AILEmitterCtx Context, bool Signed)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs
index 25a62136..e2660838 100644
--- a/Ryujinx.Core/Loaders/Executable.cs
+++ b/Ryujinx.Core/Loaders/Executable.cs
@@ -13,7 +13,6 @@ namespace Ryujinx.Core.Loaders
public long ImageBase { get; private set; }
public long ImageEnd { get; private set; }
- public Extensions Extension { get; private set; }
public Executable(IExecutable Exe, AMemory Memory, long ImageBase)
{
@@ -47,8 +46,6 @@ namespace Ryujinx.Core.Loaders
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
- Extension = Exe.Extension;
-
MapBss(BssStartOffset, BssEndOffset - BssStartOffset);
ImageEnd = BssEndOffset;
diff --git a/Ryujinx.Core/Loaders/Executables/IExecutable.cs b/Ryujinx.Core/Loaders/Executables/IExecutable.cs
index f6aa31ac..73787b1d 100644
--- a/Ryujinx.Core/Loaders/Executables/IExecutable.cs
+++ b/Ryujinx.Core/Loaders/Executables/IExecutable.cs
@@ -2,12 +2,6 @@ using System.Collections.ObjectModel;
namespace Ryujinx.Core.Loaders.Executables
{
- public enum Extensions
- {
- NRO,
- NSO
- }
-
public interface IExecutable
{
ReadOnlyCollection<byte> Text { get; }
@@ -19,7 +13,5 @@ namespace Ryujinx.Core.Loaders.Executables
int ROOffset { get; }
int DataOffset { get; }
int BssSize { get; }
-
- Extensions Extension { get; }
}
} \ No newline at end of file
diff --git a/Ryujinx.Core/Loaders/Executables/Nro.cs b/Ryujinx.Core/Loaders/Executables/Nro.cs
index 7f492cc2..3cbc4c5d 100644
--- a/Ryujinx.Core/Loaders/Executables/Nro.cs
+++ b/Ryujinx.Core/Loaders/Executables/Nro.cs
@@ -20,8 +20,6 @@ namespace Ryujinx.Core.Loaders.Executables
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
- public Extensions Extension { get; private set; }
-
public Nro(Stream Input)
{
BinaryReader Reader = new BinaryReader(Input);
@@ -49,8 +47,6 @@ namespace Ryujinx.Core.Loaders.Executables
this.DataOffset = DataOffset;
this.BssSize = BssSize;
- this.Extension = Extensions.NRO;
-
byte[] Read(long Position, int Size)
{
Input.Seek(Position, SeekOrigin.Begin);
diff --git a/Ryujinx.Core/Loaders/Executables/Nso.cs b/Ryujinx.Core/Loaders/Executables/Nso.cs
index 63ae08ae..7b8bf253 100644
--- a/Ryujinx.Core/Loaders/Executables/Nso.cs
+++ b/Ryujinx.Core/Loaders/Executables/Nso.cs
@@ -21,8 +21,6 @@ namespace Ryujinx.Core.Loaders.Executables
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
- public Extensions Extension { get; private set; }
-
[Flags]
private enum NsoFlags
{
@@ -81,8 +79,6 @@ namespace Ryujinx.Core.Loaders.Executables
this.DataOffset = DataMemOffset;
this.BssSize = BssSize;
- this.Extension = Extensions.NSO;
-
//Text segment
Input.Seek(TextOffset, SeekOrigin.Begin);
diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs
index e2d38d52..9929102d 100644
--- a/Ryujinx.Core/OsHle/Horizon.cs
+++ b/Ryujinx.Core/OsHle/Horizon.cs
@@ -114,25 +114,22 @@ namespace Ryujinx.Core.OsHle
public void LoadProgram(string FileName)
{
+ bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
+
int ProcessId = IdGen.GenerateId();
Process MainProcess = new Process(Ns, Allocator, ProcessId);
using (FileStream Input = new FileStream(FileName, FileMode.Open))
{
- if (Path.GetExtension(FileName).ToLower() == ".nro")
- {
- MainProcess.LoadProgram(new Nro(Input));
- }
- else
- {
- MainProcess.LoadProgram(new Nso(Input));
- }
+ MainProcess.LoadProgram(IsNro
+ ? (IExecutable)new Nro(Input)
+ : (IExecutable)new Nso(Input));
}
MainProcess.SetEmptyArgs();
MainProcess.InitializeHeap();
- MainProcess.Run();
+ MainProcess.Run(IsNro);
Processes.TryAdd(ProcessId, MainProcess);
}
@@ -190,6 +187,7 @@ namespace Ryujinx.Core.OsHle
if (SharedMem.TryGetLastVirtualPosition(out long Position))
{
Logging.Info($"HID shared memory successfully mapped to {Position:x16}!");
+
Ns.Hid.Init(Position);
}
}
diff --git a/Ryujinx.Core/OsHle/MemoryRegions.cs b/Ryujinx.Core/OsHle/MemoryRegions.cs
new file mode 100644
index 00000000..e8ededc8
--- /dev/null
+++ b/Ryujinx.Core/OsHle/MemoryRegions.cs
@@ -0,0 +1,13 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.Core.OsHle
+{
+ static class MemoryRegions
+ {
+ public const long MapRegionAddress = 0x80000000;
+ public const long MapRegionSize = 0x40000000;
+
+ public const long HeapRegionAddress = MapRegionAddress + MapRegionSize;
+ public const long HeapRegionSize = 0x40000000;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs b/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
index 23863099..54588383 100644
--- a/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
@@ -245,7 +245,7 @@ namespace Ryujinx.Core.OsHle.Objects.Android
HNvMap NvMap = GetNvMap(Context, Slot);
- if (NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize)
+ if (FbSize < 0 || NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize)
{
Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs
index e18c1daf..6b86a473 100644
--- a/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs
+++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs
@@ -273,7 +273,16 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv
FileStream Stream = new FileStream(FileName, FileMode.Open);
- MakeObject(Context, new IFile(Stream, FileName));
+ IFile FileInterface = new IFile(Stream, FileName);
+
+ FileInterface.Disposed += RemoveFileInUse;
+
+ lock (OpenPaths)
+ {
+ OpenPaths.Add(FileName);
+ }
+
+ MakeObject(Context, FileInterface);
return 0;
}
@@ -353,7 +362,7 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv
lock (OpenPaths)
{
- FileInterface.Disposed -= RemoveDirectoryInUse;
+ FileInterface.Disposed -= RemoveFileInUse;
OpenPaths.Remove(FileInterface.HostPath);
}
diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs
index 022dc0f9..3e265ed3 100644
--- a/Ryujinx.Core/OsHle/Process.cs
+++ b/Ryujinx.Core/OsHle/Process.cs
@@ -86,10 +86,10 @@ namespace Ryujinx.Core.OsHle
public void InitializeHeap()
{
- Memory.Manager.SetHeapAddr((ImageBase + 0x3fffffff) & ~0x3fffffff);
+ Memory.Manager.SetHeapAddr(MemoryRegions.HeapRegionAddress);
}
- public bool Run()
+ public bool Run(bool UseHbAbi = false)
{
if (Executables.Count == 0)
{
@@ -109,6 +109,14 @@ namespace Ryujinx.Core.OsHle
MainThread = Ns.Os.Handles.GetData<HThread>(Handle);
+ if (UseHbAbi)
+ {
+ Homebrew Homebrew_ABI = new Homebrew(Memory, Executables[0].ImageEnd, (long)Handle);
+
+ MainThread.Thread.ThreadState.X0 = (ulong)Executables[0].ImageEnd;
+ MainThread.Thread.ThreadState.X1 = ulong.MaxValue;
+ }
+
Scheduler.StartThread(MainThread);
return true;
@@ -186,13 +194,6 @@ namespace Ryujinx.Core.OsHle
Thread.ThreadState.X1 = (ulong)Handle;
Thread.ThreadState.X31 = (ulong)StackTop;
- if (Executables[0].Extension == Extensions.NRO)
- {
- Homebrew Homebrew_ABI = new Homebrew(Memory, Executables[0].ImageEnd, (long)Handle);
- Thread.ThreadState.X0 = (ulong)Executables[0].ImageEnd;
- Thread.ThreadState.X1 = 0xFFFFFFFFFFFFFFFF;
- }
-
Thread.WorkFinished += ThreadFinished;
ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd);
diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs
index f33d2ac8..71edfbd6 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs
+++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs
@@ -182,22 +182,22 @@ namespace Ryujinx.Core.OsHle.Svc
private ulong GetMapRegionBaseAddr()
{
- return 0x80000000;
+ return MemoryRegions.MapRegionAddress;
}
private ulong GetMapRegionSize()
{
- return 0x40000000;
+ return MemoryRegions.MapRegionSize;
}
private ulong GetHeapRegionBaseAddr()
{
- return GetMapRegionBaseAddr() + GetMapRegionSize();
+ return MemoryRegions.HeapRegionAddress;
}
private ulong GetHeapRegionSize()
{
- return 0x40000000;
+ return MemoryRegions.HeapRegionSize;
}
private ulong GetTotalMem()
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
index 6bf17d09..5ae5e225 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
@@ -3,7 +3,6 @@ using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
-
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OpenGLRenderer : IGalRenderer
@@ -29,8 +28,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private FrameBuffer FbRenderer;
- public long FrameBufferPtr { get; set; }
-
public OpenGLRenderer()
{
VertexBuffers = new List<VertexBuffer>();
@@ -58,7 +55,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
ActionsQueue.Dequeue()();
}
- }
+ }
public void Render()
{
diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs
index 7912147e..b5f8cb97 100644
--- a/Ryujinx/Ui/Program.cs
+++ b/Ryujinx/Ui/Program.cs
@@ -59,9 +59,7 @@ namespace Ryujinx
Screen.Run(60.0);
}
- Ns.FinalizeAllProcesses();
-
- Ns.Dispose();
+ Environment.Exit(0);
}
}
}