All files / src printer.js

100% Statements 23/23
100% Branches 8/8
100% Functions 5/5
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53            2x       16x 16x 16x 16x 16x       131160x   65580x   65580x 65580x 65580x       8x   8x         4x         65580x 65580x 1x 1x   65580x 7x 7x   65580x      
/**
 * Centronics printer attached to the user VIA: acknowledges each character and keeps the most
 * recent output so it can be shown whenever a printer window is opened. Dispatches "output" with
 * each character as its detail, and "first-output" once, when the machine first prints.
 */
 
export const MaxBufferedChars = 64 * 1024;
 
export class Printer extends EventTarget {
    constructor() {
        super();
        this._uservia = null;
        this._olderText = "";
        this._newerText = "";
        this._hasPrinted = false;
    }
 
    outputStrobe(level, output) {
        if (!output || level) return;
 
        const uservia = this._uservia;
        // Ack the character by pulsing CA1 low.
        uservia.setca1(false);
        uservia.setca1(true);
        this._append(String.fromCharCode(uservia.ora));
    }
 
    attach(uservia) {
        this._uservia = uservia;
        // The ack line idles high, so that each ack is a falling edge.
        uservia.setca1(true);
    }
 
    /** The most recent output, at most MaxBufferedChars of it. */
    get text() {
        return (this._olderText + this._newerText).slice(-MaxBufferedChars);
    }
 
    _append(char) {
        // Two chunks so the bound costs a trim when the text is read, not one per character.
        this._newerText += char;
        if (this._newerText.length >= MaxBufferedChars) {
            this._olderText = this._newerText;
            this._newerText = "";
        }
        if (!this._hasPrinted) {
            this._hasPrinted = true;
            this.dispatchEvent(new Event("first-output"));
        }
        this.dispatchEvent(new CustomEvent("output", { detail: char }));
    }
}