Skip to content

Commit e16c007

Browse files
committed
ifpscc: initial commit
libifpscc: initial commit readme: document ifpscc/libifpscc license: add credits for ifpscc/libifpscc (derived from code also MIT licensed) libifps: make additional fields/types public for libifpscc libifps: fix field documentation for some opcodes libifps: fix loading functions that are not exported libifps: allow saving a nonexistant primitive type if the same primitive type was added already libifps: fix parsing Extended constants libifps: fix ushort/short being mapped to the wrong types in one table csproj: set Prefer32Bit=false for release builds
1 parent b85cd77 commit e16c007

98 files changed

Lines changed: 19868 additions & 21 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

IFPSLib/Emit/BytecodeOperandType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace IFPSLib.Emit
77
/// <summary>
88
/// Type of a PascalScript operand at the bytecode level.
99
/// </summary>
10-
internal enum BytecodeOperandType : byte
10+
public enum BytecodeOperandType : byte
1111
{
1212
/// <summary>
1313
/// Operand refers to a local or global variable, or an argument.

IFPSLib/Emit/ExternalFunction.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ public override string ToString()
147147
"dll(\"{0}\",\"{1}\"{2}{3}) {4}",
148148
DllName.Replace("\"", "\\\""),
149149
ProcedureName.Replace("\"", "\\\""),
150-
DelayLoad ? "delayload, " : "",
151-
LoadWithAlteredSearchPath ? "alteredsearchpath" : "",
150+
DelayLoad ? ", delayload" : "",
151+
LoadWithAlteredSearchPath ? ", alteredsearchpath" : "",
152152
base.ToString()
153153
);
154154
}

IFPSLib/Emit/Instruction.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ internal Instruction(OpCode opcode)
8585
/// <param name="operands">Operands</param>
8686
internal Instruction(OpCode opcode, List<Operand> operands) : this(opcode)
8787
{
88+
if (operands.Any((op) => op == null)) throw new ArgumentNullException("operand");
8889
m_Operands = operands;
8990
}
9091

@@ -180,6 +181,16 @@ public static Instruction Create(OpCode opcode, IVariable op0, IVariable op1)
180181
public static Instruction Create<TType>(OpCode opcode, IVariable op0, TType val)
181182
=> Create(opcode, Operand.Create(op0), Operand.Create(val));
182183

184+
/// <summary>
185+
/// Creates a new instruction with two variable operands
186+
/// </summary>
187+
/// <param name="opcode">Opcode</param>
188+
/// <param name="op0">First operand</param>
189+
/// <param name="op1">Second operand</param>
190+
/// <returns>New instruction</returns>
191+
public static Instruction Create(OpCode opcode, IVariable op0, Operand op1)
192+
=> Create(opcode, Operand.Create(op0), op1);
193+
183194
/// <summary>
184195
/// Creates a new branch instruction with an operand
185196
/// </summary>

IFPSLib/Emit/OpCodes.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,15 @@ private static void FillTable(OpCode[] table, OpCode op)
8080
/// </summary>
8181
public static readonly OpCode Mod = new OpCode("mod", Code.Mod, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
8282
/// <summary>
83-
/// Shifts the first value left by the second value; <c>op0 <<= op1</c>
83+
/// Shifts the first value left by the second value; <c>op0 &lt;&lt;= op1</c>
8484
/// </summary>
8585
public static readonly OpCode Shl = new OpCode("shl", Code.Shl, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
8686
/// <summary>
8787
/// Shifts the first value right by the second value; <c>op0 >>= op1</c>
8888
/// </summary>
8989
public static readonly OpCode Shr = new OpCode("shr", Code.Shr, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
9090
/// <summary>
91-
/// Bitwise ANDs the first value by the second value; <c>op0 &= op1</c>
91+
/// Bitwise ANDs the first value by the second value; <c>op0 &amp;= op1</c>
9292
/// </summary>
9393
public static readonly OpCode And = new OpCode("and", Code.And, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro);
9494
/// <summary>
@@ -194,7 +194,7 @@ private static void FillTable(OpCode[] table, OpCode op)
194194
/// </summary>
195195
public static readonly OpCode CallVar = new OpCode("callvar", Code.CallVar, OperandType.InlineValue, FlowControl.Call, OpCodeType.Primitive, StackBehaviour.Push1);
196196
/// <summary>
197-
/// op0 must be a pointer.<br/>
197+
/// op0 must be a pointer, which may be a null pointer.<br/>
198198
/// If op1 is not a pointer, loads the equivalent address of op1 into op0; <c>op0 = &amp;op1</c><br/>
199199
/// If op1 is a pointer, loads op1 into op0; <c>op0 = op1</c>
200200
/// </summary>
@@ -253,7 +253,11 @@ private static void FillTable(OpCode[] table, OpCode op)
253253
/// </summary>
254254
public static readonly OpCode Not = new OpCode("not", Code.Not, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive);
255255
/// <summary>
256-
/// <c>*op0 = *op1</c>
256+
/// Copy constructor.<br/>
257+
/// Operand 0 must be a pointer, which may be a null pointer.<br/>
258+
/// First, <c>op0</c> is set to newly constructed pointer of <c>op1</c>'s type (if <c>op1</c> is a pointer, then <c>*op1</c>'s type).<br/>
259+
/// If Operand 1 is a pointer, <c>*op0 = *op1</c><br/>
260+
/// Otherwise, <c>*op0 = op1</c>
257261
/// </summary>
258262
public static readonly OpCode Cpval = new OpCode("cpval", Code.Cpval, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive);
259263
/// <summary>

IFPSLib/Emit/Operand.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ private struct Value
4141
internal BytecodeOperandType m_Type;
4242
private Value m_Value;
4343

44+
public BytecodeOperandType Type => m_Type;
45+
4446
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4547
private T EnsureType<T>(ref T value, BytecodeOperandType type)
4648
{
@@ -75,45 +77,66 @@ public Operand(TypedData imm)
7577

7678
public Operand(IVariable var)
7779
{
80+
if (var == null) throw new ArgumentNullException(nameof(var));
7881
m_Type = BytecodeOperandType.Variable;
7982
m_Value.Variable = var;
8083
}
8184

8285
public Operand(IVariable arr, uint immIdx)
8386
{
87+
if (arr == null) throw new ArgumentNullException(nameof(arr));
8488
m_Type = BytecodeOperandType.IndexedImmediate;
8589
m_Value.Indexed.Variable = arr;
8690
m_Value.Indexed.Index.m_Immediate = new StrongBox<uint>(immIdx);
8791
}
8892

8993
public Operand(IVariable arr, IVariable varIdx)
9094
{
95+
if (arr == null) throw new ArgumentNullException(nameof(arr));
96+
if (varIdx == null) throw new ArgumentNullException(nameof(varIdx));
9197
m_Type = BytecodeOperandType.IndexedVariable;
9298
m_Value.Indexed.Variable = arr;
9399
m_Value.Indexed.Index.Variable = varIdx;
94100
}
95101

102+
public Operand(Operand op)
103+
{
104+
m_Type = op.m_Type;
105+
m_Value = op.m_Value;
106+
}
107+
96108
public static Operand Create<TType>(Types.PrimitiveType type, TType value)
97109
{
98110
return new Operand(TypedData.Create(type, value));
99111
}
100112

101113
public static Operand Create<TType>(TType value)
102114
{
115+
// this is supposed to be for only primitives, but derived interfaces can also lead here.
116+
// check for those derived interfaces.
117+
switch (value)
118+
{
119+
case Types.IType type:
120+
return Create(type);
121+
case IFunction fn:
122+
return Create(fn);
123+
case IVariable var:
124+
return Create(var);
125+
}
103126
return new Operand(TypedData.Create(value));
104127
}
105128

106-
internal static Operand Create(Types.IType value)
129+
public static Operand Create(Types.IType value)
107130
{
108131
return new Operand(TypedData.Create(value));
109132
}
110133

111-
internal static Operand Create(Instruction value)
134+
public static Operand Create(Instruction value)
112135
{
113136
return new Operand(TypedData.Create(value));
114137
}
115138

116-
internal static Operand Create(IFunction value)
139+
public static Operand Create(IFunction value)
117140
{
118141
return new Operand(TypedData.Create(value));
119142
}

IFPSLib/Emit/ScriptFunction.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ internal static ScriptFunction Load(BinaryReader br, Script script, bool exporte
4848

4949
// Next are arguments. First char is the argument type ('@' means in, otherwise '!'), followed by the type index
5050
ret.Arguments = new List<FunctionArgument>(decl.Length - 1);
51-
for (int i = 1; i < decl.Length; i++) {
51+
for (int i = 1; i < decl.Length; i++)
52+
{
5253
var arg = new FunctionArgument();
5354
arg.ArgumentType = (decl[i][0] == ARGUMENT_TYPE_IN ? FunctionArgumentType.In : FunctionArgumentType.Out);
5455
if (!int.TryParse(decl[i].Substring(1), out var idxType) || idxType < 0) arg.Type = null;
@@ -57,6 +58,11 @@ internal static ScriptFunction Load(BinaryReader br, Script script, bool exporte
5758
ret.Arguments.Add(arg);
5859
}
5960
}
61+
else
62+
{
63+
ret.Name = string.Format("func_{0:x}", ret.CodeOffset);
64+
ret.Arguments = new List<FunctionArgument>();
65+
}
6066

6167
return ret;
6268
}

IFPSLib/Script.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ public static async Task<Script> LoadAsync(Stream stream)
160160
internal class SaveContext
161161
{
162162
internal readonly Dictionary<IType, int> tblTypes;
163+
internal readonly Dictionary<PascalTypeCode, int> tblPrimitiveTypes;
163164
internal readonly Dictionary<IFunction, int> tblFunctions;
164165
internal readonly Dictionary<GlobalVariable, int> tblGlobals;
165166
internal readonly Dictionary<IFunction, long> tblFunctionOffsets;
@@ -168,13 +169,19 @@ internal class SaveContext
168169
internal SaveContext(Script script)
169170
{
170171
tblTypes = new Dictionary<IType, int>();
172+
tblPrimitiveTypes = new Dictionary<PascalTypeCode, int>();
171173
tblFunctions = new Dictionary<IFunction, int>();
172174
tblGlobals = new Dictionary<GlobalVariable, int>();
173175
tblFunctionOffsets = new Dictionary<IFunction, long>();
174176

175177
FileVersion = script.FileVersion;
176178

177-
for (int i = 0; i < script.Types.Count; i++) tblTypes.Add(script.Types[i], i);
179+
for (int i = 0; i < script.Types.Count; i++)
180+
{
181+
var type = script.Types[i];
182+
tblTypes.Add(type, i);
183+
if (type is PrimitiveType && !tblPrimitiveTypes.ContainsKey(type.BaseType)) tblPrimitiveTypes.Add(type.BaseType, i);
184+
}
178185
for (int i = 0; i < script.Functions.Count; i++) tblFunctions.Add(script.Functions[i], i);
179186
for (int i = 0; i < script.GlobalVariables.Count; i++) tblGlobals.Add(script.GlobalVariables[i], i);
180187
}
@@ -185,11 +192,15 @@ internal int GetTypeIndex(IType type)
185192
{
186193
throw new ArgumentOutOfRangeException(string.Format("Used an unknown type"));
187194
}
188-
if (!tblTypes.TryGetValue(type, out var idx))
195+
if (tblTypes.TryGetValue(type, out var idx)) return idx;
196+
197+
// For a primitive type, check the primitives table too.
198+
if (type is PrimitiveType)
189199
{
190-
throw new KeyNotFoundException(string.Format("Used unreferenced type {0}, make sure it's added to the Types list.", type.Name));
200+
if (tblPrimitiveTypes.TryGetValue(type.BaseType, out idx)) return idx;
191201
}
192-
return idx;
202+
203+
throw new KeyNotFoundException(string.Format("Used unreferenced type {0}, make sure it's added to the Types list.", type.Name));
193204
}
194205

195206
internal int GetFunctionIndex(IFunction function)

IFPSLib/TypedData.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,35 @@ public TType ValueAs<TType>()
6464
return (TType)Value;
6565
}
6666

67+
private static void TrimDecimalString(StringBuilder sb)
68+
{
69+
// find the "e" looking from the end
70+
int idx = sb.Length;
71+
for (int i = sb.Length - 1; i >= 0; i--)
72+
{
73+
if (sb[i] == 'e')
74+
{
75+
idx = i - 1;
76+
break;
77+
}
78+
}
79+
80+
// remove while character is '0'
81+
int length = 0;
82+
while (idx >= 0 && sb[idx] == '0')
83+
{
84+
length++;
85+
idx--;
86+
}
87+
if (sb[idx] == '.') // need at least one zero
88+
{
89+
length--;
90+
idx++;
91+
}
92+
if (length == 0) return;
93+
sb.Remove(idx + 1, length);
94+
}
95+
6796
internal static TypedData Load(BinaryReader br, Script script)
6897
{
6998
var idxType = br.Read<uint>();
@@ -98,8 +127,13 @@ internal static TypedData Load(BinaryReader br, Script script)
98127
case PascalTypeCode.Double:
99128
return new TypedData(type, br.Read<double>());
100129
case PascalTypeCode.Extended:
101-
// BUGBUG: there must be something beter than this... but for now, it'll do
102-
return new TypedData(type, decimal.Parse(br.Read<ExtF80>().ToString()));
130+
{
131+
// BUGBUG: there must be something beter than this... but for now, it'll do
132+
var sb = new StringBuilder();
133+
ExtF80.PrintFloat80(sb, br.Read<ExtF80>(), PrintFloatFormat.ScientificFormat, 19);
134+
TrimDecimalString(sb);
135+
return new TypedData(type, decimal.Parse(sb.ToString(), System.Globalization.NumberStyles.Float));
136+
}
103137

104138
case PascalTypeCode.Currency:
105139
return new TypedData(type, new CurrencyWrapper(decimal.FromOACurrency(br.Read<long>())));

IFPSLib/Types/PascalTypeCode.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ public static partial class EnumHelpers
6060
{
6161
{ typeof(byte), PascalTypeCode.U8 },
6262
{ typeof(sbyte), PascalTypeCode.S8 },
63-
{ typeof(ushort), PascalTypeCode.S16 },
64-
{ typeof(short), PascalTypeCode.U16 },
63+
{ typeof(ushort), PascalTypeCode.U16 },
64+
{ typeof(short), PascalTypeCode.S16 },
6565
{ typeof(uint), PascalTypeCode.U32 },
6666
{ typeof(int), PascalTypeCode.S32 },
6767
{ typeof(long), PascalTypeCode.S64 },

IFPSTools.NET.sln

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ifpsdasm", "ifpsdasm\ifpsda
1111
EndProject
1212
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ifpsasm", "ifpsasm\ifpsasm.csproj", "{8C1A1532-1A40-46DB-BBDF-B4489BD0F966}"
1313
EndProject
14-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFPSAsmLib", "IFPSAsmLib\IFPSAsmLib.csproj", "{63B7741E-D712-4277-B345-F5B77AC80EB1}"
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFPSAsmLib", "IFPSAsmLib\IFPSAsmLib.csproj", "{63B7741E-D712-4277-B345-F5B77AC80EB1}"
1515
EndProject
1616
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "uninno", "uninno\uninno.csproj", "{5092701D-8D7E-4AB4-9877-DADCE1266B3D}"
1717
EndProject
1818
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "LoadSaveExtensions", "LoadSaveExtensions\LoadSaveExtensions.shproj", "{AF654578-B67D-4E5E-9CA6-E8AED9D0051A}"
1919
EndProject
20+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIFPSCC", "LibIFPSCC\LibIFPSCC.csproj", "{B56F2451-B1FF-4103-A2BC-0C61463D6971}"
21+
EndProject
22+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ifpscc", "ifpscc\ifpscc.csproj", "{8B953068-8B67-4D9A-802C-7657A7CAFC78}"
23+
EndProject
2024
Global
2125
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2226
Debug|Any CPU = Debug|Any CPU
@@ -47,6 +51,14 @@ Global
4751
{5092701D-8D7E-4AB4-9877-DADCE1266B3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
4852
{5092701D-8D7E-4AB4-9877-DADCE1266B3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
4953
{5092701D-8D7E-4AB4-9877-DADCE1266B3D}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{B56F2451-B1FF-4103-A2BC-0C61463D6971}.Release|Any CPU.Build.0 = Release|Any CPU
58+
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59+
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Debug|Any CPU.Build.0 = Debug|Any CPU
60+
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Release|Any CPU.ActiveCfg = Release|Any CPU
61+
{8B953068-8B67-4D9A-802C-7657A7CAFC78}.Release|Any CPU.Build.0 = Release|Any CPU
5062
EndGlobalSection
5163
GlobalSection(SolutionProperties) = preSolution
5264
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)