diff --git a/.circleci/scripts/install-linux-dotnet.sh b/.circleci/scripts/install-linux-dotnet.sh
index 035ffdaf6..1e23257e4 100755
--- a/.circleci/scripts/install-linux-dotnet.sh
+++ b/.circleci/scripts/install-linux-dotnet.sh
@@ -15,5 +15,5 @@ echo "done installing .Net SDK"
# we need to build the runtime before test run, since we used "--no-dependencies"
# when we call dotnet cli for restore and build, in order to speed up
echo "building runtime..."
-dotnet build -c Release -f netstandard2.0 runtime/CSharp/Antlr4.csproj
+dotnet build -c Release -f netstandard2.0 runtime/CSharp/src/Antlr4.csproj
echo "done building runtime"
diff --git a/.travis/run-tests-dotnet.sh b/.travis/run-tests-dotnet.sh
index a7780c49a..2b3f615b7 100755
--- a/.travis/run-tests-dotnet.sh
+++ b/.travis/run-tests-dotnet.sh
@@ -7,7 +7,7 @@ export PATH=$PATH:/Users/travis/.dotnet
# we need to build the runtime before test run, since we used "--no-dependencies"
# when we call dotnet cli for restore and build, in order to speed up
-dotnet build -c Release -f netstandard2.0 ../runtime/CSharp/Antlr4.csproj
+dotnet build -c Release -f netstandard2.0 ../runtime/CSharp/src/Antlr4.csproj
# call test
diff --git a/appveyor.yml b/appveyor.yml
index 11fbd4eaa..8bcec2bd9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -12,9 +12,9 @@ install:
- cinst -y dart-sdk --version=2.8.4
build_script:
- mvn -DskipTests install --batch-mode
- - dotnet build runtime/CSharp/Antlr4.csproj -c Release
+ - dotnet build runtime/CSharp/src/Antlr4.csproj -c Release
after_build:
- - dotnet pack runtime/CSharp/Antlr4.csproj -c Release
+ - dotnet pack runtime/CSharp/src/Antlr4.csproj -c Release
test_script:
- mvn install -Dantlr-php-php="C:\tools\php\php.exe" -Dantlr-dart-dart="C:\tools\dart-sdk\bin\dart.exe" -Dantlr-dart-pub="C:\tools\dart-sdk\bin\pub.bat" -Dantlr-dart-dart2native="C:\tools\dart-sdk\bin\dart2native.bat" -Dantlr-python2-python="C:\Python27\python.exe" -Dantlr-python3-python="C:\Python35\python.exe" --batch-mode
artifacts:
diff --git a/doc/antlr-project-testing.md b/doc/antlr-project-testing.md
index 1ff46aae9..3e14eac73 100644
--- a/doc/antlr-project-testing.md
+++ b/doc/antlr-project-testing.md
@@ -57,11 +57,9 @@ $ mvn install -DskipTests=true # make sure all artifacts are visible on this m
Now, make sure C# runtime is built and installed locally.
```bash
-cd ~/antlr/code/antlr4/runtime/CSharp/runtime/CSharp
-# kill previous ones manually as "xbuild /t:Clean" didn't seem to do it
-find . -name '*.dll' -exec rm {} \;
-# build
-xbuild /p:Configuration=Release Antlr4.Runtime/Antlr4.Runtime.mono.csproj
+cd ~/antlr/code/antlr4/runtime/CSharp/src
+rm -rf `find . -name '{obj,bin}'`
+dotnet build -c Release runtime/CSharp/src/Antlr4.csproj
```
C++ test rig automatically builds C++ runtime during tests. Others don't need a prebuilt lib.
diff --git a/doc/building-antlr.md b/doc/building-antlr.md
index 494bea81d..15ff6ff8e 100644
--- a/doc/building-antlr.md
+++ b/doc/building-antlr.md
@@ -25,6 +25,22 @@ Checking connectivity... done.
Checking out files: 100% (1427/1427), done.
```
+# Check your environment
+
+If you are starting from a clean, minimum Ubuntu OS, check your environment.
+
+
+```bash
+$ sudo apt-get update
+$ # Get Java
+$ java > /dev/null 2>&1
+$ if [[ "$?" != "0" ]]; then sudo apt install -y openjdk-11-jre-headless; fi
+$ # Get Mvn
+$ mvn > /dev/null 2>&1
+$ if [[ "$?" != "0" ]]; then sudo apt install -y maven; fi
+
+```
+
# Compile
```bash
diff --git a/doc/releasing-antlr.md b/doc/releasing-antlr.md
index 7323376b2..53fac7320 100644
--- a/doc/releasing-antlr.md
+++ b/doc/releasing-antlr.md
@@ -54,7 +54,7 @@ Edit the repository looking for 4.5 or whatever and update it. Bump version in t
* runtime/Python2/src/antlr4/Recognizer.py
* runtime/Python3/setup.py
* runtime/Python3/src/antlr4/Recognizer.py
- * runtime/CSharp/Antlr4.csproj
+ * runtime/CSharp/src/Antlr4.csproj
* runtime/PHP/src/RuntimeMetaData.php
* runtime/JavaScript/package.json
* runtime/JavaScript/src/antlr4/Recognizer.js
@@ -80,8 +80,8 @@ Here is a simple script to display any line from the critical files with, say, `
```bash
mvn clean
-rm -rf runtime/CSharp/bin
-rm -rf runtime/CSharp/obj
+rm -rf runtime/CSharp/src/bin
+rm -rf runtime/CSharp/src/obj
rm -rf runtime/gen
find tool runtime -type f -exec grep -l '4\.9' {} \;
find runtime runtime -type f -exec grep -l '4\.9' {} \;
@@ -322,10 +322,10 @@ Of course you need Mono and `nuget` to be installed. On mac:
From @kvanTTT: Install `dotnet` on any platform (see https://dotnet.microsoft.com/download) and run the following command on any OS (Win, Linux, macOS):
-* building: `dotnet build runtime/CSharp/Antlr4.csproj -c Release`
- Output `.dll` will be in `runtime/CSharp/bin/Release/netstandard2.0` or in `runtime/CSharp/bin/Release/netstandard2.1`
-* packing: `dotnet pack runtime/CSharp/Antlr4.csproj -c Release`
- Output `.nupkg` will be in `runtime/CSharp/bin/Release/Antlr4.Runtime.Standard.4.9.0.nupkg`
+* building: `dotnet build runtime/CSharp/src/Antlr4.csproj -c Release`
+ Output `.dll` will be in `runtime/CSharp/src/bin/Release/netstandard2.0` or in `runtime/CSharp/src/bin/Release/netstandard2.1`
+* packing: `dotnet pack runtime/CSharp/src/Antlr4.csproj -c Release`
+ Output `.nupkg` will be in `runtime/CSharp/src/bin/Release/Antlr4.Runtime.Standard.4.9.1.nupkg`
Alternatively, you can install Visual Studio 2017 and make sure to check boxes with .NET Core SDK.
diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java
index f6c4ab5c5..e1c713eb7 100644
--- a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java
+++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java
@@ -195,7 +195,7 @@ public class BaseCSharpTest extends BaseRuntimeTestSupport implements RuntimeTes
// find runtime package
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
- final URL runtimeProj = loader.getResource("CSharp/Antlr4.csproj");
+ final URL runtimeProj = loader.getResource("CSharp/src/Antlr4.csproj");
if (runtimeProj == null) {
throw new RuntimeException("C# runtime project file not found!");
}
diff --git a/runtime/CSharp/Antlr4.csproj b/runtime/CSharp/src/Antlr4.csproj
similarity index 97%
rename from runtime/CSharp/Antlr4.csproj
rename to runtime/CSharp/src/Antlr4.csproj
index 0a7b1e006..fb19e0173 100644
--- a/runtime/CSharp/Antlr4.csproj
+++ b/runtime/CSharp/src/Antlr4.csproj
@@ -3,7 +3,7 @@
The ANTLR Organization
4.9.1
en-US
- netstandard2.0;netstandard2.1
+ netstandard2.0
$(NoWarn);CS1591;CS1574;CS1580
true
Antlr4.Runtime.Standard
diff --git a/runtime/CSharp/Antlr4.snk b/runtime/CSharp/src/Antlr4.snk
similarity index 100%
rename from runtime/CSharp/Antlr4.snk
rename to runtime/CSharp/src/Antlr4.snk
diff --git a/runtime/CSharp/AntlrFileStream.cs b/runtime/CSharp/src/AntlrFileStream.cs
similarity index 100%
rename from runtime/CSharp/AntlrFileStream.cs
rename to runtime/CSharp/src/AntlrFileStream.cs
diff --git a/runtime/CSharp/AntlrInputStream.cs b/runtime/CSharp/src/AntlrInputStream.cs
similarity index 100%
rename from runtime/CSharp/AntlrInputStream.cs
rename to runtime/CSharp/src/AntlrInputStream.cs
diff --git a/runtime/CSharp/Atn/ATN.cs b/runtime/CSharp/src/Atn/ATN.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATN.cs
rename to runtime/CSharp/src/Atn/ATN.cs
diff --git a/runtime/CSharp/Atn/ATNConfig.cs b/runtime/CSharp/src/Atn/ATNConfig.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATNConfig.cs
rename to runtime/CSharp/src/Atn/ATNConfig.cs
diff --git a/runtime/CSharp/Atn/ATNConfigSet.cs b/runtime/CSharp/src/Atn/ATNConfigSet.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATNConfigSet.cs
rename to runtime/CSharp/src/Atn/ATNConfigSet.cs
diff --git a/runtime/CSharp/Atn/ATNDeserializationOptions.cs b/runtime/CSharp/src/Atn/ATNDeserializationOptions.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATNDeserializationOptions.cs
rename to runtime/CSharp/src/Atn/ATNDeserializationOptions.cs
diff --git a/runtime/CSharp/Atn/ATNDeserializer.cs b/runtime/CSharp/src/Atn/ATNDeserializer.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATNDeserializer.cs
rename to runtime/CSharp/src/Atn/ATNDeserializer.cs
diff --git a/runtime/CSharp/Atn/ATNSimulator.cs b/runtime/CSharp/src/Atn/ATNSimulator.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATNSimulator.cs
rename to runtime/CSharp/src/Atn/ATNSimulator.cs
diff --git a/runtime/CSharp/Atn/ATNState.cs b/runtime/CSharp/src/Atn/ATNState.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATNState.cs
rename to runtime/CSharp/src/Atn/ATNState.cs
diff --git a/runtime/CSharp/Atn/ATNType.cs b/runtime/CSharp/src/Atn/ATNType.cs
similarity index 100%
rename from runtime/CSharp/Atn/ATNType.cs
rename to runtime/CSharp/src/Atn/ATNType.cs
diff --git a/runtime/CSharp/Atn/AbstractPredicateTransition.cs b/runtime/CSharp/src/Atn/AbstractPredicateTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/AbstractPredicateTransition.cs
rename to runtime/CSharp/src/Atn/AbstractPredicateTransition.cs
diff --git a/runtime/CSharp/Atn/ActionTransition.cs b/runtime/CSharp/src/Atn/ActionTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/ActionTransition.cs
rename to runtime/CSharp/src/Atn/ActionTransition.cs
diff --git a/runtime/CSharp/Atn/AmbiguityInfo.cs b/runtime/CSharp/src/Atn/AmbiguityInfo.cs
similarity index 75%
rename from runtime/CSharp/Atn/AmbiguityInfo.cs
rename to runtime/CSharp/src/Atn/AmbiguityInfo.cs
index fbe0bd7e8..7017db797 100644
--- a/runtime/CSharp/Atn/AmbiguityInfo.cs
+++ b/runtime/CSharp/src/Atn/AmbiguityInfo.cs
@@ -41,6 +41,9 @@ namespace Antlr4.Runtime.Atn
/// 4.3
public class AmbiguityInfo : DecisionEventInfo
{
+ /** The set of alternative numbers for this decision event that lead to a valid parse. */
+ public BitSet ambigAlts;
+
///
/// Constructs a new instance of the
///
@@ -48,19 +51,30 @@ namespace Antlr4.Runtime.Atn
/// specified detailed ambiguity information.
///
/// The decision number
- ///
- /// The final simulator state identifying the ambiguous
+ /// The final configuration set identifying the ambiguous
/// alternatives for the current input
///
+ /// The set of alternatives in the decision that lead to a valid parse.
+ /// The predicted alt is the min(ambigAlts)
+ ///
/// The input token stream
/// The start index for the current prediction
///
/// The index at which the ambiguity was identified during
/// prediction
///
- public AmbiguityInfo(int decision, SimulatorState state, ITokenStream input, int startIndex, int stopIndex)
- : base(decision, state, input, startIndex, stopIndex, state.useContext)
+ /// @code true} if the ambiguity was identified during LL
+ /// prediction; otherwise, {@code false} if the ambiguity was identified
+ /// during SLL prediction
+ ///
+ public AmbiguityInfo(int decision,
+ ATNConfigSet configs,
+ BitSet ambigAlts,
+ ITokenStream input, int startIndex, int stopIndex,
+ bool fullCtx)
+ : base(decision, configs, input, startIndex, stopIndex, fullCtx)
{
+ this.ambigAlts = ambigAlts;
}
}
}
diff --git a/runtime/CSharp/Atn/ArrayPredictionContext.cs b/runtime/CSharp/src/Atn/ArrayPredictionContext.cs
similarity index 100%
rename from runtime/CSharp/Atn/ArrayPredictionContext.cs
rename to runtime/CSharp/src/Atn/ArrayPredictionContext.cs
diff --git a/runtime/CSharp/Atn/AtomTransition.cs b/runtime/CSharp/src/Atn/AtomTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/AtomTransition.cs
rename to runtime/CSharp/src/Atn/AtomTransition.cs
diff --git a/runtime/CSharp/Atn/BasicBlockStartState.cs b/runtime/CSharp/src/Atn/BasicBlockStartState.cs
similarity index 100%
rename from runtime/CSharp/Atn/BasicBlockStartState.cs
rename to runtime/CSharp/src/Atn/BasicBlockStartState.cs
diff --git a/runtime/CSharp/Atn/BasicState.cs b/runtime/CSharp/src/Atn/BasicState.cs
similarity index 100%
rename from runtime/CSharp/Atn/BasicState.cs
rename to runtime/CSharp/src/Atn/BasicState.cs
diff --git a/runtime/CSharp/Atn/BlockEndState.cs b/runtime/CSharp/src/Atn/BlockEndState.cs
similarity index 100%
rename from runtime/CSharp/Atn/BlockEndState.cs
rename to runtime/CSharp/src/Atn/BlockEndState.cs
diff --git a/runtime/CSharp/Atn/BlockStartState.cs b/runtime/CSharp/src/Atn/BlockStartState.cs
similarity index 100%
rename from runtime/CSharp/Atn/BlockStartState.cs
rename to runtime/CSharp/src/Atn/BlockStartState.cs
diff --git a/runtime/CSharp/Atn/ConflictInfo.cs b/runtime/CSharp/src/Atn/ConflictInfo.cs
similarity index 100%
rename from runtime/CSharp/Atn/ConflictInfo.cs
rename to runtime/CSharp/src/Atn/ConflictInfo.cs
diff --git a/runtime/CSharp/Atn/ContextSensitivityInfo.cs b/runtime/CSharp/src/Atn/ContextSensitivityInfo.cs
similarity index 86%
rename from runtime/CSharp/Atn/ContextSensitivityInfo.cs
rename to runtime/CSharp/src/Atn/ContextSensitivityInfo.cs
index b978c6786..454a72ef5 100644
--- a/runtime/CSharp/Atn/ContextSensitivityInfo.cs
+++ b/runtime/CSharp/src/Atn/ContextSensitivityInfo.cs
@@ -35,9 +35,8 @@ namespace Antlr4.Runtime.Atn
/// with the specified detailed context sensitivity information.
///
/// The decision number
- ///
- /// The final simulator state containing the unique
- /// alternative identified by full-context prediction
+ /// The final configuration set identifying the ambiguous
+ /// alternatives for the current input
///
/// The input token stream
/// The start index for the current prediction
@@ -45,8 +44,8 @@ namespace Antlr4.Runtime.Atn
/// The index at which the context sensitivity was
/// identified during full-context prediction
///
- public ContextSensitivityInfo(int decision, SimulatorState state, ITokenStream input, int startIndex, int stopIndex)
- : base(decision, state, input, startIndex, stopIndex, true)
+ public ContextSensitivityInfo(int decision, ATNConfigSet configs, ITokenStream input, int startIndex, int stopIndex)
+ : base(decision, configs, input, startIndex, stopIndex, true)
{
}
}
diff --git a/runtime/CSharp/Atn/DecisionEventInfo.cs b/runtime/CSharp/src/Atn/DecisionEventInfo.cs
similarity index 77%
rename from runtime/CSharp/Atn/DecisionEventInfo.cs
rename to runtime/CSharp/src/Atn/DecisionEventInfo.cs
index ee2098cb7..6ab88c4a1 100644
--- a/runtime/CSharp/Atn/DecisionEventInfo.cs
+++ b/runtime/CSharp/src/Atn/DecisionEventInfo.cs
@@ -25,15 +25,13 @@ namespace Antlr4.Runtime.Atn
///
public readonly int decision;
- ///
- /// The simulator state containing additional information relevant to the
- /// prediction state when the current event occurred, or
- ///
- /// if no
- /// additional information is relevant or available.
- ///
- [Nullable]
- public readonly SimulatorState state;
+ /// The configuration set containing additional information relevant to the
+ /// prediction state when the current event occurred, or {@code null} if no
+ /// additional information is relevant or available.
+ /// The configuration set containing additional information relevant to the
+ /// prediction state when the current event occurred, or {@code null} if no
+ /// additional information is relevant or available.
+ public readonly ATNConfigSet configs;
/// The input token stream which is being parsed.
/// The input token stream which is being parsed.
@@ -63,14 +61,17 @@ namespace Antlr4.Runtime.Atn
///
public readonly bool fullCtx;
- public DecisionEventInfo(int decision, SimulatorState state, ITokenStream input, int startIndex, int stopIndex, bool fullCtx)
+ public DecisionEventInfo(int decision,
+ ATNConfigSet configs,
+ ITokenStream input, int startIndex, int stopIndex,
+ bool fullCtx)
{
this.decision = decision;
this.fullCtx = fullCtx;
this.stopIndex = stopIndex;
this.input = input;
this.startIndex = startIndex;
- this.state = state;
+ this.configs = configs;
}
}
}
diff --git a/runtime/CSharp/Atn/DecisionInfo.cs b/runtime/CSharp/src/Atn/DecisionInfo.cs
similarity index 100%
rename from runtime/CSharp/Atn/DecisionInfo.cs
rename to runtime/CSharp/src/Atn/DecisionInfo.cs
diff --git a/runtime/CSharp/Atn/DecisionState.cs b/runtime/CSharp/src/Atn/DecisionState.cs
similarity index 100%
rename from runtime/CSharp/Atn/DecisionState.cs
rename to runtime/CSharp/src/Atn/DecisionState.cs
diff --git a/runtime/CSharp/Atn/EmptyPredictionContext.cs b/runtime/CSharp/src/Atn/EmptyPredictionContext.cs
similarity index 100%
rename from runtime/CSharp/Atn/EmptyPredictionContext.cs
rename to runtime/CSharp/src/Atn/EmptyPredictionContext.cs
diff --git a/runtime/CSharp/Atn/EpsilonTransition.cs b/runtime/CSharp/src/Atn/EpsilonTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/EpsilonTransition.cs
rename to runtime/CSharp/src/Atn/EpsilonTransition.cs
diff --git a/runtime/CSharp/Atn/ErrorInfo.cs b/runtime/CSharp/src/Atn/ErrorInfo.cs
similarity index 72%
rename from runtime/CSharp/Atn/ErrorInfo.cs
rename to runtime/CSharp/src/Atn/ErrorInfo.cs
index 91466fa55..93676d210 100644
--- a/runtime/CSharp/Atn/ErrorInfo.cs
+++ b/runtime/CSharp/src/Atn/ErrorInfo.cs
@@ -30,17 +30,18 @@ namespace Antlr4.Runtime.Atn
/// specified detailed syntax error information.
///
/// The decision number
- ///
- /// The final simulator state reached during prediction
- /// prior to reaching the
- ///
- /// state
+ /// The final configuration set reached during prediction
+ /// prior to reaching the {@link ATNSimulator#ERROR} state
///
/// The input token stream
/// The start index for the current prediction
/// The index at which the syntax error was identified
- public ErrorInfo(int decision, SimulatorState state, ITokenStream input, int startIndex, int stopIndex)
- : base(decision, state, input, startIndex, stopIndex, state.useContext)
+ /// {@code true} if the syntax error was identified during LL
+ /// prediction; otherwise, {@code false} if the syntax error was identified
+ /// during SLL prediction
+ ///
+ public ErrorInfo(int decision, ATNConfigSet configs, ITokenStream input, int startIndex, int stopIndex, bool fullCtx)
+ : base(decision, configs, input, startIndex, stopIndex, fullCtx)
{
}
}
diff --git a/runtime/CSharp/Atn/ILexerAction.cs b/runtime/CSharp/src/Atn/ILexerAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/ILexerAction.cs
rename to runtime/CSharp/src/Atn/ILexerAction.cs
diff --git a/runtime/CSharp/Atn/LL1Analyzer.cs b/runtime/CSharp/src/Atn/LL1Analyzer.cs
similarity index 100%
rename from runtime/CSharp/Atn/LL1Analyzer.cs
rename to runtime/CSharp/src/Atn/LL1Analyzer.cs
diff --git a/runtime/CSharp/Atn/LexerATNConfig.cs b/runtime/CSharp/src/Atn/LexerATNConfig.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerATNConfig.cs
rename to runtime/CSharp/src/Atn/LexerATNConfig.cs
diff --git a/runtime/CSharp/Atn/LexerATNSimulator.cs b/runtime/CSharp/src/Atn/LexerATNSimulator.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerATNSimulator.cs
rename to runtime/CSharp/src/Atn/LexerATNSimulator.cs
diff --git a/runtime/CSharp/Atn/LexerActionExecutor.cs b/runtime/CSharp/src/Atn/LexerActionExecutor.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerActionExecutor.cs
rename to runtime/CSharp/src/Atn/LexerActionExecutor.cs
diff --git a/runtime/CSharp/Atn/LexerActionType.cs b/runtime/CSharp/src/Atn/LexerActionType.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerActionType.cs
rename to runtime/CSharp/src/Atn/LexerActionType.cs
diff --git a/runtime/CSharp/Atn/LexerChannelAction.cs b/runtime/CSharp/src/Atn/LexerChannelAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerChannelAction.cs
rename to runtime/CSharp/src/Atn/LexerChannelAction.cs
diff --git a/runtime/CSharp/Atn/LexerCustomAction.cs b/runtime/CSharp/src/Atn/LexerCustomAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerCustomAction.cs
rename to runtime/CSharp/src/Atn/LexerCustomAction.cs
diff --git a/runtime/CSharp/Atn/LexerIndexedCustomAction.cs b/runtime/CSharp/src/Atn/LexerIndexedCustomAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerIndexedCustomAction.cs
rename to runtime/CSharp/src/Atn/LexerIndexedCustomAction.cs
diff --git a/runtime/CSharp/Atn/LexerModeAction.cs b/runtime/CSharp/src/Atn/LexerModeAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerModeAction.cs
rename to runtime/CSharp/src/Atn/LexerModeAction.cs
diff --git a/runtime/CSharp/Atn/LexerMoreAction.cs b/runtime/CSharp/src/Atn/LexerMoreAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerMoreAction.cs
rename to runtime/CSharp/src/Atn/LexerMoreAction.cs
diff --git a/runtime/CSharp/Atn/LexerPopModeAction.cs b/runtime/CSharp/src/Atn/LexerPopModeAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerPopModeAction.cs
rename to runtime/CSharp/src/Atn/LexerPopModeAction.cs
diff --git a/runtime/CSharp/Atn/LexerPushModeAction.cs b/runtime/CSharp/src/Atn/LexerPushModeAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerPushModeAction.cs
rename to runtime/CSharp/src/Atn/LexerPushModeAction.cs
diff --git a/runtime/CSharp/Atn/LexerSkipAction.cs b/runtime/CSharp/src/Atn/LexerSkipAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerSkipAction.cs
rename to runtime/CSharp/src/Atn/LexerSkipAction.cs
diff --git a/runtime/CSharp/Atn/LexerTypeAction.cs b/runtime/CSharp/src/Atn/LexerTypeAction.cs
similarity index 100%
rename from runtime/CSharp/Atn/LexerTypeAction.cs
rename to runtime/CSharp/src/Atn/LexerTypeAction.cs
diff --git a/runtime/CSharp/Atn/LookaheadEventInfo.cs b/runtime/CSharp/src/Atn/LookaheadEventInfo.cs
similarity index 67%
rename from runtime/CSharp/Atn/LookaheadEventInfo.cs
rename to runtime/CSharp/src/Atn/LookaheadEventInfo.cs
index 2b5e3f30a..8d8da4a0f 100644
--- a/runtime/CSharp/Atn/LookaheadEventInfo.cs
+++ b/runtime/CSharp/src/Atn/LookaheadEventInfo.cs
@@ -19,6 +19,13 @@ namespace Antlr4.Runtime.Atn
/// 4.3
public class LookaheadEventInfo : DecisionEventInfo
{
+ /// The alternative chosen by adaptivePredict(), not necessarily
+ /// the outermost alt shown for a rule; left-recursive rules have
+ /// user-level alts that differ from the rewritten rule with a (...) block
+ /// and a (..)* loop.
+ ///
+ public int predictedAlt;
+
///
/// Constructs a new instance of the
///
@@ -26,18 +33,15 @@ namespace Antlr4.Runtime.Atn
/// the specified detailed lookahead information.
///
/// The decision number
- ///
- /// The final simulator state containing the necessary
- /// information to determine the result of a prediction, or
- ///
- /// if
- /// the final state is not available
+ /// The final configuration set containing the necessary
+ /// information to determine the result of a prediction, or {@code null} if
+ /// the final configuration set is not available
///
+ ///
/// The input token stream
/// The start index for the current prediction
/// The index at which the prediction was finally made
///
- ///
///
/// if the current lookahead is part of an LL
/// prediction; otherwise,
@@ -45,9 +49,10 @@ namespace Antlr4.Runtime.Atn
/// if the current lookahead is part of
/// an SLL prediction
///
- public LookaheadEventInfo(int decision, SimulatorState state, ITokenStream input, int startIndex, int stopIndex, bool fullCtx)
- : base(decision, state, input, startIndex, stopIndex, fullCtx)
+ public LookaheadEventInfo(int decision, ATNConfigSet configs, int predictedAlt, ITokenStream input, int startIndex, int stopIndex, bool fullCtx)
+ : base(decision, configs, input, startIndex, stopIndex, fullCtx)
{
+ this.predictedAlt = predictedAlt;
}
}
}
diff --git a/runtime/CSharp/Atn/LoopEndState.cs b/runtime/CSharp/src/Atn/LoopEndState.cs
similarity index 100%
rename from runtime/CSharp/Atn/LoopEndState.cs
rename to runtime/CSharp/src/Atn/LoopEndState.cs
diff --git a/runtime/CSharp/Atn/MergeCache.cs b/runtime/CSharp/src/Atn/MergeCache.cs
similarity index 100%
rename from runtime/CSharp/Atn/MergeCache.cs
rename to runtime/CSharp/src/Atn/MergeCache.cs
diff --git a/runtime/CSharp/Atn/NotSetTransition.cs b/runtime/CSharp/src/Atn/NotSetTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/NotSetTransition.cs
rename to runtime/CSharp/src/Atn/NotSetTransition.cs
diff --git a/runtime/CSharp/Atn/ParseInfo.cs b/runtime/CSharp/src/Atn/ParseInfo.cs
similarity index 100%
rename from runtime/CSharp/Atn/ParseInfo.cs
rename to runtime/CSharp/src/Atn/ParseInfo.cs
diff --git a/runtime/CSharp/Atn/ParserATNSimulator.cs b/runtime/CSharp/src/Atn/ParserATNSimulator.cs
similarity index 100%
rename from runtime/CSharp/Atn/ParserATNSimulator.cs
rename to runtime/CSharp/src/Atn/ParserATNSimulator.cs
diff --git a/runtime/CSharp/Atn/PlusBlockStartState.cs b/runtime/CSharp/src/Atn/PlusBlockStartState.cs
similarity index 100%
rename from runtime/CSharp/Atn/PlusBlockStartState.cs
rename to runtime/CSharp/src/Atn/PlusBlockStartState.cs
diff --git a/runtime/CSharp/Atn/PlusLoopbackState.cs b/runtime/CSharp/src/Atn/PlusLoopbackState.cs
similarity index 100%
rename from runtime/CSharp/Atn/PlusLoopbackState.cs
rename to runtime/CSharp/src/Atn/PlusLoopbackState.cs
diff --git a/runtime/CSharp/Atn/PrecedencePredicateTransition.cs b/runtime/CSharp/src/Atn/PrecedencePredicateTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/PrecedencePredicateTransition.cs
rename to runtime/CSharp/src/Atn/PrecedencePredicateTransition.cs
diff --git a/runtime/CSharp/Atn/PredicateEvalInfo.cs b/runtime/CSharp/src/Atn/PredicateEvalInfo.cs
similarity index 86%
rename from runtime/CSharp/Atn/PredicateEvalInfo.cs
rename to runtime/CSharp/src/Atn/PredicateEvalInfo.cs
index 467ac40f2..cbc3d084d 100644
--- a/runtime/CSharp/Atn/PredicateEvalInfo.cs
+++ b/runtime/CSharp/src/Atn/PredicateEvalInfo.cs
@@ -49,7 +49,6 @@ namespace Antlr4.Runtime.Atn
/// class with the
/// specified detailed predicate evaluation information.
///
- /// The simulator state
/// The decision number
/// The input token stream
/// The start index for the current prediction
@@ -68,10 +67,15 @@ namespace Antlr4.Runtime.Atn
///
/// for more information.
///
+ /// {@code true} if the semantic context was
+ /// evaluated during LL prediction; otherwise, {@code false} if the semantic
+ /// context was evaluated during SLL prediction
+ ///
+ ///
///
///
- public PredicateEvalInfo(SimulatorState state, int decision, ITokenStream input, int startIndex, int stopIndex, SemanticContext semctx, bool evalResult, int predictedAlt)
- : base(decision, state, input, startIndex, stopIndex, state.useContext)
+ public PredicateEvalInfo(int decision, ITokenStream input, int startIndex, int stopIndex, SemanticContext semctx, bool evalResult, int predictedAlt, bool fullCtx)
+ : base(decision, new ATNConfigSet(), input, startIndex, stopIndex, fullCtx)
{
this.semctx = semctx;
this.evalResult = evalResult;
diff --git a/runtime/CSharp/Atn/PredicateTransition.cs b/runtime/CSharp/src/Atn/PredicateTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/PredicateTransition.cs
rename to runtime/CSharp/src/Atn/PredicateTransition.cs
diff --git a/runtime/CSharp/Atn/PredictionContext.cs b/runtime/CSharp/src/Atn/PredictionContext.cs
similarity index 100%
rename from runtime/CSharp/Atn/PredictionContext.cs
rename to runtime/CSharp/src/Atn/PredictionContext.cs
diff --git a/runtime/CSharp/Atn/PredictionContextCache.cs b/runtime/CSharp/src/Atn/PredictionContextCache.cs
similarity index 100%
rename from runtime/CSharp/Atn/PredictionContextCache.cs
rename to runtime/CSharp/src/Atn/PredictionContextCache.cs
diff --git a/runtime/CSharp/Atn/PredictionMode.cs b/runtime/CSharp/src/Atn/PredictionMode.cs
similarity index 100%
rename from runtime/CSharp/Atn/PredictionMode.cs
rename to runtime/CSharp/src/Atn/PredictionMode.cs
diff --git a/runtime/CSharp/Atn/ProfilingATNSimulator.cs b/runtime/CSharp/src/Atn/ProfilingATNSimulator.cs
similarity index 87%
rename from runtime/CSharp/Atn/ProfilingATNSimulator.cs
rename to runtime/CSharp/src/Atn/ProfilingATNSimulator.cs
index 8c5ac6b99..a88b819d9 100644
--- a/runtime/CSharp/Atn/ProfilingATNSimulator.cs
+++ b/runtime/CSharp/src/Atn/ProfilingATNSimulator.cs
@@ -71,7 +71,7 @@ namespace Antlr4.Runtime.Atn
{
decisions[decision].SLL_MaxLook = SLL_k;
decisions[decision].SLL_MaxLookEvent =
- new LookaheadEventInfo(decision, null/*, alt*/, input, startIndex, sllStopIndex, false);
+ new LookaheadEventInfo(decision, null, alt, input, startIndex, sllStopIndex, false);
}
if (llStopIndex >= 0)
@@ -83,7 +83,7 @@ namespace Antlr4.Runtime.Atn
{
decisions[decision].LL_MaxLook = LL_k;
decisions[decision].LL_MaxLookEvent =
- new LookaheadEventInfo(decision, null/*, alt*/, input, startIndex, llStopIndex, true);
+ new LookaheadEventInfo(decision, null, alt, input, startIndex, llStopIndex, true);
}
}
@@ -108,7 +108,7 @@ namespace Antlr4.Runtime.Atn
if (existingTargetState == ERROR)
{
decisions[currentDecision].errors.Add(
- new ErrorInfo(currentDecision, null /*previousD.configs*/, input, startIndex, sllStopIndex)
+ new ErrorInfo(currentDecision, previousD.configSet, input, startIndex, sllStopIndex, false)
);
}
}
@@ -143,7 +143,7 @@ namespace Antlr4.Runtime.Atn
else { // no reach on current lookahead symbol. ERROR.
// TODO: does not handle delayed errors per getSynValidOrSemInvalidAltThatFinishedDecisionEntryRule()
decisions[currentDecision].errors.Add(
- new ErrorInfo(currentDecision, null /*closure*/, input, startIndex, llStopIndex)
+ new ErrorInfo(currentDecision, closure, input, startIndex, llStopIndex, true)
);
}
}
@@ -154,7 +154,7 @@ namespace Antlr4.Runtime.Atn
}
else { // no reach on current lookahead symbol. ERROR.
decisions[currentDecision].errors.Add(
- new ErrorInfo(currentDecision, null /*closure*/, input, startIndex, sllStopIndex)
+ new ErrorInfo(currentDecision, closure, input, startIndex, sllStopIndex, false)
);
}
}
@@ -168,7 +168,7 @@ namespace Antlr4.Runtime.Atn
bool fullContext = llStopIndex >= 0;
int stopIndex = fullContext ? llStopIndex : sllStopIndex;
decisions[currentDecision].predicateEvals.Add(
- new PredicateEvalInfo(null , currentDecision, input, startIndex, stopIndex, pred, result, alt/*, fullCtx*/)
+ new PredicateEvalInfo(currentDecision, input, startIndex, stopIndex, pred, result, alt, fullCtx)
);
}
@@ -193,14 +193,14 @@ namespace Antlr4.Runtime.Atn
if (prediction != conflictingAltResolvedBySLL)
{
decisions[currentDecision].contextSensitivities.Add(
- new ContextSensitivityInfo(currentDecision, null /*configs*/, input, startIndex, stopIndex)
+ new ContextSensitivityInfo(currentDecision, configs, input, startIndex, stopIndex)
);
}
base.ReportContextSensitivity(dfa, prediction, configs, startIndex, stopIndex);
}
protected override void ReportAmbiguity(DFA dfa, DFAState D, int startIndex, int stopIndex, bool exact,
- BitSet ambigAlts, ATNConfigSet configSet)
+ BitSet ambigAlts, ATNConfigSet configs)
{
int prediction;
if (ambigAlts != null)
@@ -208,22 +208,22 @@ namespace Antlr4.Runtime.Atn
prediction = ambigAlts.NextSetBit(0);
}
else {
- prediction = configSet.GetAlts().NextSetBit(0);
+ prediction = configs.GetAlts().NextSetBit(0);
}
- if (configSet.fullCtx && prediction != conflictingAltResolvedBySLL)
+ if (configs.fullCtx && prediction != conflictingAltResolvedBySLL)
{
// Even though this is an ambiguity we are reporting, we can
// still detect some context sensitivities. Both SLL and LL
// are showing a conflict, hence an ambiguity, but if they resolve
// to different minimum alternatives we have also identified a
// context sensitivity.
- decisions[currentDecision].contextSensitivities.Add( new ContextSensitivityInfo(currentDecision, null /*configs*/, input, startIndex, stopIndex) );
+ decisions[currentDecision].contextSensitivities.Add( new ContextSensitivityInfo(currentDecision, configs, input, startIndex, stopIndex) );
}
decisions[currentDecision].ambiguities.Add(
- new AmbiguityInfo(currentDecision, null /*configs, ambigAlts*/,
- input, startIndex, stopIndex/*, configs.IsFullContext*/)
+ new AmbiguityInfo(currentDecision, configs, ambigAlts,
+ input, startIndex, stopIndex, configs.fullCtx)
);
- base.ReportAmbiguity(dfa, D, startIndex, stopIndex, exact, ambigAlts, configSet);
+ base.ReportAmbiguity(dfa, D, startIndex, stopIndex, exact, ambigAlts, configs);
}
// ---------------------------------------------------------------------
diff --git a/runtime/CSharp/Atn/RangeTransition.cs b/runtime/CSharp/src/Atn/RangeTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/RangeTransition.cs
rename to runtime/CSharp/src/Atn/RangeTransition.cs
diff --git a/runtime/CSharp/Atn/RuleStartState.cs b/runtime/CSharp/src/Atn/RuleStartState.cs
similarity index 100%
rename from runtime/CSharp/Atn/RuleStartState.cs
rename to runtime/CSharp/src/Atn/RuleStartState.cs
diff --git a/runtime/CSharp/Atn/RuleStopState.cs b/runtime/CSharp/src/Atn/RuleStopState.cs
similarity index 100%
rename from runtime/CSharp/Atn/RuleStopState.cs
rename to runtime/CSharp/src/Atn/RuleStopState.cs
diff --git a/runtime/CSharp/Atn/RuleTransition.cs b/runtime/CSharp/src/Atn/RuleTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/RuleTransition.cs
rename to runtime/CSharp/src/Atn/RuleTransition.cs
diff --git a/runtime/CSharp/Atn/SemanticContext.cs b/runtime/CSharp/src/Atn/SemanticContext.cs
similarity index 100%
rename from runtime/CSharp/Atn/SemanticContext.cs
rename to runtime/CSharp/src/Atn/SemanticContext.cs
diff --git a/runtime/CSharp/Atn/SetTransition.cs b/runtime/CSharp/src/Atn/SetTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/SetTransition.cs
rename to runtime/CSharp/src/Atn/SetTransition.cs
diff --git a/runtime/CSharp/Atn/SimulatorState.cs b/runtime/CSharp/src/Atn/SimulatorState.cs
similarity index 100%
rename from runtime/CSharp/Atn/SimulatorState.cs
rename to runtime/CSharp/src/Atn/SimulatorState.cs
diff --git a/runtime/CSharp/Atn/SingletonPredictionContext.cs b/runtime/CSharp/src/Atn/SingletonPredictionContext.cs
similarity index 100%
rename from runtime/CSharp/Atn/SingletonPredictionContext.cs
rename to runtime/CSharp/src/Atn/SingletonPredictionContext.cs
diff --git a/runtime/CSharp/Atn/StarBlockStartState.cs b/runtime/CSharp/src/Atn/StarBlockStartState.cs
similarity index 100%
rename from runtime/CSharp/Atn/StarBlockStartState.cs
rename to runtime/CSharp/src/Atn/StarBlockStartState.cs
diff --git a/runtime/CSharp/Atn/StarLoopEntryState.cs b/runtime/CSharp/src/Atn/StarLoopEntryState.cs
similarity index 100%
rename from runtime/CSharp/Atn/StarLoopEntryState.cs
rename to runtime/CSharp/src/Atn/StarLoopEntryState.cs
diff --git a/runtime/CSharp/Atn/StarLoopbackState.cs b/runtime/CSharp/src/Atn/StarLoopbackState.cs
similarity index 100%
rename from runtime/CSharp/Atn/StarLoopbackState.cs
rename to runtime/CSharp/src/Atn/StarLoopbackState.cs
diff --git a/runtime/CSharp/Atn/StateType.cs b/runtime/CSharp/src/Atn/StateType.cs
similarity index 100%
rename from runtime/CSharp/Atn/StateType.cs
rename to runtime/CSharp/src/Atn/StateType.cs
diff --git a/runtime/CSharp/Atn/TokensStartState.cs b/runtime/CSharp/src/Atn/TokensStartState.cs
similarity index 100%
rename from runtime/CSharp/Atn/TokensStartState.cs
rename to runtime/CSharp/src/Atn/TokensStartState.cs
diff --git a/runtime/CSharp/Atn/Transition.cs b/runtime/CSharp/src/Atn/Transition.cs
similarity index 100%
rename from runtime/CSharp/Atn/Transition.cs
rename to runtime/CSharp/src/Atn/Transition.cs
diff --git a/runtime/CSharp/Atn/TransitionType.cs b/runtime/CSharp/src/Atn/TransitionType.cs
similarity index 100%
rename from runtime/CSharp/Atn/TransitionType.cs
rename to runtime/CSharp/src/Atn/TransitionType.cs
diff --git a/runtime/CSharp/Atn/WildcardTransition.cs b/runtime/CSharp/src/Atn/WildcardTransition.cs
similarity index 100%
rename from runtime/CSharp/Atn/WildcardTransition.cs
rename to runtime/CSharp/src/Atn/WildcardTransition.cs
diff --git a/runtime/CSharp/BailErrorStrategy.cs b/runtime/CSharp/src/BailErrorStrategy.cs
similarity index 100%
rename from runtime/CSharp/BailErrorStrategy.cs
rename to runtime/CSharp/src/BailErrorStrategy.cs
diff --git a/runtime/CSharp/BaseErrorListener.cs b/runtime/CSharp/src/BaseErrorListener.cs
similarity index 100%
rename from runtime/CSharp/BaseErrorListener.cs
rename to runtime/CSharp/src/BaseErrorListener.cs
diff --git a/runtime/CSharp/BufferedTokenStream.cs b/runtime/CSharp/src/BufferedTokenStream.cs
similarity index 100%
rename from runtime/CSharp/BufferedTokenStream.cs
rename to runtime/CSharp/src/BufferedTokenStream.cs
diff --git a/runtime/CSharp/CharStreams.cs b/runtime/CSharp/src/CharStreams.cs
similarity index 100%
rename from runtime/CSharp/CharStreams.cs
rename to runtime/CSharp/src/CharStreams.cs
diff --git a/runtime/CSharp/CommonToken.cs b/runtime/CSharp/src/CommonToken.cs
similarity index 100%
rename from runtime/CSharp/CommonToken.cs
rename to runtime/CSharp/src/CommonToken.cs
diff --git a/runtime/CSharp/CommonTokenFactory.cs b/runtime/CSharp/src/CommonTokenFactory.cs
similarity index 100%
rename from runtime/CSharp/CommonTokenFactory.cs
rename to runtime/CSharp/src/CommonTokenFactory.cs
diff --git a/runtime/CSharp/CommonTokenStream.cs b/runtime/CSharp/src/CommonTokenStream.cs
similarity index 100%
rename from runtime/CSharp/CommonTokenStream.cs
rename to runtime/CSharp/src/CommonTokenStream.cs
diff --git a/runtime/CSharp/ConsoleErrorListener.cs b/runtime/CSharp/src/ConsoleErrorListener.cs
similarity index 100%
rename from runtime/CSharp/ConsoleErrorListener.cs
rename to runtime/CSharp/src/ConsoleErrorListener.cs
diff --git a/runtime/CSharp/DefaultErrorStrategy.cs b/runtime/CSharp/src/DefaultErrorStrategy.cs
similarity index 100%
rename from runtime/CSharp/DefaultErrorStrategy.cs
rename to runtime/CSharp/src/DefaultErrorStrategy.cs
diff --git a/runtime/CSharp/Dependents.cs b/runtime/CSharp/src/Dependents.cs
similarity index 100%
rename from runtime/CSharp/Dependents.cs
rename to runtime/CSharp/src/Dependents.cs
diff --git a/runtime/CSharp/Dfa/AbstractEdgeMap.cs b/runtime/CSharp/src/Dfa/AbstractEdgeMap.cs
similarity index 100%
rename from runtime/CSharp/Dfa/AbstractEdgeMap.cs
rename to runtime/CSharp/src/Dfa/AbstractEdgeMap.cs
diff --git a/runtime/CSharp/Dfa/AcceptStateInfo.cs b/runtime/CSharp/src/Dfa/AcceptStateInfo.cs
similarity index 100%
rename from runtime/CSharp/Dfa/AcceptStateInfo.cs
rename to runtime/CSharp/src/Dfa/AcceptStateInfo.cs
diff --git a/runtime/CSharp/Dfa/ArrayEdgeMap.cs b/runtime/CSharp/src/Dfa/ArrayEdgeMap.cs
similarity index 100%
rename from runtime/CSharp/Dfa/ArrayEdgeMap.cs
rename to runtime/CSharp/src/Dfa/ArrayEdgeMap.cs
diff --git a/runtime/CSharp/Dfa/DFA.cs b/runtime/CSharp/src/Dfa/DFA.cs
similarity index 100%
rename from runtime/CSharp/Dfa/DFA.cs
rename to runtime/CSharp/src/Dfa/DFA.cs
diff --git a/runtime/CSharp/Dfa/DFASerializer.cs b/runtime/CSharp/src/Dfa/DFASerializer.cs
similarity index 100%
rename from runtime/CSharp/Dfa/DFASerializer.cs
rename to runtime/CSharp/src/Dfa/DFASerializer.cs
diff --git a/runtime/CSharp/Dfa/DFAState.cs b/runtime/CSharp/src/Dfa/DFAState.cs
similarity index 100%
rename from runtime/CSharp/Dfa/DFAState.cs
rename to runtime/CSharp/src/Dfa/DFAState.cs
diff --git a/runtime/CSharp/Dfa/EmptyEdgeMap.cs b/runtime/CSharp/src/Dfa/EmptyEdgeMap.cs
similarity index 100%
rename from runtime/CSharp/Dfa/EmptyEdgeMap.cs
rename to runtime/CSharp/src/Dfa/EmptyEdgeMap.cs
diff --git a/runtime/CSharp/Dfa/IEdgeMap.cs b/runtime/CSharp/src/Dfa/IEdgeMap.cs
similarity index 100%
rename from runtime/CSharp/Dfa/IEdgeMap.cs
rename to runtime/CSharp/src/Dfa/IEdgeMap.cs
diff --git a/runtime/CSharp/Dfa/LexerDFASerializer.cs b/runtime/CSharp/src/Dfa/LexerDFASerializer.cs
similarity index 100%
rename from runtime/CSharp/Dfa/LexerDFASerializer.cs
rename to runtime/CSharp/src/Dfa/LexerDFASerializer.cs
diff --git a/runtime/CSharp/Dfa/SingletonEdgeMap.cs b/runtime/CSharp/src/Dfa/SingletonEdgeMap.cs
similarity index 100%
rename from runtime/CSharp/Dfa/SingletonEdgeMap.cs
rename to runtime/CSharp/src/Dfa/SingletonEdgeMap.cs
diff --git a/runtime/CSharp/Dfa/SparseEdgeMap.cs b/runtime/CSharp/src/Dfa/SparseEdgeMap.cs
similarity index 100%
rename from runtime/CSharp/Dfa/SparseEdgeMap.cs
rename to runtime/CSharp/src/Dfa/SparseEdgeMap.cs
diff --git a/runtime/CSharp/DiagnosticErrorListener.cs b/runtime/CSharp/src/DiagnosticErrorListener.cs
similarity index 100%
rename from runtime/CSharp/DiagnosticErrorListener.cs
rename to runtime/CSharp/src/DiagnosticErrorListener.cs
diff --git a/runtime/CSharp/FailedPredicateException.cs b/runtime/CSharp/src/FailedPredicateException.cs
similarity index 100%
rename from runtime/CSharp/FailedPredicateException.cs
rename to runtime/CSharp/src/FailedPredicateException.cs
diff --git a/runtime/CSharp/IAntlrErrorListener.cs b/runtime/CSharp/src/IAntlrErrorListener.cs
similarity index 100%
rename from runtime/CSharp/IAntlrErrorListener.cs
rename to runtime/CSharp/src/IAntlrErrorListener.cs
diff --git a/runtime/CSharp/IAntlrErrorStrategy.cs b/runtime/CSharp/src/IAntlrErrorStrategy.cs
similarity index 100%
rename from runtime/CSharp/IAntlrErrorStrategy.cs
rename to runtime/CSharp/src/IAntlrErrorStrategy.cs
diff --git a/runtime/CSharp/ICharStream.cs b/runtime/CSharp/src/ICharStream.cs
similarity index 100%
rename from runtime/CSharp/ICharStream.cs
rename to runtime/CSharp/src/ICharStream.cs
diff --git a/runtime/CSharp/IIntStream.cs b/runtime/CSharp/src/IIntStream.cs
similarity index 100%
rename from runtime/CSharp/IIntStream.cs
rename to runtime/CSharp/src/IIntStream.cs
diff --git a/runtime/CSharp/IParserErrorListener.cs b/runtime/CSharp/src/IParserErrorListener.cs
similarity index 100%
rename from runtime/CSharp/IParserErrorListener.cs
rename to runtime/CSharp/src/IParserErrorListener.cs
diff --git a/runtime/CSharp/IRecognizer.cs b/runtime/CSharp/src/IRecognizer.cs
similarity index 100%
rename from runtime/CSharp/IRecognizer.cs
rename to runtime/CSharp/src/IRecognizer.cs
diff --git a/runtime/CSharp/IToken.cs b/runtime/CSharp/src/IToken.cs
similarity index 100%
rename from runtime/CSharp/IToken.cs
rename to runtime/CSharp/src/IToken.cs
diff --git a/runtime/CSharp/ITokenFactory.cs b/runtime/CSharp/src/ITokenFactory.cs
similarity index 100%
rename from runtime/CSharp/ITokenFactory.cs
rename to runtime/CSharp/src/ITokenFactory.cs
diff --git a/runtime/CSharp/ITokenSource.cs b/runtime/CSharp/src/ITokenSource.cs
similarity index 100%
rename from runtime/CSharp/ITokenSource.cs
rename to runtime/CSharp/src/ITokenSource.cs
diff --git a/runtime/CSharp/ITokenStream.cs b/runtime/CSharp/src/ITokenStream.cs
similarity index 100%
rename from runtime/CSharp/ITokenStream.cs
rename to runtime/CSharp/src/ITokenStream.cs
diff --git a/runtime/CSharp/IVocabulary.cs b/runtime/CSharp/src/IVocabulary.cs
similarity index 100%
rename from runtime/CSharp/IVocabulary.cs
rename to runtime/CSharp/src/IVocabulary.cs
diff --git a/runtime/CSharp/IWritableToken.cs b/runtime/CSharp/src/IWritableToken.cs
similarity index 100%
rename from runtime/CSharp/IWritableToken.cs
rename to runtime/CSharp/src/IWritableToken.cs
diff --git a/runtime/CSharp/InputMismatchException.cs b/runtime/CSharp/src/InputMismatchException.cs
similarity index 100%
rename from runtime/CSharp/InputMismatchException.cs
rename to runtime/CSharp/src/InputMismatchException.cs
diff --git a/runtime/CSharp/InterpreterRuleContext.cs b/runtime/CSharp/src/InterpreterRuleContext.cs
similarity index 100%
rename from runtime/CSharp/InterpreterRuleContext.cs
rename to runtime/CSharp/src/InterpreterRuleContext.cs
diff --git a/runtime/CSharp/Lexer.cs b/runtime/CSharp/src/Lexer.cs
similarity index 100%
rename from runtime/CSharp/Lexer.cs
rename to runtime/CSharp/src/Lexer.cs
diff --git a/runtime/CSharp/LexerInterpreter.cs b/runtime/CSharp/src/LexerInterpreter.cs
similarity index 100%
rename from runtime/CSharp/LexerInterpreter.cs
rename to runtime/CSharp/src/LexerInterpreter.cs
diff --git a/runtime/CSharp/LexerNoViableAltException.cs b/runtime/CSharp/src/LexerNoViableAltException.cs
similarity index 100%
rename from runtime/CSharp/LexerNoViableAltException.cs
rename to runtime/CSharp/src/LexerNoViableAltException.cs
diff --git a/runtime/CSharp/ListTokenSource.cs b/runtime/CSharp/src/ListTokenSource.cs
similarity index 100%
rename from runtime/CSharp/ListTokenSource.cs
rename to runtime/CSharp/src/ListTokenSource.cs
diff --git a/runtime/CSharp/Misc/Args.cs b/runtime/CSharp/src/Misc/Args.cs
similarity index 100%
rename from runtime/CSharp/Misc/Args.cs
rename to runtime/CSharp/src/Misc/Args.cs
diff --git a/runtime/CSharp/Misc/ArrayList.cs b/runtime/CSharp/src/Misc/ArrayList.cs
similarity index 100%
rename from runtime/CSharp/Misc/ArrayList.cs
rename to runtime/CSharp/src/Misc/ArrayList.cs
diff --git a/runtime/CSharp/Misc/IIntSet.cs b/runtime/CSharp/src/Misc/IIntSet.cs
similarity index 100%
rename from runtime/CSharp/Misc/IIntSet.cs
rename to runtime/CSharp/src/Misc/IIntSet.cs
diff --git a/runtime/CSharp/Misc/Interval.cs b/runtime/CSharp/src/Misc/Interval.cs
similarity index 100%
rename from runtime/CSharp/Misc/Interval.cs
rename to runtime/CSharp/src/Misc/Interval.cs
diff --git a/runtime/CSharp/Misc/IntervalSet.cs b/runtime/CSharp/src/Misc/IntervalSet.cs
similarity index 100%
rename from runtime/CSharp/Misc/IntervalSet.cs
rename to runtime/CSharp/src/Misc/IntervalSet.cs
diff --git a/runtime/CSharp/Misc/MultiMap.cs b/runtime/CSharp/src/Misc/MultiMap.cs
similarity index 100%
rename from runtime/CSharp/Misc/MultiMap.cs
rename to runtime/CSharp/src/Misc/MultiMap.cs
diff --git a/runtime/CSharp/Misc/MurmurHash.cs b/runtime/CSharp/src/Misc/MurmurHash.cs
similarity index 100%
rename from runtime/CSharp/Misc/MurmurHash.cs
rename to runtime/CSharp/src/Misc/MurmurHash.cs
diff --git a/runtime/CSharp/Misc/NotNullAttribute.cs b/runtime/CSharp/src/Misc/NotNullAttribute.cs
similarity index 100%
rename from runtime/CSharp/Misc/NotNullAttribute.cs
rename to runtime/CSharp/src/Misc/NotNullAttribute.cs
diff --git a/runtime/CSharp/Misc/NullableAttribute.cs b/runtime/CSharp/src/Misc/NullableAttribute.cs
similarity index 100%
rename from runtime/CSharp/Misc/NullableAttribute.cs
rename to runtime/CSharp/src/Misc/NullableAttribute.cs
diff --git a/runtime/CSharp/Misc/Pair.cs b/runtime/CSharp/src/Misc/Pair.cs
similarity index 100%
rename from runtime/CSharp/Misc/Pair.cs
rename to runtime/CSharp/src/Misc/Pair.cs
diff --git a/runtime/CSharp/Misc/ParseCanceledException.cs b/runtime/CSharp/src/Misc/ParseCanceledException.cs
similarity index 100%
rename from runtime/CSharp/Misc/ParseCanceledException.cs
rename to runtime/CSharp/src/Misc/ParseCanceledException.cs
diff --git a/runtime/CSharp/Misc/RuleDependencyChecker.cs b/runtime/CSharp/src/Misc/RuleDependencyChecker.cs
similarity index 100%
rename from runtime/CSharp/Misc/RuleDependencyChecker.cs
rename to runtime/CSharp/src/Misc/RuleDependencyChecker.cs
diff --git a/runtime/CSharp/Misc/Utils.cs b/runtime/CSharp/src/Misc/Utils.cs
similarity index 100%
rename from runtime/CSharp/Misc/Utils.cs
rename to runtime/CSharp/src/Misc/Utils.cs
diff --git a/runtime/CSharp/NoViableAltException.cs b/runtime/CSharp/src/NoViableAltException.cs
similarity index 100%
rename from runtime/CSharp/NoViableAltException.cs
rename to runtime/CSharp/src/NoViableAltException.cs
diff --git a/runtime/CSharp/Parser.cs b/runtime/CSharp/src/Parser.cs
similarity index 100%
rename from runtime/CSharp/Parser.cs
rename to runtime/CSharp/src/Parser.cs
diff --git a/runtime/CSharp/ParserInterpreter.cs b/runtime/CSharp/src/ParserInterpreter.cs
similarity index 100%
rename from runtime/CSharp/ParserInterpreter.cs
rename to runtime/CSharp/src/ParserInterpreter.cs
diff --git a/runtime/CSharp/ParserRuleContext.cs b/runtime/CSharp/src/ParserRuleContext.cs
similarity index 100%
rename from runtime/CSharp/ParserRuleContext.cs
rename to runtime/CSharp/src/ParserRuleContext.cs
diff --git a/runtime/CSharp/Properties/AssemblyInfo.cs b/runtime/CSharp/src/Properties/AssemblyInfo.cs
similarity index 100%
rename from runtime/CSharp/Properties/AssemblyInfo.cs
rename to runtime/CSharp/src/Properties/AssemblyInfo.cs
diff --git a/runtime/CSharp/ProxyErrorListener.cs b/runtime/CSharp/src/ProxyErrorListener.cs
similarity index 100%
rename from runtime/CSharp/ProxyErrorListener.cs
rename to runtime/CSharp/src/ProxyErrorListener.cs
diff --git a/runtime/CSharp/ProxyParserErrorListener.cs b/runtime/CSharp/src/ProxyParserErrorListener.cs
similarity index 100%
rename from runtime/CSharp/ProxyParserErrorListener.cs
rename to runtime/CSharp/src/ProxyParserErrorListener.cs
diff --git a/runtime/CSharp/README.md b/runtime/CSharp/src/README.md
similarity index 100%
rename from runtime/CSharp/README.md
rename to runtime/CSharp/src/README.md
diff --git a/runtime/CSharp/RecognitionException.cs b/runtime/CSharp/src/RecognitionException.cs
similarity index 100%
rename from runtime/CSharp/RecognitionException.cs
rename to runtime/CSharp/src/RecognitionException.cs
diff --git a/runtime/CSharp/Recognizer.cs b/runtime/CSharp/src/Recognizer.cs
similarity index 100%
rename from runtime/CSharp/Recognizer.cs
rename to runtime/CSharp/src/Recognizer.cs
diff --git a/runtime/CSharp/RuleContext.cs b/runtime/CSharp/src/RuleContext.cs
similarity index 100%
rename from runtime/CSharp/RuleContext.cs
rename to runtime/CSharp/src/RuleContext.cs
diff --git a/runtime/CSharp/RuleDependencyAttribute.cs b/runtime/CSharp/src/RuleDependencyAttribute.cs
similarity index 100%
rename from runtime/CSharp/RuleDependencyAttribute.cs
rename to runtime/CSharp/src/RuleDependencyAttribute.cs
diff --git a/runtime/CSharp/RuleVersionAttribute.cs b/runtime/CSharp/src/RuleVersionAttribute.cs
similarity index 100%
rename from runtime/CSharp/RuleVersionAttribute.cs
rename to runtime/CSharp/src/RuleVersionAttribute.cs
diff --git a/runtime/CSharp/Sharpen/Arrays.cs b/runtime/CSharp/src/Sharpen/Arrays.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/Arrays.cs
rename to runtime/CSharp/src/Sharpen/Arrays.cs
diff --git a/runtime/CSharp/Sharpen/AtomicReference.cs b/runtime/CSharp/src/Sharpen/AtomicReference.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/AtomicReference.cs
rename to runtime/CSharp/src/Sharpen/AtomicReference.cs
diff --git a/runtime/CSharp/Sharpen/BitSet.cs b/runtime/CSharp/src/Sharpen/BitSet.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/BitSet.cs
rename to runtime/CSharp/src/Sharpen/BitSet.cs
diff --git a/runtime/CSharp/Sharpen/Collections.cs b/runtime/CSharp/src/Sharpen/Collections.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/Collections.cs
rename to runtime/CSharp/src/Sharpen/Collections.cs
diff --git a/runtime/CSharp/Sharpen/DictionaryExtensions.cs b/runtime/CSharp/src/Sharpen/DictionaryExtensions.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/DictionaryExtensions.cs
rename to runtime/CSharp/src/Sharpen/DictionaryExtensions.cs
diff --git a/runtime/CSharp/Sharpen/ListExtensions.cs b/runtime/CSharp/src/Sharpen/ListExtensions.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/ListExtensions.cs
rename to runtime/CSharp/src/Sharpen/ListExtensions.cs
diff --git a/runtime/CSharp/Sharpen/Runtime.cs b/runtime/CSharp/src/Sharpen/Runtime.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/Runtime.cs
rename to runtime/CSharp/src/Sharpen/Runtime.cs
diff --git a/runtime/CSharp/Sharpen/SequenceEqualityComparer.cs b/runtime/CSharp/src/Sharpen/SequenceEqualityComparer.cs
similarity index 100%
rename from runtime/CSharp/Sharpen/SequenceEqualityComparer.cs
rename to runtime/CSharp/src/Sharpen/SequenceEqualityComparer.cs
diff --git a/runtime/CSharp/TokenStreamRewriter.cs b/runtime/CSharp/src/TokenStreamRewriter.cs
similarity index 100%
rename from runtime/CSharp/TokenStreamRewriter.cs
rename to runtime/CSharp/src/TokenStreamRewriter.cs
diff --git a/runtime/CSharp/TokenTypes.cs b/runtime/CSharp/src/TokenTypes.cs
similarity index 100%
rename from runtime/CSharp/TokenTypes.cs
rename to runtime/CSharp/src/TokenTypes.cs
diff --git a/runtime/CSharp/Tree/AbstractParseTreeVisitor.cs b/runtime/CSharp/src/Tree/AbstractParseTreeVisitor.cs
similarity index 100%
rename from runtime/CSharp/Tree/AbstractParseTreeVisitor.cs
rename to runtime/CSharp/src/Tree/AbstractParseTreeVisitor.cs
diff --git a/runtime/CSharp/Tree/ErrorNodeImpl.cs b/runtime/CSharp/src/Tree/ErrorNodeImpl.cs
similarity index 100%
rename from runtime/CSharp/Tree/ErrorNodeImpl.cs
rename to runtime/CSharp/src/Tree/ErrorNodeImpl.cs
diff --git a/runtime/CSharp/Tree/IErrorNode.cs b/runtime/CSharp/src/Tree/IErrorNode.cs
similarity index 100%
rename from runtime/CSharp/Tree/IErrorNode.cs
rename to runtime/CSharp/src/Tree/IErrorNode.cs
diff --git a/runtime/CSharp/Tree/IParseTree.cs b/runtime/CSharp/src/Tree/IParseTree.cs
similarity index 100%
rename from runtime/CSharp/Tree/IParseTree.cs
rename to runtime/CSharp/src/Tree/IParseTree.cs
diff --git a/runtime/CSharp/Tree/IParseTreeListener.cs b/runtime/CSharp/src/Tree/IParseTreeListener.cs
similarity index 100%
rename from runtime/CSharp/Tree/IParseTreeListener.cs
rename to runtime/CSharp/src/Tree/IParseTreeListener.cs
diff --git a/runtime/CSharp/Tree/IParseTreeVisitor.cs b/runtime/CSharp/src/Tree/IParseTreeVisitor.cs
similarity index 100%
rename from runtime/CSharp/Tree/IParseTreeVisitor.cs
rename to runtime/CSharp/src/Tree/IParseTreeVisitor.cs
diff --git a/runtime/CSharp/Tree/IRuleNode.cs b/runtime/CSharp/src/Tree/IRuleNode.cs
similarity index 100%
rename from runtime/CSharp/Tree/IRuleNode.cs
rename to runtime/CSharp/src/Tree/IRuleNode.cs
diff --git a/runtime/CSharp/Tree/ISyntaxTree.cs b/runtime/CSharp/src/Tree/ISyntaxTree.cs
similarity index 100%
rename from runtime/CSharp/Tree/ISyntaxTree.cs
rename to runtime/CSharp/src/Tree/ISyntaxTree.cs
diff --git a/runtime/CSharp/Tree/ITerminalNode.cs b/runtime/CSharp/src/Tree/ITerminalNode.cs
similarity index 100%
rename from runtime/CSharp/Tree/ITerminalNode.cs
rename to runtime/CSharp/src/Tree/ITerminalNode.cs
diff --git a/runtime/CSharp/Tree/ITree.cs b/runtime/CSharp/src/Tree/ITree.cs
similarity index 100%
rename from runtime/CSharp/Tree/ITree.cs
rename to runtime/CSharp/src/Tree/ITree.cs
diff --git a/runtime/CSharp/Tree/ParseTreeProperty.cs b/runtime/CSharp/src/Tree/ParseTreeProperty.cs
similarity index 100%
rename from runtime/CSharp/Tree/ParseTreeProperty.cs
rename to runtime/CSharp/src/Tree/ParseTreeProperty.cs
diff --git a/runtime/CSharp/Tree/ParseTreeWalker.cs b/runtime/CSharp/src/Tree/ParseTreeWalker.cs
similarity index 100%
rename from runtime/CSharp/Tree/ParseTreeWalker.cs
rename to runtime/CSharp/src/Tree/ParseTreeWalker.cs
diff --git a/runtime/CSharp/Tree/Pattern/Chunk.cs b/runtime/CSharp/src/Tree/Pattern/Chunk.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/Chunk.cs
rename to runtime/CSharp/src/Tree/Pattern/Chunk.cs
diff --git a/runtime/CSharp/Tree/Pattern/ParseTreeMatch.cs b/runtime/CSharp/src/Tree/Pattern/ParseTreeMatch.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/ParseTreeMatch.cs
rename to runtime/CSharp/src/Tree/Pattern/ParseTreeMatch.cs
diff --git a/runtime/CSharp/Tree/Pattern/ParseTreePattern.cs b/runtime/CSharp/src/Tree/Pattern/ParseTreePattern.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/ParseTreePattern.cs
rename to runtime/CSharp/src/Tree/Pattern/ParseTreePattern.cs
diff --git a/runtime/CSharp/Tree/Pattern/ParseTreePatternMatcher.cs b/runtime/CSharp/src/Tree/Pattern/ParseTreePatternMatcher.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/ParseTreePatternMatcher.cs
rename to runtime/CSharp/src/Tree/Pattern/ParseTreePatternMatcher.cs
diff --git a/runtime/CSharp/Tree/Pattern/RuleTagToken.cs b/runtime/CSharp/src/Tree/Pattern/RuleTagToken.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/RuleTagToken.cs
rename to runtime/CSharp/src/Tree/Pattern/RuleTagToken.cs
diff --git a/runtime/CSharp/Tree/Pattern/TagChunk.cs b/runtime/CSharp/src/Tree/Pattern/TagChunk.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/TagChunk.cs
rename to runtime/CSharp/src/Tree/Pattern/TagChunk.cs
diff --git a/runtime/CSharp/Tree/Pattern/TextChunk.cs b/runtime/CSharp/src/Tree/Pattern/TextChunk.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/TextChunk.cs
rename to runtime/CSharp/src/Tree/Pattern/TextChunk.cs
diff --git a/runtime/CSharp/Tree/Pattern/TokenTagToken.cs b/runtime/CSharp/src/Tree/Pattern/TokenTagToken.cs
similarity index 100%
rename from runtime/CSharp/Tree/Pattern/TokenTagToken.cs
rename to runtime/CSharp/src/Tree/Pattern/TokenTagToken.cs
diff --git a/runtime/CSharp/Tree/TerminalNodeImpl.cs b/runtime/CSharp/src/Tree/TerminalNodeImpl.cs
similarity index 100%
rename from runtime/CSharp/Tree/TerminalNodeImpl.cs
rename to runtime/CSharp/src/Tree/TerminalNodeImpl.cs
diff --git a/runtime/CSharp/Tree/Trees.cs b/runtime/CSharp/src/Tree/Trees.cs
similarity index 100%
rename from runtime/CSharp/Tree/Trees.cs
rename to runtime/CSharp/src/Tree/Trees.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPath.cs b/runtime/CSharp/src/Tree/Xpath/XPath.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPath.cs
rename to runtime/CSharp/src/Tree/Xpath/XPath.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathElement.cs b/runtime/CSharp/src/Tree/Xpath/XPathElement.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathElement.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathElement.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathLexer.cs b/runtime/CSharp/src/Tree/Xpath/XPathLexer.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathLexer.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathLexer.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathLexer.g4 b/runtime/CSharp/src/Tree/Xpath/XPathLexer.g4
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathLexer.g4
rename to runtime/CSharp/src/Tree/Xpath/XPathLexer.g4
diff --git a/runtime/CSharp/Tree/Xpath/XPathLexer.tokens b/runtime/CSharp/src/Tree/Xpath/XPathLexer.tokens
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathLexer.tokens
rename to runtime/CSharp/src/Tree/Xpath/XPathLexer.tokens
diff --git a/runtime/CSharp/Tree/Xpath/XPathLexerErrorListener.cs b/runtime/CSharp/src/Tree/Xpath/XPathLexerErrorListener.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathLexerErrorListener.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathLexerErrorListener.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathRuleAnywhereElement.cs b/runtime/CSharp/src/Tree/Xpath/XPathRuleAnywhereElement.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathRuleAnywhereElement.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathRuleAnywhereElement.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathRuleElement.cs b/runtime/CSharp/src/Tree/Xpath/XPathRuleElement.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathRuleElement.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathRuleElement.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathTokenAnywhereElement.cs b/runtime/CSharp/src/Tree/Xpath/XPathTokenAnywhereElement.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathTokenAnywhereElement.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathTokenAnywhereElement.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathTokenElement.cs b/runtime/CSharp/src/Tree/Xpath/XPathTokenElement.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathTokenElement.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathTokenElement.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathWildcardAnywhereElement.cs b/runtime/CSharp/src/Tree/Xpath/XPathWildcardAnywhereElement.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathWildcardAnywhereElement.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathWildcardAnywhereElement.cs
diff --git a/runtime/CSharp/Tree/Xpath/XPathWildcardElement.cs b/runtime/CSharp/src/Tree/Xpath/XPathWildcardElement.cs
similarity index 100%
rename from runtime/CSharp/Tree/Xpath/XPathWildcardElement.cs
rename to runtime/CSharp/src/Tree/Xpath/XPathWildcardElement.cs
diff --git a/runtime/CSharp/UnbufferedCharStream.cs b/runtime/CSharp/src/UnbufferedCharStream.cs
similarity index 100%
rename from runtime/CSharp/UnbufferedCharStream.cs
rename to runtime/CSharp/src/UnbufferedCharStream.cs
diff --git a/runtime/CSharp/UnbufferedTokenStream.cs b/runtime/CSharp/src/UnbufferedTokenStream.cs
similarity index 100%
rename from runtime/CSharp/UnbufferedTokenStream.cs
rename to runtime/CSharp/src/UnbufferedTokenStream.cs
diff --git a/runtime/CSharp/Vocabulary.cs b/runtime/CSharp/src/Vocabulary.cs
similarity index 100%
rename from runtime/CSharp/Vocabulary.cs
rename to runtime/CSharp/src/Vocabulary.cs
diff --git a/runtime/CSharp/tests/issue-2693/ErrorListener.cs b/runtime/CSharp/tests/issue-2693/ErrorListener.cs
new file mode 100644
index 000000000..1d610a3b5
--- /dev/null
+++ b/runtime/CSharp/tests/issue-2693/ErrorListener.cs
@@ -0,0 +1,18 @@
+using Antlr4.Runtime;
+using Antlr4.Runtime.Misc;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+public class ErrorListener : ConsoleErrorListener
+{
+ public bool had_error;
+
+ public override void SyntaxError(TextWriter output, IRecognizer recognizer, S offendingSymbol, int line,
+ int col, string msg, RecognitionException e)
+ {
+ had_error = true;
+ base.SyntaxError(output, recognizer, offendingSymbol, line, col, msg, e);
+ }
+}
diff --git a/runtime/CSharp/tests/issue-2693/Program.cs b/runtime/CSharp/tests/issue-2693/Program.cs
new file mode 100644
index 000000000..a7b3efdb0
--- /dev/null
+++ b/runtime/CSharp/tests/issue-2693/Program.cs
@@ -0,0 +1,90 @@
+using Antlr4.Runtime;
+using System;
+using System.Linq;
+using System.Text;
+
+public class Program
+{
+ static void Main(string[] args)
+ {
+ bool show_tree = false;
+ bool show_tokens = false;
+ string file_name = null;
+ string input = null;
+ for (int i = 0; i < args.Length; ++i)
+ {
+ if (args[i].Equals("-tokens"))
+ {
+ show_tokens = true;
+ continue;
+ }
+ else if (args[i].Equals("-tree"))
+ {
+ show_tree = true;
+ continue;
+ }
+ else if (args[i].Equals("-input"))
+ input = args[i];
+ else if (args[i].Equals("-file"))
+ file_name = args[++i];
+ }
+ ICharStream str = null;
+ if (input == null && file_name == null)
+ {
+ StringBuilder sb = new StringBuilder();
+ int ch;
+ while ((ch = System.Console.Read()) != -1)
+ {
+ sb.Append((char)ch);
+ }
+ input = sb.ToString();
+ str = CharStreams.fromString(input);
+ }
+ else if (input != null)
+ {
+ str = CharStreams.fromString(input);
+ }
+ else if (file_name != null)
+ {
+ str = CharStreams.fromPath(file_name);
+ }
+ var lexer = new asm8080Lexer(str);
+ if (show_tokens)
+ {
+ StringBuilder new_s = new StringBuilder();
+ for (int i = 0; ; ++i)
+ {
+ var ro_token = lexer.NextToken();
+ var token = (CommonToken)ro_token;
+ token.TokenIndex = i;
+ new_s.AppendLine(token.ToString());
+ if (token.Type == Antlr4.Runtime.TokenConstants.EOF)
+ break;
+ }
+ System.Console.Error.WriteLine(new_s.ToString());
+ }
+ lexer.Reset();
+ var tokens = new CommonTokenStream(lexer);
+ var parser = new asm8080Parser(tokens);
+ var listener_lexer = new ErrorListener();
+ var listener_parser = new ErrorListener();
+ lexer.AddErrorListener(listener_lexer);
+ parser.AddErrorListener(listener_parser);
+ parser.Profile = true;
+ var tree = parser.prog();
+ if (listener_lexer.had_error || listener_parser.had_error)
+ {
+ System.Console.Error.WriteLine("parse failed.");
+ }
+ else
+ {
+ System.Console.Error.WriteLine("parse succeeded.");
+ }
+ if (show_tree)
+ {
+ System.Console.Error.WriteLine(tree.ToStringTree());
+ }
+ System.Console.Out.WriteLine(String.Join(", ", parser.ParseInfo.getDecisionInfo().Select(d => d.ToString())));
+ System.Environment.Exit(listener_lexer.had_error || listener_parser.had_error ? 1 : 0);
+ }
+}
diff --git a/runtime/CSharp/tests/issue-2693/Test.csproj b/runtime/CSharp/tests/issue-2693/Test.csproj
new file mode 100644
index 000000000..33b85beef
--- /dev/null
+++ b/runtime/CSharp/tests/issue-2693/Test.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net5.0
+ Exe
+
+
+
+
+
+
+
+ ../../../../tool/target/antlr4-*-SNAPSHOT-complete.jar
+
+
+
+
+
+
+
+
+ PackageReference
+
+
+ 1701;1702;3021
+
+
diff --git a/runtime/CSharp/tests/issue-2693/TreeOutput.cs b/runtime/CSharp/tests/issue-2693/TreeOutput.cs
new file mode 100644
index 000000000..297504946
--- /dev/null
+++ b/runtime/CSharp/tests/issue-2693/TreeOutput.cs
@@ -0,0 +1,108 @@
+
+// Template generated code from Antlr4BuildTasks.dotnet-antlr v 1.3
+
+using Antlr4.Runtime;
+using Antlr4.Runtime.Misc;
+using Antlr4.Runtime.Tree;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+public class TreeOutput
+{
+ private static int changed = 0;
+ private static bool first_time = true;
+
+ public static StringBuilder OutputTree(IParseTree tree, Lexer lexer, Parser parser, CommonTokenStream stream)
+ {
+ changed = 0;
+ first_time = true;
+ var sb = new StringBuilder();
+ ParenthesizedAST(tree, sb, lexer, parser, stream);
+ return sb;
+ }
+
+ private static void ParenthesizedAST(IParseTree tree, StringBuilder sb, Lexer lexer, Parser parser, CommonTokenStream stream, int level = 0)
+ {
+ if (tree as TerminalNodeImpl != null)
+ {
+ TerminalNodeImpl tok = tree as TerminalNodeImpl;
+ Interval interval = tok.SourceInterval;
+ IList inter = null;
+ if (tok.Symbol.TokenIndex >= 0)
+ inter = stream?.GetHiddenTokensToLeft(tok.Symbol.TokenIndex);
+ if (inter != null)
+ foreach (var t in inter)
+ {
+ var ty = tok.Symbol.Type;
+ var name = lexer.Vocabulary.GetSymbolicName(ty);
+ StartLine(sb, level);
+ sb.AppendLine("(" + name + " text = " + PerformEscapes(t.Text) + " " + lexer.ChannelNames[t.Channel]);
+ }
+ {
+ var ty = tok.Symbol.Type;
+ var name = lexer.Vocabulary.GetSymbolicName(ty);
+ StartLine(sb, level);
+ sb.AppendLine("( " + name + " i =" + tree.SourceInterval.a
+ + " txt =" + PerformEscapes(tree.GetText())
+ + " tt =" + tok.Symbol.Type
+ + " " + lexer.ChannelNames[tok.Symbol.Channel]);
+ }
+ }
+ else
+ {
+ var x = tree as RuleContext;
+ var ri = x.RuleIndex;
+ var name = parser.RuleNames[ri];
+ StartLine(sb, level);
+ sb.Append("( " + name);
+ sb.AppendLine();
+ }
+ for (int i = 0; i= 0)
+ {
+ if (!first_time)
+ {
+ for (int j = 0; j < level; ++j) sb.Append(" ");
+ for (int k = 0; k < 1 + changed - level; ++k) sb.Append(") ");
+ sb.AppendLine();
+ }
+ changed = 0;
+ first_time = false;
+ }
+ changed = level;
+ for (int j = 0; j < level; ++j) sb.Append(" ");
+ }
+
+ private static string ToLiteral(string input)
+ {
+ using (var writer = new StringWriter())
+ {
+ var literal = input;
+ literal = literal.Replace("\\", "\\\\");
+ return literal;
+ }
+ }
+
+ public static string PerformEscapes(string s)
+ {
+ StringBuilder new_s = new StringBuilder();
+ new_s.Append(ToLiteral(s));
+ return new_s.ToString();
+ }
+}
diff --git a/runtime/CSharp/tests/issue-2693/asm8080.g4 b/runtime/CSharp/tests/issue-2693/asm8080.g4
new file mode 100644
index 000000000..6f22bd08d
--- /dev/null
+++ b/runtime/CSharp/tests/issue-2693/asm8080.g4
@@ -0,0 +1,288 @@
+/*
+BSD License
+
+Copyright (c) 2018, Tom Everett
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of Tom Everett nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*
+* http://fms.komkon.org/comp/CPUs/8080.txt
+*/
+
+grammar asm8080;
+
+prog
+ : (line? EOL) +
+ ;
+
+line
+ : lbl? (instruction | directive)? comment?
+ ;
+
+instruction
+ : opcode expressionlist?
+ ;
+
+opcode
+ : OPCODE
+ ;
+
+register_
+ : REGISTER
+ ;
+
+directive
+ : argument? assemblerdirective expressionlist
+ ;
+
+assemblerdirective
+ : ASSEMBLER_DIRECTIVE
+ ;
+
+lbl
+ : label ':'?
+ ;
+
+expressionlist
+ : expression (',' expression)*
+ ;
+
+label
+ : name
+ ;
+
+expression
+ : multiplyingExpression (('+' | '-') multiplyingExpression)*
+ ;
+
+multiplyingExpression
+ : argument (('*' | '/') argument)*
+ ;
+
+argument
+ : number
+ | register_
+ | dollar
+ | name
+ | string
+ | ('(' expression ')')
+ ;
+
+dollar
+ : '$'
+ ;
+
+string
+ : STRING
+ ;
+
+name
+ : NAME
+ ;
+
+number
+ : NUMBER
+ ;
+
+comment
+ : COMMENT
+ ;
+
+
+ASSEMBLER_DIRECTIVE
+ : (O R G) | (E N D) | (E Q U) | (D B) | (D W) | (D S) | (I F) | (E N D I F) | (S E T)
+ ;
+
+
+REGISTER
+ : 'A' | 'B' | 'C' | 'D' | 'E' | 'H' | 'L' | 'PC' | 'SP'
+ ;
+
+
+OPCODE
+ : (M O V) | (M V I) | (L D A) | (S T A) | (L D A X) | (S T A X) | (L H L D) | (S H L D) | (L X I) | (P U S H) | (P O P) | (X T H L) | (S P H L) | (P C H L) | (X C H G) | (A D D) | (S U B) | (I N R) | (D C R) | (C M P) | (A N A) | (O R A) | (X R A) | (A D I) | (S U I) | (C P I) | (A N I) | (O R I) | (X R I) | (D A A) | (A D C) | (A C I) | (S B B) | (S B I) | (D A D) | (I N X) | (D C X) | (J M P) | (C A L L) | (R E T) | (R A L) | (R A R) | (R L C) | (R R C) | (I N) | (O U T) | (C M C) | (S T C) | (C M A) | (H L T) | (N O P) | (D I) | (E I) | (R S T) | (J N Z) | (J Z) | (J N C) | (J C) | (J P O) | (J P E) | (J P) | (J M) | (C N Z) | (C Z) | (C N C) | (C C) | (C P O) | (C P E) | (C P) | (C M) | (R N Z) | (R Z) | (R N C) | (R C) | (R P O) | (R P E) | (R P) | (R M)
+ ;
+
+
+fragment A
+ : ('a' | 'A')
+ ;
+
+
+fragment B
+ : ('b' | 'B')
+ ;
+
+
+fragment C
+ : ('c' | 'C')
+ ;
+
+
+fragment D
+ : ('d' | 'D')
+ ;
+
+
+fragment E
+ : ('e' | 'E')
+ ;
+
+
+fragment F
+ : ('f' | 'F')
+ ;
+
+
+fragment G
+ : ('g' | 'G')
+ ;
+
+
+fragment H
+ : ('h' | 'H')
+ ;
+
+
+fragment I
+ : ('i' | 'I')
+ ;
+
+
+fragment J
+ : ('j' | 'J')
+ ;
+
+
+fragment K
+ : ('k' | 'K')
+ ;
+
+
+fragment L
+ : ('l' | 'L')
+ ;
+
+
+fragment M
+ : ('m' | 'M')
+ ;
+
+
+fragment N
+ : ('n' | 'N')
+ ;
+
+
+fragment O
+ : ('o' | 'O')
+ ;
+
+
+fragment P
+ : ('p' | 'P')
+ ;
+
+
+fragment Q
+ : ('q' | 'Q')
+ ;
+
+
+fragment R
+ : ('r' | 'R')
+ ;
+
+
+fragment S
+ : ('s' | 'S')
+ ;
+
+
+fragment T
+ : ('t' | 'T')
+ ;
+
+
+fragment U
+ : ('u' | 'U')
+ ;
+
+
+fragment V
+ : ('v' | 'V')
+ ;
+
+
+fragment W
+ : ('w' | 'W')
+ ;
+
+
+fragment X
+ : ('x' | 'X')
+ ;
+
+
+fragment Y
+ : ('y' | 'Y')
+ ;
+
+
+fragment Z
+ : ('z' | 'Z')
+ ;
+
+
+NAME
+ : [a-zA-Z] [a-zA-Z0-9."]*
+ ;
+
+
+NUMBER
+ : '$'? [0-9a-fA-F] + ('H' | 'h')?
+ ;
+
+
+COMMENT
+ : ';' ~ [\r\n]* -> skip
+ ;
+
+
+STRING
+ : '\u0027' ~'\u0027'* '\u0027'
+ ;
+
+
+EOL
+ : [\r\n] +
+ ;
+
+
+WS
+ : [ \t] -> skip
+ ;
diff --git a/runtime/CSharp/tests/issue-2693/cpm22.asm b/runtime/CSharp/tests/issue-2693/cpm22.asm
new file mode 100644
index 000000000..28cfe03f0
--- /dev/null
+++ b/runtime/CSharp/tests/issue-2693/cpm22.asm
@@ -0,0 +1,3739 @@
+
+;**************************************************************
+;*
+;* C P / M version 2 . 2
+;*
+;* Reconstructed from memory image on February 27, 1981
+;*
+;* by Clark A. Calkins
+;*
+;**************************************************************
+;
+; Set memory limit here. This is the amount of contigeous
+; ram starting from 0000. CP/M will reside at the end of this space.
+;
+MEM EQU 62 ;for a 62k system (TS802 TEST - WORKS OK).
+;
+IOBYTE EQU 3 ;i/o definition byte.
+TDRIVE EQU 4 ;current drive name and user number.
+ENTRY EQU 5 ;entry point for the cp/m bdos.
+TFCB EQU 5CH ;default file control block.
+TBUFF EQU 80H ;i/o buffer and command line storage.
+TBASE EQU 100H ;transiant program storage area.
+;
+; Set control character equates.
+;
+CNTRLC EQU 3 ;control-c
+CNTRLE EQU 05H ;control-e
+BS EQU 08H ;backspace
+TAB EQU 09H ;tab
+LF EQU 0AH ;line feed
+FF EQU 0CH ;form feed
+CR EQU 0DH ;carriage return
+CNTRLP EQU 10H ;control-p
+CNTRLR EQU 12H ;control-r
+CNTRLS EQU 13H ;control-s
+CNTRLU EQU 15H ;control-u
+CNTRLX EQU 18H ;control-x
+CNTRLZ EQU 1AH ;control-z (end-of-file mark)
+DEL EQU 7FH ;rubout
+;
+; Set origin for CP/M
+;
+ ORG (MEM-7)*1024
+;
+CBASE JMP COMMAND ;execute command processor (ccp).
+ JMP CLEARBUF ;entry to empty input buffer before starting ccp.
+
+;
+; Standard cp/m ccp input buffer. Format is (max length),
+; (actual length), (char #1), (char #2), (char #3), etc.
+;
+INBUFF DB 127 ;length of input buffer.
+ DB 0 ;current length of contents.
+ DB 'Copyright'
+ DB ' 1979 (c) by Digital Research '
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+INPOINT DW INBUFF+2;input line pointer
+NAMEPNT DW 0 ;input line pointer used for error message. Points to
+; ;start of name in error.
+;
+; Routine to print (A) on the console. All registers used.
+;
+PRINT MOV E,A ;setup bdos call.
+ MVI C,2
+ JMP ENTRY
+;
+; Routine to print (A) on the console and to save (BC).
+;
+PRINTB PUSH B
+ CALL PRINT
+ POP B
+ RET
+;
+; Routine to send a carriage return, line feed combination
+; to the console.
+;
+CRLF MVI A,CR
+ CALL PRINTB
+ MVI A,LF
+ JMP PRINTB
+;
+; Routine to send one space to the console and save (BC).
+;
+SPACE MVI A,' '
+ JMP PRINTB
+;
+; Routine to print character string pointed to be (BC) on the
+; console. It must terminate with a null byte.
+;
+PLINE PUSH B
+ CALL CRLF
+ POP H
+PLINE2 MOV A,M
+ ORA A
+ RZ
+ INX H
+ PUSH H
+ CALL PRINT
+ POP H
+ JMP PLINE2
+;
+; Routine to reset the disk system.
+;
+RESDSK MVI C,13
+ JMP ENTRY
+;
+; Routine to select disk (A).
+;
+DSKSEL MOV E,A
+ MVI C,14
+ JMP ENTRY
+;
+; Routine to call bdos and save the return code. The zero
+; flag is set on a return of 0ffh.
+;
+ENTRY1 CALL ENTRY
+ STA RTNCODE ;save return code.
+ INR A ;set zero if 0ffh returned.
+ RET
+;
+; Routine to open a file. (DE) must point to the FCB.
+;
+OPEN MVI C,15
+ JMP ENTRY1
+;
+; Routine to open file at (FCB).
+;
+OPENFCB XRA A ;clear the record number byte at fcb+32
+ STA FCB+32
+ LXI D,FCB
+ JMP OPEN
+;
+; Routine to close a file. (DE) points to FCB.
+;
+CLOSE MVI C,16
+ JMP ENTRY1
+;
+; Routine to search for the first file with ambigueous name
+; (DE).
+;
+SRCHFST MVI C,17
+ JMP ENTRY1
+;
+; Search for the next ambigeous file name.
+;
+SRCHNXT MVI C,18
+ JMP ENTRY1
+;
+; Search for file at (FCB).
+;
+SRCHFCB LXI D,FCB
+ JMP SRCHFST
+;
+; Routine to delete a file pointed to by (DE).
+;
+DELETE MVI C,19
+ JMP ENTRY
+;
+; Routine to call the bdos and set the zero flag if a zero
+; status is returned.
+;
+ENTRY2 CALL ENTRY
+ ORA A ;set zero flag if appropriate.
+ RET
+;
+; Routine to read the next record from a sequential file.
+; (DE) points to the FCB.
+;
+RDREC MVI C,20
+ JMP ENTRY2
+;
+; Routine to read file at (FCB).
+;
+READFCB LXI D,FCB
+ JMP RDREC
+;
+; Routine to write the next record of a sequential file.
+; (DE) points to the FCB.
+;
+WRTREC MVI C,21
+ JMP ENTRY2
+;
+; Routine to create the file pointed to by (DE).
+;
+CREATE MVI C,22
+ JMP ENTRY1
+;
+; Routine to rename the file pointed to by (DE). Note that
+; the new name starts at (DE+16).
+;
+RENAM MVI C,23
+ JMP ENTRY
+;
+; Get the current user code.
+;
+GETUSR MVI E,0FFH
+;
+; Routne to get or set the current user code.
+; If (E) is FF then this is a GET, else it is a SET.
+;
+GETSETUC:MVI C,32
+ JMP ENTRY
+;
+; Routine to set the current drive byte at (TDRIVE).
+;
+SETCDRV CALL GETUSR ;get user number
+ ADD A ;and shift into the upper 4 bits.
+ ADD A
+ ADD A
+ ADD A
+ LXI H,CDRIVE;now add in the current drive number.
+ ORA M
+ STA TDRIVE ;and save.
+ RET
+;
+; Move currently active drive down to (TDRIVE).
+;
+MOVECD LDA CDRIVE
+ STA TDRIVE
+ RET
+;
+; Routine to convert (A) into upper case ascii. Only letters
+; are affected.
+;
+UPPER CPI 'a' ;check for letters in the range of 'a' to 'z'.
+ RC
+ CPI '{'
+ RNC
+ ANI 5FH ;convert it if found.
+ RET
+;
+; Routine to get a line of input. We must check to see if the
+; user is in (BATCH) mode. If so, then read the input from file
+; ($$$.SUB). At the end, reset to console input.
+;
+GETINP LDA BATCH ;if =0, then use console input.
+ ORA A
+ JZ GETINP1
+;
+; Use the submit file ($$$.sub) which is prepared by a
+; SUBMIT run. It must be on drive (A) and it will be deleted
+; if and error occures (like eof).
+;
+ LDA CDRIVE ;select drive 0 if need be.
+ ORA A
+ MVI A,0 ;always use drive A for submit.
+ CNZ DSKSEL ;select it if required.
+ LXI D,BATCHFCB
+ CALL OPEN ;look for it.
+ JZ GETINP1 ;if not there, use normal input.
+ LDA BATCHFCB+15;get last record number+1.
+ DCR A
+ STA BATCHFCB+32
+ LXI D,BATCHFCB
+ CALL RDREC ;read last record.
+ JNZ GETINP1 ;quit on end of file.
+;
+; Move this record into input buffer.
+;
+ LXI D,INBUFF+1
+ LXI H,TBUFF ;data was read into buffer here.
+ MVI B,128 ;all 128 characters may be used.
+ CALL HL2DE ;(HL) to (DE), (B) bytes.
+ LXI H,BATCHFCB+14
+ MVI M,0 ;zero out the 's2' byte.
+ INX H ;and decrement the record count.
+ DCR M
+ LXI D,BATCHFCB;close the batch file now.
+ CALL CLOSE
+ JZ GETINP1 ;quit on an error.
+ LDA CDRIVE ;re-select previous drive if need be.
+ ORA A
+ CNZ DSKSEL ;don't do needless selects.
+;
+; Print line just read on console.
+;
+ LXI H,INBUFF+2
+ CALL PLINE2
+ CALL CHKCON ;check console, quit on a key.
+ JZ GETINP2 ;jump if no key is pressed.
+;
+; Terminate the submit job on any keyboard input. Delete this
+; file such that it is not re-started and jump to normal keyboard
+; input section.
+;
+ CALL DELBATCH;delete the batch file.
+ JMP CMMND1 ;and restart command input.
+;
+; Get here for normal keyboard input. Delete the submit file
+; incase there was one.
+;
+GETINP1 CALL DELBATCH;delete file ($$$.sub).
+ CALL SETCDRV ;reset active disk.
+ MVI C,10 ;get line from console device.
+ LXI D,INBUFF
+ CALL ENTRY
+ CALL MOVECD ;reset current drive (again).
+;
+; Convert input line to upper case.
+;
+GETINP2 LXI H,INBUFF+1
+ MOV B,M ;(B)=character counter.
+GETINP3 INX H
+ MOV A,B ;end of the line?
+ ORA A
+ JZ GETINP4
+ MOV A,M ;convert to upper case.
+ CALL UPPER
+ MOV M,A
+ DCR B ;adjust character count.
+ JMP GETINP3
+GETINP4 MOV M,A ;add trailing null.
+ LXI H,INBUFF+2
+ SHLD INPOINT ;reset input line pointer.
+ RET
+;
+; Routine to check the console for a key pressed. The zero
+; flag is set is none, else the character is returned in (A).
+;
+CHKCON MVI C,11 ;check console.
+ CALL ENTRY
+ ORA A
+ RZ ;return if nothing.
+ MVI C,1 ;else get character.
+ CALL ENTRY
+ ORA A ;clear zero flag and return.
+ RET
+;
+; Routine to get the currently active drive number.
+;
+GETDSK MVI C,25
+ JMP ENTRY
+;
+; Set the stabdard dma address.
+;
+STDDMA LXI D,TBUFF
+;
+; Routine to set the dma address to (DE).
+;
+DMASET MVI C,26
+ JMP ENTRY
+;
+; Delete the batch file created by SUBMIT.
+;
+DELBATCH:LXI H,BATCH ;is batch active?
+ MOV A,M
+ ORA A
+ RZ
+ MVI M,0 ;yes, de-activate it.
+ XRA A
+ CALL DSKSEL ;select drive 0 for sure.
+ LXI D,BATCHFCB;and delete this file.
+ CALL DELETE
+ LDA CDRIVE ;reset current drive.
+ JMP DSKSEL
+;
+; Check to two strings at (PATTRN1) and (PATTRN2). They must be
+; the same or we halt....
+;
+VERIFY LXI D,PATTRN1;these are the serial number bytes.
+ LXI H,PATTRN2;ditto, but how could they be different?
+ MVI B,6 ;6 bytes each.
+VERIFY1 LDAX D
+ CMP M
+ JNZ HALT ;jump to halt routine.
+ INX D
+ INX H
+ DCR B
+ JNZ VERIFY1
+ RET
+;
+; Print back file name with a '?' to indicate a syntax error.
+;
+SYNERR CALL CRLF ;end current line.
+ LHLD NAMEPNT ;this points to name in error.
+SYNERR1 MOV A,M ;print it until a space or null is found.
+ CPI ' '
+ JZ SYNERR2
+ ORA A
+ JZ SYNERR2
+ PUSH H
+ CALL PRINT
+ POP H
+ INX H
+ JMP SYNERR1
+SYNERR2 MVI A,'?' ;add trailing '?'.
+ CALL PRINT
+ CALL CRLF
+ CALL DELBATCH;delete any batch file.
+ JMP CMMND1 ;and restart from console input.
+;
+; Check character at (DE) for legal command input. Note that the
+; zero flag is set if the character is a delimiter.
+;
+CHECK LDAX D
+ ORA A
+ RZ
+ CPI ' ' ;control characters are not legal here.
+ JC SYNERR
+ RZ ;check for valid delimiter.
+ CPI '='
+ RZ
+ CPI '_'
+ RZ
+ CPI '.'
+ RZ
+ CPI ':'
+ RZ
+ CPI ';'
+ RZ
+ CPI '<'
+ RZ
+ CPI '>'
+ RZ
+ RET
+;
+; Get the next non-blank character from (DE).
+;
+NONBLANK:LDAX D
+ ORA A ;string ends with a null.
+ RZ
+ CPI ' '
+ RNZ
+ INX D
+ JMP NONBLANK
+;
+; Add (HL)=(HL)+(A)
+;
+ADDHL ADD L
+ MOV L,A
+ RNC ;take care of any carry.
+ INR H
+ RET
+;
+; Convert the first name in (FCB).
+;
+CONVFST MVI A,0
+;
+; Format a file name (convert * to '?', etc.). On return,
+; (A)=0 is an unambigeous name was specified. Enter with (A) equal to
+; the position within the fcb for the name (either 0 or 16).
+;
+CONVERT LXI H,FCB
+ CALL ADDHL
+ PUSH H
+ PUSH H
+ XRA A
+ STA CHGDRV ;initialize drive change flag.
+ LHLD INPOINT ;set (HL) as pointer into input line.
+ XCHG
+ CALL NONBLANK;get next non-blank character.
+ XCHG
+ SHLD NAMEPNT ;save pointer here for any error message.
+ XCHG
+ POP H
+ LDAX D ;get first character.
+ ORA A
+ JZ CONVRT1
+ SBI 'A'-1 ;might be a drive name, convert to binary.
+ MOV B,A ;and save.
+ INX D ;check next character for a ':'.
+ LDAX D
+ CPI ':'
+ JZ CONVRT2
+ DCX D ;nope, move pointer back to the start of the line.
+CONVRT1 LDA CDRIVE
+ MOV M,A
+ JMP CONVRT3
+CONVRT2 MOV A,B
+ STA CHGDRV ;set change in drives flag.
+ MOV M,B
+ INX D
+;
+; Convert the basic file name.
+;
+CONVRT3 MVI B,08H
+CONVRT4 CALL CHECK
+ JZ CONVRT8
+ INX H
+ CPI '*' ;note that an '*' will fill the remaining
+ JNZ CONVRT5 ;field with '?'.
+ MVI M,'?'
+ JMP CONVRT6
+CONVRT5 MOV M,A
+ INX D
+CONVRT6 DCR B
+ JNZ CONVRT4
+CONVRT7 CALL CHECK ;get next delimiter.
+ JZ GETEXT
+ INX D
+ JMP CONVRT7
+CONVRT8 INX H ;blank fill the file name.
+ MVI M,' '
+ DCR B
+ JNZ CONVRT8
+;
+; Get the extension and convert it.
+;
+GETEXT MVI B,03H
+ CPI '.'
+ JNZ GETEXT5
+ INX D
+GETEXT1 CALL CHECK
+ JZ GETEXT5
+ INX H
+ CPI '*'
+ JNZ GETEXT2
+ MVI M,'?'
+ JMP GETEXT3
+GETEXT2 MOV M,A
+ INX D
+GETEXT3 DCR B
+ JNZ GETEXT1
+GETEXT4 CALL CHECK
+ JZ GETEXT6
+ INX D
+ JMP GETEXT4
+GETEXT5 INX H
+ MVI M,' '
+ DCR B
+ JNZ GETEXT5
+GETEXT6 MVI B,3
+GETEXT7 INX H
+ MVI M,0
+ DCR B
+ JNZ GETEXT7
+ XCHG
+ SHLD INPOINT ;save input line pointer.
+ POP H
+;
+; Check to see if this is an ambigeous file name specification.
+; Set the (A) register to non zero if it is.
+;
+ LXI B,11 ;set name length.
+GETEXT8 INX H
+ MOV A,M
+ CPI '?' ;any question marks?
+ JNZ GETEXT9
+ INR B ;count them.
+GETEXT9 DCR C
+ JNZ GETEXT8
+ MOV A,B
+ ORA A
+ RET
+;
+; CP/M command table. Note commands can be either 3 or 4 characters long.
+;
+NUMCMDS EQU 6 ;number of commands
+CMDTBL DB 'DIR '
+ DB 'ERA '
+ DB 'TYPE'
+ DB 'SAVE'
+ DB 'REN '
+ DB 'USER'
+;
+; The following six bytes must agree with those at (PATTRN2)
+; or cp/m will HALT. Why?
+;
+PATTRN1 DB 0,22,0,0,0,0;(* serial number bytes *).
+;
+; Search the command table for a match with what has just
+; been entered. If a match is found, then we jump to the
+; proper section. Else jump to (UNKNOWN).
+; On return, the (C) register is set to the command number
+; that matched (or NUMCMDS+1 if no match).
+;
+SEARCH LXI H,CMDTBL
+ MVI C,0
+SEARCH1 MOV A,C
+ CPI NUMCMDS ;this commands exists.
+ RNC
+ LXI D,FCB+1 ;check this one.
+ MVI B,4 ;max command length.
+SEARCH2 LDAX D
+ CMP M
+ JNZ SEARCH3 ;not a match.
+ INX D
+ INX H
+ DCR B
+ JNZ SEARCH2
+ LDAX D ;allow a 3 character command to match.
+ CPI ' '
+ JNZ SEARCH4
+ MOV A,C ;set return register for this command.
+ RET
+SEARCH3 INX H
+ DCR B
+ JNZ SEARCH3
+SEARCH4 INR C
+ JMP SEARCH1
+;
+; Set the input buffer to empty and then start the command
+; processor (ccp).
+;
+CLEARBUF:XRA A
+ STA INBUFF+1;second byte is actual length.
+;
+;**************************************************************
+;*
+;*
+;* C C P - C o n s o l e C o m m a n d P r o c e s s o r
+;*
+;**************************************************************
+;*
+COMMAND LXI SP,CCPSTACK;setup stack area.
+ PUSH B ;note that (C) should be equal to:
+ MOV A,C ;(uuuudddd) where 'uuuu' is the user number
+ RAR ;and 'dddd' is the drive number.
+ RAR
+ RAR
+ RAR
+ ANI 0FH ;isolate the user number.
+ MOV E,A
+ CALL GETSETUC;and set it.
+ CALL RESDSK ;reset the disk system.
+ STA BATCH ;clear batch mode flag.
+ POP B
+ MOV A,C
+ ANI 0FH ;isolate the drive number.
+ STA CDRIVE ;and save.
+ CALL DSKSEL ;...and select.
+ LDA INBUFF+1
+ ORA A ;anything in input buffer already?
+ JNZ CMMND2 ;yes, we just process it.
+;
+; Entry point to get a command line from the console.
+;
+CMMND1 LXI SP,CCPSTACK;set stack straight.
+ CALL CRLF ;start a new line on the screen.
+ CALL GETDSK ;get current drive.
+ ADI 'a'
+ CALL PRINT ;print current drive.
+ MVI A,'>'
+ CALL PRINT ;and add prompt.
+ CALL GETINP ;get line from user.
+;
+; Process command line here.
+;
+CMMND2 LXI D,TBUFF
+ CALL DMASET ;set standard dma address.
+ CALL GETDSK
+ STA CDRIVE ;set current drive.
+ CALL CONVFST ;convert name typed in.
+ CNZ SYNERR ;wild cards are not allowed.
+ LDA CHGDRV ;if a change in drives was indicated,
+ ORA A ;then treat this as an unknown command
+ JNZ UNKNOWN ;which gets executed.
+ CALL SEARCH ;else search command table for a match.
+;
+; Note that an unknown command returns
+; with (A) pointing to the last address
+; in our table which is (UNKNOWN).
+;
+ LXI H,CMDADR;now, look thru our address table for command (A).
+ MOV E,A ;set (DE) to command number.
+ MVI D,0
+ DAD D
+ DAD D ;(HL)=(CMDADR)+2*(command number).
+ MOV A,M ;now pick out this address.
+ INX H
+ MOV H,M
+ MOV L,A
+ PCHL ;now execute it.
+;
+; CP/M command address table.
+;
+CMDADR DW DIRECT,ERASE,TYPE,SAVE
+ DW RENAME,USER,UNKNOWN
+;
+; Halt the system. Reason for this is unknown at present.
+;
+HALT LXI H,76F3H ;'DI HLT' instructions.
+ SHLD CBASE
+ LXI H,CBASE
+ PCHL
+;
+; Read error while TYPEing a file.
+;
+RDERROR LXI B,RDERR
+ JMP PLINE
+RDERR DB 'Read error',0
+;
+; Required file was not located.
+;
+NONE LXI B,NOFILE
+ JMP PLINE
+NOFILE DB 'No file',0
+;
+; Decode a command of the form 'A>filename number{ filename}.
+; Note that a drive specifier is not allowed on the first file
+; name. On return, the number is in register (A). Any error
+; causes 'filename?' to be printed and the command is aborted.
+;
+DECODE CALL CONVFST ;convert filename.
+ LDA CHGDRV ;do not allow a drive to be specified.
+ ORA A
+ JNZ SYNERR
+ LXI H,FCB+1 ;convert number now.
+ LXI B,11 ;(B)=sum register, (C)=max digit count.
+DECODE1 MOV A,M
+ CPI ' ' ;a space terminates the numeral.
+ JZ DECODE3
+ INX H
+ SUI '0' ;make binary from ascii.
+ CPI 10 ;legal digit?
+ JNC SYNERR
+ MOV D,A ;yes, save it in (D).
+ MOV A,B ;compute (B)=(B)*10 and check for overflow.
+ ANI 0E0H
+ JNZ SYNERR
+ MOV A,B
+ RLC
+ RLC
+ RLC ;(A)=(B)*8
+ ADD B ;.......*9
+ JC SYNERR
+ ADD B ;.......*10
+ JC SYNERR
+ ADD D ;add in new digit now.
+DECODE2 JC SYNERR
+ MOV B,A ;and save result.
+ DCR C ;only look at 11 digits.
+ JNZ DECODE1
+ RET
+DECODE3 MOV A,M ;spaces must follow (why?).
+ CPI ' '
+ JNZ SYNERR
+ INX H
+DECODE4 DCR C
+ JNZ DECODE3
+ MOV A,B ;set (A)=the numeric value entered.
+ RET
+;
+; Move 3 bytes from (HL) to (DE). Note that there is only
+; one reference to this at (A2D5h).
+;
+MOVE3 MVI B,3
+;
+; Move (B) bytes from (HL) to (DE).
+;
+HL2DE MOV A,M
+ STAX D
+ INX H
+ INX D
+ DCR B
+ JNZ HL2DE
+ RET
+;
+; Compute (HL)=(TBUFF)+(A)+(C) and get the byte that's here.
+;
+EXTRACT LXI H,TBUFF
+ ADD C
+ CALL ADDHL
+ MOV A,M
+ RET
+;
+; Check drive specified. If it means a change, then the new
+; drive will be selected. In any case, the drive byte of the
+; fcb will be set to null (means use current drive).
+;
+DSELECT XRA A ;null out first byte of fcb.
+ STA FCB
+ LDA CHGDRV ;a drive change indicated?
+ ORA A
+ RZ
+ DCR A ;yes, is it the same as the current drive?
+ LXI H,CDRIVE
+ CMP M
+ RZ
+ JMP DSKSEL ;no. Select it then.
+;
+; Check the drive selection and reset it to the previous
+; drive if it was changed for the preceeding command.
+;
+RESETDR LDA CHGDRV ;drive change indicated?
+ ORA A
+ RZ
+ DCR A ;yes, was it a different drive?
+ LXI H,CDRIVE
+ CMP M
+ RZ
+ LDA CDRIVE ;yes, re-select our old drive.
+ JMP DSKSEL
+;
+;**************************************************************
+;*
+;* D I R E C T O R Y C O M M A N D
+;*
+;**************************************************************
+;
+DIRECT CALL CONVFST ;convert file name.
+ CALL DSELECT ;select indicated drive.
+ LXI H,FCB+1 ;was any file indicated?
+ MOV A,M
+ CPI ' '
+ JNZ DIRECT2
+ MVI B,11 ;no. Fill field with '?' - same as *.*.
+DIRECT1 MVI M,'?'
+ INX H
+ DCR B
+ JNZ DIRECT1
+DIRECT2 MVI E,0 ;set initial cursor position.
+ PUSH D
+ CALL SRCHFCB ;get first file name.
+ CZ NONE ;none found at all?
+DIRECT3 JZ DIRECT9 ;terminate if no more names.
+ LDA RTNCODE ;get file's position in segment (0-3).
+ RRC
+ RRC
+ RRC
+ ANI 60H ;(A)=position*32
+ MOV C,A
+ MVI A,10
+ CALL EXTRACT ;extract the tenth entry in fcb.
+ RAL ;check system file status bit.
+ JC DIRECT8 ;we don't list them.
+ POP D
+ MOV A,E ;bump name count.
+ INR E
+ PUSH D
+ ANI 03H ;at end of line?
+ PUSH PSW
+ JNZ DIRECT4
+ CALL CRLF ;yes, end this line and start another.
+ PUSH B
+ CALL GETDSK ;start line with ('A:').
+ POP B
+ ADI 'A'
+ CALL PRINTB
+ MVI A,':'
+ CALL PRINTB
+ JMP DIRECT5
+DIRECT4 CALL SPACE ;add seperator between file names.
+ MVI A,':'
+ CALL PRINTB
+DIRECT5 CALL SPACE
+ MVI B,1 ;'extract' each file name character at a time.
+DIRECT6 MOV A,B
+ CALL EXTRACT
+ ANI 7FH ;strip bit 7 (status bit).
+ CPI ' ' ;are we at the end of the name?
+ JNZ DRECT65
+ POP PSW ;yes, don't print spaces at the end of a line.
+ PUSH PSW
+ CPI 3
+ JNZ DRECT63
+ MVI A,9 ;first check for no extension.
+ CALL EXTRACT
+ ANI 7FH
+ CPI ' '
+ JZ DIRECT7 ;don't print spaces.
+DRECT63 MVI A,' ' ;else print them.
+DRECT65 CALL PRINTB
+ INR B ;bump to next character psoition.
+ MOV A,B
+ CPI 12 ;end of the name?
+ JNC DIRECT7
+ CPI 9 ;nope, starting extension?
+ JNZ DIRECT6
+ CALL SPACE ;yes, add seperating space.
+ JMP DIRECT6
+DIRECT7 POP PSW ;get the next file name.
+DIRECT8 CALL CHKCON ;first check console, quit on anything.
+ JNZ DIRECT9
+ CALL SRCHNXT ;get next name.
+ JMP DIRECT3 ;and continue with our list.
+DIRECT9 POP D ;restore the stack and return to command level.
+ JMP GETBACK
+;
+;**************************************************************
+;*
+;* E R A S E C O M M A N D
+;*
+;**************************************************************
+;
+ERASE CALL CONVFST ;convert file name.
+ CPI 11 ;was '*.*' entered?
+ JNZ ERASE1
+ LXI B,YESNO ;yes, ask for confirmation.
+ CALL PLINE
+ CALL GETINP
+ LXI H,INBUFF+1
+ DCR M ;must be exactly 'y'.
+ JNZ CMMND1
+ INX H
+ MOV A,M
+ CPI 'Y'
+ JNZ CMMND1
+ INX H
+ SHLD INPOINT ;save input line pointer.
+ERASE1 CALL DSELECT ;select desired disk.
+ LXI D,FCB
+ CALL DELETE ;delete the file.
+ INR A
+ CZ NONE ;not there?
+ JMP GETBACK ;return to command level now.
+YESNO DB 'All (y/n)?',0
+;
+;**************************************************************
+;*
+;* T Y P E C O M M A N D
+;*
+;**************************************************************
+;
+TYPE CALL CONVFST ;convert file name.
+ JNZ SYNERR ;wild cards not allowed.
+ CALL DSELECT ;select indicated drive.
+ CALL OPENFCB ;open the file.
+ JZ TYPE5 ;not there?
+ CALL CRLF ;ok, start a new line on the screen.
+ LXI H,NBYTES;initialize byte counter.
+ MVI M,0FFH ;set to read first sector.
+TYPE1 LXI H,NBYTES
+TYPE2 MOV A,M ;have we written the entire sector?
+ CPI 128
+ JC TYPE3
+ PUSH H ;yes, read in the next one.
+ CALL READFCB
+ POP H
+ JNZ TYPE4 ;end or error?
+ XRA A ;ok, clear byte counter.
+ MOV M,A
+TYPE3 INR M ;count this byte.
+ LXI H,TBUFF ;and get the (A)th one from the buffer (TBUFF).
+ CALL ADDHL
+ MOV A,M
+ CPI CNTRLZ ;end of file mark?
+ JZ GETBACK
+ CALL PRINT ;no, print it.
+ CALL CHKCON ;check console, quit if anything ready.
+ JNZ GETBACK
+ JMP TYPE1
+;
+; Get here on an end of file or read error.
+;
+TYPE4 DCR A ;read error?
+ JZ GETBACK
+ CALL RDERROR ;yes, print message.
+TYPE5 CALL RESETDR ;and reset proper drive
+ JMP SYNERR ;now print file name with problem.
+;
+;**************************************************************
+;*
+;* S A V E C O M M A N D
+;*
+;**************************************************************
+;
+SAVE CALL DECODE ;get numeric number that follows SAVE.
+ PUSH PSW ;save number of pages to write.
+ CALL CONVFST ;convert file name.
+ JNZ SYNERR ;wild cards not allowed.
+ CALL DSELECT ;select specified drive.
+ LXI D,FCB ;now delete this file.
+ PUSH D
+ CALL DELETE
+ POP D
+ CALL CREATE ;and create it again.
+ JZ SAVE3 ;can't create?
+ XRA A ;clear record number byte.
+ STA FCB+32
+ POP PSW ;convert pages to sectors.
+ MOV L,A
+ MVI H,0
+ DAD H ;(HL)=number of sectors to write.
+ LXI D,TBASE ;and we start from here.
+SAVE1 MOV A,H ;done yet?
+ ORA L
+ JZ SAVE2
+ DCX H ;nope, count this and compute the start
+ PUSH H ;of the next 128 byte sector.
+ LXI H,128
+ DAD D
+ PUSH H ;save it and set the transfer address.
+ CALL DMASET
+ LXI D,FCB ;write out this sector now.
+ CALL WRTREC
+ POP D ;reset (DE) to the start of the last sector.
+ POP H ;restore sector count.
+ JNZ SAVE3 ;write error?
+ JMP SAVE1
+;
+; Get here after writing all of the file.
+;
+SAVE2 LXI D,FCB ;now close the file.
+ CALL CLOSE
+ INR A ;did it close ok?
+ JNZ SAVE4
+;
+; Print out error message (no space).
+;
+SAVE3 LXI B,NOSPACE
+ CALL PLINE
+SAVE4 CALL STDDMA ;reset the standard dma address.
+ JMP GETBACK
+NOSPACE DB 'No space',0
+;
+;**************************************************************
+;*
+;* R E N A M E C O M M A N D
+;*
+;**************************************************************
+;
+RENAME CALL CONVFST ;convert first file name.
+ JNZ SYNERR ;wild cards not allowed.
+ LDA CHGDRV ;remember any change in drives specified.
+ PUSH PSW
+ CALL DSELECT ;and select this drive.
+ CALL SRCHFCB ;is this file present?
+ JNZ RENAME6 ;yes, print error message.
+ LXI H,FCB ;yes, move this name into second slot.
+ LXI D,FCB+16
+ MVI B,16
+ CALL HL2DE
+ LHLD INPOINT ;get input pointer.
+ XCHG
+ CALL NONBLANK;get next non blank character.
+ CPI '=' ;only allow an '=' or '_' seperator.
+ JZ RENAME1
+ CPI '_'
+ JNZ RENAME5
+RENAME1 XCHG
+ INX H ;ok, skip seperator.
+ SHLD INPOINT ;save input line pointer.
+ CALL CONVFST ;convert this second file name now.
+ JNZ RENAME5 ;again, no wild cards.
+ POP PSW ;if a drive was specified, then it
+ MOV B,A ;must be the same as before.
+ LXI H,CHGDRV
+ MOV A,M
+ ORA A
+ JZ RENAME2
+ CMP B
+ MOV M,B
+ JNZ RENAME5 ;they were different, error.
+RENAME2 MOV M,B; reset as per the first file specification.
+ XRA A
+ STA FCB ;clear the drive byte of the fcb.
+RENAME3 CALL SRCHFCB ;and go look for second file.
+ JZ RENAME4 ;doesn't exist?
+ LXI D,FCB
+ CALL RENAM ;ok, rename the file.
+ JMP GETBACK
+;
+; Process rename errors here.
+;
+RENAME4 CALL NONE ;file not there.
+ JMP GETBACK
+RENAME5 CALL RESETDR ;bad command format.
+ JMP SYNERR
+RENAME6 LXI B,EXISTS;destination file already exists.
+ CALL PLINE
+ JMP GETBACK
+EXISTS DB 'File exists',0
+;
+;**************************************************************
+;*
+;* U S E R C O M M A N D
+;*
+;**************************************************************
+;
+USER CALL DECODE ;get numeric value following command.
+ CPI 16 ;legal user number?
+ JNC SYNERR
+ MOV E,A ;yes but is there anything else?
+ LDA FCB+1
+ CPI ' '
+ JZ SYNERR ;yes, that is not allowed.
+ CALL GETSETUC;ok, set user code.
+ JMP GETBACK1
+;
+;**************************************************************
+;*
+;* T R A N S I A N T P R O G R A M C O M M A N D
+;*
+;**************************************************************
+;
+UNKNOWN CALL VERIFY ;check for valid system (why?).
+ LDA FCB+1 ;anything to execute?
+ CPI ' '
+ JNZ UNKWN1
+ LDA CHGDRV ;nope, only a drive change?
+ ORA A
+ JZ GETBACK1;neither???
+ DCR A
+ STA CDRIVE ;ok, store new drive.
+ CALL MOVECD ;set (TDRIVE) also.
+ CALL DSKSEL ;and select this drive.
+ JMP GETBACK1;then return.
+;
+; Here a file name was typed. Prepare to execute it.
+;
+UNKWN1 LXI D,FCB+9 ;an extension specified?
+ LDAX D
+ CPI ' '
+ JNZ SYNERR ;yes, not allowed.
+UNKWN2 PUSH D
+ CALL DSELECT ;select specified drive.
+ POP D
+ LXI H,COMFILE ;set the extension to 'COM'.
+ CALL MOVE3
+ CALL OPENFCB ;and open this file.
+ JZ UNKWN9 ;not present?
+;
+; Load in the program.
+;
+ LXI H,TBASE ;store the program starting here.
+UNKWN3 PUSH H
+ XCHG
+ CALL DMASET ;set transfer address.
+ LXI D,FCB ;and read the next record.
+ CALL RDREC
+ JNZ UNKWN4 ;end of file or read error?
+ POP H ;nope, bump pointer for next sector.
+ LXI D,128
+ DAD D
+ LXI D,CBASE ;enough room for the whole file?
+ MOV A,L
+ SUB E
+ MOV A,H
+ SBB D
+ JNC UNKWN0 ;no, it can't fit.
+ JMP UNKWN3
+;
+; Get here after finished reading.
+;
+UNKWN4 POP H
+ DCR A ;normal end of file?
+ JNZ UNKWN0
+ CALL RESETDR ;yes, reset previous drive.
+ CALL CONVFST ;convert the first file name that follows
+ LXI H,CHGDRV;command name.
+ PUSH H
+ MOV A,M ;set drive code in default fcb.
+ STA FCB
+ MVI A,16 ;put second name 16 bytes later.
+ CALL CONVERT ;convert second file name.
+ POP H
+ MOV A,M ;and set the drive for this second file.
+ STA FCB+16
+ XRA A ;clear record byte in fcb.
+ STA FCB+32
+ LXI D,TFCB ;move it into place at(005Ch).
+ LXI H,FCB
+ MVI B,33
+ CALL HL2DE
+ LXI H,INBUFF+2;now move the remainder of the input
+UNKWN5 MOV A,M ;line down to (0080h). Look for a non blank.
+ ORA A ;or a null.
+ JZ UNKWN6
+ CPI ' '
+ JZ UNKWN6
+ INX H
+ JMP UNKWN5
+;
+; Do the line move now. It ends in a null byte.
+;
+UNKWN6 MVI B,0 ;keep a character count.
+ LXI D,TBUFF+1;data gets put here.
+UNKWN7 MOV A,M ;move it now.
+ STAX D
+ ORA A
+ JZ UNKWN8
+ INR B
+ INX H
+ INX D
+ JMP UNKWN7
+UNKWN8 MOV A,B ;now store the character count.
+ STA TBUFF
+ CALL CRLF ;clean up the screen.
+ CALL STDDMA ;set standard transfer address.
+ CALL SETCDRV ;reset current drive.
+ CALL TBASE ;and execute the program.
+;
+; Transiant programs return here (or reboot).
+;
+ LXI SP,BATCH ;set stack first off.
+ CALL MOVECD ;move current drive into place (TDRIVE).
+ CALL DSKSEL ;and reselect it.
+ JMP CMMND1 ;back to comand mode.
+;
+; Get here if some error occured.
+;
+UNKWN9 CALL RESETDR ;inproper format.
+ JMP SYNERR
+UNKWN0 LXI B,BADLOAD;read error or won't fit.
+ CALL PLINE
+ JMP GETBACK
+BADLOAD DB 'Bad load',0
+COMFILE DB 'COM' ;command file extension.
+;
+; Get here to return to command level. We will reset the
+; previous active drive and then either return to command
+; level directly or print error message and then return.
+;
+GETBACK CALL RESETDR ;reset previous drive.
+GETBACK1:CALL CONVFST ;convert first name in (FCB).
+ LDA FCB+1 ;if this was just a drive change request,
+ SUI ' ' ;make sure it was valid.
+ LXI H,CHGDRV
+ ORA M
+ JNZ SYNERR
+ JMP CMMND1 ;ok, return to command level.
+;
+; ccp stack area.
+;
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+CCPSTACK:EQU $ ;end of ccp stack area.
+;
+; Batch (or SUBMIT) processing information storage.
+;
+BATCH DB 0 ;batch mode flag (0=not active).
+BATCHFCB:DB 0,'$$$ SUB',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+;
+; File control block setup by the CCP.
+;
+FCB DB 0,' ',0,0,0,0,0,' ',0,0,0,0,0
+RTNCODE DB 0 ;status returned from bdos call.
+CDRIVE DB 0 ;currently active drive.
+CHGDRV DB 0 ;change in drives flag (0=no change).
+NBYTES DW 0 ;byte counter used by TYPE.
+;
+; Room for expansion?
+;
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0
+;
+; Note that the following six bytes must match those at
+; (PATTRN1) or cp/m will HALT. Why?
+;
+PATTRN2 DB 0,22,0,0,0,0;(* serial number bytes *).
+;
+;**************************************************************
+;*
+;* B D O S E N T R Y
+;*
+;**************************************************************
+;
+FBASE JMP FBASE1
+;
+; Bdos error table.
+;
+BADSCTR DW ERROR1 ;bad sector on read or write.
+BADSLCT DW ERROR2 ;bad disk select.
+RODISK DW ERROR3 ;disk is read only.
+ROFILE DW ERROR4 ;file is read only.
+;
+; Entry into bdos. (DE) or (E) are the parameters passed. The
+; function number desired is in register (C).
+;
+FBASE1 XCHG ;save the (DE) parameters.
+ SHLD PARAMS
+ XCHG
+ MOV A,E ;and save register (E) in particular.
+ STA EPARAM
+ LXI H,0
+ SHLD STATUS ;clear return status.
+ DAD SP
+ SHLD USRSTACK;save users stack pointer.
+ LXI SP,STKAREA;and set our own.
+ XRA A ;clear auto select storage space.
+ STA AUTOFLAG
+ STA AUTO
+ LXI H,GOBACK;set return address.
+ PUSH H
+ MOV A,C ;get function number.
+ CPI NFUNCTS ;valid function number?
+ RNC
+ MOV C,E ;keep single register function here.
+ LXI H,FUNCTNS;now look thru the function table.
+ MOV E,A
+ MVI D,0 ;(DE)=function number.
+ DAD D
+ DAD D ;(HL)=(start of table)+2*(function number).
+ MOV E,M
+ INX H
+ MOV D,M ;now (DE)=address for this function.
+ LHLD PARAMS ;retrieve parameters.
+ XCHG ;now (DE) has the original parameters.
+ PCHL ;execute desired function.
+;
+; BDOS function jump table.
+;
+NFUNCTS EQU 41 ;number of functions in followin table.
+;
+FUNCTNS DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB
+ DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL
+ DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE
+ DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR
+ DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN
+ DW RTN,WTSPECL
+;
+; Bdos error message section.
+;
+ERROR1 LXI H,BADSEC ;bad sector message.
+ CALL PRTERR ;print it and get a 1 char responce.
+ CPI CNTRLC ;re-boot request (control-c)?
+ JZ 0 ;yes.
+ RET ;no, return to retry i/o function.
+;
+ERROR2 LXI H,BADSEL ;bad drive selected.
+ JMP ERROR5
+;
+ERROR3 LXI H,DISKRO ;disk is read only.
+ JMP ERROR5
+;
+ERROR4 LXI H,FILERO ;file is read only.
+;
+ERROR5 CALL PRTERR
+ JMP 0 ;always reboot on these errors.
+;
+BDOSERR DB 'Bdos Err On '
+BDOSDRV DB ' : $'
+BADSEC DB 'Bad Sector$'
+BADSEL DB 'Select$'
+FILERO DB 'File '
+DISKRO DB 'R/O$'
+;
+; Print bdos error message.
+;
+PRTERR PUSH H ;save second message pointer.
+ CALL OUTCRLF ;send (cr)(lf).
+ LDA ACTIVE ;get active drive.
+ ADI 'A' ;make ascii.
+ STA BDOSDRV ;and put in message.
+ LXI B,BDOSERR;and print it.
+ CALL PRTMESG
+ POP B ;print second message line now.
+ CALL PRTMESG
+;
+; Get an input character. We will check our 1 character
+; buffer first. This may be set by the console status routine.
+;
+GETCHAR LXI H,CHARBUF;check character buffer.
+ MOV A,M ;anything present already?
+ MVI M,0 ;...either case clear it.
+ ORA A
+ RNZ ;yes, use it.
+ JMP CONIN ;nope, go get a character responce.
+;
+; Input and echo a character.
+;
+GETECHO CALL GETCHAR ;input a character.
+ CALL CHKCHAR ;carriage control?
+ RC ;no, a regular control char so don't echo.
+ PUSH PSW ;ok, save character now.
+ MOV C,A
+ CALL OUTCON ;and echo it.
+ POP PSW ;get character and return.
+ RET
+;
+; Check character in (A). Set the zero flag on a carriage
+; control character and the carry flag on any other control
+; character.
+;
+CHKCHAR CPI CR ;check for carriage return, line feed, backspace,
+ RZ ;or a tab.
+ CPI LF
+ RZ
+ CPI TAB
+ RZ
+ CPI BS
+ RZ
+ CPI ' ' ;other control char? Set carry flag.
+ RET
+;
+; Check the console during output. Halt on a control-s, then
+; reboot on a control-c. If anything else is ready, clear the
+; zero flag and return (the calling routine may want to do
+; something).
+;
+CKCONSOL:LDA CHARBUF ;check buffer.
+ ORA A ;if anything, just return without checking.
+ JNZ CKCON2
+ CALL CONST ;nothing in buffer. Check console.
+ ANI 01H ;look at bit 0.
+ RZ ;return if nothing.
+ CALL CONIN ;ok, get it.
+ CPI CNTRLS ;if not control-s, return with zero cleared.
+ JNZ CKCON1
+ CALL CONIN ;halt processing until another char
+ CPI CNTRLC ;is typed. Control-c?
+ JZ 0 ;yes, reboot now.
+ XRA A ;no, just pretend nothing was ever ready.
+ RET
+CKCON1 STA CHARBUF ;save character in buffer for later processing.
+CKCON2 MVI A,1 ;set (A) to non zero to mean something is ready.
+ RET
+;
+; Output (C) to the screen. If the printer flip-flop flag
+; is set, we will send character to printer also. The console
+; will be checked in the process.
+;
+OUTCHAR LDA OUTFLAG ;check output flag.
+ ORA A ;anything and we won't generate output.
+ JNZ OUTCHR1
+ PUSH B
+ CALL CKCONSOL;check console (we don't care whats there).
+ POP B
+ PUSH B
+ CALL CONOUT ;output (C) to the screen.
+ POP B
+ PUSH B
+ LDA PRTFLAG ;check printer flip-flop flag.
+ ORA A
+ CNZ LIST ;print it also if non-zero.
+ POP B
+OUTCHR1 MOV A,C ;update cursors position.
+ LXI H,CURPOS
+ CPI DEL ;rubouts don't do anything here.
+ RZ
+ INR M ;bump line pointer.
+ CPI ' ' ;and return if a normal character.
+ RNC
+ DCR M ;restore and check for the start of the line.
+ MOV A,M
+ ORA A
+ RZ ;ingnore control characters at the start of the line.
+ MOV A,C
+ CPI BS ;is it a backspace?
+ JNZ OUTCHR2
+ DCR M ;yes, backup pointer.
+ RET
+OUTCHR2 CPI LF ;is it a line feed?
+ RNZ ;ignore anything else.
+ MVI M,0 ;reset pointer to start of line.
+ RET
+;
+; Output (A) to the screen. If it is a control character
+; (other than carriage control), use ^x format.
+;
+SHOWIT MOV A,C
+ CALL CHKCHAR ;check character.
+ JNC OUTCON ;not a control, use normal output.
+ PUSH PSW
+ MVI C,'^' ;for a control character, preceed it with '^'.
+ CALL OUTCHAR
+ POP PSW
+ ORI '@' ;and then use the letter equivelant.
+ MOV C,A
+;
+; Function to output (C) to the console device and expand tabs
+; if necessary.
+;
+OUTCON MOV A,C
+ CPI TAB ;is it a tab?
+ JNZ OUTCHAR ;use regular output.
+OUTCON1 MVI C,' ' ;yes it is, use spaces instead.
+ CALL OUTCHAR
+ LDA CURPOS ;go until the cursor is at a multiple of 8
+
+ ANI 07H ;position.
+ JNZ OUTCON1
+ RET
+;
+; Echo a backspace character. Erase the prevoius character
+; on the screen.
+;
+BACKUP CALL BACKUP1 ;backup the screen 1 place.
+ MVI C,' ' ;then blank that character.
+ CALL CONOUT
+BACKUP1 MVI C,BS ;then back space once more.
+ JMP CONOUT
+;
+; Signal a deleted line. Print a '#' at the end and start
+; over.
+;
+NEWLINE MVI C,'#'
+ CALL OUTCHAR ;print this.
+ CALL OUTCRLF ;start new line.
+NEWLN1 LDA CURPOS ;move the cursor to the starting position.
+ LXI H,STARTING
+ CMP M
+ RNC ;there yet?
+ MVI C,' '
+ CALL OUTCHAR ;nope, keep going.
+ JMP NEWLN1
+;
+; Output a (cr) (lf) to the console device (screen).
+;
+OUTCRLF MVI C,CR
+ CALL OUTCHAR
+ MVI C,LF
+ JMP OUTCHAR
+;
+; Print message pointed to by (BC). It will end with a '$'.
+;
+PRTMESG LDAX B ;check for terminating character.
+ CPI '$'
+ RZ
+ INX B
+ PUSH B ;otherwise, bump pointer and print it.
+ MOV C,A
+ CALL OUTCON
+ POP B
+ JMP PRTMESG
+;
+; Function to execute a buffered read.
+;
+RDBUFF LDA CURPOS ;use present location as starting one.
+ STA STARTING
+ LHLD PARAMS ;get the maximum buffer space.
+ MOV C,M
+ INX H ;point to first available space.
+ PUSH H ;and save.
+ MVI B,0 ;keep a character count.
+RDBUF1 PUSH B
+ PUSH H
+RDBUF2 CALL GETCHAR ;get the next input character.
+ ANI 7FH ;strip bit 7.
+ POP H ;reset registers.
+ POP B
+ CPI CR ;en of the line?
+ JZ RDBUF17
+ CPI LF
+ JZ RDBUF17
+ CPI BS ;how about a backspace?
+ JNZ RDBUF3
+ MOV A,B ;yes, but ignore at the beginning of the line.
+ ORA A
+ JZ RDBUF1
+ DCR B ;ok, update counter.
+ LDA CURPOS ;if we backspace to the start of the line,
+ STA OUTFLAG ;treat as a cancel (control-x).
+ JMP RDBUF10
+RDBUF3 CPI DEL ;user typed a rubout?
+ JNZ RDBUF4
+ MOV A,B ;ignore at the start of the line.
+ ORA A
+ JZ RDBUF1
+ MOV A,M ;ok, echo the prevoius character.
+ DCR B ;and reset pointers (counters).
+ DCX H
+ JMP RDBUF15
+RDBUF4 CPI CNTRLE ;physical end of line?
+ JNZ RDBUF5
+ PUSH B ;yes, do it.
+ PUSH H
+ CALL OUTCRLF
+ XRA A ;and update starting position.
+ STA STARTING
+ JMP RDBUF2
+RDBUF5 CPI CNTRLP ;control-p?
+ JNZ RDBUF6
+ PUSH H ;yes, flip the print flag filp-flop byte.
+ LXI H,PRTFLAG
+ MVI A,1 ;PRTFLAG=1-PRTFLAG
+ SUB M
+ MOV M,A
+ POP H
+ JMP RDBUF1
+RDBUF6 CPI CNTRLX ;control-x (cancel)?
+ JNZ RDBUF8
+ POP H
+RDBUF7 LDA STARTING;yes, backup the cursor to here.
+ LXI H,CURPOS
+ CMP M
+ JNC RDBUFF ;done yet?
+ DCR M ;no, decrement pointer and output back up one space.
+ CALL BACKUP
+ JMP RDBUF7
+RDBUF8 CPI CNTRLU ;cntrol-u (cancel line)?
+ JNZ RDBUF9
+ CALL NEWLINE ;start a new line.
+ POP H
+ JMP RDBUFF
+RDBUF9 CPI CNTRLR ;control-r?
+ JNZ RDBUF14
+RDBUF10 PUSH B ;yes, start a new line and retype the old one.
+ CALL NEWLINE
+ POP B
+ POP H
+ PUSH H
+ PUSH B
+RDBUF11 MOV A,B ;done whole line yet?
+ ORA A
+ JZ RDBUF12
+ INX H ;nope, get next character.
+ MOV C,M
+ DCR B ;count it.
+ PUSH B
+ PUSH H
+ CALL SHOWIT ;and display it.
+ POP H
+ POP B
+ JMP RDBUF11
+RDBUF12 PUSH H ;done with line. If we were displaying
+ LDA OUTFLAG ;then update cursor position.
+ ORA A
+ JZ RDBUF2
+ LXI H,CURPOS;because this line is shorter, we must
+ SUB M ;back up the cursor (not the screen however)
+ STA OUTFLAG ;some number of positions.
+RDBUF13 CALL BACKUP ;note that as long as (OUTFLAG) is non
+ LXI H,OUTFLAG;zero, the screen will not be changed.
+ DCR M
+ JNZ RDBUF13
+ JMP RDBUF2 ;now just get the next character.
+;
+; Just a normal character, put this in our buffer and echo.
+;
+RDBUF14 INX H
+ MOV M,A ;store character.
+ INR B ;and count it.
+RDBUF15 PUSH B
+ PUSH H
+ MOV C,A ;echo it now.
+ CALL SHOWIT
+ POP H
+ POP B
+ MOV A,M ;was it an abort request?
+ CPI CNTRLC ;control-c abort?
+ MOV A,B
+ JNZ RDBUF16
+ CPI 1 ;only if at start of line.
+ JZ 0
+RDBUF16 CMP C ;nope, have we filled the buffer?
+ JC RDBUF1
+RDBUF17 POP H ;yes end the line and return.
+ MOV M,B
+ MVI C,CR
+ JMP OUTCHAR ;output (cr) and return.
+;
+; Function to get a character from the console device.
+;
+GETCON CALL GETECHO ;get and echo.
+ JMP SETSTAT ;save status and return.
+;
+; Function to get a character from the tape reader device.
+;
+GETRDR CALL READER ;get a character from reader, set status and return.
+ JMP SETSTAT
+;
+; Function to perform direct console i/o. If (C) contains (FF)
+; then this is an input request. If (C) contains (FE) then
+; this is a status request. Otherwise we are to output (C).
+;
+DIRCIO MOV A,C ;test for (FF).
+ INR A
+ JZ DIRC1
+ INR A ;test for (FE).
+ JZ CONST
+ JMP CONOUT ;just output (C).
+DIRC1 CALL CONST ;this is an input request.
+ ORA A
+ JZ GOBACK1 ;not ready? Just return (directly).
+ CALL CONIN ;yes, get character.
+ JMP SETSTAT ;set status and return.
+;
+; Function to return the i/o byte.
+;
+GETIOB LDA IOBYTE
+ JMP SETSTAT
+;
+; Function to set the i/o byte.
+;
+SETIOB LXI H,IOBYTE
+ MOV M,C
+ RET
+;
+; Function to print the character string pointed to by (DE)
+; on the console device. The string ends with a '$'.
+;
+PRTSTR XCHG
+ MOV C,L
+ MOV B,H ;now (BC) points to it.
+ JMP PRTMESG
+;
+; Function to interigate the console device.
+;
+GETCSTS CALL CKCONSOL
+;
+; Get here to set the status and return to the cleanup
+; section. Then back to the user.
+;
+SETSTAT STA STATUS
+RTN RET
+;
+; Set the status to 1 (read or write error code).
+;
+IOERR1 MVI A,1
+ JMP SETSTAT
+;
+OUTFLAG DB 0 ;output flag (non zero means no output).
+STARTING:DB 2 ;starting position for cursor.
+CURPOS DB 0 ;cursor position (0=start of line).
+PRTFLAG DB 0 ;printer flag (control-p toggle). List if non zero.
+CHARBUF DB 0 ;single input character buffer.
+;
+; Stack area for BDOS calls.
+;
+USRSTACK:DW 0 ;save users stack pointer here.
+;
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+STKAREA EQU $ ;end of stack area.
+;
+USERNO DB 0 ;current user number.
+ACTIVE DB 0 ;currently active drive.
+PARAMS DW 0 ;save (DE) parameters here on entry.
+STATUS DW 0 ;status returned from bdos function.
+;
+; Select error occured, jump to error routine.
+;
+SLCTERR LXI H,BADSLCT
+;
+; Jump to (HL) indirectly.
+;
+JUMPHL MOV E,M
+ INX H
+ MOV D,M ;now (DE) contain the desired address.
+ XCHG
+ PCHL
+;
+; Block move. (DE) to (HL), (C) bytes total.
+;
+DE2HL INR C ;is count down to zero?
+DE2HL1 DCR C
+ RZ ;yes, we are done.
+ LDAX D ;no, move one more byte.
+ MOV M,A
+ INX D
+ INX H
+ JMP DE2HL1 ;and repeat.
+;
+; Select the desired drive.
+;
+SELECT LDA ACTIVE ;get active disk.
+ MOV C,A
+ CALL SELDSK ;select it.
+ MOV A,H ;valid drive?
+ ORA L ;valid drive?
+ RZ ;return if not.
+;
+; Here, the BIOS returned the address of the parameter block
+; in (HL). We will extract the necessary pointers and save them.
+;
+ MOV E,M ;yes, get address of translation table into (DE).
+ INX H
+ MOV D,M
+ INX H
+ SHLD SCRATCH1 ;save pointers to scratch areas.
+ INX H
+ INX H
+ SHLD SCRATCH2 ;ditto.
+ INX H
+ INX H
+ SHLD SCRATCH3 ;ditto.
+ INX H
+ INX H
+ XCHG ;now save the translation table address.
+ SHLD XLATE
+ LXI H,DIRBUF ;put the next 8 bytes here.
+ MVI C,8 ;they consist of the directory buffer
+ CALL DE2HL ;pointer, parameter block pointer,
+ LHLD DISKPB ;check and allocation vectors.
+ XCHG
+ LXI H,SECTORS ;move parameter block into our ram.
+ MVI C,15 ;it is 15 bytes long.
+ CALL DE2HL
+ LHLD DSKSIZE ;check disk size.
+ MOV A,H ;more than 256 blocks on this?
+ LXI H,BIGDISK
+ MVI M,0FFH ;set to samll.
+ ORA A
+ JZ SELECT1
+ MVI M,0 ;wrong, set to large.
+SELECT1 MVI A,0FFH ;clear the zero flag.
+ ORA A
+ RET
+;
+; Routine to home the disk track head and clear pointers.
+;
+HOMEDRV CALL HOME ;home the head.
+ XRA A
+ LHLD SCRATCH2;set our track pointer also.
+ MOV M,A
+ INX H
+ MOV M,A
+ LHLD SCRATCH3;and our sector pointer.
+ MOV M,A
+ INX H
+ MOV M,A
+ RET
+;
+; Do the actual disk read and check the error return status.
+;
+DOREAD CALL READ
+ JMP IORET
+;
+; Do the actual disk write and handle any bios error.
+;
+DOWRITE CALL WRITE
+IORET ORA A
+ RZ ;return unless an error occured.
+ LXI H,BADSCTR;bad read/write on this sector.
+ JMP JUMPHL
+;
+; Routine to select the track and sector that the desired
+; block number falls in.
+;
+TRKSEC LHLD FILEPOS ;get position of last accessed file
+ MVI C,2 ;in directory and compute sector #.
+ CALL SHIFTR ;sector #=file-position/4.
+ SHLD BLKNMBR ;save this as the block number of interest.
+ SHLD CKSUMTBL;what's it doing here too?
+;
+; if the sector number has already been set (BLKNMBR), enter
+; at this point.
+;
+TRKSEC1 LXI H,BLKNMBR
+ MOV C,M ;move sector number into (BC).
+ INX H
+ MOV B,M
+ LHLD SCRATCH3;get current sector number and
+ MOV E,M ;move this into (DE).
+ INX H
+ MOV D,M
+ LHLD SCRATCH2;get current track number.
+ MOV A,M ;and this into (HL).
+ INX H
+ MOV H,M
+ MOV L,A
+TRKSEC2 MOV A,C ;is desired sector before current one?
+ SUB E
+ MOV A,B
+ SBB D
+ JNC TRKSEC3
+ PUSH H ;yes, decrement sectors by one track.
+ LHLD SECTORS ;get sectors per track.
+ MOV A,E
+ SUB L
+ MOV E,A
+ MOV A,D
+ SBB H
+ MOV D,A ;now we have backed up one full track.
+ POP H
+ DCX H ;adjust track counter.
+ JMP TRKSEC2
+TRKSEC3 PUSH H ;desired sector is after current one.
+ LHLD SECTORS ;get sectors per track.
+ DAD D ;bump sector pointer to next track.
+ JC TRKSEC4
+ MOV A,C ;is desired sector now before current one?
+ SUB L
+ MOV A,B
+ SBB H
+ JC TRKSEC4
+ XCHG ;not yes, increment track counter
+ POP H ;and continue until it is.
+ INX H
+ JMP TRKSEC3
+;
+; here we have determined the track number that contains the
+; desired sector.
+;
+TRKSEC4 POP H ;get track number (HL).
+ PUSH B
+ PUSH D
+ PUSH H
+ XCHG
+ LHLD OFFSET ;adjust for first track offset.
+ DAD D
+ MOV B,H
+ MOV C,L
+ CALL SETTRK ;select this track.
+ POP D ;reset current track pointer.
+ LHLD SCRATCH2
+ MOV M,E
+ INX H
+ MOV M,D
+ POP D
+ LHLD SCRATCH3;reset the first sector on this track.
+ MOV M,E
+ INX H
+ MOV M,D
+ POP B
+ MOV A,C ;now subtract the desired one.
+ SUB E ;to make it relative (1-# sectors/track).
+ MOV C,A
+ MOV A,B
+ SBB D
+ MOV B,A
+ LHLD XLATE ;translate this sector according to this table.
+ XCHG
+ CALL SECTRN ;let the bios translate it.
+ MOV C,L
+ MOV B,H
+ JMP SETSEC ;and select it.
+;
+; Compute block number from record number (SAVNREC) and
+; extent number (SAVEXT).
+;
+GETBLOCK:LXI H,BLKSHFT;get logical to physical conversion.
+ MOV C,M ;note that this is base 2 log of ratio.
+ LDA SAVNREC ;get record number.
+GETBLK1 ORA A ;compute (A)=(A)/2^BLKSHFT.
+ RAR
+ DCR C
+ JNZ GETBLK1
+ MOV B,A ;save result in (B).
+ MVI A,8
+ SUB M
+ MOV C,A ;compute (C)=8-BLKSHFT.
+ LDA SAVEXT
+GETBLK2 DCR C ;compute (A)=SAVEXT*2^(8-BLKSHFT).
+ JZ GETBLK3
+ ORA A
+ RAL
+ JMP GETBLK2
+GETBLK3 ADD B
+ RET
+;
+; Routine to extract the (BC) block byte from the fcb pointed
+; to by (PARAMS). If this is a big-disk, then these are 16 bit
+; block numbers, else they are 8 bit numbers.
+; Number is returned in (HL).
+;
+EXTBLK LHLD PARAMS ;get fcb address.
+ LXI D,16 ;block numbers start 16 bytes into fcb.
+ DAD D
+ DAD B
+ LDA BIGDISK ;are we using a big-disk?
+ ORA A
+ JZ EXTBLK1
+ MOV L,M ;no, extract an 8 bit number from the fcb.
+ MVI H,0
+ RET
+EXTBLK1 DAD B ;yes, extract a 16 bit number.
+ MOV E,M
+ INX H
+ MOV D,M
+ XCHG ;return in (HL).
+ RET
+;
+; Compute block number.
+;
+COMBLK CALL GETBLOCK
+ MOV C,A
+ MVI B,0
+ CALL EXTBLK
+ SHLD BLKNMBR
+ RET
+;
+; Check for a zero block number (unused).
+;
+CHKBLK LHLD BLKNMBR
+ MOV A,L ;is it zero?
+ ORA H
+ RET
+;
+; Adjust physical block (BLKNMBR) and convert to logical
+; sector (LOGSECT). This is the starting sector of this block.
+; The actual sector of interest is then added to this and the
+; resulting sector number is stored back in (BLKNMBR). This
+; will still have to be adjusted for the track number.
+;
+LOGICAL LDA BLKSHFT ;get log2(physical/logical sectors).
+ LHLD BLKNMBR ;get physical sector desired.
+LOGICL1 DAD H ;compute logical sector number.
+ DCR A ;note logical sectors are 128 bytes long.
+ JNZ LOGICL1
+ SHLD LOGSECT ;save logical sector.
+ LDA BLKMASK ;get block mask.
+ MOV C,A
+ LDA SAVNREC ;get next sector to access.
+ ANA C ;extract the relative position within physical block.
+ ORA L ;and add it too logical sector.
+ MOV L,A
+ SHLD BLKNMBR ;and store.
+ RET
+;
+; Set (HL) to point to extent byte in fcb.
+;
+SETEXT LHLD PARAMS
+ LXI D,12 ;it is the twelth byte.
+ DAD D
+ RET
+;
+; Set (HL) to point to record count byte in fcb and (DE) to
+; next record number byte.
+;
+SETHLDE LHLD PARAMS
+ LXI D,15 ;record count byte (#15).
+ DAD D
+ XCHG
+ LXI H,17 ;next record number (#32).
+ DAD D
+ RET
+;
+; Save current file data from fcb.
+;
+STRDATA CALL SETHLDE
+ MOV A,M ;get and store record count byte.
+ STA SAVNREC
+ XCHG
+ MOV A,M ;get and store next record number byte.
+ STA SAVNXT
+ CALL SETEXT ;point to extent byte.
+ LDA EXTMASK ;get extent mask.
+ ANA M
+ STA SAVEXT ;and save extent here.
+ RET
+;
+; Set the next record to access. If (MODE) is set to 2, then
+; the last record byte (SAVNREC) has the correct number to access.
+; For sequential access, (MODE) will be equal to 1.
+;
+SETNREC CALL SETHLDE
+ LDA MODE ;get sequential flag (=1).
+ CPI 2 ;a 2 indicates that no adder is needed.
+ JNZ STNREC1
+ XRA A ;clear adder (random access?).
+STNREC1 MOV C,A
+ LDA SAVNREC ;get last record number.
+ ADD C ;increment record count.
+ MOV M,A ;and set fcb's next record byte.
+ XCHG
+ LDA SAVNXT ;get next record byte from storage.
+ MOV M,A ;and put this into fcb as number of records used.
+ RET
+;
+; Shift (HL) right (C) bits.
+;
+SHIFTR INR C
+SHIFTR1 DCR C
+ RZ
+ MOV A,H
+ ORA A
+ RAR
+ MOV H,A
+ MOV A,L
+ RAR
+ MOV L,A
+ JMP SHIFTR1
+;
+; Compute the check-sum for the directory buffer. Return
+; integer sum in (A).
+;
+CHECKSUM:MVI C,128 ;length of buffer.
+ LHLD DIRBUF ;get its location.
+ XRA A ;clear summation byte.
+CHKSUM1 ADD M ;and compute sum ignoring carries.
+ INX H
+ DCR C
+ JNZ CHKSUM1
+ RET
+;
+; Shift (HL) left (C) bits.
+;
+SHIFTL INR C
+SHIFTL1 DCR C
+ RZ
+ DAD H ;shift left 1 bit.
+ JMP SHIFTL1
+;
+; Routine to set a bit in a 16 bit value contained in (BC).
+; The bit set depends on the current drive selection.
+;
+SETBIT PUSH B ;save 16 bit word.
+ LDA ACTIVE ;get active drive.
+ MOV C,A
+ LXI H,1
+ CALL SHIFTL ;shift bit 0 into place.
+ POP B ;now 'or' this with the original word.
+ MOV A,C
+ ORA L
+ MOV L,A ;low byte done, do high byte.
+ MOV A,B
+ ORA H
+ MOV H,A
+ RET
+;
+; Extract the write protect status bit for the current drive.
+; The result is returned in (A), bit 0.
+;
+GETWPRT LHLD WRTPRT ;get status bytes.
+ LDA ACTIVE ;which drive is current?
+ MOV C,A
+ CALL SHIFTR ;shift status such that bit 0 is the
+ MOV A,L ;one of interest for this drive.
+ ANI 01H ;and isolate it.
+ RET
+;
+; Function to write protect the current disk.
+;
+WRTPRTD LXI H,WRTPRT;point to status word.
+ MOV C,M ;set (BC) equal to the status.
+ INX H
+ MOV B,M
+ CALL SETBIT ;and set this bit according to current drive.
+ SHLD WRTPRT ;then save.
+ LHLD DIRSIZE ;now save directory size limit.
+ INX H ;remember the last one.
+ XCHG
+ LHLD SCRATCH1;and store it here.
+ MOV M,E ;put low byte.
+ INX H
+ MOV M,D ;then high byte.
+ RET
+;
+; Check for a read only file.
+;
+CHKROFL CALL FCB2HL ;set (HL) to file entry in directory buffer.
+CKROF1 LXI D,9 ;look at bit 7 of the ninth byte.
+ DAD D
+ MOV A,M
+ RAL
+ RNC ;return if ok.
+ LXI H,ROFILE;else, print error message and terminate.
+ JMP JUMPHL
+;
+; Check the write protect status of the active disk.
+;
+CHKWPRT CALL GETWPRT
+ RZ ;return if ok.
+ LXI H,RODISK;else print message and terminate.
+ JMP JUMPHL
+;
+; Routine to set (HL) pointing to the proper entry in the
+; directory buffer.
+;
+FCB2HL LHLD DIRBUF ;get address of buffer.
+ LDA FCBPOS ;relative position of file.
+;
+; Routine to add (A) to (HL).
+;
+ADDA2HL ADD L
+ MOV L,A
+ RNC
+ INR H ;take care of any carry.
+ RET
+;
+; Routine to get the 's2' byte from the fcb supplied in
+; the initial parameter specification.
+;
+GETS2 LHLD PARAMS ;get address of fcb.
+ LXI D,14 ;relative position of 's2'.
+ DAD D
+ MOV A,M ;extract this byte.
+ RET
+;
+; Clear the 's2' byte in the fcb.
+;
+CLEARS2 CALL GETS2 ;this sets (HL) pointing to it.
+ MVI M,0 ;now clear it.
+ RET
+;
+; Set bit 7 in the 's2' byte of the fcb.
+;
+SETS2B7 CALL GETS2 ;get the byte.
+ ORI 80H ;and set bit 7.
+ MOV M,A ;then store.
+ RET
+;
+; Compare (FILEPOS) with (SCRATCH1) and set flags based on
+; the difference. This checks to see if there are more file
+; names in the directory. We are at (FILEPOS) and there are
+; (SCRATCH1) of them to check.
+;
+MOREFLS LHLD FILEPOS ;we are here.
+ XCHG
+ LHLD SCRATCH1;and don't go past here.
+ MOV A,E ;compute difference but don't keep.
+ SUB M
+ INX H
+ MOV A,D
+ SBB M ;set carry if no more names.
+ RET
+;
+; Call this routine to prevent (SCRATCH1) from being greater
+; than (FILEPOS).
+;
+CHKNMBR CALL MOREFLS ;SCRATCH1 too big?
+ RC
+ INX D ;yes, reset it to (FILEPOS).
+ MOV M,D
+ DCX H
+ MOV M,E
+ RET
+;
+; Compute (HL)=(DE)-(HL)
+;
+SUBHL MOV A,E ;compute difference.
+ SUB L
+ MOV L,A ;store low byte.
+ MOV A,D
+ SBB H
+ MOV H,A ;and then high byte.
+ RET
+;
+; Set the directory checksum byte.
+;
+SETDIR MVI C,0FFH
+;
+; Routine to set or compare the directory checksum byte. If
+; (C)=0ffh, then this will set the checksum byte. Else the byte
+; will be checked. If the check fails (the disk has been changed),
+; then this disk will be write protected.
+;
+CHECKDIR:LHLD CKSUMTBL
+ XCHG
+ LHLD ALLOC1
+ CALL SUBHL
+ RNC ;ok if (CKSUMTBL) > (ALLOC1), so return.
+ PUSH B
+ CALL CHECKSUM;else compute checksum.
+ LHLD CHKVECT ;get address of checksum table.
+ XCHG
+ LHLD CKSUMTBL
+ DAD D ;set (HL) to point to byte for this drive.
+ POP B
+ INR C ;set or check ?
+ JZ CHKDIR1
+ CMP M ;check them.
+ RZ ;return if they are the same.
+ CALL MOREFLS ;not the same, do we care?
+ RNC
+ CALL WRTPRTD ;yes, mark this as write protected.
+ RET
+CHKDIR1 MOV M,A ;just set the byte.
+ RET
+;
+; Do a write to the directory of the current disk.
+;
+DIRWRITE:CALL SETDIR ;set checksum byte.
+ CALL DIRDMA ;set directory dma address.
+ MVI C,1 ;tell the bios to actually write.
+ CALL DOWRITE ;then do the write.
+ JMP DEFDMA
+;
+; Read from the directory.
+;
+DIRREAD CALL DIRDMA ;set the directory dma address.
+ CALL DOREAD ;and read it.
+;
+; Routine to set the dma address to the users choice.
+;
+DEFDMA LXI H,USERDMA;reset the default dma address and return.
+ JMP DIRDMA1
+;
+; Routine to set the dma address for directory work.
+;
+DIRDMA LXI H,DIRBUF
+;
+; Set the dma address. On entry, (HL) points to
+; word containing the desired dma address.
+;
+DIRDMA1 MOV C,M
+ INX H
+ MOV B,M ;setup (BC) and go to the bios to set it.
+ JMP SETDMA
+;
+; Move the directory buffer into user's dma space.
+;
+MOVEDIR LHLD DIRBUF ;buffer is located here, and
+ XCHG
+ LHLD USERDMA; put it here.
+ MVI C,128 ;this is its length.
+ JMP DE2HL ;move it now and return.
+;
+; Check (FILEPOS) and set the zero flag if it equals 0ffffh.
+;
+CKFILPOS:LXI H,FILEPOS
+ MOV A,M
+ INX H
+ CMP M ;are both bytes the same?
+ RNZ
+ INR A ;yes, but are they each 0ffh?
+ RET
+;
+; Set location (FILEPOS) to 0ffffh.
+;
+STFILPOS:LXI H,0FFFFH
+ SHLD FILEPOS
+ RET
+;
+; Move on to the next file position within the current
+; directory buffer. If no more exist, set pointer to 0ffffh
+; and the calling routine will check for this. Enter with (C)
+; equal to 0ffh to cause the checksum byte to be set, else we
+; will check this disk and set write protect if checksums are
+; not the same (applies only if another directory sector must
+; be read).
+;
+NXENTRY LHLD DIRSIZE ;get directory entry size limit.
+ XCHG
+ LHLD FILEPOS ;get current count.
+ INX H ;go on to the next one.
+ SHLD FILEPOS
+ CALL SUBHL ;(HL)=(DIRSIZE)-(FILEPOS)
+ JNC NXENT1 ;is there more room left?
+ JMP STFILPOS;no. Set this flag and return.
+NXENT1 LDA FILEPOS ;get file position within directory.
+ ANI 03H ;only look within this sector (only 4 entries fit).
+ MVI B,5 ;convert to relative position (32 bytes each).
+NXENT2 ADD A ;note that this is not efficient code.
+ DCR B ;5 'ADD A's would be better.
+ JNZ NXENT2
+ STA FCBPOS ;save it as position of fcb.
+ ORA A
+ RNZ ;return if we are within buffer.
+ PUSH B
+ CALL TRKSEC ;we need the next directory sector.
+ CALL DIRREAD
+ POP B
+ JMP CHECKDIR
+;
+; Routine to to get a bit from the disk space allocation
+; map. It is returned in (A), bit position 0. On entry to here,
+; set (BC) to the block number on the disk to check.
+; On return, (D) will contain the original bit position for
+; this block number and (HL) will point to the address for it.
+;
+CKBITMAP:MOV A,C ;determine bit number of interest.
+ ANI 07H ;compute (D)=(E)=(C and 7)+1.
+ INR A
+ MOV E,A ;save particular bit number.
+ MOV D,A
+;
+; compute (BC)=(BC)/8.
+;
+ MOV A,C
+ RRC ;now shift right 3 bits.
+ RRC
+ RRC
+ ANI 1FH ;and clear bits 7,6,5.
+ MOV C,A
+ MOV A,B
+ ADD A ;now shift (B) into bits 7,6,5.
+ ADD A
+ ADD A
+ ADD A
+ ADD A
+ ORA C ;and add in (C).
+ MOV C,A ;ok, (C) ha been completed.
+ MOV A,B ;is there a better way of doing this?
+ RRC
+ RRC
+ RRC
+ ANI 1FH
+ MOV B,A ;and now (B) is completed.
+;
+; use this as an offset into the disk space allocation
+; table.
+;
+ LHLD ALOCVECT
+ DAD B
+ MOV A,M ;now get correct byte.
+CKBMAP1 RLC ;get correct bit into position 0.
+ DCR E
+ JNZ CKBMAP1
+ RET
+;
+; Set or clear the bit map such that block number (BC) will be marked
+; as used. On entry, if (E)=0 then this bit will be cleared, if it equals
+; 1 then it will be set (don't use anyother values).
+;
+STBITMAP:PUSH D
+ CALL CKBITMAP;get the byte of interest.
+ ANI 0FEH ;clear the affected bit.
+ POP B
+ ORA C ;and now set it acording to (C).
+;
+; entry to restore the original bit position and then store
+; in table. (A) contains the value, (D) contains the bit
+; position (1-8), and (HL) points to the address within the
+; space allocation table for this byte.
+;
+STBMAP1 RRC ;restore original bit position.
+ DCR D
+ JNZ STBMAP1
+ MOV M,A ;and stor byte in table.
+ RET
+;
+; Set/clear space used bits in allocation map for this file.
+; On entry, (C)=1 to set the map and (C)=0 to clear it.
+;
+SETFILE CALL FCB2HL ;get address of fcb
+ LXI D,16
+ DAD D ;get to block number bytes.
+ PUSH B
+ MVI C,17 ;check all 17 bytes (max) of table.
+SETFL1 POP D
+ DCR C ;done all bytes yet?
+ RZ
+ PUSH D
+ LDA BIGDISK ;check disk size for 16 bit block numbers.
+ ORA A
+ JZ SETFL2
+ PUSH B ;only 8 bit numbers. set (BC) to this one.
+ PUSH H
+ MOV C,M ;get low byte from table, always
+ MVI B,0 ;set high byte to zero.
+ JMP SETFL3
+SETFL2 DCR C ;for 16 bit block numbers, adjust counter.
+ PUSH B
+ MOV C,M ;now get both the low and high bytes.
+ INX H
+ MOV B,M
+ PUSH H
+SETFL3 MOV A,C ;block used?
+ ORA B
+ JZ SETFL4
+ LHLD DSKSIZE ;is this block number within the
+ MOV A,L ;space on the disk?
+ SUB C
+ MOV A,H
+ SBB B
+ CNC STBITMAP;yes, set the proper bit.
+SETFL4 POP H ;point to next block number in fcb.
+ INX H
+ POP B
+ JMP SETFL1
+;
+; Construct the space used allocation bit map for the active
+; drive. If a file name starts with '$' and it is under the
+; current user number, then (STATUS) is set to minus 1. Otherwise
+; it is not set at all.
+;
+BITMAP LHLD DSKSIZE ;compute size of allocation table.
+ MVI C,3
+ CALL SHIFTR ;(HL)=(HL)/8.
+ INX H ;at lease 1 byte.
+ MOV B,H
+ MOV C,L ;set (BC) to the allocation table length.
+;
+; Initialize the bitmap for this drive. Right now, the first
+; two bytes are specified by the disk parameter block. However
+; a patch could be entered here if it were necessary to setup
+; this table in a special mannor. For example, the bios could
+; determine locations of 'bad blocks' and set them as already
+; 'used' in the map.
+;
+ LHLD ALOCVECT;now zero out the table now.
+BITMAP1 MVI M,0
+ INX H
+ DCX B
+ MOV A,B
+ ORA C
+ JNZ BITMAP1
+ LHLD ALLOC0 ;get initial space used by directory.
+ XCHG
+ LHLD ALOCVECT;and put this into map.
+ MOV M,E
+ INX H
+ MOV M,D
+;
+; End of initialization portion.
+;
+ CALL HOMEDRV ;now home the drive.
+ LHLD SCRATCH1
+ MVI M,3 ;force next directory request to read
+ INX H ;in a sector.
+ MVI M,0
+ CALL STFILPOS;clear initial file position also.
+BITMAP2 MVI C,0FFH ;read next file name in directory
+ CALL NXENTRY ;and set checksum byte.
+ CALL CKFILPOS;is there another file?
+ RZ
+ CALL FCB2HL ;yes, get its address.
+ MVI A,0E5H
+ CMP M ;empty file entry?
+ JZ BITMAP2
+ LDA USERNO ;no, correct user number?
+ CMP M
+ JNZ BITMAP3
+ INX H
+ MOV A,M ;yes, does name start with a '$'?
+ SUI '$'
+ JNZ BITMAP3
+ DCR A ;yes, set atatus to minus one.
+ STA STATUS
+BITMAP3 MVI C,1 ;now set this file's space as used in bit map.
+ CALL SETFILE
+ CALL CHKNMBR ;keep (SCRATCH1) in bounds.
+ JMP BITMAP2
+;
+; Set the status (STATUS) and return.
+;
+STSTATUS:LDA FNDSTAT
+ JMP SETSTAT
+;
+; Check extents in (A) and (C). Set the zero flag if they
+; are the same. The number of 16k chunks of disk space that
+; the directory extent covers is expressad is (EXTMASK+1).
+; No registers are modified.
+;
+SAMEXT PUSH B
+ PUSH PSW
+ LDA EXTMASK ;get extent mask and use it to
+ CMA ;to compare both extent numbers.
+ MOV B,A ;save resulting mask here.
+ MOV A,C ;mask first extent and save in (C).
+ ANA B
+ MOV C,A
+ POP PSW ;now mask second extent and compare
+ ANA B ;with the first one.
+ SUB C
+ ANI 1FH ;(* only check buts 0-4 *)
+ POP B ;the zero flag is set if they are the same.
+ RET ;restore (BC) and return.
+;
+; Search for the first occurence of a file name. On entry,
+; register (C) should contain the number of bytes of the fcb
+; that must match.
+;
+FINDFST MVI A,0FFH
+ STA FNDSTAT
+ LXI H,COUNTER;save character count.
+ MOV M,C
+ LHLD PARAMS ;get filename to match.
+ SHLD SAVEFCB ;and save.
+ CALL STFILPOS;clear initial file position (set to 0ffffh).
+ CALL HOMEDRV ;home the drive.
+;
+; Entry to locate the next occurence of a filename within the
+; directory. The disk is not expected to have been changed. If
+; it was, then it will be write protected.
+;
+FINDNXT MVI C,0 ;write protect the disk if changed.
+ CALL NXENTRY ;get next filename entry in directory.
+ CALL CKFILPOS;is file position = 0ffffh?
+ JZ FNDNXT6 ;yes, exit now then.
+ LHLD SAVEFCB ;set (DE) pointing to filename to match.
+ XCHG
+ LDAX D
+ CPI 0E5H ;empty directory entry?
+ JZ FNDNXT1 ;(* are we trying to reserect erased entries? *)
+ PUSH D
+ CALL MOREFLS ;more files in directory?
+ POP D
+ JNC FNDNXT6 ;no more. Exit now.
+FNDNXT1 CALL FCB2HL ;get address of this fcb in directory.
+ LDA COUNTER ;get number of bytes (characters) to check.
+ MOV C,A
+ MVI B,0 ;initialize byte position counter.
+FNDNXT2 MOV A,C ;are we done with the compare?
+ ORA A
+ JZ FNDNXT5
+ LDAX D ;no, check next byte.
+ CPI '?' ;don't care about this character?
+ JZ FNDNXT4
+ MOV A,B ;get bytes position in fcb.
+ CPI 13 ;don't care about the thirteenth byte either.
+ JZ FNDNXT4
+ CPI 12 ;extent byte?
+ LDAX D
+ JZ FNDNXT3
+ SUB M ;otherwise compare characters.
+ ANI 7FH
+ JNZ FINDNXT ;not the same, check next entry.
+ JMP FNDNXT4 ;so far so good, keep checking.
+FNDNXT3 PUSH B ;check the extent byte here.
+ MOV C,M
+ CALL SAMEXT
+ POP B
+ JNZ FINDNXT ;not the same, look some more.
+;
+; So far the names compare. Bump pointers to the next byte
+; and continue until all (C) characters have been checked.
+;
+FNDNXT4 INX D ;bump pointers.
+ INX H
+ INR B
+ DCR C ;adjust character counter.
+ JMP FNDNXT2
+FNDNXT5 LDA FILEPOS ;return the position of this entry.
+ ANI 03H
+ STA STATUS
+ LXI H,FNDSTAT
+ MOV A,M
+ RAL
+ RNC
+ XRA A
+ MOV M,A
+ RET
+;
+; Filename was not found. Set appropriate status.
+;
+FNDNXT6 CALL STFILPOS;set (FILEPOS) to 0ffffh.
+ MVI A,0FFH ;say not located.
+ JMP SETSTAT
+;
+; Erase files from the directory. Only the first byte of the
+; fcb will be affected. It is set to (E5).
+;
+ERAFILE CALL CHKWPRT ;is disk write protected?
+ MVI C,12 ;only compare file names.
+ CALL FINDFST ;get first file name.
+ERAFIL1 CALL CKFILPOS;any found?
+ RZ ;nope, we must be done.
+ CALL CHKROFL ;is file read only?
+ CALL FCB2HL ;nope, get address of fcb and
+ MVI M,0E5H ;set first byte to 'empty'.
+ MVI C,0 ;clear the space from the bit map.
+ CALL SETFILE
+ CALL DIRWRITE;now write the directory sector back out.
+ CALL FINDNXT ;find the next file name.
+ JMP ERAFIL1 ;and repeat process.
+;
+; Look through the space allocation map (bit map) for the
+; next available block. Start searching at block number (BC-1).
+; The search procedure is to look for an empty block that is
+; before the starting block. If not empty, look at a later
+; block number. In this way, we return the closest empty block
+; on either side of the 'target' block number. This will speed
+; access on random devices. For serial devices, this should be
+; changed to look in the forward direction first and then start
+; at the front and search some more.
+;
+; On return, (DE)= block number that is empty and (HL) =0
+; if no empry block was found.
+;
+FNDSPACE:MOV D,B ;set (DE) as the block that is checked.
+ MOV E,C
+;
+; Look before target block. Registers (BC) are used as the lower
+; pointer and (DE) as the upper pointer.
+;
+FNDSPA1 MOV A,C ;is block 0 specified?
+ ORA B
+ JZ FNDSPA2
+ DCX B ;nope, check previous block.
+ PUSH D
+ PUSH B
+ CALL CKBITMAP
+ RAR ;is this block empty?
+ JNC FNDSPA3 ;yes. use this.
+;
+; Note that the above logic gets the first block that it finds
+; that is empty. Thus a file could be written 'backward' making
+; it very slow to access. This could be changed to look for the
+; first empty block and then continue until the start of this
+; empty space is located and then used that starting block.
+; This should help speed up access to some files especially on
+; a well used disk with lots of fairly small 'holes'.
+;
+ POP B ;nope, check some more.
+ POP D
+;
+; Now look after target block.
+;
+FNDSPA2 LHLD DSKSIZE ;is block (DE) within disk limits?
+ MOV A,E
+ SUB L
+ MOV A,D
+ SBB H
+ JNC FNDSPA4
+ INX D ;yes, move on to next one.
+ PUSH B
+ PUSH D
+ MOV B,D
+ MOV C,E
+ CALL CKBITMAP;check it.
+ RAR ;empty?
+ JNC FNDSPA3
+ POP D ;nope, continue searching.
+ POP B
+ JMP FNDSPA1
+;
+; Empty block found. Set it as used and return with (HL)
+; pointing to it (true?).
+;
+FNDSPA3 RAL ;reset byte.
+ INR A ;and set bit 0.
+ CALL STBMAP1 ;update bit map.
+ POP H ;set return registers.
+ POP D
+ RET
+;
+; Free block was not found. If (BC) is not zero, then we have
+; not checked all of the disk space.
+;
+FNDSPA4 MOV A,C
+ ORA B
+ JNZ FNDSPA1
+ LXI H,0 ;set 'not found' status.
+ RET
+;
+; Move a complete fcb entry into the directory and write it.
+;
+FCBSET MVI C,0
+ MVI E,32 ;length of each entry.
+;
+; Move (E) bytes from the fcb pointed to by (PARAMS) into
+; fcb in directory starting at relative byte (C). This updated
+; directory buffer is then written to the disk.
+;
+UPDATE PUSH D
+ MVI B,0 ;set (BC) to relative byte position.
+ LHLD PARAMS ;get address of fcb.
+ DAD B ;compute starting byte.
+ XCHG
+ CALL FCB2HL ;get address of fcb to update in directory.
+ POP B ;set (C) to number of bytes to change.
+ CALL DE2HL
+UPDATE1 CALL TRKSEC ;determine the track and sector affected.
+ JMP DIRWRITE ;then write this sector out.
+;
+; Routine to change the name of all files on the disk with a
+; specified name. The fcb contains the current name as the
+; first 12 characters and the new name 16 bytes into the fcb.
+;
+CHGNAMES:CALL CHKWPRT ;check for a write protected disk.
+ MVI C,12 ;match first 12 bytes of fcb only.
+ CALL FINDFST ;get first name.
+ LHLD PARAMS ;get address of fcb.
+ MOV A,M ;get user number.
+ LXI D,16 ;move over to desired name.
+ DAD D
+ MOV M,A ;keep same user number.
+CHGNAM1 CALL CKFILPOS;any matching file found?
+ RZ ;no, we must be done.
+ CALL CHKROFL ;check for read only file.
+ MVI C,16 ;start 16 bytes into fcb.
+ MVI E,12 ;and update the first 12 bytes of directory.
+ CALL UPDATE
+ CALL FINDNXT ;get te next file name.
+ JMP CHGNAM1 ;and continue.
+;
+; Update a files attributes. The procedure is to search for
+; every file with the same name as shown in fcb (ignoring bit 7)
+; and then to update it (which includes bit 7). No other changes
+; are made.
+;
+SAVEATTR:MVI C,12 ;match first 12 bytes.
+ CALL FINDFST ;look for first filename.
+SAVATR1 CALL CKFILPOS;was one found?
+ RZ ;nope, we must be done.
+ MVI C,0 ;yes, update the first 12 bytes now.
+ MVI E,12
+ CALL UPDATE ;update filename and write directory.
+ CALL FINDNXT ;and get the next file.
+ JMP SAVATR1 ;then continue until done.
+;
+; Open a file (name specified in fcb).
+;
+OPENIT MVI C,15 ;compare the first 15 bytes.
+ CALL FINDFST ;get the first one in directory.
+ CALL CKFILPOS;any at all?
+ RZ
+OPENIT1 CALL SETEXT ;point to extent byte within users fcb.
+ MOV A,M ;and get it.
+ PUSH PSW ;save it and address.
+ PUSH H
+ CALL FCB2HL ;point to fcb in directory.
+ XCHG
+ LHLD PARAMS ;this is the users copy.
+ MVI C,32 ;move it into users space.
+ PUSH D
+ CALL DE2HL
+ CALL SETS2B7 ;set bit 7 in 's2' byte (unmodified).
+ POP D ;now get the extent byte from this fcb.
+ LXI H,12
+ DAD D
+ MOV C,M ;into (C).
+ LXI H,15 ;now get the record count byte into (B).
+ DAD D
+ MOV B,M
+ POP H ;keep the same extent as the user had originally.
+ POP PSW
+ MOV M,A
+ MOV A,C ;is it the same as in the directory fcb?
+ CMP M
+ MOV A,B ;if yes, then use the same record count.
+ JZ OPENIT2
+ MVI A,0 ;if the user specified an extent greater than
+ JC OPENIT2 ;the one in the directory, then set record count to 0.
+ MVI A,128 ;otherwise set to maximum.
+OPENIT2 LHLD PARAMS ;set record count in users fcb to (A).
+ LXI D,15
+ DAD D ;compute relative position.
+ MOV M,A ;and set the record count.
+ RET
+;
+; Move two bytes from (DE) to (HL) if (and only if) (HL)
+; point to a zero value (16 bit).
+; Return with zero flag set it (DE) was moved. Registers (DE)
+; and (HL) are not changed. However (A) is.
+;
+MOVEWORD:MOV A,M ;check for a zero word.
+ INX H
+ ORA M ;both bytes zero?
+ DCX H
+ RNZ ;nope, just return.
+ LDAX D ;yes, move two bytes from (DE) into
+ MOV M,A ;this zero space.
+ INX D
+ INX H
+ LDAX D
+ MOV M,A
+ DCX D ;don't disturb these registers.
+ DCX H
+ RET
+;
+; Get here to close a file specified by (fcb).
+;
+CLOSEIT XRA A ;clear status and file position bytes.
+ STA STATUS
+ STA FILEPOS
+ STA FILEPOS+1
+ CALL GETWPRT ;get write protect bit for this drive.
+ RNZ ;just return if it is set.
+ CALL GETS2 ;else get the 's2' byte.
+ ANI 80H ;and look at bit 7 (file unmodified?).
+ RNZ ;just return if set.
+ MVI C,15 ;else look up this file in directory.
+ CALL FINDFST
+ CALL CKFILPOS;was it found?
+ RZ ;just return if not.
+ LXI B,16 ;set (HL) pointing to records used section.
+ CALL FCB2HL
+ DAD B
+ XCHG
+ LHLD PARAMS ;do the same for users specified fcb.
+ DAD B
+ MVI C,16 ;this many bytes are present in this extent.
+CLOSEIT1:LDA BIGDISK ;8 or 16 bit record numbers?
+ ORA A
+ JZ CLOSEIT4
+ MOV A,M ;just 8 bit. Get one from users fcb.
+ ORA A
+ LDAX D ;now get one from directory fcb.
+ JNZ CLOSEIT2
+ MOV M,A ;users byte was zero. Update from directory.
+CLOSEIT2:ORA A
+ JNZ CLOSEIT3
+ MOV A,M ;directories byte was zero, update from users fcb.
+ STAX D
+CLOSEIT3:CMP M ;if neither one of these bytes were zero,
+ JNZ CLOSEIT7 ;then close error if they are not the same.
+ JMP CLOSEIT5 ;ok so far, get to next byte in fcbs.
+CLOSEIT4:CALL MOVEWORD;update users fcb if it is zero.
+ XCHG
+ CALL MOVEWORD;update directories fcb if it is zero.
+ XCHG
+ LDAX D ;if these two values are no different,
+ CMP M ;then a close error occured.
+ JNZ CLOSEIT7
+ INX D ;check second byte.
+ INX H
+ LDAX D
+ CMP M
+ JNZ CLOSEIT7
+ DCR C ;remember 16 bit values.
+CLOSEIT5:INX D ;bump to next item in table.
+ INX H
+ DCR C ;there are 16 entries only.
+ JNZ CLOSEIT1;continue if more to do.
+ LXI B,0FFECH;backup 20 places (extent byte).
+ DAD B
+ XCHG
+ DAD B
+ LDAX D
+ CMP M ;directory's extent already greater than the
+ JC CLOSEIT6 ;users extent?
+ MOV M,A ;no, update directory extent.
+ LXI B,3 ;and update the record count byte in
+ DAD B ;directories fcb.
+ XCHG
+ DAD B
+ MOV A,M ;get from user.
+ STAX D ;and put in directory.
+CLOSEIT6:MVI A,0FFH ;set 'was open and is now closed' byte.
+ STA CLOSEFLG
+ JMP UPDATE1 ;update the directory now.
+CLOSEIT7:LXI H,STATUS;set return status and then return.
+ DCR M
+ RET
+;
+; Routine to get the next empty space in the directory. It
+; will then be cleared for use.
+;
+GETEMPTY:CALL CHKWPRT ;make sure disk is not write protected.
+ LHLD PARAMS ;save current parameters (fcb).
+ PUSH H
+ LXI H,EMPTYFCB;use special one for empty space.
+ SHLD PARAMS
+ MVI C,1 ;search for first empty spot in directory.
+ CALL FINDFST ;(* only check first byte *)
+ CALL CKFILPOS;none?
+ POP H
+ SHLD PARAMS ;restore original fcb address.
+ RZ ;return if no more space.
+ XCHG
+ LXI H,15 ;point to number of records for this file.
+ DAD D
+ MVI C,17 ;and clear all of this space.
+ XRA A
+GETMT1 MOV M,A
+ INX H
+ DCR C
+ JNZ GETMT1
+ LXI H,13 ;clear the 's1' byte also.
+ DAD D
+ MOV M,A
+ CALL CHKNMBR ;keep (SCRATCH1) within bounds.
+ CALL FCBSET ;write out this fcb entry to directory.
+ JMP SETS2B7 ;set 's2' byte bit 7 (unmodified at present).
+;
+; Routine to close the current extent and open the next one
+; for reading.
+;
+GETNEXT XRA A
+ STA CLOSEFLG;clear close flag.
+ CALL CLOSEIT ;close this extent.
+ CALL CKFILPOS
+ RZ ;not there???
+ LHLD PARAMS ;get extent byte.
+ LXI B,12
+ DAD B
+ MOV A,M ;and increment it.
+ INR A
+ ANI 1FH ;keep within range 0-31.
+ MOV M,A
+ JZ GTNEXT1 ;overflow?
+ MOV B,A ;mask extent byte.
+ LDA EXTMASK
+ ANA B
+ LXI H,CLOSEFLG;check close flag (0ffh is ok).
+ ANA M
+ JZ GTNEXT2 ;if zero, we must read in next extent.
+ JMP GTNEXT3 ;else, it is already in memory.
+GTNEXT1 LXI B,2 ;Point to the 's2' byte.
+ DAD B
+ INR M ;and bump it.
+ MOV A,M ;too many extents?
+ ANI 0FH
+ JZ GTNEXT5 ;yes, set error code.
+;
+; Get here to open the next extent.
+;
+GTNEXT2 MVI C,15 ;set to check first 15 bytes of fcb.
+ CALL FINDFST ;find the first one.
+ CALL CKFILPOS;none available?
+ JNZ GTNEXT3
+ LDA RDWRTFLG;no extent present. Can we open an empty one?
+ INR A ;0ffh means reading (so not possible).
+ JZ GTNEXT5 ;or an error.
+ CALL GETEMPTY;we are writing, get an empty entry.
+ CALL CKFILPOS;none?
+ JZ GTNEXT5 ;error if true.
+ JMP GTNEXT4 ;else we are almost done.
+GTNEXT3 CALL OPENIT1 ;open this extent.
+GTNEXT4 CALL STRDATA ;move in updated data (rec #, extent #, etc.)
+ XRA A ;clear status and return.
+ JMP SETSTAT
+;
+; Error in extending the file. Too many extents were needed
+; or not enough space on the disk.
+;
+GTNEXT5 CALL IOERR1 ;set error code, clear bit 7 of 's2'
+ JMP SETS2B7 ;so this is not written on a close.
+;
+; Read a sequential file.
+;
+RDSEQ MVI A,1 ;set sequential access mode.
+ STA MODE
+RDSEQ1 MVI A,0FFH ;don't allow reading unwritten space.
+ STA RDWRTFLG
+ CALL STRDATA ;put rec# and ext# into fcb.
+ LDA SAVNREC ;get next record to read.
+ LXI H,SAVNXT;get number of records in extent.
+ CMP M ;within this extent?
+ JC RDSEQ2
+ CPI 128 ;no. Is this extent fully used?
+ JNZ RDSEQ3 ;no. End-of-file.
+ CALL GETNEXT ;yes, open the next one.
+ XRA A ;reset next record to read.
+ STA SAVNREC
+ LDA STATUS ;check on open, successful?
+ ORA A
+ JNZ RDSEQ3 ;no, error.
+RDSEQ2 CALL COMBLK ;ok. compute block number to read.
+ CALL CHKBLK ;check it. Within bounds?
+ JZ RDSEQ3 ;no, error.
+ CALL LOGICAL ;convert (BLKNMBR) to logical sector (128 byte).
+ CALL TRKSEC1 ;set the track and sector for this block #.
+ CALL DOREAD ;and read it.
+ JMP SETNREC ;and set the next record to be accessed.
+;
+; Read error occured. Set status and return.
+;
+RDSEQ3 JMP IOERR1
+;
+; Write the next sequential record.
+;
+WTSEQ MVI A,1 ;set sequential access mode.
+ STA MODE
+WTSEQ1 MVI A,0 ;allow an addition empty extent to be opened.
+ STA RDWRTFLG
+ CALL CHKWPRT ;check write protect status.
+ LHLD PARAMS
+ CALL CKROF1 ;check for read only file, (HL) already set to fcb.
+ CALL STRDATA ;put updated data into fcb.
+ LDA SAVNREC ;get record number to write.
+ CPI 128 ;within range?
+ JNC IOERR1 ;no, error(?).
+ CALL COMBLK ;compute block number.
+ CALL CHKBLK ;check number.
+ MVI C,0 ;is there one to write to?
+ JNZ WTSEQ6 ;yes, go do it.
+ CALL GETBLOCK;get next block number within fcb to use.
+ STA RELBLOCK;and save.
+ LXI B,0 ;start looking for space from the start
+ ORA A ;if none allocated as yet.
+ JZ WTSEQ2
+ MOV C,A ;extract previous block number from fcb
+ DCX B ;so we can be closest to it.
+ CALL EXTBLK
+ MOV B,H
+ MOV C,L
+WTSEQ2 CALL FNDSPACE;find the next empty block nearest number (BC).
+ MOV A,L ;check for a zero number.
+ ORA H
+ JNZ WTSEQ3
+ MVI A,2 ;no more space?
+ JMP SETSTAT
+WTSEQ3 SHLD BLKNMBR ;save block number to access.
+ XCHG ;put block number into (DE).
+ LHLD PARAMS ;now we must update the fcb for this
+ LXI B,16 ;newly allocated block.
+ DAD B
+ LDA BIGDISK ;8 or 16 bit block numbers?
+ ORA A
+ LDA RELBLOCK ;(* update this entry *)
+ JZ WTSEQ4 ;zero means 16 bit ones.
+ CALL ADDA2HL ;(HL)=(HL)+(A)
+ MOV M,E ;store new block number.
+ JMP WTSEQ5
+WTSEQ4 MOV C,A ;compute spot in this 16 bit table.
+ MVI B,0
+ DAD B
+ DAD B
+ MOV M,E ;stuff block number (DE) there.
+ INX H
+ MOV M,D
+WTSEQ5 MVI C,2 ;set (C) to indicate writing to un-used disk space.
+WTSEQ6 LDA STATUS ;are we ok so far?
+ ORA A
+ RNZ
+ PUSH B ;yes, save write flag for bios (register C).
+ CALL LOGICAL ;convert (BLKNMBR) over to loical sectors.
+ LDA MODE ;get access mode flag (1=sequential,
+ DCR A ;0=random, 2=special?).
+ DCR A
+ JNZ WTSEQ9
+;
+; Special random i/o from function #40. Maybe for M/PM, but the
+; current block, if it has not been written to, will be zeroed
+; out and then written (reason?).
+;
+ POP B
+ PUSH B
+ MOV A,C ;get write status flag (2=writing unused space).
+ DCR A
+ DCR A
+ JNZ WTSEQ9
+ PUSH H
+ LHLD DIRBUF ;zero out the directory buffer.
+ MOV D,A ;note that (A) is zero here.
+WTSEQ7 MOV M,A
+ INX H
+ INR D ;do 128 bytes.
+ JP WTSEQ7
+ CALL DIRDMA ;tell the bios the dma address for directory access.
+ LHLD LOGSECT ;get sector that starts current block.
+ MVI C,2 ;set 'writing to unused space' flag.
+WTSEQ8 SHLD BLKNMBR ;save sector to write.
+ PUSH B
+ CALL TRKSEC1 ;determine its track and sector numbers.
+ POP B
+ CALL DOWRITE ;now write out 128 bytes of zeros.
+ LHLD BLKNMBR ;get sector number.
+ MVI C,0 ;set normal write flag.
+ LDA BLKMASK ;determine if we have written the entire
+ MOV B,A ;physical block.
+ ANA L
+ CMP B
+ INX H ;prepare for the next one.
+ JNZ WTSEQ8 ;continue until (BLKMASK+1) sectors written.
+ POP H ;reset next sector number.
+ SHLD BLKNMBR
+ CALL DEFDMA ;and reset dma address.
+;
+; Normal disk write. Set the desired track and sector then
+; do the actual write.
+;
+WTSEQ9 CALL TRKSEC1 ;determine track and sector for this write.
+ POP B ;get write status flag.
+ PUSH B
+ CALL DOWRITE ;and write this out.
+ POP B
+ LDA SAVNREC ;get number of records in file.
+ LXI H,SAVNXT;get last record written.
+ CMP M
+ JC WTSEQ10
+ MOV M,A ;we have to update record count.
+ INR M
+ MVI C,2
+;
+;* This area has been patched to correct disk update problem
+;* when using blocking and de-blocking in the BIOS.
+;
+WTSEQ10 NOP ;was 'dcr c'
+ NOP ;was 'dcr c'
+ LXI H,0 ;was 'jnz wtseq99'
+;
+; * End of patch.
+;
+ PUSH PSW
+ CALL GETS2 ;set 'extent written to' flag.
+ ANI 7FH ;(* clear bit 7 *)
+ MOV M,A
+ POP PSW ;get record count for this extent.
+WTSEQ99 CPI 127 ;is it full?
+ JNZ WTSEQ12
+ LDA MODE ;yes, are we in sequential mode?
+ CPI 1
+ JNZ WTSEQ12
+ CALL SETNREC ;yes, set next record number.
+ CALL GETNEXT ;and get next empty space in directory.
+ LXI H,STATUS;ok?
+ MOV A,M
+ ORA A
+ JNZ WTSEQ11
+ DCR A ;yes, set record count to -1.
+ STA SAVNREC
+WTSEQ11 MVI M,0 ;clear status.
+WTSEQ12 JMP SETNREC ;set next record to access.
+;
+; For random i/o, set the fcb for the desired record number
+; based on the 'r0,r1,r2' bytes. These bytes in the fcb are
+; used as follows:
+;
+; fcb+35 fcb+34 fcb+33
+; | 'r-2' | 'r-1' | 'r-0' |
+; |7 0 | 7 0 | 7 0|
+; |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0|
+; | overflow | | extra | extent | record # |
+; | ______________| |_extent|__number___|_____________|
+; also 's2'
+;
+; On entry, register (C) contains 0ffh if this is a read
+; and thus we can not access unwritten disk space. Otherwise,
+; another extent will be opened (for writing) if required.
+;
+POSITION:XRA A ;set random i/o flag.
+ STA MODE
+;
+; Special entry (function #40). M/PM ?
+;
+POSITN1 PUSH B ;save read/write flag.
+ LHLD PARAMS ;get address of fcb.
+ XCHG
+ LXI H,33 ;now get byte 'r0'.
+ DAD D
+ MOV A,M
+ ANI 7FH ;keep bits 0-6 for the record number to access.
+ PUSH PSW
+ MOV A,M ;now get bit 7 of 'r0' and bits 0-3 of 'r1'.
+ RAL
+ INX H
+ MOV A,M
+ RAL
+ ANI 1FH ;and save this in bits 0-4 of (C).
+ MOV C,A ;this is the extent byte.
+ MOV A,M ;now get the extra extent byte.
+ RAR
+ RAR
+ RAR
+ RAR
+ ANI 0FH
+ MOV B,A ;and save it in (B).
+ POP PSW ;get record number back to (A).
+ INX H ;check overflow byte 'r2'.
+ MOV L,M
+ INR L
+ DCR L
+ MVI L,6 ;prepare for error.
+ JNZ POSITN5 ;out of disk space error.
+ LXI H,32 ;store record number into fcb.
+ DAD D
+ MOV M,A
+ LXI H,12 ;and now check the extent byte.
+ DAD D
+ MOV A,C
+ SUB M ;same extent as before?
+ JNZ POSITN2
+ LXI H,14 ;yes, check extra extent byte 's2' also.
+ DAD D
+ MOV A,B
+ SUB M
+ ANI 7FH
+ JZ POSITN3;same, we are almost done then.
+;
+; Get here when another extent is required.
+;
+POSITN2 PUSH B
+ PUSH D
+ CALL CLOSEIT ;close current extent.
+ POP D
+ POP B
+ MVI L,3 ;prepare for error.
+ LDA STATUS
+ INR A
+ JZ POSITN4 ;close error.
+ LXI H,12 ;put desired extent into fcb now.
+ DAD D
+ MOV M,C
+ LXI H,14 ;and store extra extent byte 's2'.
+ DAD D
+ MOV M,B
+ CALL OPENIT ;try and get this extent.
+ LDA STATUS ;was it there?
+ INR A
+ JNZ POSITN3
+ POP B ;no. can we create a new one (writing?).
+ PUSH B
+ MVI L,4 ;prepare for error.
+ INR C
+ JZ POSITN4 ;nope, reading unwritten space error.
+ CALL GETEMPTY;yes we can, try to find space.
+ MVI L,5 ;prepare for error.
+ LDA STATUS
+ INR A
+ JZ POSITN4 ;out of space?
+;
+; Normal return location. Clear error code and return.
+;
+POSITN3 POP B ;restore stack.
+ XRA A ;and clear error code byte.
+ JMP SETSTAT
+;
+; Error. Set the 's2' byte to indicate this (why?).
+;
+POSITN4 PUSH H
+ CALL GETS2
+ MVI M,0C0H
+ POP H
+;
+; Return with error code (presently in L).
+;
+POSITN5 POP B
+ MOV A,L ;get error code.
+ STA STATUS
+ JMP SETS2B7
+;
+; Read a random record.
+;
+READRAN MVI C,0FFH ;set 'read' status.
+ CALL POSITION;position the file to proper record.
+ CZ RDSEQ1 ;and read it as usual (if no errors).
+ RET
+;
+; Write to a random record.
+;
+WRITERAN:MVI C,0 ;set 'writing' flag.
+ CALL POSITION;position the file to proper record.
+ CZ WTSEQ1 ;and write as usual (if no errors).
+ RET
+;
+; Compute the random record number. Enter with (HL) pointing
+; to a fcb an (DE) contains a relative location of a record
+; number. On exit, (C) contains the 'r0' byte, (B) the 'r1'
+; byte, and (A) the 'r2' byte.
+;
+; On return, the zero flag is set if the record is within
+; bounds. Otherwise, an overflow occured.
+;
+COMPRAND:XCHG ;save fcb pointer in (DE).
+ DAD D ;compute relative position of record #.
+ MOV C,M ;get record number into (BC).
+ MVI B,0
+ LXI H,12 ;now get extent.
+ DAD D
+ MOV A,M ;compute (BC)=(record #)+(extent)*128.
+ RRC ;move lower bit into bit 7.
+ ANI 80H ;and ignore all other bits.
+ ADD C ;add to our record number.
+ MOV C,A
+ MVI A,0 ;take care of any carry.
+ ADC B
+ MOV B,A
+ MOV A,M ;now get the upper bits of extent into
+ RRC ;bit positions 0-3.
+ ANI 0FH ;and ignore all others.
+ ADD B ;add this in to 'r1' byte.
+ MOV B,A
+ LXI H,14 ;get the 's2' byte (extra extent).
+ DAD D
+ MOV A,M
+ ADD A ;and shift it left 4 bits (bits 4-7).
+ ADD A
+ ADD A
+ ADD A
+ PUSH PSW ;save carry flag (bit 0 of flag byte).
+ ADD B ;now add extra extent into 'r1'.
+ MOV B,A
+ PUSH PSW ;and save carry (overflow byte 'r2').
+ POP H ;bit 0 of (L) is the overflow indicator.
+ MOV A,L
+ POP H ;and same for first carry flag.
+ ORA L ;either one of these set?
+ ANI 01H ;only check the carry flags.
+ RET
+;
+; Routine to setup the fcb (bytes 'r0', 'r1', 'r2') to
+; reflect the last record used for a random (or other) file.
+; This reads the directory and looks at all extents computing
+; the largerst record number for each and keeping the maximum
+; value only. Then 'r0', 'r1', and 'r2' will reflect this
+; maximum record number. This is used to compute the space used
+; by a random file.
+;
+RANSIZE MVI C,12 ;look thru directory for first entry with
+ CALL FINDFST ;this name.
+ LHLD PARAMS ;zero out the 'r0, r1, r2' bytes.
+ LXI D,33
+ DAD D
+ PUSH H
+ MOV M,D ;note that (D)=0.
+ INX H
+ MOV M,D
+ INX H
+ MOV M,D
+RANSIZ1 CALL CKFILPOS;is there an extent to process?
+ JZ RANSIZ3 ;no, we are done.
+ CALL FCB2HL ;set (HL) pointing to proper fcb in dir.
+ LXI D,15 ;point to last record in extent.
+ CALL COMPRAND;and compute random parameters.
+ POP H
+ PUSH H ;now check these values against those
+ MOV E,A ;already in fcb.
+ MOV A,C ;the carry flag will be set if those
+ SUB M ;in the fcb represent a larger size than
+ INX H ;this extent does.
+ MOV A,B
+ SBB M
+ INX H
+ MOV A,E
+ SBB M
+ JC RANSIZ2
+ MOV M,E ;we found a larger (in size) extent.
+ DCX H ;stuff these values into fcb.
+ MOV M,B
+ DCX H
+ MOV M,C
+RANSIZ2 CALL FINDNXT ;now get the next extent.
+ JMP RANSIZ1 ;continue til all done.
+RANSIZ3 POP H ;we are done, restore the stack and
+ RET ;return.
+;
+; Function to return the random record position of a given
+; file which has been read in sequential mode up to now.
+;
+SETRAN LHLD PARAMS ;point to fcb.
+ LXI D,32 ;and to last used record.
+ CALL COMPRAND;compute random position.
+ LXI H,33 ;now stuff these values into fcb.
+ DAD D
+ MOV M,C ;move 'r0'.
+ INX H
+ MOV M,B ;and 'r1'.
+ INX H
+ MOV M,A ;and lastly 'r2'.
+ RET
+;
+; This routine select the drive specified in (ACTIVE) and
+; update the login vector and bitmap table if this drive was
+; not already active.
+;
+LOGINDRV:LHLD LOGIN ;get the login vector.
+ LDA ACTIVE ;get the default drive.
+ MOV C,A
+ CALL SHIFTR ;position active bit for this drive
+ PUSH H ;into bit 0.
+ XCHG
+ CALL SELECT ;select this drive.
+ POP H
+ CZ SLCTERR ;valid drive?
+ MOV A,L ;is this a newly activated drive?
+ RAR
+ RC
+ LHLD LOGIN ;yes, update the login vector.
+ MOV C,L
+ MOV B,H
+ CALL SETBIT
+ SHLD LOGIN ;and save.
+ JMP BITMAP ;now update the bitmap.
+;
+; Function to set the active disk number.
+;
+SETDSK LDA EPARAM ;get parameter passed and see if this
+ LXI H,ACTIVE;represents a change in drives.
+ CMP M
+ RZ
+ MOV M,A ;yes it does, log it in.
+ JMP LOGINDRV
+;
+; This is the 'auto disk select' routine. The firsst byte
+; of the fcb is examined for a drive specification. If non
+; zero then the drive will be selected and loged in.
+;
+AUTOSEL MVI A,0FFH ;say 'auto-select activated'.
+ STA AUTO
+ LHLD PARAMS ;get drive specified.
+ MOV A,M
+ ANI 1FH ;look at lower 5 bits.
+ DCR A ;adjust for (1=A, 2=B) etc.
+ STA EPARAM ;and save for the select routine.
+ CPI 1EH ;check for 'no change' condition.
+ JNC AUTOSL1 ;yes, don't change.
+ LDA ACTIVE ;we must change, save currently active
+ STA OLDDRV ;drive.
+ MOV A,M ;and save first byte of fcb also.
+ STA AUTOFLAG;this must be non-zero.
+ ANI 0E0H ;whats this for (bits 6,7 are used for
+ MOV M,A ;something)?
+ CALL SETDSK ;select and log in this drive.
+AUTOSL1 LDA USERNO ;move user number into fcb.
+ LHLD PARAMS ;(* upper half of first byte *)
+ ORA M
+ MOV M,A
+ RET ;and return (all done).
+;
+; Function to return the current cp/m version number.
+;
+GETVER MVI A,022h ;version 2.2
+ JMP SETSTAT
+;
+; Function to reset the disk system.
+;
+RSTDSK LXI H,0 ;clear write protect status and log
+ SHLD WRTPRT ;in vector.
+ SHLD LOGIN
+ XRA A ;select drive 'A'.
+ STA ACTIVE
+ LXI H,TBUFF ;setup default dma address.
+ SHLD USERDMA
+ CALL DEFDMA
+ JMP LOGINDRV;now log in drive 'A'.
+;
+; Function to open a specified file.
+;
+OPENFIL CALL CLEARS2 ;clear 's2' byte.
+ CALL AUTOSEL ;select proper disk.
+ JMP OPENIT ;and open the file.
+;
+; Function to close a specified file.
+;
+CLOSEFIL:CALL AUTOSEL ;select proper disk.
+ JMP CLOSEIT ;and close the file.
+;
+; Function to return the first occurence of a specified file
+; name. If the first byte of the fcb is '?' then the name will
+; not be checked (get the first entry no matter what).
+;
+GETFST MVI C,0 ;prepare for special search.
+ XCHG
+ MOV A,M ;is first byte a '?'?
+ CPI '?'
+ JZ GETFST1 ;yes, just get very first entry (zero length match).
+ CALL SETEXT ;get the extension byte from fcb.
+ MOV A,M ;is it '?'? if yes, then we want
+ CPI '?' ;an entry with a specific 's2' byte.
+ CNZ CLEARS2 ;otherwise, look for a zero 's2' byte.
+ CALL AUTOSEL ;select proper drive.
+ MVI C,15 ;compare bytes 0-14 in fcb (12&13 excluded).
+GETFST1 CALL FINDFST ;find an entry and then move it into
+ JMP MOVEDIR ;the users dma space.
+;
+; Function to return the next occurence of a file name.
+;
+GETNXT LHLD SAVEFCB ;restore pointers. note that no
+ SHLD PARAMS ;other dbos calls are allowed.
+ CALL AUTOSEL ;no error will be returned, but the
+ CALL FINDNXT ;results will be wrong.
+ JMP MOVEDIR
+;
+; Function to delete a file by name.
+;
+DELFILE CALL AUTOSEL ;select proper drive.
+ CALL ERAFILE ;erase the file.
+ JMP STSTATUS;set status and return.
+;
+; Function to execute a sequential read of the specified
+; record number.
+;
+READSEQ CALL AUTOSEL ;select proper drive then read.
+ JMP RDSEQ
+;
+; Function to write the net sequential record.
+;
+WRTSEQ CALL AUTOSEL ;select proper drive then write.
+ JMP WTSEQ
+;
+; Create a file function.
+;
+FCREATE CALL CLEARS2 ;clear the 's2' byte on all creates.
+ CALL AUTOSEL ;select proper drive and get the next
+ JMP GETEMPTY;empty directory space.
+;
+; Function to rename a file.
+;
+RENFILE CALL AUTOSEL ;select proper drive and then switch
+ CALL CHGNAMES;file names.
+ JMP STSTATUS
+;
+; Function to return the login vector.
+;
+GETLOG LHLD LOGIN
+ JMP GETPRM1
+;
+; Function to return the current disk assignment.
+;
+GETCRNT LDA ACTIVE
+ JMP SETSTAT
+;
+; Function to set the dma address.
+;
+PUTDMA XCHG
+ SHLD USERDMA ;save in our space and then get to
+ JMP DEFDMA ;the bios with this also.
+;
+; Function to return the allocation vector.
+;
+GETALOC LHLD ALOCVECT
+ JMP GETPRM1
+;
+; Function to return the read-only status vector.
+;
+GETROV LHLD WRTPRT
+ JMP GETPRM1
+;
+; Function to set the file attributes (read-only, system).
+;
+SETATTR CALL AUTOSEL ;select proper drive then save attributes.
+ CALL SAVEATTR
+ JMP STSTATUS
+;
+; Function to return the address of the disk parameter block
+; for the current drive.
+;
+GETPARM LHLD DISKPB
+GETPRM1 SHLD STATUS
+ RET
+;
+; Function to get or set the user number. If (E) was (FF)
+; then this is a request to return the current user number.
+; Else set the user number from (E).
+;
+GETUSER LDA EPARAM ;get parameter.
+ CPI 0FFH ;get user number?
+ JNZ SETUSER
+ LDA USERNO ;yes, just do it.
+ JMP SETSTAT
+SETUSER ANI 1FH ;no, we should set it instead. keep low
+ STA USERNO ;bits (0-4) only.
+ RET
+;
+; Function to read a random record from a file.
+;
+RDRANDOM:CALL AUTOSEL ;select proper drive and read.
+ JMP READRAN
+;
+; Function to compute the file size for random files.
+;
+WTRANDOM:CALL AUTOSEL ;select proper drive and write.
+ JMP WRITERAN
+;
+; Function to compute the size of a random file.
+;
+FILESIZE:CALL AUTOSEL ;select proper drive and check file length
+ JMP RANSIZE
+;
+; Function #37. This allows a program to log off any drives.
+; On entry, set (DE) to contain a word with bits set for those
+; drives that are to be logged off. The log-in vector and the
+; write protect vector will be updated. This must be a M/PM
+; special function.
+;
+LOGOFF LHLD PARAMS ;get drives to log off.
+ MOV A,L ;for each bit that is set, we want
+ CMA ;to clear that bit in (LOGIN)
+ MOV E,A ;and (WRTPRT).
+ MOV A,H
+ CMA
+ LHLD LOGIN ;reset the login vector.
+ ANA H
+ MOV D,A
+ MOV A,L
+ ANA E
+ MOV E,A
+ LHLD WRTPRT
+ XCHG
+ SHLD LOGIN ;and save.
+ MOV A,L ;now do the write protect vector.
+ ANA E
+ MOV L,A
+ MOV A,H
+ ANA D
+ MOV H,A
+ SHLD WRTPRT ;and save. all done.
+ RET
+;
+; Get here to return to the user.
+;
+GOBACK LDA AUTO ;was auto select activated?
+ ORA A
+ JZ GOBACK1
+ LHLD PARAMS ;yes, but was a change made?
+ MVI M,0 ;(* reset first byte of fcb *)
+ LDA AUTOFLAG
+ ORA A
+ JZ GOBACK1
+ MOV M,A ;yes, reset first byte properly.
+ LDA OLDDRV ;and get the old drive and select it.
+ STA EPARAM
+ CALL SETDSK
+GOBACK1 LHLD USRSTACK;reset the users stack pointer.
+ SPHL
+ LHLD STATUS ;get return status.
+ MOV A,L ;force version 1.4 compatability.
+ MOV B,H
+ RET ;and go back to user.
+;
+; Function #40. This is a special entry to do random i/o.
+; For the case where we are writing to unused disk space, this
+; space will be zeroed out first. This must be a M/PM special
+; purpose function, because why would any normal program even
+; care about the previous contents of a sector about to be
+; written over.
+;
+WTSPECL CALL AUTOSEL ;select proper drive.
+ MVI A,2 ;use special write mode.
+ STA MODE
+ MVI C,0 ;set write indicator.
+ CALL POSITN1 ;position the file.
+ CZ WTSEQ1 ;and write (if no errors).
+ RET
+;
+;**************************************************************
+;*
+;* BDOS data storage pool.
+;*
+;**************************************************************
+;
+EMPTYFCB:DB 0E5H ;empty directory segment indicator.
+WRTPRT DW 0 ;write protect status for all 16 drives.
+LOGIN DW 0 ;drive active word (1 bit per drive).
+USERDMA DW 080H ;user's dma address (defaults to 80h).
+;
+; Scratch areas from parameter block.
+;
+SCRATCH1:DW 0 ;relative position within dir segment for file (0-3).
+SCRATCH2:DW 0 ;last selected track number.
+SCRATCH3:DW 0 ;last selected sector number.
+;
+; Disk storage areas from parameter block.
+;
+DIRBUF DW 0 ;address of directory buffer to use.
+DISKPB DW 0 ;contains address of disk parameter block.
+CHKVECT DW 0 ;address of check vector.
+ALOCVECT:DW 0 ;address of allocation vector (bit map).
+;
+; Parameter block returned from the bios.
+;
+SECTORS DW 0 ;sectors per track from bios.
+BLKSHFT DB 0 ;block shift.
+BLKMASK DB 0 ;block mask.
+EXTMASK DB 0 ;extent mask.
+DSKSIZE DW 0 ;disk size from bios (number of blocks-1).
+DIRSIZE DW 0 ;directory size.
+ALLOC0 DW 0 ;storage for first bytes of bit map (dir space used).
+ALLOC1 DW 0
+OFFSET DW 0 ;first usable track number.
+XLATE DW 0 ;sector translation table address.
+;
+;
+CLOSEFLG:DB 0 ;close flag (=0ffh is extent written ok).
+RDWRTFLG:DB 0 ;read/write flag (0ffh=read, 0=write).
+FNDSTAT DB 0 ;filename found status (0=found first entry).
+MODE DB 0 ;I/o mode select (0=random, 1=sequential, 2=special random).
+EPARAM DB 0 ;storage for register (E) on entry to bdos.
+RELBLOCK:DB 0 ;relative position within fcb of block number written.
+COUNTER DB 0 ;byte counter for directory name searches.
+SAVEFCB DW 0,0 ;save space for address of fcb (for directory searches).
+BIGDISK DB 0 ;if =0 then disk is > 256 blocks long.
+AUTO DB 0 ;if non-zero, then auto select activated.
+OLDDRV DB 0 ;on auto select, storage for previous drive.
+AUTOFLAG:DB 0 ;if non-zero, then auto select changed drives.
+SAVNXT DB 0 ;storage for next record number to access.
+SAVEXT DB 0 ;storage for extent number of file.
+SAVNREC DW 0 ;storage for number of records in file.
+BLKNMBR DW 0 ;block number (physical sector) used within a file or logical sector.
+LOGSECT DW 0 ;starting logical (128 byte) sector of block (physical sector).
+FCBPOS DB 0 ;relative position within buffer for fcb of file of interest.
+FILEPOS DW 0 ;files position within directory (0 to max entries -1).
+;
+; Disk directory buffer checksum bytes. One for each of the
+; 16 possible drives.
+;
+CKSUMTBL:DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+;
+; Extra space ?
+;
+ DB 0,0,0,0
+;
+;**************************************************************
+;*
+;* B I O S J U M P T A B L E
+;*
+;**************************************************************
+;
+BOOT JMP 0 ;NOTE WE USE FAKE DESTINATIONS
+WBOOT JMP 0
+CONST JMP 0
+CONIN JMP 0
+CONOUT JMP 0
+LIST JMP 0
+PUNCH JMP 0
+READER JMP 0
+HOME JMP 0
+SELDSK JMP 0
+SETTRK JMP 0
+SETSEC JMP 0
+SETDMA JMP 0
+READ JMP 0
+WRITE JMP 0
+PRSTAT JMP 0
+SECTRN JMP 0
+;
+;*
+;****************** E N D O F C P / M *****************
+;*
+
diff --git a/runtime/CSharp/tests/issue-2693/test.sh b/runtime/CSharp/tests/issue-2693/test.sh
new file mode 100644
index 000000000..8efb0a1e0
--- /dev/null
+++ b/runtime/CSharp/tests/issue-2693/test.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/bash
+
+dotnet restore
+dotnet build
+dotnet run -file cpm22.asm
+if [[ "$?" != "0" ]]
+then
+ echo "Issue 2693 test failed."
+ exit 1
+fi
+