-- SimpleTTY.mesa modified September 10, 1982 2:59 pm by Taft -- This module also implements non-basic tty features (see end of code). -- if these are not needed, String and Format may be left unbound DIRECTORY BitBlt USING [AlignedBBTable, BBptr, BBTableSpace, BITBLT], Environment USING [BitAddress], Format USING [ Date, Decimal, LongDecimal, LongNumber, LongOctal, LongSubStringItem, Number, Octal, SubString], Inline USING [BITAND], Process USING [Detach, SetPriority], Runtime USING [GetTableBase, Interrupt], SimpleTTYExtras, String USING [ AppendChar, AppendLongNumber, AppendNumber, StringToLongNumber, StringToNumber, SubString], Time USING [Packed], TTY USING [DateFormat, Handle, LongSubString, NumberFormat], UserTerminal USING [ Coordinate, cursor, GetBitBltTable, keyboard, mouse, screenHeight, screenWidth, SetMousePosition, SetState, SetBackground, WaitForScanLine]; SimpleTTY: MONITOR LOCKS m USING m: POINTER TO MONITORLOCK IMPORTS BitBlt, Format, Inline, Process, Runtime, String, UserTerminal EXPORTS SimpleTTYExtras, TTY = BEGIN OPEN BitBlt; screenLock: MONITORLOCK; keyboardLock: MONITORLOCK; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Common Definitions -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CHAR: TYPE = CHARACTER; ControlA: CHARACTER = 'A - 100B; BS: CHARACTER = 10C; TAB: CHARACTER = 11C; FF: CHARACTER = 14C; CR: CHARACTER = 15C; ControlR: CHARACTER = 'R - 100B; ControlQ: CHARACTER = 'Q - 100B; ControlV: CHARACTER = 'V - 100B; ControlW: CHARACTER = 'W - 100B; ControlX: CHARACTER = 'X - 100B; ESC: CHARACTER = 33C; SP: CHARACTER = ' ; DEL: CHARACTER = 177C; TTYHandle: TYPE = TTY.Handle; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- FONT Definitions and variables -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ font: LONG POINTER TO MACHINE DEPENDENT RECORD [ newStyle(0:0..0): BOOLEAN, indexed(0:1..1): BOOLEAN, fixed(0:2..2): BOOLEAN, kerned(0:3..3): BOOLEAN, pad(0:4..15): [0..7777B], min(1): CHARACTER, -- limits of chars in font max(2): CHARACTER, -- limits of chars in font maxwidth(3): CARDINAL, length(4): CARDINAL, ascent(5): CARDINAL, descent(6): CARDINAL, xoffset(7): CARDINAL, raster(8): CARDINAL, 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] = GetFont[]; bitmap: LONG POINTER = IF font.kerned THEN @font.BBBitmap ELSE @font.bitmap; xInSegment: LONG POINTER TO ARRAY CHARACTER [0C..0C) OF CARDINAL = bitmap + font.raster*FontHeight[] - (font.min-0C); height: INTEGER[0..LAST[INTEGER]] = FontHeight[]; CharWidth: PROC [char: CHARACTER] RETURNS [[0..LAST[INTEGER]]] = INLINE BEGIN IF char NOT IN [font.min..font.max] THEN char _ font.max+1; RETURN[xInSegment[char+1] - xInSegment[char]] END; FontHeight: PROC RETURNS [[0..LAST[INTEGER]]] = INLINE {RETURN[font.ascent+font.descent]}; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Keyboard Definitions and Constants -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ downUp: TYPE = {down, up}; keycount: CARDINAL = 80; -- must be 0 mod 16 Keyarray: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID * FROM b => [bits: PACKED ARRAY [0..keycount) OF downUp], wds => [wds: ARRAY [0..keycount/16) OF WORD], ENDCASE]; KeyItem: TYPE = RECORD [ Letter: BOOLEAN, ShiftCode: CHAR [0C..177C], NormalCode: CHAR [0C..377C]]; -- Keyboard Info ctrl: CARDINAL = 52; leftShift: CARDINAL = 57; shiftLock: CARDINAL = 72; rightShift: CARDINAL = 76; spare3: CARDINAL = 77; KeyTable: ARRAY [16..keycount) OF KeyItem = [ -- Index [0..15] mouse, etc -- Index [16..31] [FALSE, 45C, 65C], -- %,5 [FALSE, 44C, 64C], -- $,4 [FALSE, 176C, 66C], -- ~,6 [TRUE, 105C, 145C], -- E [FALSE, 46C, 67C], -- &,7 [TRUE, 104C, 144C], -- D [TRUE, 125C, 165C], -- U [TRUE, 126C, 166C], -- V [FALSE, 51C, 60C], -- ),0 [TRUE, 113C, 153C], -- K [FALSE, 30C, 55C], -- `,- [TRUE, 120C, 160C], -- P [FALSE, 77C, 57C], -- ?,/ [FALSE, 174C, 134C], -- |,\ [FALSE, 12C, 12C], -- LF [FALSE, 10C, 10C], -- BS -- Index [32..47] [FALSE, 43C, 63C], -- #,3 [FALSE, 100C, 62C], -- @,2 [TRUE, 127C, 167C], -- W [TRUE, 121C, 161C], -- Q [TRUE, 123C, 163C], -- S [TRUE, 101C, 141C], -- A [FALSE, 50C, 71C], -- (,9 [TRUE, 111C, 151C], -- I [TRUE, 130C, 170C], -- X [TRUE, 117C, 157C], -- O [TRUE, 114C, 154C], -- L [FALSE, 74C, 54C], -- <,, [FALSE, 42C, 47C], -- ",' [FALSE, 175C, 135C], --},] [FALSE, 0C, 0C], -- SPARE2 [FALSE, 0C, 0C], -- SPARE1 -- Index [48..63] [FALSE, 41C, 61C], -- !,1 [FALSE, 33C, 33C], -- ESCAPE [FALSE, 11C, 11C], -- TAB [TRUE, 106C, 146C], -- F [FALSE, 0C, 0C], -- CONTROL [TRUE, 103C, 143C], -- C [TRUE, 112C, 152C], -- J [TRUE, 102C, 142C], -- B [TRUE, 132C, 172C], -- Z [FALSE, 0C, 0C], -- SHIFT [FALSE, 76C, 56C], -- >,. [FALSE, 72C, 73C], -- :,; [FALSE, 15C, 15C], -- CR [FALSE, 136C, 137C], -- ^,_ [FALSE, 177C, 177C], -- DEL [FALSE, 0C, 0C], -- NOT USED (FL3) -- Index [64..79] [TRUE, 122C, 162C], -- R [TRUE, 124C, 164C], -- T [TRUE, 107C, 147C], -- G [TRUE, 131C, 171C], -- Y [TRUE, 110C, 150C], -- H [FALSE, 52C, 70C], -- *,8 [TRUE, 116C, 156C], -- N [TRUE, 115C, 155C], -- M [FALSE, 0C, 0C], -- LOCK [FALSE, 40C, 40C], -- SPACE [FALSE, 173C, 133C], -- {,[ [FALSE, 53C, 75C], -- +,= [FALSE, 0C, 0C], -- Shift [FALSE, 0C, 0C], -- Spare3 [FALSE, 0C, 0C], -- not user (FR4) [FALSE, 0C, 0C]]; -- not user (FR5) -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Simple TTY Procedures -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OutOfInstances: PUBLIC ERROR = CODE; useCount: CARDINAL _ 0; Create: PUBLIC PROC [STRING] RETURNS [TTYHandle] = BEGIN CreateEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [CARDINAL]= INLINE {RETURN[useCount _ useCount+1]}; IF CreateEntry[@keyboardLock]#1 THEN ERROR OutOfInstances; RETURN[LOOPHOLE[100000B]] END; Destroy: PUBLIC PROC [TTYHandle] = BEGIN DestroyEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {useCount _ useCount-1}; DestroyEntry[@keyboardLock]; END; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Keyboard Implementation -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CDT: BOOLEAN _ FALSE; charactersAvailable: CONDITION; echo: BOOLEAN _ TRUE; debuggerEnabled: BOOLEAN _ TRUE; -- EXTERNAL PROCEDURES CtrlChar: PROC [c: CHAR] RETURNS [CHAR] = INLINE {RETURN[LOOPHOLE[Inline.BITAND[c, 37B]]]}; -- ENTRY PROCEDURES ProcessKeyboard: PROC = BEGIN old, new: Keyarray; kp: LONG POINTER TO Keyarray = LOOPHOLE[UserTerminal.keyboard]; blinkCount: CARDINAL _ 33; interruptState: downUp _ up; Process.SetPriority[6]; new _ kp^; DO Brdcst: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {BROADCAST charactersAvailable}; charsSeen: BOOLEAN _ FALSE; old _ new; UserTerminal.WaitForScanLine[0]; new _ kp^; -- interrupt processing IF new.bits[ctrl]=down AND new.bits[spare3]=down AND new.bits[leftShift]=up AND debuggerEnabled THEN {IF interruptState=up THEN {interruptState_down; Runtime.Interrupt[]}} ELSE interruptState _ up; TrackCursor[]; IF (blinkCount_blinkCount-1) = 0 THEN {BlinkCursor[]; blinkCount _ 34}; FOR i: CARDINAL IN [1..keycount/16) DO IF old.wds[i]#new.wds[i] THEN FOR j: CARDINAL IN [i*16..(i+1)*16) DO char: CHAR; entry: KeyItem; IF new.bits[j]=up OR old.bits[j]=down THEN LOOP; IF (char _ (entry_KeyTable[j]).NormalCode)#0C THEN BEGIN SELECT TRUE FROM new.bits[ctrl]=down => IF char=177C THEN {CDT _ TRUE; LOOP} ELSE char _ CtrlChar[char]; new.bits[leftShift]=down, new.bits[rightShift]=down => char _ entry.ShiftCode; new.bits[shiftLock]=down AND entry.Letter => char _ entry.ShiftCode; ENDCASE; StuffBuffer[char, @keyboardLock]; charsSeen _ TRUE END; ENDLOOP ENDLOOP; IF charsSeen THEN Brdcst[@keyboardLock]; ENDLOOP END; SetEcho: PUBLIC PROC [h: TTYHandle, new: BOOLEAN] RETURNS [old: BOOLEAN] = BEGIN SetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {old _ echo; echo _ new}; SetEntry[@keyboardLock] END; DebuggerEnabled: PUBLIC PROC RETURNS [enabled: BOOLEAN] = {RETURN [debuggerEnabled]}; SetDebuggerEnabled: PUBLIC PROC [enabled: BOOLEAN] = {debuggerEnabled _ enabled}; TrackCursor: PROC = INLINE BEGIN mouse: UserTerminal.Coordinate _ UserTerminal.mouse^; mouse.x _ MIN[MAX[0, mouse.x], UserTerminal.screenWidth]; mouse.y _ MIN[MAX[0, mouse.y], UserTerminal.screenHeight]; UserTerminal.cursor^ _ mouse; UserTerminal.SetMousePosition[mouse] END; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Keyboard RingBuffer -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ in, out: CARDINAL _ 0; buffer: PACKED ARRAY [0..50) OF CHAR; CharsAvailable: PUBLIC PROC [h: TTYHandle] RETURNS [CARDINAL] = BEGIN P: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [CARDINAL] = INLINE {RETURN[IF in>=out THEN in-out ELSE in+LENGTH[buffer]-out]}; RETURN[P[@keyboardLock]] END; GetChar: PUBLIC PROC [h: TTYHandle] RETURNS [c: CHAR] = BEGIN P: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE BEGIN WHILE in=out DO WAIT charactersAvailable ENDLOOP; c _ buffer[out]; IF (out_out+1) = LENGTH[buffer] THEN out _ 0; END; P[@keyboardLock] END; PutBackChar: PUBLIC PROC [h: TTYHandle, c: CHAR] = BEGIN P: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE BEGIN newout: CARDINAL = IF out=0 THEN LENGTH[buffer]-1 ELSE out-1; IF newout#in THEN {buffer[out _ newout] _ c; BROADCAST charactersAvailable}; END; P[@keyboardLock] END; ResetUserAbort: PUBLIC PROC = BEGIN CDResetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {CDT _ FALSE}; CDResetEntry[@keyboardLock] END; StuffBuffer: ENTRY PROC [c: CHAR, m: POINTER TO MONITORLOCK] = INLINE BEGIN newin: CARDINAL; IF (newin_in+1) = LENGTH[buffer] THEN newin _ 0; IF newin#out THEN {buffer[in] _ c; in _ newin}; END; -- perhaps this should be monitored, but since it is a snapshot... UserAbort: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN[CDT]}; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- DISPLAY -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BBTable: BBTableSpace; bbPtr: BBptr = AlignedBBTable[@BBTable]; charPos, line, nCharPos, nLines: CARDINAL; firstLine, thisLine: Environment.BitAddress; bitsPerTextLine: CARDINAL; -- = screenWidth*font.height doBlink: BOOLEAN _ FALSE; -- EXTERNAL DISPLAY PROCEDURES GetBitAddress: PROC [p: LONG POINTER, o: CARDINAL] RETURNS [Environment.BitAddress] = {RETURN[[p+o/16, 0, o MOD 16]]}; PutBlanks: PUBLIC PROC [h: TTYHandle, n: CARDINAL] = {THROUGH [0..n) DO PutChar[h, ' ] ENDLOOP}; -- ENTRY DISPLAY PROCEDURES BlinkCursor: PUBLIC PROC = BEGIN BlinkCursorEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = BEGIN blinker: CARDINAL _ 60000B; IF doBlink THEN BEGIN bbPtr.src _ [@blinker, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE, dstFunc: xor]; BITBLT[bbPtr]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.flags _ []; END; END; IF doBlink THEN BlinkCursorEntry[@screenLock]; END; NewLine: PUBLIC PROC [TTYHandle] RETURNS [BOOLEAN] = {RETURN[charPos=0]}; PutChar: PUBLIC PROC [h: TTYHandle, c: CHARACTER] = BEGIN PutCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {doBlink _ FALSE; ClearThisChar[]; DisplayChar[c]; doBlink _ TRUE}; PutCharEntry[@screenLock]; END; PutLongString: PUBLIC PROC [h: TTYHandle, s: LONG STRING] = {IF s#NIL THEN FOR i: CARDINAL IN [0..s.length) DO PutChar[h, s[i]] ENDLOOP}; PutString: PUBLIC PROC [h: TTYHandle, s: STRING] = {PutLongString[h, s]}; -- INTERNAL DISPLAY PROCEDURES Backup: INTERNAL PROC = BEGIN t: CARDINAL = 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; END; ClearScreen: INTERNAL PROC = BEGIN zero: CARDINAL _ 0; bbPtr^ _ UserTerminal.GetBitBltTable[]; bitsPerTextLine _ bbPtr.dstBpl*height; firstLine _ thisLine _ GetBitAddress[ bbPtr.dst.word, bbPtr.dst.bit+8+8*bbPtr.dstBpl]; charPos _ 0; line _ 1; nCharPos _ (bbPtr.width-16)/font.maxwidth; nLines _ (bbPtr.height-16)/height; bbPtr.src _ [@zero, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE]; BITBLT[bbPtr]; -- set up standard arguments for character painting bbPtr.dst _ firstLine; --bbPtr.dstBpl set --bbPtr.src set when proc called bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.height _ height; bbPtr.width _ font.maxwidth; bbPtr.flags _ []; END; ClearThisChar: INTERNAL PROC = BEGIN zero: CARDINAL _ 0; bbPtr.src _ [@zero, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE]; BITBLT[bbPtr]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.flags _ []; END; DisplayChar: INTERNAL PROC [c: CHARACTER] = BEGIN SELECT c FROM IN (SP..'~] => BEGIN IF c NOT IN [font.min..font.max] THEN c _ font.max+1; bbPtr.src _ GetBitAddress[bitmap, xInSegment[c]]; BitBlt.BITBLT[bbPtr]; END; SP => NULL; CR => {Newline[]; RETURN}; BS => {Backup[]; ClearThisChar[]; RETURN}; TAB => {UNTIL (charPos MOD 8)=0 DO DisplayChar[SP] ENDLOOP; RETURN}; FF => {ClearScreen[]; RETURN}; IN [0C..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]; END; Newline: INTERNAL PROC = BEGIN IF line SIGNAL Rubout; ControlA, BS => -- backspace IF s.length > 0 THEN {IF echo THEN PutChar[h, BS]; s.length _ s.length - 1}; ControlW, ControlQ => -- backword BEGIN -- text to be backed up is of the form ...
  • , the and are to -- be removed. state: {ti, v, li} _ ti; FOR i: CARDINAL DECREASING IN [0..s.length) DO SELECT s[i] FROM IN ['A..'Z], IN ['a..'z], IN ['0..'9] => IF state = ti THEN state _ v; ENDCASE => IF state = v THEN state _ li; IF state = li THEN GO TO Done; IF echo THEN PutChar[h, BS]; REPEAT Done => s.length _ i + 1; FINISHED => s.length _ 0; ENDLOOP; END; ControlX => -- back everything BEGIN IF echo THEN FOR i: CARDINAL IN [0..s.length) DO PutChar[h, BS] ENDLOOP; s.length _ 0; END; ControlR => -- refresh-- IF echo THEN {PutChar[h, CR]; PutString[h, s]}; ControlV => -- dont parse next char BEGIN WHILE s.length >= s.maxlength DO s _ SIGNAL LineOverflow[s] ENDLOOP; s[s.length] _ c _ GetChar[h]; s.length _ s.length + 1; IF echo THEN PutChar[h, c] END; ENDCASE => BEGIN WHILE s.length >= s.maxlength DO s _ SIGNAL LineOverflow[s] ENDLOOP; s[s.length] _ c; s.length _ s.length + 1; IF echo THEN PutChar[h, c]; END; c _ GetChar[h]; ENDLOOP; END; IsAtom: PROC [c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = SP OR c = CR]}; IsCR: PROC [c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = CR]}; -- extended output procedures OutString: PROC [s: STRING] = {PutLongString[LOOPHOLE[0], s]}; PutDate: PUBLIC PROC [h: TTYHandle, gmt: Time.Packed, format: TTY.DateFormat] = {Format.Date[gmt, format, OutString]}; PutDecimal: PUBLIC PROC [h: TTYHandle, n: INTEGER] = {Format.Decimal[n, OutString]}; PutLongDecimal: PUBLIC PROC [h: TTYHandle, n: LONG INTEGER] = {Format.LongDecimal[n, OutString]}; PutLongNumber: PUBLIC PROC [h: TTYHandle, n: LONG UNSPECIFIED, format: TTY.NumberFormat] = {Format.LongNumber[n, format, OutString]}; PutLongOctal: PUBLIC PROC [h: TTYHandle, n: LONG UNSPECIFIED] = {Format.LongOctal[n, OutString]}; PutNumber: PUBLIC PROC [h: TTYHandle, n: UNSPECIFIED, format: TTY.NumberFormat] = {Format.Number[n, format, OutString]}; PutOctal: PUBLIC PROC [h: TTYHandle, n: UNSPECIFIED] = {Format.Octal[n, OutString]}; PutLongSubString: PUBLIC PROC [h: TTYHandle, ss: TTY.LongSubString] = {Format.LongSubStringItem[ss, OutString]}; PutSubString: PUBLIC PROC [h: TTYHandle, ss: String.SubString] = {Format.SubString[ss, OutString]}; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- MAINLINE CODE -- --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FontError: ERROR = CODE; IF ~font.newStyle OR font.indexed OR font.min NOT IN [0C..177C] OR font.max+1 NOT IN [0C..177C] THEN ERROR FontError; [] _ UserTerminal.SetState[on]; [] _ UserTerminal.SetBackground[white]; PutChar[LOOPHOLE[0], FF]; Process.Detach[FORK ProcessKeyboard]; END.... September 10, 1982 2:53 pm Taft Add SetDebuggerEnabled