diff --git a/Source/Emulator/io.c b/Source/Emulator/io.c index 1f6de28..6137777 100644 --- a/Source/Emulator/io.c +++ b/Source/Emulator/io.c @@ -386,6 +386,12 @@ static int (*inputHook)(int mayWait) = NULL; // cursor blinking at somebody who is thinking about what to type. #define CONSOLE_WAIT_CYCLES 16667 +// The same span in milliseconds, for waiting on a terminal rather than on a window. The two +// have to mean the same length of time: one is what the devices are told went by, the other +// is how long was really spent, and a machine whose clock ran at a different rate from the +// wall while it was waiting would be a machine whose music slowed down when nobody typed. +#define CONSOLE_WAIT_MS 17 + static unsigned long idleCycles = 0; // The machine's clock as the devices last heard it. Kept here rather than passed about, @@ -487,6 +493,9 @@ void consoleSetInputHook(int (*hook)(int mayWait)) { // Answers a byte, CONSOLE_NOTHING_YET when nothing is waiting and it was told not to wait, // or CONSOLE_GONE at the end of input. Deliberately the same three answers a front end's // hook gives, so that everything above this treats a terminal and a window alike. +// Whether standard input is really a terminal, defined below and wanted here. +static int consoleTranslatingKeys(void); + static int consoleFromInput(int mayWait) { if (!mayWait) { struct pollfd waiting = { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 }; @@ -496,6 +505,37 @@ static int consoleFromInput(int mayWait) { } unsigned char byte; for (;;) { + // ---- WAITING FOR A KEY IS NOT THE SAME AS TIME STOPPING ---- + // + // This used to block in read, and while it was blocked nothing told the devices that + // any time had gone by - so the whole machine stood still. A window has never done + // that: consoleGatherLine and the key mode loop both spend a frame and then say so, + // and the comment on consoleWaited gives the reason. A display controller blinking a + // cursor does not stop because the processor is waiting on a key, and neither does a + // disk finishing a read, or a synthesiser finishing a note. + // + // A note left in its release was the way this showed. Frozen mid-decay, it came out + // a snippet at a time, one per keystroke, because a keystroke was the only thing that + // let the machine run. Four seconds of sitting at the prompt bought no cycles at all. + // + // ONLY WHEN THERE IS REALLY A TERMINAL, which is the same rule the escape sequences + // and the erase character follow. A file or a pipe must keep blocking: emulated time + // would otherwise depend on how fast the other end of the pipe was writing, and every + // recorded test on this machine is reproducible because it does not. + if (consoleTranslatingKeys()) { + struct pollfd waiting = { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 }; + const int ready = poll(&waiting, 1, CONSOLE_WAIT_MS); + if (ready == 0) { + consoleWaited(); + continue; + } + if (ready < 0) { + if (errno == EINTR) { + continue; + } + return CONSOLE_GONE; + } + } const ssize_t got = read(STDIN_FILENO, &byte, 1); if (got == 1) { return byte; diff --git a/SplitBit Programming Manual.md b/SplitBit Programming Manual.md index 0fb55f9..5bbb31f 100644 --- a/SplitBit Programming Manual.md +++ b/SplitBit Programming Manual.md @@ -190,6 +190,12 @@ is how a program says it has finished; WAIT stops only the CPU's use of the bus. runs, devices run, and the moment any of them raises a line the CPU carries on with the instruction after the WAIT. +**The same is true of a program waiting on a key.** A console read that has nothing to hand +over stops the CPU and not the machine: the clock goes on, the cursor goes on blinking, a disk +goes on settling and a note goes on decaying. Time spent waiting for somebody to type is time, +and a device that stopped because the processor was waiting would be a device that behaved +differently depending on how fast the person at the keyboard was. + **A line that is already up means there is nothing to wait for**, and WAIT does nothing at all. That is what makes the ordinary shape of it safe: