(*------------------------------------------------------------------------*) IMPLEMENTATION MODULE Interface; (* Interfaces with all the things not on the second processor *) FROM SYSTEM IMPORT TKCALL, ADR, REGISTER, WORD, ADDRESS; FROM Universe IMPORT DebugFlags, Debugging, BYTE, Editing, EndOfLineCh; FROM TKCalls IMPORT TKbyte, TKword; FROM Strings IMPORT LengthC; IMPORT Vdu, Windows, Display, Buffers, Fast; IMPORT Errors; IMPORT Debug; IMPORT IO, Handler, Program, File; FROM GlobalString IMPORT XGetGlobalString, GetGlobalString; IMPORT DisplayPosition; IMPORT TKCalls; IMPORT Commands; IMPORT HostCode; (*------------------------------------------------------------------------*) CONST HostCodeVersionNumber = 00113H; (* Version of 6502 code *) EditOsword = 0F0H; EventNumber = 5; EscapeEvent = 6; MaxObeyStreams = 4; DisasterChar = 0C7H; (* Ctrl-Shift-Escape *) TYPE PROCWORD = PROCEDURE (WORD); PROCBYTE = PROCEDURE (BYTE); (*------------------------------------------------------------------------*) VAR Obey : RECORD Index : CARDINAL; Streams : ARRAY [1..MaxObeyStreams] OF INTEGER; END ; RawKeyBoard : CARDINAL; (* RawVduStream : CARDINAL; *) DebugStream : CARDINAL; LogFileStream : CARDINAL; KeyHistoryStream : CARDINAL; MosType : (Mos100, Mos120, UnKnownMos); HostCodeActive : BOOLEAN; tube : POINTER TO ARRAY [0..15] OF CHAR; DisasterFlag : BOOLEAN; pandoraVersion : CARDINAL; (*------------------------------------------------------------------------*) (* Panos Event handler called when Panos commands do output to vdu: *) PROCEDURE EventHandler ( EventCode, X, Y : INTEGER; Handle : INTEGER; VAR Env : Handler.UsersEnvironment); CONST Disaster = 2; (* Codes passed from the 6502 *) Escape = 3; BEGIN IF X = Disaster THEN IF DisasterFlag THEN (* IF NOT Commands.idle THEN TellHostCode (ReflectWrchCommand, TurnOff, 0); Program.Stop (0); ELSE Program.Stop (INTEGER (08400B199H)); END (* if *); *) Program.Stop (INTEGER (08400B199H)); ELSE DisasterFlag := TRUE; END (* if *); ELSIF X = Escape THEN EscapeFlag := TRUE END (* if *); END EventHandler; (*------------------------------------------------------------------------*) (* Panos Event handler called when escape detected - always ignore it ! *) PROCEDURE EscapeHandler (EventCode, X, Y: INTEGER; Handle: INTEGER; VAR Env: Handler.UsersEnvironment); BEGIN (* Ignore any escape events that happen *) END EscapeHandler; (*------------------------------------------------------------------------*) PROCEDURE ReadLine( VAR Line : ARRAY OF CHAR ) ; (* Reads a line with buffering and line editing *) VAR KeyBoard : INTEGER; XY : INTEGER; Length : CARDINAL; BEGIN Terminate () ; (* Will only be called after a disaster *) XY := TkByte( 077H , 0 , 0 ) ; (* Close all exec files *) XY := TkByte( 015H , 0 , 0 ) ; (* Flush keyboard buffer *) KeyBoard := IO.XFindInput( "kb:" ) ; Length := IO.XSBlockRead( KeyBoard , HIGH( Line ) , ADR( Line[ 0 ] ) ) ; Line[ Length - 1 ] := 0C ; IO.XCloseStream( KeyBoard ) ; END ReadLine ; (*------------------------------------------------------------------------*) PROCEDURE HostCommand (command : HostCommands; param0 : CARDINAL; param1 : CARDINAL ): INTEGER; VAR block : ARRAY [0..6] OF CHAR; junk : INTEGER; BEGIN (* Pandora pre version 1.00 passes host OSWORD code a pointer to a copy of the block whose address is passed to TKCALL (i.e. including the two "header" bytes). Subsequent versions omit the "header" bytes. To allow a single version of Edit + HostCode to run with Pandora of either specification, the Hostcode assumes the old specification, and Edit (below) fudges the passed block with new Pandora to end up with the same thing being passed *) block [0] := CHAR (7); block [1] := CHAR (2); block [2] := CHAR (5); block [3] := CHAR (2); block [4] := CHAR (command); block [5] := CHAR (param0); block [6] := CHAR (param1); IF pandoraVersion < 0100H THEN junk := TKCALL (TKword, EditOsword, ADR (block [2])); ELSE junk := TKCALL (TKword, EditOsword, ADR (block [0])); END (* if *); IF junk < 0 THEN RETURN -1 ELSE RETURN CARDINAL (block [2]) + (CARDINAL (block [3]) * 256) END (* if *); END HostCommand; (*------------------------------------------------------------------------*) PROCEDURE TellHostCode (Command: HostCommands; Param0, Param1: CARDINAL); VAR Result: CARDINAL; BEGIN IF NOT HostCodeActive THEN Errors.Panic ("TellHostCode: Host code inactive"); END (* if *); Result := HostCommand (Command, Param0, Param1); IF Command = TerminateCommand THEN HostCodeActive := FALSE ELSE IF Result # HostCodeVersionNumber THEN Errors.Panic ("Incorrect version of HostCode") END (* if *); END (* if *) END TellHostCode; (*------------------------------------------------------------------------*) PROCEDURE TkByte( Number , X , Y : INTEGER ) : INTEGER ; (* Does a TkByte , returns Y,,X *) VAR Junk: INTEGER; BEGIN Junk := TKCALL( TKbyte , Number , X , Y ) ; RETURN REGISTER( 2 ) ; END TkByte ; (*------------------------------------------------------------------------*) PROCEDURE Wrch (ch: BYTE); BEGIN WHILE NOT (6 IN BITSET(tube^[0])) DO (* nothing *) END; tube^[2] := ch; END Wrch; (*------------------------------------------------------------------------*) PROCEDURE WriteBuffer (Buffer: ARRAY OF CHAR); VAR j: CARDINAL; BEGIN FOR j := 1 TO 1+HIGH (Buffer) DO WHILE NOT (6 IN BITSET (tube^[0])) DO (* nothing *) END; tube^[2] := Buffer[j-1] END; END WriteBuffer; (*------------------------------------------------------------------------*) PROCEDURE DebugWrch (Ch: BYTE); BEGIN IO.XSWriteByte (DebugStream, CARDINAL (Ch)); IO.XSWriteByte (LogFileStream, CARDINAL (Ch)); END DebugWrch; (*------------------------------------------------------------------------*) PROCEDURE Peek( Address : CARDINAL ) : BYTE ; (* Sneaks a look at a byte in the HOST processor *) VAR Junk : INTEGER ; Buffer : ARRAY [ 0..4 ] OF BYTE ; BEGIN Buffer[ 0 ] := BYTE( Address MOD 256 ) ; Buffer[ 1 ] := BYTE( ( Address DIV 256 ) MOD 256 ) ; Buffer[ 2 ] := BYTE( 0FFH ) ; Buffer[ 3 ] := BYTE( 0FFH ) ; Junk := TKCALL( TKword , 5 , ADR( Buffer[ 0 ] ) ) ; RETURN Buffer[ 4 ] ; END Peek ; (*------------------------------------------------------------------------*) PROCEDURE Poke( Address : CARDINAL ; Data : BYTE ) ; (* Puts byte in the HOST processor ***** Use with extreme caution *****) VAR Junk : INTEGER ; Buffer : ARRAY [ 0..4 ] OF BYTE ; BEGIN Buffer[ 0 ] := BYTE( Address MOD 256 ) ; Buffer[ 1 ] := BYTE( ( Address DIV 256 ) MOD 256 ) ; Buffer[ 2 ] := BYTE( 0FFH ) ; Buffer[ 3 ] := BYTE( 0FFH ) ; Buffer[ 4 ] := Data ; Junk := TKCALL( TKword , 6 , ADR( Buffer[ 0 ] ) ) ; END Poke ; (*------------------------------------------------------------------------*) PROCEDURE NoScrollBottomCorner( Enable : BOOLEAN ) : BOOLEAN ; (* Attempts to stop scrolling in bottom corner *) PROCEDURE PokeMos120 ; (* For details see Advanced User Guide OSBYTE &75 (p 139) and zero page memory usage (p 268 ). *) CONST VduStatusByteAddress = 0FFFF00D0H ; VAR RealStatus : BITSET ; Status : BITSET ; Junk : INTEGER ; BEGIN Status := BITSET( Peek( VduStatusByteAddress ) ) ; Junk := TKCALL( TKbyte , 075H ) ; RealStatus := BITSET( REGISTER( 2 ) MOD 256 ) ; IF RealStatus <> Status THEN Errors.Panic( "Status in Poke MOS does not correspond*N" ) ; END (* if *) ; IF Enable THEN INCL( Status , 1 ) ; ELSE EXCL( Status , 1 ) ; END (* if *) ; Poke( VduStatusByteAddress , BYTE( Status ) ) ; END PokeMos120 ; BEGIN (* This is trick because of different MOSs and having to poke them ! *) CASE MosType OF Mos120 : PokeMos120 ; RETURN TRUE ; ELSE RETURN FALSE ; END (* if *) ; END NoScrollBottomCorner ; (*------------------------------------------------------------------------*) PROCEDURE KeyTypedAhead() : BOOLEAN ; BEGIN WHILE Obey.Index > 0 DO IF IO.XBytesOutstanding( Obey.Streams[ Obey.Index ] ) > 0 THEN RETURN TRUE ; END (* if *) ; IF IO.CloseStream( Obey.Streams[ Obey.Index ] ) < 0 THEN END (* if *) ; DEC( Obey.Index ) ; END (* while *) ; RETURN IO.XBytesOutstanding( RawKeyBoard ) > 0 ; END KeyTypedAhead ; (*------------------------------------------------------------------------*) PROCEDURE GetKey() : BYTE ; VAR Value : INTEGER ; BEGIN IF Obey.Index > 0 THEN (* The type ahead test will reselect streams if at end of file *) IF KeyTypedAhead() THEN END (* if *) ; IF Obey.Index > 0 THEN Value := IO.XSReadByte( Obey.Streams[ Obey.Index ] ) ; RETURN BYTE( Value ) ; END (* if *) ; END (* if *) ; WHILE IO.XBytesOutstanding( RawKeyBoard ) = 0 DO Display.Something ; END (* while *) ; Value := IO.XSReadByte( RawKeyBoard ) ; IF Value # DisasterChar THEN DisasterFlag := FALSE END; EscapeFlag := FALSE ; IF IO.SWriteByte( KeyHistoryStream , Value ) < 0 THEN END (* if *) ; RETURN BYTE( Value ) ; END GetKey ; (*------------------------------------------------------------------------*) PROCEDURE DeduceMosType ; BEGIN CASE TkByte( 0 , 1 , 0 ) MOD 256 OF 0 : MosType := Mos100 ; | 1 : MosType := Mos120 ; ELSE MosType := UnKnownMos ; END (* case *) ; END DeduceMosType ; (*------------------------------------------------------------------------*) PROCEDURE SetInputStream( FileName : ARRAY OF CHAR ) ; BEGIN INC( Obey.Index ) ; Obey.Streams[ Obey.Index ] := IO.XFindInput( FileName ) ; END SetInputStream ; (*------------------------------------------------------------------------*) (* Put the contents of the buffer into the Key History file ( if any ) *) PROCEDURE RecordHistory (Buffer: ARRAY OF BYTE; Length: CARDINAL); VAR BytesWritten: CARDINAL; BEGIN IF IO.SBlockWrite (BytesWritten, KeyHistoryStream, Length , ADR (Buffer [0])) < 0 THEN (* ignore any error *) END (* if *); END RecordHistory; (*------------------------------------------------------------------------*) PROCEDURE Initialise; VAR r : TKCalls.Registers; value : ARRAY [0..100] OF CHAR; len : CARDINAL; res : INTEGER; PROCEDURE LoadHostCode; BEGIN IF HostCommand (ShiftLockCommand, TurnOn, 0) >= 0 THEN Errors.Panic ("Recursive invocation of editor not permitted") END (* if *); res := GetGlobalString (value, len, "EDIT$HostCode"); IF res < 0 THEN Errors.Panic ("Interface: Unable to read EDIT$HostCode"); END (* if *); res := HostCode.Invoke (value); IF res < 0 THEN Errors.Panic ("Interface: Unable to start hostcode (installation error ?)"); END (* if *); HostCodeActive := TRUE; TellHostCode (ShiftLockCommand, TurnOn, 0); (* Disable shift-lock *) END LoadHostCode; BEGIN r [0] := TKCalls.TKgetVersion; TKCalls.TKCall (r); pandoraVersion := r [1]; IO.XSelectInput (RawKeyBoard); IF GetGlobalString (value, len, "EDIT$KeyHistory") < 0 THEN value := "null:"; END (* if *) ; KeyHistoryStream := IO.XFindOutput (value); DeduceMosType; res := Handler.DeclareEventHandler (EscapeHandler, EscapeEvent, 1, 1); IF res < 0 THEN Program.Stop (res) END; res := Handler.SetEventStatus (EscapeEvent, FALSE); IF res < 0 THEN Program.Stop (res) END; res := Handler.DeclareEventHandler (EventHandler, EventNumber, 1, 42); IF res < 0 THEN Program.Stop (res) END; res := Handler.SetEventStatus (5, TRUE); IF res < 0 THEN Program.Stop (res) END; res := Handler.DeclareConditionHandler (TerminationHandler); IF res < 0 THEN Program.Stop (res) END; LoadHostCode; END Initialise; (*------------------------------------------------------------------------*) PROCEDURE InitialiseDebug; VAR value : ARRAY [0..100] OF CHAR; len : CARDINAL; BEGIN IF GetGlobalString (value, len, "EDIT$DebugStream") < 0 THEN value := "null:"; END (* if *) ; DebugStream := IO.XFindOutput (value); IF GetGlobalString (value, len, "EDIT$DebugLog") < 0 THEN value := "null:"; END (* if *) ; LogFileStream := IO.XFindOutput (value); Debug.UseWrch (PROCWORD (DebugWrch)); Debug.WriteS ("*P"); END InitialiseDebug; (*------------------------------------------------------------------------*) PROCEDURE Terminate; BEGIN IF HostCodeActive THEN TellHostCode (TerminateCommand, 0, 0); (* Turn off hostcode *) END (* if *); END Terminate; (*------------------------------------------------------------------------*) (* Panos calls this before blowing us away !! *) PROCEDURE TerminationHandler ( Why : Handler.TypeCode; Result : INTEGER; VAR E1 : Handler.UsersEnvironment; VAR E2 : Handler.UsersEnvironment ): INTEGER; BEGIN IF (Why = Handler.Stop) AND HostCodeActive THEN TellHostCode (TerminateCommand, 0, 0); (* Turn off hostcode *) END (* if *); IF IO.CloseStream( KeyHistoryStream ) < 0 THEN END; RETURN 0; END TerminationHandler; (*------------------------------------------------------------------------*) BEGIN tube := ADDRESS(0FFFFFFF0H); (* RawVduStream := IO.XFindOutput ("RAWVDU:"); *) RawKeyBoard := IO.XFindInput ("RAWKB:"); Obey.Index := 0 ; DisasterFlag := FALSE ; EscapeFlag := FALSE ; HostCodeActive := FALSE ; InitialiseDebug ; END Interface. (*------------------------------------------------------------------------*)