Future<void>
runProgramLoop(- {Set<int> acceptableErrors = const <int>{}}
)
Implementation
Future<void> runProgramLoop(
{Set<int> acceptableErrors = const <int>{}}) async {
final ProgramListener listener = model.memory.program.programListener;
final settings = controller.model.settings;
final program = model.memory.program;
var lastDelay = DateTime.now();
for (;;) {
if (!_stopNext) {
if (pendingDelay >= 4) {
lastDelay = DateTime.now();
await (Future<void>.delayed(
Duration(milliseconds: (pendingDelay ~/ 4) * 4)));
} else {
final now = DateTime.now();
if (now.difference(lastDelay).inMilliseconds >= 1000) {
// Delay at least 4 ms every second, so the calculator doesn't become
// non-responsive if the user cranks the speed up all the way.
await Future<void>.delayed(const Duration(milliseconds: 4));
lastDelay = now;
}
}
}
// Javascript clock granularity is 4ms
pendingDelay = pendingDelay % 4;
final int line = program.currentLine;
final ProgramInstruction<Operation> instr = program[line];
if (instr.op == Operations.rs) {
// If we let it execute, it would recursively create another state!
listener.onRS();
_stopNext = true;
program.incrementCurrentLine();
} else {
int oldLine = program.currentLine;
program.incrementCurrentLine();
_fake.setArg(instr.arg);
_fake.buttonDown(instr.op);
// This bounces back to RunningController.runWithArg() if there's an
// argument.
_fake.buttonUp();
if (_fake.pendingError != null) {
program.currentLine = oldLine;
}
}
model.addProgramTraceToSnapshot(
() => ' ${line.toString().padLeft(3, '0')}'
' ${instr.programListing.padRight(14)}');
if (settings.traceProgramToStdout) {
final out = StringBuffer();
// ignore: dead_code
if (false) {
// Simplified version, useful for comparisons
out.write(line.toString());
out.write(' ');
out.write(model.xF.toStringAsExponential(9));
out.write(' ');
out.write(model.yF.toStringAsExponential(9));
} else {
out.write(' ');
out.write(line.toString().padLeft(3, '0'));
out.write(' ');
out.write(instr.programListing.padRight(14));
out.write('xyzt:');
for (int i = 0; i < 4; i++) {
out.write(' ');
final Value v = model.getStackByIndex(i);
final int? vm = v.asMatrix;
if (vm != null) {
out.write(String.fromCharCodes([('A'.codeUnitAt(0) + vm)]));
} else if (model.isComplexMode) {
out.write(model.getStackByIndexC(i));
} else if (model.isFloatMode) {
out.write(v.asDouble);
} else {
out.write('0x');
out.write(v.internal.toRadixString(16));
}
}
}
// ignore: avoid_print
print(out.toString());
// @@ print(program.debugReturnStack());
if (_fake.pendingError != null) {
// ignore: avoid_print
print("*********** ${_fake.pendingError}");
}
}
pendingDelay =
settings.msPerInstruction / ((instr.op.numericValue == null) ? 1 : 5);
// While we're not simulating real instruction time, the number keys
// are a lot faster than most other operations on a real calculator,
// and they might be pretty common. At a guess, we say 5x faster.
final err = _fake.pendingError;
if (_fake.pause != null && err == null && !_stopNext) {
// PSE instruction, show-BIN, etc.
final updateDisplay = _fake.pause!;
_fake.pause = null;
listener.onPause();
showRunningTimer.cancel();
model.displayDisabled = false;
int? window = updateDisplay();
await listener.resumeFromPause();
if (window != null) {
model.display.window = window;
}
model.displayDisabled = true;
showRunningTimer = _showRunning();
}
if (program.returnStackPos < _runner.returnStackStartPos) {
// If we've popped off our return value
assert(program.returnStackPos == -1 ||
program.currentLine == MProgramRunner.pseudoReturnAddress);
assert(_pushedRunner == null);
break;
} else if (err != null) {
assert(_pushedRunner == null);
if (acceptableErrors.contains(err.num15)) {
_fake.pendingError = null;
throw err;
}
listener.onError(err);
_onDone();
_stopNext = false;
await _runner.suspend();
_onStart();
} else if (_stopNext) {
_stopNext = false;
listener.onStop();
_onDone();
await _runner.suspend();
_onStart();
}
if (_pushedRunner != null) {
final parent = _runner;
_runner = _pushedRunner!;
_pushedRunner = null;
_runner.pushPseudoReturn(model);
_runner.returnStackStartPos = program.returnStackPos;
await _runner._run(this);
_runner = parent;
}
}
}