'=========================================================================== ' Subject: PB ANSI-DRIVER Date: Year of 1993/95 ' Author: Jamshid Khoshrangi Code: PB ' Origin: qjackson@direct.ca Packet: ANSI.ABC '=========================================================================== $IF 0 LANSI.BAS The FSA ANSI-Driver LANSI.BAS Laleh's ANSI Version 3.1 LANSI.BAS is now PowerBASIC Compatible! Written by Jamshid Khoshrangi (aka "Quinn Tyler Jackson") Copyright (C)1993,95 by AhuraMazda(tm) Software. ALL RIGHTS RESERVED. USAGE RIGHTS: Although Jamshid Khoshrangi reserves all rights to LANSI.BAS, he grants others the right to use it in whole or in part as long as any product that uses it explicitly includes in its documentation or opening screen (this left to the discretion of the individual programmer) the notice: "ANSI emulation provided by Laleh's ANSI (C)1993,95 by AMS." DEDICATION: This driver is dedicated to my wife Laleh, who has tolerated my sitting at a yucky computer terminal for thousands of hours with little or no direct return to her. UPDATE NOTES: UPDATE NOTES: (10 SEP 95) I have upgraded LANSI to version 3.0 from version 2.0, and it is now compatible 100% with PowerBASIC 3.x. The upgrade consisted mainly of changing a few dozen CONST x statements to %x, and the like, as well as using INCR x and DECR x instead of x = x + 1 and x = x - 1. During the upgrade, I discovered a few insidious glitches in some routines, which I have fixed.... TECHNICAL NOTES: (31 OCT 93) LANSI.BAS was written as an exercise in my investigation into the the complexities of Finite State Automata (FSA's). ANSI graphics are particularly suitable to an FSA model, since they rely on a finite set of commands and use a type of Reverse Polish Notation (RPN). Reverse Polish Notation lends itself amazingly well to efficient implementations of Finite State systems. This driver includes non-ANSI "ANSI" music support, but this support can be turned off by setting the global variable GV.Music is set to %FALSE (0). With this variable set to %FALSE, LANSI treats music strings in exactly the same manner a typical ANSI.SYS driver would: it prints them to the screen. This option has been added for ANSI purists. I have yet to call a BBS with such music anyway, but hey, why not have it all? Note that the music supported here can be full background. A 6.5 minute song can be downloaded as a series of music sequences in about 5 seconds and then played over then next 6.5 minutes in the background on a typical 2400 baud modem using the system I have implemented here. This system buffers up to 200 "lines" of music sequences. Many terminals don't allow full background music, since they are not written in BASIC and therefore have to emulate the PLAY metalanguage. BASIC has direct access to "MB", so this is no problem if double-buffering is used. Also, LANSI.BAS supports a subset of the ANSI keyboard redefinition capabilities, unlike many other ANSI emulators that totally ignore this part of the ANSI standard. Support for keyboard redefinition can be toggled on and off as well. The only two formats allowed are: ESC[{old_ascii_val};"some string"p ESC[{old_ascii_val};{new_ascii_val}p Since keyboard redefinition is limited to the emulator, there is absolutely no chance of so-called ANSI bombs slipping past LANSI and into the DOS prompt. This code is, for the most part, raw and undocumented. I've done the hard coding; it's up to you to figure out what I've done. My belief is simple in this regard: if I cannot decipher the code without detailed English comments and remarks, then I probably should NOT be changing the code! If you can understand the raw code, then you might want to go and tweak it. I've found that I do best to leave code I don't "quite" understand well-enough-alone. Be warned that FSA systems are quick, but prone to nastiness when incorrectly tweaked. That's the nature of the finite state paradigm. Also, you will notice the term "VisiPlex" from time to time. Just ignore it. This will follow much later, and is included in LANSI.BAS just for future compatibility. I sincerely hope you find LANSI.BAS useful! Jamshid Khoshrangi (aka "Quinn Tyler Jackson") $ENDIF %DEBUG = 0 $IF %DEBUG $COMPILE MEMORY $CPU 80386 $ELSE $CODE SEG "AHURAMAZDA" $COMPILE UNIT "LANSI.PBU" $CPU 8086 $ENDIF $OPTIMIZE SPEED %TRUE = -1 %FALSE = NOT %TRUE DEFINT A-Z TYPE GlobalVarType STATE AS INTEGER ' What state is the FSA in? X AS INTEGER ' Cursor ROW Y AS INTEGER ' Cursor COLUMN OldX AS INTEGER ' For saving ROW with $e[s OldY AS INTEGER ' For saving COLUMN with $e[s RemoteX AS INTEGER ' The remote's ROW RemoteY AS INTEGER ' The remote's COLUMN ScreenHeight AS INTEGER ' What is the height of our screen? ScreenWidth AS INTEGER ' What is the width of our screen? Bold AS INTEGER ' Bold attribute Blink AS INTEGER ' Blink attribute Reversed AS INTEGER ' Reversed attribute Concealed AS INTEGER ' Concealed attribute DesBackspace AS INTEGER ' Set to %TRUE if ' is destructive ExpandTab AS INTEGER ' Set to %TRUE if is expanded TabStep AS INTEGER ' Number of spaces to expand 1 tab LineWrap AS INTEGER ' Set to %TRUE if in linewrap mode CursorVis AS INTEGER ' Set to 1 if cursor is visible Music AS INTEGER ' Set to %FALSE if in ANSI only mode Speaker AS INTEGER ' Set to %FALSE if sound turned off BeepHz AS INTEGER ' Beep tone in Hertz BeepDur AS INTEGER ' Duration of Beep in ticks MapActive AS INTEGER ForeColor AS INTEGER BackColor AS INTEGER ColorAttr AS INTEGER ScreenSeg AS INTEGER ' For direct screen writes SavedFlag AS INTEGER ' Has a $e[s been previously executed? VisiPlex AS INTEGER ' Are we in VisiPlex mode VisiVersion AS INTEGER ' If so, what is version of other? END TYPE %LOW.LEVEL = %FALSE %HIGH.LEVEL = %TRUE %ANSI.F.BLACK = 30 %ANSI.F.RED = 31 %ANSI.F.GREEN = 32 %ANSI.F.YELLOW = 33 %ANSI.F.BLUE = 34 %ANSI.F.MAGENTA = 35 %ANSI.F.CYAN = 36 %ANSI.F.WHITE = 37 %ANSI.B.BLACK = 40 %ANSI.B.RED = 41 %ANSI.B.GREEN = 42 %ANSI.B.YELLOW = 43 %ANSI.B.BLUE = 44 %ANSI.B.MAGENTA = 45 %ANSI.B.CYAN = 46 %ANSI.B.WHITE = 47 %STATE.NORMAL = 0 %STATE.READ.ESC = 1 %STATE.IN.ANSI = 2 %STATE.IN.INT.PARAM = 3 %STATE.READ.SEMICOLON = 4 %STATE.READ.ANSI.COMMAND= 5 %STATE.READ.OPEN.QUOTE = 6 %STATE.IN.STRING.LITERAL= 7 %STATE.READ.CLOSE.QUOTE = 8 %STATE.READ.CONTROL.CODE= 9 %STATE.ERROR.RESET.ANSI = 10 %STATE.IN.MUSIC = 11 %STATE.INTEGER.PUSH = 12 ' push integer to integer stack %STATE.STRING.PUSH = 13 ' push string to string stack ' TOKEN TYPES: ' ' e = ESC %TOKEN.ESC = 1 ' [ = [ %TOKEN.BRACKET = 2 ' 0 = 0,1,2,3,4,5,6,7,8,9 %TOKEN.DIGIT = 3 ' ; = ; %TOKEN.SEMICOLON = 4 ' H = H,f,A,B,C,D,s,u,J,K,m,h,l,p,n %TOKEN.ANSI.COMMAND = 5 ' " = " %TOKEN.QUOTE = 6 ' < = ASCII code less than 32 %TOKEN.CONTROL.CODE = 7 ' A = Standard A-Z %TOKEN.ASCII = 8 ' M = M %TOKEN.MUSIC.STRING.START = 9 ' # = ^N %TOKEN.ANSI.MUSIC = 10 TokenTableData: ' ASCII Type ASCII Type DATA "", 01, "[", 02 DATA "0", 03, "1", 03 DATA "2", 03, "3", 03 DATA "4", 03, "5", 03 DATA "6", 03, "7", 03 DATA "8", 03, "9", 03 DATA ";", 04, "H", 05 DATA "f", 05, "A", 05 DATA "B", 05, "C", 05 DATA "D", 05, "s", 05 DATA "u", 05, "J", 05 DATA "K", 05, "m", 05 DATA "h", 05, "l", 05 DATA "p", 05, "R", 05 DATA "n", 05, "", 10 DATA "M", 09, "", 07 DATA " ", 08, "", 07 DATA "", 07, "", 07 DATA "", 07, "", 07 DATA " ", 07 "", -1 StateShiftTableData: ' WARNING: ' ' Any tweaking of this table may be FATAL to the working ' of this driver! Unless you ABSOLUTELY understand what ' you are doing, please DO NOT twiddle these bits! ' e [ 0 ; H " < A M # DATA 00, 01, 00, 00, 00, 00, 00, 09, 00, 00, 00 DATA 01, 00, 02, 00, 00, 00, 00, 00, 00, 00, 00 DATA 02, 00, 10, 03, 04, 05, 06, 10, 10, 11, 10 DATA 03, 10, 10, 03, 12, 05, 10, 10, 10, 10, 10 DATA 04, 10, 10, 12, 10, 10, 06, 10, 10, 10, 10 DATA 06, 07, 07, 07, 07, 07, 08, 07, 07, 07, 10 DATA 07, 07, 07, 07, 07, 07, 08, 07, 07, 07, 07 DATA 08, 10, 10, 10, 11, 05, 10, 10, 10, 10, 10 DATA 09, 01, 00, 00, 00, 00, 00, 09, 00, 00, 00 DATA 11, 10, 10, 11, 10, 11, 10, 10, 11, 11, 05 $IF %DEBUG DIM AnsiGv AS SHARED GlobalVarType $ELSE DIM AnsiGv AS SHARED GlobalVarType EXTERNAL AnsiGv $ENDIF DIM TokenTable(255) AS SHARED INTEGER DIM StateTable(13, 10) AS SHARED INTEGER DIM KeyTable(255) AS SHARED STRING DIM MapActive(255) AS SHARED INTEGER DIM CharBuffer AS SHARED STRING DIM IntStack(10) AS SHARED INTEGER DIM StrStack(10) AS SHARED STRING DIM IntPtr AS SHARED INTEGER DIM StringPtr AS SHARED INTEGER DIM MusicBuffer(200) AS SHARED STRING DIM BarPtr AS SHARED INTEGER DIM TopPtr AS SHARED INTEGER ' DEBUG CODE STARTS HERE! $IF %DEBUG LansiSystemInit CLS OPEN "C:\DOS\TRM\UTILS\TERMINAT.LGO" FOR BINARY AS #1 GET$ #1, LOF(1), Test$ CLOSE #1 FOR i = 1 TO LEN(Test$) LansiByteInterpret ASC(MID$(Test$, i, 1)) NEXT i DO LOOP UNTIL LEN(INKEY$) $ENDIF END 'DEBUG CODE ENDS HERE! MusicHandler: INCR BarPtr INCR TotChar, LEN(MusicBuffer(BarPtr)) SELECT CASE BarPtr CASE 201 BarPtr = 1 CASE TopPtr PLAY "MF" + MusicBuffer(BarPtr) OverFlag = %TRUE BarPtr = 0 TopPtr = 0 PLAY OFF END SELECT IF TopPtr > 1 THEN PLAY MusicBuffer(BarPtr) END IF MusicBuffer(BarPtr) = "" RETURN ' From MusicHandler: SUB LansiSystemInit () PUBLIC StateTableInit TokenTableInit END SUB SUB BarPush (Score AS STRING) INCR TopPtr IF TopPtr = 201 THEN NotFirst = %TRUE TopPtr = 1 END IF MusicBuffer(TopPtr) = Score IF TopPtr = 1 AND NOT NotFirst THEN PLAY "MBT255N0N0N0T120" END IF END SUB SUB ControlCodeReact (code AS INTEGER) %CONT.CTRL.D = 4 %CONT.CTRL.E = 5 %CONT.CTRL.G = 7 %CONT.BACKSPACE = 8 %CONT.TAB = 9 %CONT.PAGEFEED = 12 %CONT.CTRL.S = 19 %CONT.CTRL.X = 24 SELECT CASE code CASE %CONT.CTRL.E CursorUp 1 CASE %CONT.CTRL.X CursorDown 1 CASE %CONT.CTRL.D CursorRight 1 CASE %CONT.CTRL.S CursorLeft 1 CASE %CONT.CTRL.G IF AnsiGv.Speaker THEN SOUND AnsiGv.BeepHz, AnsiGv.BeepDur END IF CASE %CONT.PAGEFEED LansiScreenClear CASE %CONT.BACKSPACE IF AnsiGv.DesBackspace THEN IF AnsiGv.Y > 1 THEN DECR AnsiGv.Y CursorLocate AnsiGv.X, AnsiGv.Y sqjPRINT 32, %HIGH.LEVEL' Print a space DECR AnsiGv.Y CursorLocate AnsiGv.X, AnsiGv.Y END IF END IF CASE %CONT.TAB IF AnsiGv.ExpandTab THEN IF AnsiGv.Y + AnsiGv.TabStep < 79 THEN FOR i = 1 TO AnsiGv.TabStep sqjPRINT 32, %LOW.LEVEL NEXT i END IF ELSE sqjPRINT 32, %HIGH.LEVEL END IF END SELECT END SUB SUB CursorDown (RowsDown AS INTEGER) IF IntPtr = 0 THEN RowsDown = 1 END IF TempX = AnsiGv.X + RowsDown IF TempX > AnsiGv.ScreenHeight THEN TempX = AnsiGv.ScreenHeight END IF AnsiGv.X = TempX CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB CursorLeft (ColsLeft AS INTEGER) IF IntPtr = 0 THEN ColsLeft = 1 END IF TempY = AnsiGv.Y - ColsLeft IF TempY < 1 THEN TempY = 1 END IF AnsiGv.Y = TempY CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB CursorLocate (X AS INTEGER,_ Y AS INTEGER) IF Y > AnsiGv.ScreenWidth THEN Y = 1 IF AnsiGv.LineWrap THEN INCR X END IF ELSE IF Y < 1 THEN Y = 1 END IF END IF IF X > AnsiGv.ScreenHeight THEN EXIT SUB ELSE IF X < 1 THEN X = 1 END IF END IF AnsiGv.X = X AnsiGv.Y = Y IF AnsiGv.CursorVis THEN LOCATE AnsiGv.X, AnsiGv.Y, AnsiGv.CursorVis, 6, 7 END IF END SUB SUB CursorRestore IF AnsiGv.SavedFlag THEN CursorLocate AnsiGv.OldX, AnsiGv.OldY END IF END SUB SUB CursorRight (ColsRight AS INTEGER) IF IntPtr = 0 THEN ColsRight = 1 END IF TempY = AnsiGv.Y + ColsRight IF TempY > AnsiGv.ScreenWidth THEN TempY = AnsiGv.ScreenWidth END IF AnsiGv.Y = TempY CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB CursorSave AnsiGv.SavedFlag = %TRUE AnsiGv.OldX = AnsiGv.X AnsiGv.OldY = AnsiGv.Y END SUB SUB CursorUp (RowsUp AS INTEGER) IF IntPtr = 0 THEN RowsUp = 1 END IF TempX = AnsiGv.X - RowsUp IF TempX < 1 THEN TempX = 1 END IF AnsiGv.X = TempX CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB EOLErase CursorSave CursorVis = AnsiGv.CursorVis AnsiGv.CursorVis = %FALSE FOR Ptr = AnsiGv.Y TO AnsiGv.ScreenWidth sqjPRINT 0, %LOW.LEVEL NEXT Ptr AnsiGv.CursorVis = CursorVis CursorRestore CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB IntPush IF LEN(CharBuffer) THEN INCR IntPtr IntStack(IntPtr) = VAL(CharBuffer) CharBuffer = "" END IF END SUB SUB KeyboardMap (KeyCode AS INTEGER,_ Redefinition AS STRING) SELECT CASE KeyCode CASE 8474 ' Not a key! This is the VisiPlex flag! ' "8474" is "VISI" dialed on a phone... IF LEFT$(Redefinition, 10) = "VisiPlex V" THEN AnsiGv.VisiPlex = %TRUE AnsiGv.MapActive = %FALSE AnsiGv.VisiVersion = VAL(MID$(Redefinition, 11)) END IF CASE ELSE SELECT CASE AnsiGv.VisiPlex CASE %TRUE VisiPlexComReact KeyCode, Redefinition CASE %FALSE IF KeyCode < 256 THEN KeyTable(KeyCode) = Redefinition MapActive(KeyCode) = %TRUE END IF END SELECT END SELECT END SUB SUB LinewrapDisable AnsiGv.LineWrap = %FALSE END SUB SUB MusicPlay (Score AS STRING) SELECT CASE AnsiGv.Music CASE %TRUE IF AnsiGv.Speaker THEN sqjPLAY Score END IF CASE %FALSE FOR i = 1 TO LEN(Score) sqjPRINT ASC(MID$(Score, i, 1)), %LOW.LEVEL NEXT i sqjPRINT 14, %LOW.LEVEL END SELECT END SUB SUB sqjPLAY (Score AS STRING) Score = UCASE$(MID$(Score,2)) SELECT CASE INSTR(Score, "MB") CASE 0 PLAY Score CASE ELSE ON PLAY(3) GOSUB MusicHandler PLAY ON BarPush Score END SELECT END SUB SUB sqjPRINT (Bite AS INTEGER,_ Level AS INTEGER) SELECT CASE Level * MapActive(Bite) * AnsiGv.MapActive CASE 0 SELECT CASE Bite CASE 13 AnsiGv.Y = 1 CASE 10 IF AnsiGv.X < AnsiGv.ScreenHeight THEN INCR AnsiGv.X ELSE ' This forces a screen scroll LOCATE AnsiGv.ScreenHeight + 1, 1 PRINT END IF CASE ELSE FPRINT Bite INCR AnsiGv.Y END SELECT CASE ELSE FOR i = 1 TO LEN(KeyTable(Bite)) sqjPRINT ASC(MID$(KeyTable(Bite), i, 1)), %LOW.LEVEL NEXT i END SELECT CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB RemoteCursorSet (X AS INTEGER,_ Y AS INTEGER) ' This information is mostly useless, and is received as ' a result of sending a ESC[6n sequence.... AnsiGv.RemoteX = X AnsiGv.RemoteY = Y END SUB SUB ScreenAttrReact () SELECT CASE AnsiGv.Reversed CASE %TRUE TempFore = AnsiGv.BackColor TempBack = AnsiGv.ForeColor CASE %FALSE TempFore = AnsiGv.ForeColor TempBack = AnsiGv.BackColor END SELECT IF AnsiGv.Bold THEN TempFore = TempFore + 8 END IF IF AnsiGv.Concealed THEN TempFore = TempBack ' Version 3.1 fix... turns off cursor by force LOCATE , , 0 ELSE ' Otherwise, we must turn the cursor on by force LOCATE , , 1 END IF AnsiGv.ColorAttr = TempBack * 16 + TempFore IF AnsiGv.Blink THEN BIT SET AnsiGv.ColorAttr, 7 END IF COLOR TempFore, TempBack END SUB SUB ScreenAttrSet (Attribute AS INTEGER) SELECT CASE Attribute CASE 0 AnsiGv.Bold = %FALSE AnsiGv.Blink = %FALSE AnsiGv.Reversed = %FALSE AnsiGv.Concealed= %FALSE AnsiGv.ForeColor= 7 AnsiGv.BackColor= 0 CASE 1 AnsiGv.Bold = %TRUE CASE 5 AnsiGv.Blink = %TRUE CASE 7 AnsiGv.Reversed = %TRUE CASE 8 AnsiGv.Concealed= %TRUE CASE %ANSI.F.BLACK AnsiGv.ForeColor= 0 CASE %ANSI.F.RED AnsiGv.ForeColor= 4 CASE %ANSI.F.GREEN AnsiGv.ForeColor= 2 CASE %ANSI.F.YELLOW AnsiGv.ForeColor= 6 CASE %ANSI.F.BLUE AnsiGv.ForeColor= 1 CASE %ANSI.F.MAGENTA AnsiGv.ForeColor= 5 CASE %ANSI.F.CYAN AnsiGv.ForeColor= 3 CASE %ANSI.F.WHITE AnsiGv.ForeColor= 7 CASE %ANSI.B.BLACK AnsiGv.BackColor= 0 CASE %ANSI.B.RED AnsiGv.BackColor= 4 CASE %ANSI.B.GREEN AnsiGv.BackColor= 2 CASE %ANSI.B.YELLOW AnsiGv.BackColor= 6 CASE %ANSI.B.BLUE AnsiGv.BackColor= 1 CASE %ANSI.B.MAGENTA AnsiGv.BackColor= 5 CASE %ANSI.B.CYAN AnsiGv.BackColor= 3 CASE %ANSI.B.WHITE AnsiGv.BackColor= 7 END SELECT AnsiGv.ColorAttr = AnsiGv.ForeColor + AnsiGv.BackColor * 16 END SUB SUB LansiScreenClear () PUBLIC AnsiGv.X = 1 AnsiGv.Y = 1 COLOR AnsiGv.ForeColor, AnsiGv.BackColor CLS TEXT CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB ScreenModeSet (Mode AS INTEGER) SELECT CASE Mode CASE 0, 1 ScreenWidthSet 40 CASE 2, 3 ScreenWidthSet 80 CASE 7 AnsiGv.LineWrap = %TRUE END SELECT END SUB SUB ScreenWidthSet (Columns AS INTEGER) WIDTH Columns, 25 AnsiGv.ScreenWidth = Columns END SUB SUB StateReact (Bite AS INTEGER) SELECT CASE AnsiGv.STATE CASE %STATE.NORMAL sqjPRINT Bite, %HIGH.LEVEL CASE %STATE.READ.ESC ' No need to "do" anything. CASE %STATE.IN.ANSI ' No need to "do" anything. CASE %STATE.IN.INT.PARAM CharBuffer = CharBuffer + CHR$(Bite) CASE %STATE.READ.SEMICOLON IntPush AnsiGv.STATE = %STATE.IN.ANSI StateReact 0 CASE %STATE.READ.ANSI.COMMAND SELECT CASE CHR$(Bite) CASE "H", "f" IntPush SELECT CASE IntPtr CASE 2 CursorLocate IntStack(1), IntStack(2) CASE 1 ' I added this during the debugging of v3.0 ' since I had overlooked it for some reason.... CursorLocate IntStack(1), 1 CASE 0 CursorLocate 1, 1 END SELECT IntPtr = 0 CASE "A" IntPush CursorUp IntStack(1) IntPtr = 0 CASE "B" IntPush CursorDown IntStack(1) IntPtr = 0 CASE "C" IntPush CursorRight IntStack(1) IntPtr = 0 CASE "D" IntPush CursorLeft IntStack(1) IntPtr = 0 CASE "s" CursorSave CASE "u" CursorRestore CASE "J" IntPush LansiScreenClear IntPtr = 0 CASE "K" EOLErase CASE "m" IntPush FOR Ptr = 1 TO IntPtr ScreenAttrSet IntStack(Ptr) ' This was moved here to fix a glitch since ' [0;xxx was not read properly ScreenAttrReact NEXT Ptr IntPtr = 0 CASE "h" IntPush ScreenModeSet IntStack(1) IntPtr = 0 CASE "l" IntPush ' Version 3.1 fix -- all modes except 7 act as ' with "h" command SELECT CASE InStack(1) CASE 7 LinewrapDisable CASE ELSE ScreenModeSet IntStack(1) END SELECT CASE "p" IntPush SELECT CASE IntPtr CASE 1 KeyboardMap IntStack(1), StrStack(1) StringPtr = 0 CASE 2 KeyboardMap IntStack(1), CHR$(IntStack(2)) END SELECT IntPtr = 0 CASE "n" IntPush SystemReqReact IntStack(1) IntPtr = 0 CASE "" StringPush MusicPlay StrStack(1) StringPtr = 0 CASE "R" IntPush RemoteCursorSet IntStack(1), IntStack(2) IntPtr = 0 END SELECT AnsiGv.STATE = %STATE.NORMAL CASE %STATE.READ.OPEN.QUOTE CASE %STATE.IN.STRING.LITERAL CharBuffer = CharBuffer + CHR$(Bite) CASE %STATE.READ.CLOSE.QUOTE StringPush CASE %STATE.READ.CONTROL.CODE ControlCodeReact Bite CASE %STATE.ERROR.RESET.ANSI IntPtr = 0 StringPtr = 0 CharBuffer = "" sqjPRINT Bite, %LOW.LEVEL AnsiGv.STATE = %STATE.NORMAL CASE %STATE.IN.MUSIC CharBuffer = CharBuffer + CHR$(Bite) CASE %STATE.INTEGER.PUSH IntPush AnsiGv.STATE = %STATE.IN.ANSI CASE %STATE.STRING.PUSH StringPush AnsiGv.STATE = %STATE.IN.ANSI END SELECT END SUB SUB StateTableInit () RESTORE StateShiftTableData DIM Tkn(1 TO 10) FOR STATE = 0 TO 10 READ STATE READ Tkn(1),_ Tkn(2),_ Tkn(3),_ Tkn(4),_ Tkn(5),_ Tkn(6),_ Tkn(7),_ Tkn(8),_ Tkn(9),_ Tkn(10) FOR TokenType = 1 TO 10 StateTable(STATE, TokenType) = Tkn(TokenType) NEXT NEXT STATE END SUB SUB StringPush INCR StringPtr StrStack(StringPtr) = CharBuffer CharBuffer = "" END SUB SUB SystemReqReact (Request AS INTEGER) SELECT CASE Request CASE 6 ' Request cursor position! ' Put code here that sends cursor position in format: ' ' $e[xx;yyR CASE ELSE ' DOS's %ANSI.SYS responds just as above! END SELECT END SUB SUB TokenTableInit () ' Set some default start up values for the global system variables ' These will suffice for most purposes. AnsiGv.X = 1 AnsiGv.Y = 1 AnsiGv.ScreenWidth = 80 AnsiGv.ScreenHeight = 24 AnsiGv.DesBackspace = %TRUE AnsiGv.ExpandTab = %TRUE AnsiGv.TabStep = 5 AnsiGv.LineWrap = %TRUE AnsiGv.ForeColor = 7 AnsiGv.BackColor = 0 AnsiGv.ColorAttr = &H07 AnsiGv.CursorVis = 1 AnsiGv.Music = %TRUE AnsiGv.Speaker = %TRUE AnsiGv.BeepHz = 300 AnsiGv.BeepDur = 3 AnsiGv.MapActive = %TRUE IF (pbvScrnCard AND 1) = 0 THEN AnsiGv.ScreenSeg = &HB800 ' color monitor ELSE AnsiGv.ScreenSeg = &HB000 ' mono monitor END IF RESTORE TokenTableData FOR i = 0 TO 255 TokenTable(i) = %TOKEN.ASCII KeyTable(i) = CHR$(i) MapActive(i) = %FALSE NEXT i TokenTable(9) = 7 TokenTable(10) = 8 TokenTable(34) = 6 DO READ Char$, TokenType IF TokenType > 0 THEN TokenTable(ASC(Char$)) = TokenType ELSE EXIT DO END IF LOOP CursorLocate AnsiGv.X, AnsiGv.Y END SUB SUB VisiPlexComReact (ComType AS INTEGER,_ VisiCommand AS STRING) ' VisiPlex driver goes here and responds to the specific VisiPlex ' commands that will be standardized later! Until then, you'll just ' have to wait. ' ' It will be a simple matter of doing this here: ' ' VisiDriver ComType, VisiCommand ' ' AND BINGO! Instant support! END SUB SUB LansiByteInterpret (BYVAL Bite AS INTEGER) PUBLIC AnsiGv.STATE = StateTable(AnsiGv.STATE, TokenTable(Bite)) StateReact Bite END SUB SUB FPRINT(BYVAL Char AS INTEGER) ScrnSeg = AnsiGv.ScreenSeg ! push DS ; save DS for PowerBASIC ! mov AX, ScrnSeg ; put screen segment in AX ! mov ES, AX ; move to ES Row = AnsiGv.X ! mov AX, Row ; put row in AX ! dec AX ; minus one ! mov CX, 160 ; AX = ! mul CX ; AX * 160 ! mov DI, AX ; put it in DI Col = AnsiGv.Y ! mov AX, Col ; put column in AX ! dec AX ; minus one ! shl AX, 1 ; times 2 ! add DI, AX ; add to DI Attr = AnsiGv.ColorAttr ! mov AH, Attr ; put attribute in AH ! mov AL, Char WriteChar: ! stosw ; write char and attribute to screen QPExit: ! pop DS ; restore DS for PowerBASIC END SUB