$CHECKING:OFF SUB vwatch (globalVariables AS _OFFSET, localVariables AS _OFFSET) STATIC AS LONG ideHost, breakpointCount, skipCount, timeout, startLevel, lastLine STATIC AS LONG callStackLength, runToLine STATIC AS _BYTE pauseMode, stepOver, bypass, setNextLine STATIC buffer$, endc$ DIM AS LONG i, tempIndex, localIndex DIM AS _OFFSET address DIM start!, temp$, cmd$, value$, k&, dataType$, result$ DECLARE LIBRARY SUB vwatch_stoptimers ALIAS stop_timers SUB vwatch_starttimers ALIAS start_timers SUB unlockvWatchHandle END DECLARE IF bypass THEN EXIT SUB vwatch_goto = 0 IF ideHost = 0 THEN timeout = 10 endc$ = "" 'initial setup GOSUB Connect 'send this binary's path/exe name cmd$ = "me:" + COMMAND$(0) GOSUB SendCommand DO GOSUB GetCommand SELECT CASE cmd$ CASE "vwatch" IF value$ <> "ok" THEN unlockvWatchHandle: CLOSE #ideHost bypass = -1 EXIT SUB END IF CASE "line count" REDIM vwatch_breakpoints(CVL(value$)) AS _BYTE REDIM vwatch_skiplines(CVL(value$)) AS _BYTE CASE "breakpoint count" breakpointCount = CVL(value$) CASE "breakpoint list" IF LEN(value$) \ 4 <> breakpointCount THEN cmd$ = "quit:Communication error." GOSUB SendCommand unlockvWatchHandle: CLOSE #ideHost bypass = -1 EXIT SUB END IF FOR i = 1 TO breakpointCount temp$ = MID$(value$, i * 4 - 3, 4) vwatch_breakpoints(CVL(temp$)) = -1 NEXT CASE "skip count" skipCount = CVL(value$) CASE "skip list" IF LEN(value$) \ 4 <> skipCount THEN cmd$ = "quit:Communication error." GOSUB SendCommand unlockvWatchHandle: CLOSE #ideHost bypass = -1 EXIT SUB END IF FOR i = 1 TO skipCount temp$ = MID$(value$, i * 4 - 3, 4) vwatch_skiplines(CVL(temp$)) = -1 NEXT CASE "run" IF vwatch_breakpoints(vwatch_linenumber) THEN EXIT DO pauseMode = 0 EXIT SUB CASE "break" pauseMode = -1 EXIT DO END SELECT LOOP END IF IF vwatch_linenumber = 0 THEN GOSUB SendCallStack cmd$ = "quit:Program ended." GOSUB SendCommand unlockvWatchHandle: CLOSE #ideHost bypass = -1 ideHost = 0 EXIT SUB ELSEIF vwatch_linenumber = -1 THEN 'report an error in the most recent line GOSUB SendCallStack cmd$ = "error:" + MKL$(lastLine) GOSUB SendCommand EXIT SUB ELSEIF vwatch_linenumber = -2 THEN 'report a new sub/function has been "entered" IF LEN(vwatch_callstack) > 100000 THEN vwatch_callstack = "" callStackLength = 0 END IF callStackLength = callStackLength + 1 IF LEN(vwatch_callstack) THEN vwatch_callstack = vwatch_callstack + CHR$(0) vwatch_callstack = vwatch_callstack + vwatch_subname$ + ", line" + STR$(lastLine) EXIT SUB ELSEIF vwatch_linenumber = -3 THEN 'handle STOP - instead of quitting, pause execution pauseMode = -1 stepOver = 0 EXIT SUB END IF IF vwatch_linenumber = lastLine AND setNextLine = 0 THEN EXIT SUB setNextLine = 0 lastLine = vwatch_linenumber GOSUB GetCommand SELECT CASE cmd$ CASE "break" pauseMode = -1 stepOver = 0 runToLine = 0 cmd$ = "" CASE "set breakpoint" vwatch_breakpoints(CVL(value$)) = -1 vwatch_skiplines(CVL(value$)) = 0 CASE "clear breakpoint" vwatch_breakpoints(CVL(value$)) = 0 CASE "set skip line" vwatch_skiplines(CVL(value$)) = -1 vwatch_breakpoints(CVL(value$)) = 0 CASE "clear skip line" vwatch_skiplines(CVL(value$)) = 0 CASE "clear all breakpoints" REDIM vwatch_breakpoints(UBOUND(vwatch_breakpoints)) AS _BYTE CASE "clear all skips" REDIM vwatch_skiplines(UBOUND(vwatch_skiplines)) AS _BYTE END SELECT IF vwatch_skiplines(vwatch_linenumber) THEN vwatch_goto = -vwatch_linenumber: EXIT SUB IF stepOver = -1 AND vwatch_sublevel > startLevel AND vwatch_breakpoints(vwatch_linenumber) = 0 THEN EXIT SUB ELSEIF stepOver = -1 AND vwatch_sublevel = startLevel THEN stepOver = 0 pauseMode = -1 END IF IF runToLine > 0 AND runToLine <> vwatch_linenumber THEN EXIT SUB ELSEIF runToLine > 0 AND runToLine = vwatch_linenumber THEN pauseMode = -1 runToLine = 0 END IF IF vwatch_breakpoints(vwatch_linenumber) = 0 AND pauseMode = 0 THEN EXIT SUB END IF vwatch_stoptimers cmd$ = "line number:" IF vwatch_breakpoints(vwatch_linenumber) THEN cmd$ = "breakpoint:" cmd$ = cmd$ + MKL$(vwatch_linenumber) GOSUB SendCommand DO 'main loop SELECT CASE cmd$ CASE "run" pauseMode = 0 stepOver = 0 vwatch_starttimers EXIT SUB CASE "run to line" pauseMode = 0 stepOver = 0 runToLine = CVL(value$) vwatch_starttimers EXIT SUB CASE "step" pauseMode = -1 stepOver = 0 EXIT SUB CASE "step over" pauseMode = -1 stepOver = -1 startLevel = vwatch_sublevel vwatch_starttimers EXIT SUB CASE "step out" pauseMode = -1 stepOver = -1 startLevel = vwatch_sublevel - 1 vwatch_starttimers EXIT SUB CASE "free" unlockvWatchHandle: CLOSE #ideHost ideHost = 0 bypass = -1 vwatch_starttimers EXIT SUB CASE "set breakpoint" vwatch_breakpoints(CVL(value$)) = -1 vwatch_skiplines(CVL(value$)) = 0 CASE "clear breakpoint" vwatch_breakpoints(CVL(value$)) = 0 CASE "clear all breakpoints" REDIM vwatch_breakpoints(UBOUND(vwatch_breakpoints)) AS _BYTE CASE "clear all skips" REDIM vwatch_skiplines(UBOUND(vwatch_skiplines)) AS _BYTE CASE "call stack" 'send call stack history" GOSUB SendCallStack CASE "global var" tempIndex = CVL(LEFT$(value$, 4)) localIndex = CVL(MID$(value$, 5, 4)) dataType$ = MID$(value$, 9) address = globalVariables + LEN(address) * localIndex GOSUB GetMemData $CONSOLE _ECHO "global var requested:" + STR$(tempIndex) + STR$(localIndex) + " " + dataType$ _ECHO "== result = " + result$ cmd$ = "global var:" + MKL$(tempIndex) + result$ GOSUB SendCommand CASE "local var" tempIndex = CVL(LEFT$(value$, 4)) localIndex = CVL(MID$(value$, 5, 4)) dataType$ = MID$(value$, 9) address = localVariables + LEN(address) * localIndex GOSUB GetMemData _ECHO "local var requested:" + STR$(tempIndex) + STR$(localIndex) + " " + dataType$ _ECHO "== result = " + result$ cmd$ = "local var:" + MKL$(tempIndex) + result$ GOSUB SendCommand CASE "current sub" cmd$ = "current sub:" + vwatch_subname GOSUB SendCommand CASE "set next line" pauseMode = -1 stepOver = 0 setNextLine = -1 vwatch_goto = CVL(value$) EXIT SUB CASE "set skip line" vwatch_skiplines(CVL(value$)) = -1 vwatch_breakpoints(CVL(value$)) = 0 CASE "clear skip line" vwatch_skiplines(CVL(value$)) = 0 END SELECT GOSUB GetCommand _LIMIT 100 LOOP vwatch_starttimers EXIT SUB Connect: DIM ideport$ ideport$ = ENVIRON$("QB64DEBUGPORT") IF ideport$ = "" THEN bypass = -1: EXIT SUB start! = TIMER DO k& = _KEYHIT ideHost = _OPENCLIENT("QB64IDE:" + ideport$ + ":localhost") _LIMIT 30 LOOP UNTIL k& = 27 OR ideHost <> 0 OR TIMER - start! > timeout IF ideHost = 0 THEN bypass = -1: EXIT SUB RETURN GetCommand: GET #ideHost, , temp$ buffer$ = buffer$ + temp$ IF INSTR(buffer$, endc$) THEN cmd$ = LEFT$(buffer$, INSTR(buffer$, endc$) - 1) buffer$ = MID$(buffer$, INSTR(buffer$, endc$) + LEN(endc$)) IF INSTR(cmd$, ":") THEN value$ = MID$(cmd$, INSTR(cmd$, ":") + 1) cmd$ = LEFT$(cmd$, INSTR(cmd$, ":") - 1) ELSE value$ = "" END IF ELSE cmd$ = "": value$ = "" END IF RETURN SendCallStack: cmd$ = "call stack size:" + MKL$(callStackLength) GOSUB SendCommand cmd$ = "call stack:" + vwatch_callstack GOSUB SendCommand RETURN SendCommand: cmd$ = cmd$ + endc$ PUT #ideHost, , cmd$ cmd$ = "" RETURN GetMemData: DIM integerType AS INTEGER, uintegerType AS _UNSIGNED INTEGER DIM longType AS LONG, ulongType AS _UNSIGNED LONG DIM singleType AS SINGLE, doubleType AS DOUBLE DIM varOffset AS _OFFSET, m AS _MEM, m2 AS _MEM SELECT CASE dataType$ CASE "INTEGER" m = _MEM(address, 2) m2 = _MEM(integerType) _MEMCOPY m, m.OFFSET, m.SIZE TO m2, m2.OFFSET _MEMFREE m _MEMFREE m2 result$ = STR$(integerType) CASE "_UNSIGNED INTEGER" m = _MEM(address, 2) m2 = _MEM(uintegerType) _MEMCOPY m, m.OFFSET, m.SIZE TO m2, m2.OFFSET _MEMFREE m _MEMFREE m2 result$ = STR$(uintegerType) CASE "LONG" m = _MEM(address, 4) m2 = _MEM(longType) _MEMCOPY m, m.OFFSET, m.SIZE TO m2, m2.OFFSET _MEMFREE m _MEMFREE m2 result$ = STR$(longType) CASE "_UNSIGNED LONG" m = _MEM(address, 4) m2 = _MEM(ulongType) _MEMCOPY m, m.OFFSET, m.SIZE TO m2, m2.OFFSET _MEMFREE m _MEMFREE m2 result$ = STR$(ulongType) CASE "SINGLE" m = _MEM(address, 4) m2 = _MEM(singleType) _MEMCOPY m, m.OFFSET, m.SIZE TO m2, m2.OFFSET _MEMFREE m _MEMFREE m2 result$ = STR$(singleType) CASE "DOUBLE" m = _MEM(address, 8) m2 = _MEM(doubleType) _MEMCOPY m, m.OFFSET, m.SIZE TO m2, m2.OFFSET _MEMFREE m _MEMFREE m2 result$ = STR$(doubleType) END SELECT RETURN END SUB