SimpleTerminalImpl.mesa
Derived from SimpleTTY.mesa last edited by Forrest January 6, 1981 7:28 PM
last edited by Levin on December 15, 1983 4:40 pm
last edited by McGregor on April 22, 1982 4:23 pm
Last Edited by: Birrell, July 7, 1983 3:20 pm
Last Edited by: MBrown, September 17, 1983 8:18 pm
DIRECTORY
Ascii USING [BS, CR, FF, SP, TAB],
Basics USING [bitsPerWord],
BasicTime USING [earliestGMT, FromPupTime, GMT, Now, Period],
Convert USING [RopeFromTime],
File USING [GetVolumeName, SystemVolume, Volume],
IO USING [
card, CreateStream, CreateStreamProcs, PutFR, STREAM, StreamProcs, time],
KeyboardFace USING [KeyBits, KeyStation],
Keys USING [KeyBits],
PrincOps USING [BBptr, BBTableSpace, BitAddress],
PrincOpsUtils USING [AlignedBBTable, BITAND, BITBLT, Codebase, MyGlobalFrame],
Process USING [
Abort, DisableTimeout, Pause, priorityForeground, priorityRealTime, SecondsToTicks, SetPriority, SetTimeout, Ticks],
Rope USING [Fetch, Length, ROPE],
SimpleTerminal USING [],
SystemVersion USING [bootFileDate, release],
Terminal USING [
Current, GetBitBltTable, GetBWBitmapState, GetKeys, GetMousePosition, Position, RegisterNotifier, Select, SetBWBackground, SetBWBitmapState, SetBWCursorPosition, SetMousePosition, SwapNotifier, Virtual, WaitForBWVerticalRetrace];
SimpleTerminalImpl: MONITOR LOCKS m USING m: POINTER TO MONITORLOCK
IMPORTS BasicTime, Convert, File, IO, PrincOpsUtils, Process, Rope, SystemVersion, Terminal
EXPORTS SimpleTerminal =
BEGIN
ROPE: TYPE = Rope.ROPE;
Constraints to satisfy externally:
A statevector is needed at priority 6 for the keyboard watching process, at least until facilities exist to let it allocate one itself.
If keyboard watcher is to avoid faults, its code and global frame should be pinned.
State: TYPE = {off, turningOn, on, turningOff, joining};
Global variables protected by "terminalLock"
terminalLock: MONITORLOCK;
state: State ← off;
stateChange: CONDITION ← [timeout: 0];
terminal: Terminal.Virtual ← NIL;
turnOnCount: NAT ← 0;
originalTerminal: Terminal.Virtual;
inStream, outStream: IO.STREAMNIL;
keyboardWatcher, timePainter: PROCESS;
Exported to SimpleTerminal
TurnOn: PUBLIC SAFE PROC [nameStripe: ROPENIL] RETURNS [in, out: IO.STREAM] = TRUSTED {
TurnOnEntryA: ENTRY PROC [m: POINTER TO MONITORLOCK]
RETURNS [turnOnNeeded: BOOL] = -- INLINE -- {
oldState: State = state;
DO
SELECT state FROM
off => {
IF turnOnCount ~= 0 THEN ERROR;
originalTerminal ← Terminal.Current[];
state ← turningOn;
EXIT
};
turningOff => {
Shutdown was deferred and is still pending. We simply cancel it.
state ← on;
EXIT
};
on => EXIT;
ENDCASE;
WAIT stateChange;
ENDLOOP;
[] ← terminal.Select[];
turnOnCount ← turnOnCount.SUCC;
IF oldState ~= state THEN BROADCAST stateChange;
RETURN[state ~= on]
};
TurnOnEntryB: ENTRY PROC [m: POINTER TO MONITORLOCK] = -- INLINE -- {
state ← on;
BROADCAST stateChange;
};
IF TurnOnEntryA[@terminalLock] THEN {
IF nameStripe = NIL THEN
nameStripe ← IO.PutFR["Cedar %g.%g.%g of %t",
IO.card[SystemVersion.release.major],
IO.card[SystemVersion.release.minor],
IO.card[SystemVersion.release.patch],
IO.time[BasicTime.FromPupTime[SystemVersion.bootFileDate]]
];
InitScreen[nameStripe];
keyboardWatcher ← FORK ProcessKeyboard[];
inStream ← IO.CreateStream[inStreamProcs, NIL];
outStream ← IO.CreateStream[outStreamProcs, NIL];
TurnOnEntryB[@terminalLock];
};
RETURN [inStream, outStream]
};
TurnOff: PUBLIC SAFE PROC = TRUSTED {
TurnOffEntry: ENTRY PROC [m: POINTER TO MONITORLOCK]
RETURNS [shutdown: BOOL] = -- INLINE -- {
DO
SELECT state FROM
off => RETURN[FALSE];
on => EXIT;
ENDCASE => WAIT stateChange;
ENDLOOP;
IF (shutdown ← ((turnOnCount ← turnOnCount.PRED) = 0)) THEN {
state ← turningOff;
BROADCAST stateChange;
};
};
selected: BOOL = Terminal.Current[] = terminal;
The following is intended to be TRUE when the terminal that was current at the time TurnOn was called has a screen displayed. It is therefore more interesting than ours and we should shut our screen down now and make its visible again. If there wasn't a visible screen when TurnOn was called and we are still the current terminal, we leave our screen visible until someone else invokes Terminal.Select.
wantPreviousTerminal: BOOL =
selected AND
originalTerminal ~= terminal AND originalTerminal.GetBWBitmapState[] = displayed;
IF TurnOffEntry[@terminalLock] AND (~selected OR wantPreviousTerminal) THEN {
DoShutDown[terminal, going, NIL];
IF wantPreviousTerminal THEN [] ← originalTerminal.Select[];
};
};
DoShutDown: Terminal.SwapNotifier = TRUSTED {
DoShutDownEntryA: ENTRY PROC [m: POINTER TO MONITORLOCK]
RETURNS [needed: BOOL] = INLINE {
IF (needed ← state = turningOff) THEN {
state ← joining;
BROADCAST stateChange;
};
};
DoShutDownEntryB: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
state ← off;
BROADCAST stateChange;
};
IF action = going AND DoShutDownEntryA[@terminalLock] THEN {
Process.Abort[keyboardWatcher];
JOIN keyboardWatcher;
JOIN timePainter;
inStream ← outStream ← NIL;
[] ← terminal.SetBWBitmapState[none];
DoShutDownEntryB[@terminalLock];
};
};
InputTimeout: PUBLIC SAFE SIGNAL = CODE;
SetInputTimeout: PUBLIC SAFE PROC [ticks: Process.Ticks] = TRUSTED {
SetInputTimeoutEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
IF ticks = 0 THEN Process.DisableTimeout[@charactersAvailable]
ELSE Process.SetTimeout[@charactersAvailable, ticks];
};
SetInputTimeoutEntry[@keyboardLock];
};
EnableCursorTracking: PUBLIC SAFE PROC = TRUSTED {
EnableEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
IF (noTrack ← noTrack - 1) = 0 THEN doBlink ← TRUE;
};
EnableEntry[@screenLock];
};
DisableCursorTracking: PUBLIC SAFE PROC = TRUSTED {
DisableEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
IF (noTrack ← noTrack + 1) ~= 0 THEN doBlink ← FALSE;
};
DisableEntry[@screenLock];
};
Keyboard Definitions
KeyArray: TYPE = MACHINE DEPENDENT RECORD [
SELECT OVERLAID * FROM
nb => [namedBits: Keys.KeyBits],
ub => [unnamedBits: KeyboardFace.KeyBits],
wds => [words: ARRAY [0..SIZE[Keys.KeyBits]) OF WORD],
ENDCASE
];
KeyItem: TYPE = RECORD [
letter: BOOL,
shiftCode: CHAR [0C..177C],
normalCode: CHAR [0C..377C]
];
KeyStation: TYPE = KeyboardFace.KeyStation;
lowestInterestingKey: KeyStation = 16;
highestInterestingKey: KeyStation = 75;
KeyTable: ARRAY [lowestInterestingKey..highestInterestingKey] OF KeyItem = [
-- [bit number: KeyStation] KeyName Ascii codes
-- [0: ]
-- [1: ]
-- [2: ]
-- [3: ]
-- [4: ]
-- [5: ]
-- [6: ]
-- [7: ]
-- [8: KS1] Keyset1
-- [9: KS2] Keyset2
-- [10: KS3] Keyset3
-- [11: KS4] Keyset4
-- [12: KS5] Keyset5
-- [13: M1] Red
-- [14: M3] Blue
-- [15: M2] Yellow
[FALSE, 45C, 65C], -- [16: k16] Five %,5
[FALSE, 44C, 64C], -- [17: k12] Four $,4
[FALSE, 176C, 66C], -- [18: k20] Six ~,6
[TRUE, 105C, 145C], -- [19: k10] E E,e
[FALSE, 46C, 67C], -- [20: k24] Seven &,7
[TRUE, 104C, 144C], -- [21: k11] D D,d
[TRUE, 125C, 165C], -- [22: k26] U U,u
[TRUE, 126C, 166C], -- [23: k17] V V,v
[FALSE, 51C, 60C], -- [24: k36] Zero ),0
[TRUE, 113C, 153C], -- [25: k31] K K,k
[FALSE, 30C, 55C], -- [26: k40] Dash `,-
[TRUE, 120C, 160C], -- [27: k38] P P,p
[FALSE, 77C, 57C], -- [28: k41] Slash ?,/
[FALSE, 174C, 134C], -- [29: T9] BackSlash |,\
[FALSE, 12C, 12C], -- [30: L6] LF LF
[FALSE, 10C, 10C], -- [31: A2] BS BS
[FALSE, 43C, 63C], -- [32: k8] Three #,3
[FALSE, 100C, 62C], -- [33: k4] Two @,2
[TRUE, 127C, 167C], -- [34: k6] W W,w
[TRUE, 121C, 161C], -- [35: k2] Q Q,q
[TRUE, 123C, 163C], -- [36: k7] S S,s
[TRUE, 101C, 141C], -- [37: k3] A A,a
[FALSE, 50C, 71C], -- [38: k32] Nine (,9
[TRUE, 111C, 151C], -- [39: k30] I I,i
[TRUE, 130C, 170C], -- [40: k9] X X,x
[TRUE, 117C, 157C], -- [41: k34] O O,o
[TRUE, 114C, 154C], -- [42: k35] L L,l
[FALSE, 74C, 54C], -- [43: k33] Comma <,,
[FALSE, 42C, 47C], -- [44: k43] Quote ",'
[FALSE, 175C, 135C], -- [45: k45] RightBracket },]
[FALSE, 0C, 0C], -- [46: R11] Spare2
[FALSE, 0C, 0C], -- [47: R6] Spare1
[FALSE, 41C, 61C], -- [48: k1] One !,1
[FALSE, 33C, 33C], -- [49: T2] ESC ESC
[FALSE, 11C, 11C], -- [50: A1] TAB TAB
[TRUE, 106C, 146C], -- [51: k15] F F,f
[FALSE, 0C, 0C], -- [52: L11] Ctrl
[TRUE, 103C, 143C], -- [53: k13] C C,c
[TRUE, 112C, 152C], -- [54: k27] J J,j
[TRUE, 102C, 142C], -- [55: k21] B B,b
[TRUE, 132C, 172C], -- [56: k5] Z Z,z
[FALSE, 0C, 0C], -- [57: A5] LeftShift
[FALSE, 76C, 56C], -- [58: k37] Period >,.
[FALSE, 72C, 73C], -- [59: k39] SemiColon :,;
[FALSE, 15C, 15C], -- [60: A4] RETURN CR
[FALSE, 136C, 137C], -- [61: k46] Arrow ^,←
[FALSE, 177C, 177C], -- [62: L3] DEL DEL
[FALSE, 0C, 0C], -- [63: L9] FL3
[TRUE, 122C, 162C], -- [64: k14] R R,r
[TRUE, 124C, 164C], -- [65: k18] T T,t
[TRUE, 107C, 147C], -- [66: k19] G G,g
[TRUE, 131C, 171C], -- [67: k22] Y Y,y
[TRUE, 110C, 150C], -- [68: k23] H H,h
[FALSE, 52C, 70C], -- [69: k28] Eight *,8
[TRUE, 116C, 156C], -- [70: k25] N N,n
[TRUE, 115C, 155C], -- [71: k29] M M,m
[FALSE, 0C, 0C], -- [72: A3] Lock
[FALSE, 40C, 40C], -- [73: A7] Space SP
[FALSE, 173C, 133C], -- [74: k42] LeftBracket {,[
[FALSE, 53C, 75C]]; -- [75: k44] Equal +,=
-- [76: A6] RightShift
-- [77: R12] Spare3
-- [78: L12] FL4
-- [79: R1] FR5
-- [80: R5] R5
-- [81: R9] R9
-- [82: L10] L10
-- [83: L7] L7
-- [84: L4] L4
-- [85: L1] L1
-- [86: A9] A9
-- [87: R10] R10
-- [88: A8] A8
-- [89: L8] L8
-- [90: L5] L5
-- [91: L2] L2
-- [92: R2] R2
-- [93: R7] R7
-- [94: R4] R4
-- [95: D2] D2
-- [96: D1] D1
-- [97: k48] Key48
-- [98: T1] T1
-- [99: T3] T3
-- [100: T4] T4
-- [101: T5] T5
-- [102: T6] T6
-- [103: T7] T7
-- [104: T8] T8
-- [105: T10] T10
-- [106: R3] R3
-- [107: k47] Key47
-- [108: A10] A10
-- [109: R8] R8
-- [110: A11]
-- [111: A12]
Keyboard Implementation
Global variables protected by "keyboardLock"
keyboardLock: MONITORLOCK;
charactersAvailable: CONDITION;
in, out: NAT;
buffer: PACKED ARRAY NAT[0..50) OF CHAR;
ProcessKeyboard: PROC = {
old, new: KeyArray;
blinkCount: NAT ← 33;
GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] = INLINE {
RETURN[state = joining]};
TrackCursor: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
mouse: Terminal.Position ← terminal.GetMousePosition[];
IF noTrack > 0 THEN RETURN;
mouse.x ← MIN[MAX[0, mouse.x], terminal.bwWidth];
mouse.y ← MIN[MAX[0, mouse.y], terminal.bwHeight];
terminal.SetBWCursorPosition[mouse];
terminal.SetMousePosition[mouse]
};
MakeCtrlChar: PROC [c: CHAR] RETURNS [CHAR] = INLINE {
RETURN[LOOPHOLE[PrincOpsUtils.BITAND[c, 37B]]]
};
AddToBuffer: ENTRY PROC [m: POINTER TO MONITORLOCK, c: CHAR] = INLINE {
newin: NAT ← in + 1;
IF newin = buffer.LENGTH THEN newin ← 0;
IF newin ~= out THEN {buffer[in] ← c; in ← newin};
};
in ← out ← 0;
Process.SetPriority[Process.priorityRealTime];
old.namedBits ← terminal.GetKeys[];
UNTIL GoAway[@terminalLock] DO
NotifyCharsAvailable: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
BROADCAST charactersAvailable};
charsSeen: BOOLFALSE;
terminal.WaitForBWVerticalRetrace[ ! ABORTED => CONTINUE];
new.namedBits ← terminal.GetKeys[];
TrackCursor[@screenLock];
IF (blinkCount ← blinkCount - 1) = 0 THEN {BlinkCursor[]; blinkCount ← 34};
FOR i: NAT IN [0..SIZE[KeyArray]) DO
IF old.words[i] ~= new.words[i] THEN
FOR k: KeyStation IN [i*Basics.bitsPerWord..(i+1)*Basics.bitsPerWord) DO
char: CHAR;
entry: KeyItem;
IF ~(k IN [lowestInterestingKey..highestInterestingKey]) THEN LOOP;
IF new.unnamedBits[k] = up OR old.unnamedBits[k] = down THEN LOOP;
IF (char ← (entry ← KeyTable[k]).normalCode) ~= 0C THEN {
SELECT TRUE FROM
new.namedBits[Ctrl] = down =>
char ← MakeCtrlChar[char];
new.namedBits[LeftShift] = down, new.namedBits[RightShift] = down =>
char ← entry.shiftCode;
new.namedBits[Lock] = down AND entry.letter =>
char ← entry.shiftCode;
ENDCASE;
AddToBuffer[@keyboardLock, char];
charsSeen ← TRUE;
};
ENDLOOP;
ENDLOOP;
IF charsSeen THEN NotifyCharsAvailable[@keyboardLock];
old ← new;
ENDLOOP;
};
Font Definitions
FontRecord: TYPE = MACHINE DEPENDENT RECORD [
newStyle(0:0..0): BOOL,
indexed(0:1..1): BOOL,
fixed(0:2..2): BOOL,
kerned(0:3..3): BOOL,
pad(0:4..15): [0..7777B],
min(1): CHAR, -- limits of chars in font
max(2): CHAR, -- limits of chars in font
maxwidth(3): NAT,
length(4): NAT,
ascent(5): NAT,
descent(6): NAT,
xoffset(7): NAT,
raster(8): NAT,
chars(9:0..63): SELECT OVERLAID * FROM
hasBoundingBox => [
boundingBox(9:0..63): RECORD [FontBBox, FontBBoy, FontBBdx, FontBBDy: INTEGER],
BBBitmap(13): ARRAY [0..0) OF WORD],
noBoundingBox => [bitmap(9): ARRAY [0..0) OF WORD],
ENDCASE
];
font: LONG POINTER TO FontRecord = GetFont[];
bitmap: LONG POINTER = IF font.kerned THEN @font.BBBitmap ELSE @font.bitmap;
height: NAT = font.ascent + font.descent;
xInSegment: LONG POINTER TO ARRAY CHAR [0C..0C) OF NAT =
bitmap + font.raster*height - (font.min-0C);
GetFont: PROC RETURNS [LONG POINTER] = {
Note: Even though it doesn't say so in its strike header, this is a fixed-width
font. Bitter experience has shown that not all Gacha10.strike font files are, in
fact, fixed width. You need to painstakingly examine the xInSegment table for
the truth.
Gacha10Strike: ARRAY [0..1142B) OF WORD = [
-- 0-- 100000B, 000040B, 000176B, 000007B, 001136B, 000010B, 000004B, 000000B,
-- 10-- 000052B, 000000B, 000001B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 20-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 30-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 40-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 50-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 60-- 000000B, 000000B, 017000B, 000040B, 120243B, 106204B, 004010B, 040100B,
-- 70-- 000000B, 000002B, 034040B, 160701B, 107616B, 037070B, 070000B, 000200B,
-- 100-- 010034B, 034041B, 140707B, 107637B, 016104B, 070162B, 022010B, 110434B,
-- 110-- 074161B, 160707B, 144221B, 021104B, 104761B, 141007B, 000000B, 000200B,
-- 120-- 000040B, 001600B, 020010B, 010401B, 140000B, 000000B, 000000B, 000000B,
-- 130-- 000000B, 000000B, 000060B, 103000B, 017000B, 000040B, 120245B, 052412B,
-- 140-- 004020B, 020520B, 000000B, 000002B, 042141B, 011042B, 104021B, 001104B,
-- 150-- 104000B, 000400B, 004042B, 042121B, 021044B, 044020B, 021104B, 020022B,
-- 160-- 042015B, 114442B, 042211B, 011041B, 004221B, 021104B, 104021B, 001001B,
-- 170-- 000000B, 000200B, 000040B, 002000B, 020000B, 000400B, 040000B, 000000B,
-- 200-- 000000B, 000400B, 000000B, 000000B, 000100B, 100400B, 017000B, 000040B,
-- 210-- 121765B, 054412B, 004020B, 020340B, 100000B, 000004B, 046241B, 010042B,
-- 220-- 104020B, 002104B, 104100B, 101000B, 002042B, 056121B, 021044B, 044020B,
-- 230-- 021104B, 020022B, 102015B, 114442B, 042211B, 011041B, 004221B, 025050B,
-- 240-- 050041B, 000401B, 002010B, 034260B, 160643B, 107415B, 026070B, 070440B,
-- 250-- 043313B, 007054B, 032260B, 161704B, 044221B, 021104B, 174100B, 100400B,
-- 260-- 017000B, 000040B, 000503B, 001004B, 000040B, 010520B, 100000B, 000004B,
-- 270-- 052040B, 020704B, 107436B, 002070B, 104100B, 102017B, 101004B, 052121B,
-- 300-- 161004B, 047436B, 020174B, 020023B, 102012B, 112442B, 042211B, 160601B,
-- 310-- 004212B, 025020B, 050101B, 000401B, 007020B, 002311B, 011144B, 042023B,
-- 320-- 031010B, 010500B, 042514B, 110462B, 046311B, 010404B, 044221B, 012104B,
-- 330-- 010100B, 100406B, 057000B, 000040B, 000501B, 101012B, 100040B, 010103B,
-- 340-- 160017B, 100010B, 052040B, 040044B, 104221B, 004104B, 074000B, 002000B,
-- 350-- 001010B, 052211B, 011004B, 044020B, 027104B, 020022B, 042012B, 112442B,
-- 360-- 074211B, 040101B, 004212B, 025020B, 020101B, 000201B, 012476B, 036211B,
-- 370-- 001047B, 142021B, 021010B, 010600B, 042510B, 110442B, 042200B, 140404B,
-- 400-- 042425B, 004104B, 020100B, 100411B, 117000B, 000000B, 003745B, 042321B,
-- 410-- 000040B, 010000B, 100000B, 000010B, 062040B, 100047B, 140221B, 004104B,
-- 420-- 004000B, 001017B, 102000B, 056371B, 011044B, 044020B, 021104B, 020422B,
-- 430-- 042012B, 111442B, 040211B, 021041B, 004212B, 012050B, 020201B, 000201B,
-- 440-- 002020B, 042211B, 001044B, 002021B, 021010B, 010500B, 042510B, 110442B,
-- 450-- 042200B, 020404B, 042425B, 004050B, 020600B, 100300B, 017000B, 000040B,
-- 460-- 001205B, 042521B, 000040B, 010000B, 101400B, 002020B, 042041B, 001040B,
-- 470-- 104221B, 010104B, 104101B, 100400B, 004010B, 040211B, 011044B, 044020B,
-- 500-- 021104B, 020422B, 022012B, 111442B, 040211B, 011041B, 004204B, 012104B,
-- 510-- 020401B, 000101B, 002010B, 042311B, 011144B, 042023B, 021010B, 010440B,
-- 520-- 042510B, 110462B, 046201B, 010444B, 141012B, 012050B, 040100B, 100400B,
-- 530-- 017000B, 000040B, 001203B, 104616B, 100040B, 010000B, 000400B, 002020B,
-- 540-- 034371B, 170700B, 103416B, 010070B, 070100B, 100200B, 010010B, 036211B,
-- 550-- 160707B, 107620B, 016104B, 070342B, 023710B, 110434B, 040161B, 010701B,
-- 560-- 003404B, 012104B, 020761B, 000101B, 002000B, 036260B, 160643B, 102015B,
-- 570-- 021010B, 010420B, 042510B, 107054B, 032200B, 160303B, 041012B, 021020B,
-- 600-- 174100B, 100400B, 000000B, 000000B, 000001B, 000000B, 000020B, 020000B,
-- 610-- 000400B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 100000B,
-- 620-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 630-- 000040B, 000000B, 000000B, 000000B, 000001B, 000001B, 000000B, 000000B,
-- 640-- 000000B, 000001B, 000000B, 110000B, 000000B, 000040B, 002000B, 000000B,
-- 650-- 000000B, 000020B, 000100B, 100400B, 000000B, 000000B, 000000B, 000000B,
-- 660-- 000020B, 020000B, 001000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 670-- 000001B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 700-- 000000B, 000000B, 000030B, 000000B, 000000B, 000000B, 000001B, 000001B,
-- 710-- 000000B, 000000B, 000000B, 000016B, 000000B, 060000B, 000000B, 000040B,
-- 720-- 002000B, 000000B, 000000B, 000140B, 000100B, 100400B, 000000B, 000000B,
-- 730-- 000000B, 000000B, 000010B, 040000B, 000000B, 000000B, 000000B, 000000B,
-- 740-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 750-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 760-- 000001B, 140007B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 770-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000060B, 103000B,
--1000-- 012602B, 000000B, 000007B, 000016B, 000025B, 000034B, 000043B, 000052B,
--1010-- 000061B, 000070B, 000077B, 000106B, 000115B, 000124B, 000133B, 000142B,
--1020-- 000151B, 000160B, 000167B, 000176B, 000205B, 000214B, 000223B, 000232B,
--1030-- 000241B, 000250B, 000257B, 000266B, 000275B, 000304B, 000313B, 000322B,
--1040-- 000331B, 000340B, 000347B, 000356B, 000365B, 000374B, 000403B, 000412B,
--1050-- 000421B, 000430B, 000437B, 000446B, 000455B, 000464B, 000473B, 000502B,
--1060-- 000511B, 000520B, 000527B, 000536B, 000545B, 000554B, 000563B, 000572B,
--1070-- 000601B, 000610B, 000617B, 000626B, 000635B, 000644B, 000653B, 000662B,
--1100-- 000671B, 000700B, 000700B, 000707B, 000716B, 000725B, 000734B, 000743B,
--1110-- 000752B, 000761B, 000770B, 000777B, 001006B, 001015B, 001024B, 001033B,
--1120-- 001042B, 001051B, 001060B, 001067B, 001076B, 001105B, 001114B, 001123B,
--1130-- 001132B, 001141B, 001150B, 001157B, 001166B, 001175B, 001204B, 001213B,
--1140-- 001222B, 001230B];
The following hack finds the address of constant array Gacha10Strike in the code segment and returns it. This is slightly unethical, but it saves duplicate allocation.
p: LONG POINTER TO ARRAY [0..1142B) OF WORD
PrincOpsUtils.Codebase[PrincOpsUtils.MyGlobalFrame[]];
DO
IF p[0] = Gacha10Strike[0] AND p^ = Gacha10Strike THEN RETURN[p];
p ← p+1;
ENDLOOP;
};
Display Implementation
Global variables protected by "screenLock"
screenLock: MONITORLOCK;
noTrack: CARDINAL ← 0;
doBlink: BOOLFALSE;
bbTable: PrincOps.BBTableSpace;
bbPtr: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@bbTable];
charPos, line, nCharPos, nLines: NAT;
usableHeight, usableWidth: NAT;
firstLine, thisLine: PrincOps.BitAddress;
bitsPerTextLine: NAT; -- = screenWidth*font.height
nameStripeOrg: PrincOps.BitAddress;
nameStripeWidth: NAT;
GetBitAddress: PROC [p: LONG POINTER, o: LONG CARDINAL] RETURNS [PrincOps.BitAddress] = {
RETURN[[p+o/16, 0, o MOD 16]]
};
InitScreen: PROC [nameStripe: ROPE] = {
borderWidth: NAT = 16;
boxWidth: NAT = 1;
marginWidth: NAT = 8;
org: PrincOps.BitAddress;
InitBitmap: PROC = {
zero: CARDINAL ← 0;
bbPtr.dst, bbPtr.height, and bbPtr.width are set up
bbPtr.src ← [@zero, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE];
PrincOpsUtils.BITBLT[bbPtr];
};
MakeBorder: PROC [w: NAT] = {
stipple: ARRAY [0..3] OF CARDINAL ← [104210B, 104210B, 021042B, 021042B];
bbPtr.dstBpl never changes
bbPtr.src ← [@stipple, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 3]]];
bbPtr.flags ← [gray: TRUE];
top stripe
bbPtr.dst ← org;
bbPtr.width ← usableWidth;
bbPtr.height ← w;
PrincOpsUtils.BITBLT[bbPtr];
bottom stripe
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*(usableHeight-w)];
PrincOpsUtils.BITBLT[bbPtr];
left side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w];
bbPtr.width ← w;
bbPtr.height ← usableHeight - w - w;
PrincOpsUtils.BITBLT[bbPtr];
right side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+usableWidth-w];
PrincOpsUtils.BITBLT[bbPtr];
org ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+w];
usableHeight ← usableHeight - w - w;
usableWidth ← usableWidth - w - w;
};
MakeBox: PROC [w: NAT] = {
black: CARDINAL ← 177777B;
bbPtr.src ← [@black, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: w-1]]];
bbPtr.flags ← [gray: TRUE];
top stripe
bbPtr.dst ← org;
bbPtr.width ← usableWidth;
bbPtr.height ← w;
PrincOpsUtils.BITBLT[bbPtr];
bottom stripe
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*(usableHeight-w)];
PrincOpsUtils.BITBLT[bbPtr];
left side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w];
bbPtr.width ← w;
bbPtr.height ← usableHeight-w-w;
PrincOpsUtils.BITBLT[bbPtr];
right side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+usableWidth-w];
PrincOpsUtils.BITBLT[bbPtr];
org ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+w];
usableHeight ← usableHeight - w - w;
usableWidth ← usableWidth - w - w;
};
MakeNameStripe: PROC [nameStripe: ROPE] = {
black: CARDINAL ← 177777B;
systemVolume: File.Volume = File.SystemVolume[];
volumeLabel: ROPE =
IF systemVolume = NIL THEN "No System Volume"
ELSE File.GetVolumeName[systemVolume];
nameStripeOrg ← org;
nameStripeWidth ← usableWidth;
paint version text
bbPtr.dst ← GetBitAddress[org.word, org.bit+marginWidth];
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.height ← height;
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [];
FOR i: CARDINAL IN CARDINAL[0..nameStripe.Length[]) DO
bbPtr.src ← GetBitAddress[bitmap, xInSegment[nameStripe.Fetch[i]]];
PrincOpsUtils.BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
ENDLOOP;
paint volume name
bbPtr.dst ← GetBitAddress[org.word,
org.bit+usableWidth-marginWidth-volumeLabel.Length[]*font.maxwidth];
FOR i: CARDINAL IN CARDINAL[0..volumeLabel.Length[]) DO
bbPtr.src ← GetBitAddress[bitmap, xInSegment[volumeLabel.Fetch[i]]];
PrincOpsUtils.BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
ENDLOOP;
overlay with stripe
bbPtr.src ← [@black, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]];
bbPtr.flags ← [gray: TRUE, dstFunc: xor];
bbPtr.dst ← org;
bbPtr.width ← usableWidth;
bbPtr.height ← height;
PrincOpsUtils.BITBLT[bbPtr];
org ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*height];
usableHeight ← usableHeight - height;
timePainter ← FORK MaintainTime[];
};
[] ← terminal.SetBWBackground[white];
[] ← terminal.SetBWBitmapState[allocated];
bbPtr^ ← terminal.GetBitBltTable[];
org ← bbPtr.dst;
usableHeight ← bbPtr.height;
usableWidth ← bbPtr.width;
InitBitmap[];
MakeBorder[borderWidth];
MakeBox[boxWidth];
MakeNameStripe[nameStripe];
now display the bitmap
[] ← terminal.SetBWBitmapState[displayed];
usableHeight ← usableHeight - 2*marginWidth;
usableWidth ← usableWidth - 2*marginWidth;
bitsPerTextLine ← bbPtr.dstBpl*height;
firstLine ←
GetBitAddress[org.word, org.bit+LONG[marginWidth]*bbPtr.dstBpl+marginWidth];
nCharPos ← usableWidth/font.maxwidth;
nLines ← usableHeight/height;
InitPainting[];
};
MaintainTime: PROC = {
GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = INLINE {
RETURN[state = joining]};
bbTable: PrincOps.BBTableSpace;
bbPtr: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@bbTable];
black: CARDINAL ← 177777B;
date: ROPENIL;
dateLength: NAT ← 0;
secondsOrg: PrincOps.BitAddress;
lastMinutes: INT ← 0;
SetToBlacken: PROC = {
bbPtr.src ← [@black, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]];
bbPtr.flags ← [gray: TRUE]};
PaintTime: PROC = {
current: BasicTime.GMT = BasicTime.Now[];
secondsSinceGenesis: INT = BasicTime.Period[from: BasicTime.earliestGMT, to: current];
minutesSinceGenesis: INT = secondsSinceGenesis/60;
IF lastMinutes = minutesSinceGenesis THEN {
seconds: [0..59] ← secondsSinceGenesis MOD 60;
SetToBlacken[];
bbPtr.dst ← secondsOrg;
bbPtr.width ← 2*font.maxwidth;
bbPtr.height ← height;
PrincOpsUtils.BITBLT[bbPtr];
bbPtr.dst & bbPtr.height still OK
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [srcFunc: complement];
bbPtr.src ← GetBitAddress[bitmap, xInSegment['0+seconds/10]];
PrincOpsUtils.BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
bbPtr.src ← GetBitAddress[bitmap, xInSegment['0+seconds MOD 10]];
PrincOpsUtils.BITBLT[bbPtr]}
ELSE {
SetToBlacken[];
bbPtr.dst ← GetBitAddress[nameStripeOrg.word,
nameStripeOrg.bit+(nameStripeWidth-dateLength*font.maxwidth)/2];
bbPtr.width ← dateLength*font.maxwidth;
bbPtr.height ← height;
PrincOpsUtils.BITBLT[bbPtr];
date ← Convert.RopeFromTime[from: current, start: years, end: seconds,
includeDayOfWeek: TRUE, useAMPM: FALSE, includeZone: TRUE];
dateLength ← date.Length[];
bbPtr.dst ← GetBitAddress[nameStripeOrg.word,
nameStripeOrg.bit+(nameStripeWidth-dateLength*font.maxwidth)/2];
secondsOrg ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+(dateLength-2)*font.maxwidth];
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [srcFunc: complement];
FOR i: CARDINAL IN [0..dateLength) DO
bbPtr.src ← GetBitAddress[bitmap, xInSegment[date.Fetch[i]]];
PrincOpsUtils.BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
ENDLOOP;
};
lastMinutes ← minutesSinceGenesis;
};
Process.SetPriority[Process.priorityForeground];
bbPtr^ ← terminal.GetBitBltTable[];
UNTIL GoAway[@terminalLock] DO
PaintTime[];
Process.Pause[Process.SecondsToTicks[1]];
ENDLOOP;
};
InitPainting: PROC = {
set up standard arguments for character painting
charPos ← 0; line ← 1;
bbPtr.dstBpl already set up
bbPtr.src set when proc called
bbPtr.dst ← thisLine ← firstLine;
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.height ← height;
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [];
};
ClearThisChar: INTERNAL PROC = {
zero: CARDINAL ← 0;
bbPtr.src ← [@zero, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE];
PrincOpsUtils.BITBLT[bbPtr];
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.flags ← [];
};
DisplayChar: INTERNAL PROC [c: CHAR] = {
Backup: INTERNAL PROC = {
t: NAT = bbPtr.dst.bit+16-font.maxwidth;
IF charPos=0 THEN RETURN;
charPos ← charPos - 1;
bbPtr.dst.word ← bbPtr.dst.word + t/16 - 1;
bbPtr.dst.bit ← t MOD 16;
};
ClearScreen: PROC = {
zero: CARDINAL ← 0;
bbPtr.dst ← firstLine;
bbPtr.src ← [@zero, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE];
bbPtr.height ← usableHeight;
bbPtr.width ← usableWidth;
PrincOpsUtils.BITBLT[bbPtr];
InitPainting[];
};
Newline: INTERNAL PROC = {
IF line < nLines THEN
{thisLine ← GetBitAddress[thisLine.word, thisLine.bit+bitsPerTextLine]; line ← line+1}
ELSE {
zero: CARDINAL ← 0;
sBBTable: PrincOps.BBTableSpace;
sbbPtr: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@sBBTable];
sbbPtr^ ← [
dst: firstLine, dstBpl: bbPtr.dstBpl,
src: GetBitAddress[firstLine.word, firstLine.bit+bitsPerTextLine],
srcDesc: [srcBpl[bbPtr.dstBpl]], flags: [direction: forward],
width: nCharPos*font.maxwidth, height: height*(nLines-1)];
PrincOpsUtils.BITBLT[sbbPtr];
sbbPtr^ ← [
dst: thisLine, src: [@zero, 0, 0],
dstBpl: bbPtr.dstBpl, srcDesc: [gray[[0, 0, 0, 0]]],
width: nCharPos*font.maxwidth, height: height,
flags: [gray: TRUE]];
PrincOpsUtils.BITBLT[sbbPtr];
};
bbPtr.dst ← thisLine;
charPos ← 0;
};
SELECT c FROM
IN (Ascii.SP..'~] => {
IF ~(c IN [font.min..font.max]) THEN c ← font.max + 1;
bbPtr.src ← GetBitAddress[bitmap, xInSegment[c]];
PrincOpsUtils.BITBLT[bbPtr];
};
Ascii.SP => NULL;
Ascii.CR => {Newline[]; RETURN};
Ascii.BS => {Backup[]; ClearThisChar[]; RETURN};
Ascii.TAB => {UNTIL charPos MOD 8 = 0 DO DisplayChar[Ascii.SP]; ENDLOOP; RETURN};
Ascii.FF => {ClearScreen[]; RETURN};
IN [0C..Ascii.SP) => {DisplayChar['^]; DisplayChar[c+('A-1C)]; RETURN};
ENDCASE => RETURN;
IF (charPos ← charPos + 1) >= nCharPos THEN Newline[]
ELSE bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit + font.maxwidth];
};
BlinkCursor: PROC = {
BlinkCursorEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
blinker: CARDINAL ← 60000B;
IF doBlink THEN {
bbPtr.src ← [@blinker, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE, dstFunc: xor];
PrincOpsUtils.BITBLT[bbPtr];
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.flags ← [];
};
};
IF doBlink THEN BlinkCursorEntry[@screenLock];
};
IO Streams implementation
CreateStreams: PROC = {
};
CharsAvail: SAFE PROC [self: IO.STREAM, wait: BOOL] RETURNS [INT] = TRUSTED {
CharsAvailEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] =
INLINE {RETURN[in ~= out]};
RETURN[IF CharsAvailEntry[@keyboardLock] THEN 1 ELSE 0]
};
EndOf: SAFE PROC [self: IO.STREAM] RETURNS [BOOL] = CHECKED {RETURN[FALSE]};
GetChar: SAFE PROC [self: IO.STREAM] RETURNS [c: CHAR] = TRUSTED {
GetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] = INLINE {
ENABLE UNWIND => NULL;
WHILE in = out DO
WAIT charactersAvailable;
IF in = out THEN RETURN[FALSE];
ENDLOOP;
c ← buffer[out];
IF (out ← out + 1) = buffer.LENGTH THEN out ← 0;
RETURN[TRUE]
};
UNTIL GetEntry[@keyboardLock] DO SIGNAL InputTimeout; ENDLOOP;
};
PutChar: SAFE PROC [self: IO.STREAM, char: CHAR] = TRUSTED {
PutCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
doBlink ← FALSE;
ClearThisChar[]; -- to get rid of possible blinker
DisplayChar[char];
doBlink ← TRUE;
};
PutCharEntry[@screenLock];
};
EraseChar: SAFE PROC [self: IO.STREAM, char: CHAR] = TRUSTED {
EraseCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
doBlink ← FALSE;
ClearThisChar[]; -- to get rid of possible blinker
DisplayChar[Ascii.BS];
doBlink ← TRUE;
};
EraseCharEntry[@screenLock];
};
Initialization
inStreamProcs: REF IO.StreamProcs;
outStreamProcs: REF IO.StreamProcs;
Initialize: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
IF ~font.newStyle OR font.indexed OR ~(font.min IN [0C..177C])
OR ~(font.max+1 IN [0C..177C]) THEN ERROR;
inStreamProcs ← IO.CreateStreamProcs[
variety: $input, class: $SimpleTerminal,
getChar: GetChar,
endOf: EndOf,
charsAvail: CharsAvail
];
outStreamProcs ← IO.CreateStreamProcs[
variety: $output, class: $SimpleTerminal,
putChar: PutChar,
eraseChar: EraseChar
];
terminal ← Terminal.Current[];
terminal.RegisterNotifier[DoShutDown];
};
Initialize[@terminalLock];
END.