runProgramLoop method Null safety

Future<void> runProgramLoop(
  1. {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;
    }
  }
}