; Black box code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; USER INTERFACE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Note:  To abort the program while a black box window is
; active, define the ..BB_AbortProgram label at the beginning of
; the program's shutdown and clean up code.  Define the
; ..BB_KillProgram to shut it down immediately.

; Use the key defined as BB_ResumeKey to step through
; breakpoints, the key defined as BB_AbortProgramKey to abort
; the program, and the key defined as BB_KillProgramKey to
; immediately shut down the program.  Redefine the
; ..BB_AbortProgram label (and, if desired, the ..BB_KillProgram
; label) as aliases for other labels, using the equ directive,
; at different stages of the program if you want the program to
; jump to different parts of the code depending on where it is
; at when you tell it to abort (or forcibly end).

; User Def's ===================================================

  ON equ -1
  OFF equ 0

  if ~ defined BB_DebugMode
    BB_DebugMode  = ON
  end if

  if ~ defined BB_ResumeKey
    BB_ResumeKey	  = VK_RETURN
  end if
  if ~ defined BB_AbortProgramKey
    BB_AbortProgramKey	  = VK_ESCAPE
  end if
  if ~ defined BB_KillProgramKey
    BB_KillProgramKey	  = VK_PAUSE
  end if

  BBFile_Name	    db 'blackbox.log', $0

; User parameter flags

  ; Action flags
  BB_KeepMessage    = $00000001 ; save as window flag
  BB_ScrapMessage   = $00000002 ; save as window flag

  ; Type specifier flags
  BB_1Byte	    = $10000000
  BB_2Byte	    = $20000000
  BB_4Byte	    = $30000000

; User macros ==================================================

; Black box activation & deactivation

  macro BB_Activate FileName, Message
  {
  if BB_DebugMode = ON
    stdcall BB_StartUp, FileName, Message
  end if
  }

  macro BB_Deactivate Message
  {
  if BB_DebugMode = ON
    stdcall BB_ShutDown, Message
  end if
  }

; Window creation & destruction

  macro BB_UseNewWindow TitleStr
  {
  if BB_DebugMode = ON
    stdcall BB_NewWindow, TitleStr
  end if
  }

  macro BB_DiscardWindow
  {
  if BB_DebugMode = ON
    stdcall BB_ScrapWindow
  end if
  }

; Output control

  macro BB_BreakPoint Flags, Message
  {
  if BB_DebugMode = ON
    if ~ Message eq
      stdcall BB_Update, Flags, Message
    else if ~ Flags eq
      stdcall BB_Update, Flags, 0
    else
      stdcall BB_Update, 0, 0
    end if
  end if
  }

  macro BB_EnteredProc ProcNameStr
  {
  if BB_DebugMode = ON
    stdcall BB_RecProcEntry, ProcNameStr
  end if
  }

  macro BB_LeftProc
  {
  if BB_DebugMode = ON
    stdcall BB_RecProcReturn
  end if
  }

  macro BB_Monitor VarName, DataType, NameStr
  {
  if BB_DebugMode = ON
    stdcall BB_AddVar VarName, DataType, NameStr
  end if
  }

  macro BB_DontMonitor VarName
  {
  if BB_DebugMode = ON
    stdcall BB_DropVar VarName
  end if
  }

; INTERNALS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  if ~ defined GotGeneralDefs
    include 'general defs.inc'
  end if
  if ~ defined GotWindowsDefs
    include 'windows defs.inc'
  end if

;Windows functions used:
;CreateFont       gdi32    a
;DeleteObject     gdi32
;TextOut          gdi32    a
;CloseHandle      kernel32
;CreateFile       kernel32 a
;GetModuleHandle  kernel32 a
;SelectObject     kernel32
;WriteFile        kernel32
;AdjustWindowRect user32
;CreateWindow     user32
;DestroyWindow    user32
;GetDC            user32
;GetKeyState      user32
;ReleaseDC        user32

; Internal def's ===============================================

  BBWindow_MaxNumber	    = $40 ; max. number of windows
  BBWindow_MaxMonitoredVars = $100
  BBWindow_MsgBfrMaxLines   = $8
  BBWindow_MsgBufferLength  = BBWindow_MsgBfrMaxLines * \
				  BBDisplay_MaxWidth
  BBWindow_CharsAcross	    = BBDisplay_MaxWidth
  BBWindow_DfltLinesOfText  = $8
  BBWindow_DefaultXPos	    = $20
  BBWindow_DefaultYPos	    = $20
  BBWindow_NextWndXDelta    = $10
  BBWindow_NextWndYDelta    = $10

  BBDisplay_MaxWidth	    = $40
  BBDisplay_MaxHeight	    = $120

  BBFont_Height 	    = 14
  BBFont_Width		    = 7

  BB_VarNameMaxLength	    = BBDisplay_MaxWidth - $10
  BB_VarListMaxLength	    = $200

  BB_ProcNameMaxLength	    = BBDisplay_MaxWidth - $10
  BB_ProcNameStackLength    = $8

; Flags

  ; Black box flags
  BB_IsOperable     = $80000000
  BB_StartedProc    = $00000001
  BB_FinishedProc   = $00000002
  BB_ProcFlagMask   = $00000003

  ; File flags
  BBFile_IsUsable   = $80000000

  ; Window flags
  BBWindow_IsValid  = $80000000

  ; Watched var. array slot flags
  BBVarList_UsedSlot	    = $80000000
  BBVarList_DataChanged     = $00000001

; Parameter flags

  ; Masks
  BB_SizeMask	    = $70000000

  ; Window flags
  BBWindow_IsValid  = $80000000

; Other flags

  EFlags_Overflow   = $00000800
  EFlags_Direction  = $00000400
  EFlags_Sign	    = $00000080
  EFlags_Zero	    = $00000040
  EFlags_Carry	    = $00000001

; Data =========================================================

  struc BB_FONT {
    label .base
    .Handle		dd ?
    .PointSize		dd 8
    .nHeight		dd 14
    .nWidth		dd 0 ; (i.e. 'Use default.')
    .nEscapement	dd 0
    .nOrientation	dd 0
    .fnWeight		dd FW_NORMAL
    .fdwItalic		dd FALSE
    .fdwUnderline	dd FALSE
    .fdwStrikeOut	dd FALSE
    .fdwCharSet 	dd DEFAULT_CHARSET
    .fdwOutputPrecision dd OUT_DEFAULT_PRECIS
    .fdwClipPrecision	dd CLIP_DEFAULT_PRECIS
    .fdwQuality 	dd DEFAULT_QUALITY
    .fdwPitchAndFamily	dd FIXED_PITCH or FF_MODERN
    .lpszFace		dd szCourierNew
    .sizeof = $ - .base
  }

  struc BB_DATAINFO {
    label .base
    .Flags	    dd ?
    .VarAddress     dd ?
    .VarSpecs	    dd ?
    .VarName	    rb BB_VarNameMaxLength
    .sizeof = $ - .base
  }
  virtual at 0
    Type_BBDataInfo BB_DATAINFO
  end virtual
  virtual at ebx
    EBX_BBDataInfo BB_DATAINFO
  end virtual
  virtual at esi
    ESI_BBDataInfo BB_DATAINFO
  end virtual
  virtual at edi
    EDI_BBDataInfo BB_DATAINFO
  end virtual

  struc DATAPROCWORKSPACE {
    label .base
    .Flags	    dd 0
    .OldValue	    dd 0
    .NewValue	    dd 0
    .sizeof = $ - .base
  }

  struc BB_FILL {
    label .base
    .LineCount	    db ?
    .CharsPerLine   db ?
    .FillChar	    db ?
    .sizeof = $ - .base
  }
  virtual at 0
    Type_BBFill BB_FILL
  end virtual
  virtual at ebx
    EBX_BBFill BB_FILL
  end virtual
  virtual at esi
    ESI_BBFill BB_FILL
  end virtual
  virtual at edi
    EDI_BBFill BB_FILL
  end virtual

  struc BLACKBOXFILE {
  ; Data
    label .base
    .Flags	    dd 0
    .Handle	    dd 0
    .CurOffset	    dd 0
    .BytesToWrite   dd 0
    .BytesWritten   dd 0
    .MaxSize	    dd 0
    .dwDesiredAccess	    dd GENERIC_READ or GENERIC_WRITE
    .dwShareMode	    dd 0
    .lpSecurityAttributes   dd NULL
    .dwCreationDistribution dd CREATE_ALWAYS
    .dwFlagsAndAttributes   dd FILE_ATTRIBUTE_NORMAL
    .hTemplateFile	    dd NULL
    .sizeof = $ - .base
  }

  struc BLACKBOXWINDOW {
    label .base
    .Flags		dd ?
    .Handle		dd ?
    .DCHandle		dd ?
    .WndInfo		EXTWINDOWINFO
    .WndRect		RECT
    .WriteOrigin	COORD2D
    .sizeof = $ - .base
  }
  virtual at 0
    Type_BBWindow BLACKBOXWINDOW
  end virtual
  virtual at ebx
    EBX_BBWindow BLACKBOXWINDOW
  end virtual
  virtual at esi
    ESI_BBWindow BLACKBOXWINDOW
  end virtual
  virtual at edi
    EDI_BBWindow BLACKBOXWINDOW
  end virtual

  struc BLACKBOX {
    label .base
    .Flags		dd 0
    .AppHandle		dd 0
    .Font		BB_FONT
    .CurWndPos		COORD2D
    .TopWindowNumber	dd -1
    .BreakNumber	dd 0 ; total exec. breaks done
    .ProcNameStackPos	dd 0
    .SubBreakNumbers	rd BB_ProcNameStackLength
    .ProcNameStack	rb BB_ProcNameMaxLength * \
			     BB_ProcNameStackLength
    .RegsTemp		GENREGBACKUP
    .EFlagsReg		dd 0
    .File		BLACKBOXFILE
    .Windows		rb Type_BBWindow.sizeof * \
			     BBWindow_MaxNumber
    .DataProcWorkSpace	DATAPROCWORKSPACE
    .WatchedVarCount	dd 0
    .WatchedVarArray	rb Type_BBDataInfo.sizeof * \
			     BBWindow_MaxMonitoredVars
    .TextPos		COORD2D
    .MessageLineCount	dd 0
    .Message		rb BBWindow_MsgBufferLength
    .TextBfrTotalLines	dd 0
    .TextBuffer 	rb BBDisplay_MaxWidth * \
			     BBDisplay_MaxHeight
    .sizeof = $ - .base
  }
  BlackBox BLACKBOX

; Strings def's ================================================

; Format for fill parameters:
;   FillSets, Fill1_LineCount, Fill1_CharsPerLine, Fill1_Char,
;             Fill2_LineCount, Fill2_CharsPerLine, Fill2_Char,
;             Fill3_LineCount, Fill3_CharsPerLine, Fill3_Char,
;             ...

; Format for text parameters:
;   StrCount, Str1_YPos, Str1_XPos, Str1_MaxLength, Str1, $0,
;             Str2_YPos, Str1_XPos, Str2_MaxLength, Str2, $0,
;             Str3_YPos, Str1_XPos, Str3_MaxLength, Str3, $0,
;             ...

; Fills

BB_BlankFill	    equ BBDisplay_MaxWidth, ' '
BB_Separator	    equ BBDisplay_MaxWidth, '-'
BB_DoubleSeparator  equ BBDisplay_MaxWidth, '='

; Text

  struc BB_TEXTDATA {
    label .base
    .YPos	db ?
    label .LineNumber byte at .YPos
    .XPos	db ?
    label .StartPos byte at .XPos
    .ZString	db ?
  }
  virtual at ebx
    EBX_BBTextData BB_TEXTDATA
  end virtual
  virtual at esi
    ESI_BBTextData BB_TEXTDATA
  end virtual
  virtual at edi
    EDI_BBTextData BB_TEXTDATA
  end virtual

BBStrs_ON		db 'ON  '
label BBStrs_ON_DWORD dword at BBStrs_ON
BBStrs_OFF		db 'OFF '
label BBStrs_OFF_DWORD dword at BBStrs_OFF
BBStrs_DataChanged	db '*'

BBStrs_FileHeaderFill	db $2, \
  $1, BB_DoubleSeparator,	$1, BB_BlankFill
BBStrs_FileHeaderText	db $1, \
  $1, $0, BBDisplay_MaxWidth, 'START OF LOG', $0

BBStrs_FileEndFill	db $2, \
  $1, BB_BlankFill,		$1, BB_DoubleSeparator
BBStrs_FileEndText   db $1, \
  $0, $0, BBDisplay_MaxWidth, 'END OF LOG', $0

BBStrs_EnteredProcFill	db $3, \
  $1, BB_DoubleSeparator,	$2, BB_BlankFill, \
  $1, BB_Separator
BBStrs_EnteredProcText	db $2, \
  $1, $0, BBDisplay_MaxWidth, 'Global break #:  HHHHHHHH', $0, \
  $2, $0, BBDisplay_MaxWidth, 'Entered procedure:  _', $0

BBStrs_LeftProcFill	db $3, \
  $1, BB_Separator,		$2, BB_BlankFill, \
  $1, BB_DoubleSeparator
BBStrs_LeftProcText  db $2, \
  $1, $0, BBDisplay_MaxWidth, 'Global break #:  HHHHHHHH', $0, \
  $2, $0, BBDisplay_MaxWidth, 'Left procedure:  _', $0

BBStrs_UpdateTopFill	db $1, \
  $3, BB_BlankFill
BBStrs_UpdateTopText	db $3, \
  $1, $0, BBDisplay_MaxWidth, 'Global break #:  HHHHHHHH', $0, \
  $1, BBDisplay_MaxWidth / 2, BBDisplay_MaxWidth, \
    'Local break no.:  HHHHHHHH', $0, \
  $2, $0, BBDisplay_MaxWidth, 'Procedure:  _', $0
BBStrs_UpdateMsgFill	db $1, \
  $0, BB_BlankFill
BBStrs_UpdateBodyFill	db $1, \
  $9, BB_BlankFill
BBStrs_UpdateBodyText	db $10, \
  $1, $0, BBDisplayMaxWidth, 'Register contents:', $0, \
  $2, $02, BBDisplayMaxWidth, 'EAX = HHHHHHHH', $0, \
  $2, $12, BBDisplayMaxWidth, 'EBX = HHHHHHHH', $0, \
  $2, $22, BBDisplayMaxWidth, 'ECX = HHHHHHHH', $0, \
  $2, $32, BBDisplayMaxWidth, 'EDX = HHHHHHHH', $0, \
  $3, $02, BBDisplayMaxWidth, 'ESP = HHHHHHHH', $0, \
  $3, $12, BBDisplayMaxWidth, 'EBP = HHHHHHHH', $0, \
  $3, $22, BBDisplayMaxWidth, 'ESI = HHHHHHHH', $0, \
  $3, $32, BBDisplayMaxWidth, 'EDI = HHHHHHHH', $0, \
  $4, $0, BBDisplayMaxWidth, 'Flags:', $0, \
  $5, $2, BBDisplayMaxWidth, 'Carry (CF) = ___', $0, \
  $5, $15, BBDisplayMaxWidth, 'Sign (SF) = ___', $0, \
  $5, $2C, BBDisplayMaxWidth, 'Overflow (OF) = ___', $0, \
  $6, $2, BBDisplayMaxWidth, 'Zero (ZF) = ___', $0, \
  $6, $15, BBDisplayMaxWidth, 'Direction (DF) = ___', $0, \
  $8, $0, BBDisplayMaxWidth, 'Variable contents:', $0
BBStrs_UpdateVarFill	db $1, \
  $1, BB_BlankFill
BBStrs_UpdateVarText	db $1, \
  $0, $36, '=', $0
BBStrs_UpdateEndFill	db $1, \
  $1, BB_BlankFill

BBStrs_UpdateSeparator	db $1, $1, BB_BlankFill

; General black box strings
BB_ClassName		    db 'BlackBox', $0

; Window strings
BB_DefaultWindowTitle	    db 'PROGRAM BLACK BOX', $0
szCourierNew		    db 'Courier New', $0

; Error message strings
BB_StartUpFailed	    db \
  'The program black box', NewLine, \
  'could not be activated.', $0
BB_FileCreationFailed	    db \
  'The black box file', NewLine, \
  'could not be created.', $0
BB_WindowCreationFailed     db \
  'A new window could not be created.', $0
BB_AdjustWndRectFailed	    db \
  'AdjustWindowRect(...) failed.', $0
BB_CreateWindowFailed	    db \
  'CreateWindow(...) failed.', $0

; Internal macros ==============================================

; Param. extraction & processing macros

  macro BB_Type Param { and Param, BB_TypeMask }

  macro BB_Size Result, Param
  {
    push  ecx
    and   Param, $F0000000
    shr   Param, $1C ; 1Ch = 28
    mov   ecx, Value
    dec   ecx
    mov   Result, $00000001
    shl   Result, cl
    pop   ecx
  }

  macro BB_Reps Param { and Param, BB_RepsMask }

; Output macros

  macro BB_Fill Address, [ LineCount, CharsPerLine, FillChar ]
  {
    local LoopTop

    push  edi eax ecx
  forward
    mov   ecx, [LineCount]
    LoopTop:
      push  ecx
      cld
      mov   al, [FillChar]
      mov   ecx, [CharsPerLine]
      mov   edi, [Address]
      rep stosb
      pop   ecx
      add   edi, [CharsPerLine]
      dec   ecx
      jnz   LoopTop
  common
    pop   ecx eax edi
  }

  macro BB_FillLine Address, Char
  { BB_Fill Address, $1, $40, Char }

  macro BB_ClearLine Address
  { BB_FillLine Address, ' ' }

  macro BB_DrawSeparator Address
  { BB_FillLine Address, '-' }

  macro BB_DrawDoubleSeparator Address
  { BB_FillLine Address, '=' }

; Proc's =======================================================

  proc BB_StartUp, FileName, Message

    enter
    pusha
    pushfd
    invoke  CreateFile, BBFile_Name, \
	      BlackBox.File.dwDesiredAccess, \
	      BlackBox.File.dwShareMode, \
	      BlackBox.File.lpSecurityAttributes, \
	      BlackBox.File.dwCreationDistribution, \
	      BlackBox.File.dwFlagsAndAttributes, \
	      BlackBox.File.hTemplateFile
    mov     [BlackBox.File.Handle], eax
    or	    eax, eax
    jnz     .FileCreationOkay
      ReportErrorToUser BB_FileCreationFailed, \
	BB_DefaultWindowTitle
      jmp     .Abort
    .FileCreationOkay:
    invoke  GetModuleHandle, 0
    mov     [BlackBox.AppHandle], eax
    mov     [BlackBox.BreakNumber], 0
    mov     [BlackBox.WindowCount], 0
    mov     [BlackBox.CurWndPos.x], BBWindow_DefaultXPos
    mov     [BlackBox.CurWndPos.y], BBWindow_DefaultYPos
    mov     [BlackBox.ProcNameStackPos], 0
    cld
    xor     eax, eax
    lea     edi, [BlackBox.ProcNameStack]
    mov     ecx, BB_ProcNameMaxLength * BB_ProcNameStackLength
    rep stosb
    mov     [BlackBox.File.Flags], BBFile_IsUsable
    mov     [BlackBox.File.CurOffset], 0
    mov     [BlackBox.File.MaxSize], BBFile_MaxSize
    lea     edi, [BlackBox.File.Buffer]
    mov     ecx, BBDisplay_MaxWidth * BBDisplay_MaxHeight
    rep stosb
    invoke  CreateFont, [BlackBox.Font.nHeight], \
	      [BlackBox.Font.nHeight], \
	      [BlackBox.Font.nWidth], \
	      [BlackBox.Font.nEscapement], \
	      [BlackBox.Font.nOrientation], \
	      [BlackBox.Font.fnWeight], \
	      [BlackBox.Font.fdwItalic], \
	      [BlackBox.Font.fdwUnderline], \
	      [BlackBox.Font.fdwStrikeOut], \
	      [BlackBox.Font.fdwCharSet], \
	      [BlackBox.Font.fdwOutputPrecision], \
	      [BlackBox.Font.fdwClipPrecision], \
	      [BlackBox.Font.fdwQuality], \
	      [BlackBox.Font.fdwPitchAndFamily], \
	      [BlackBox.Font.lpszFace]
    mov     [BlackBox.Font.Handle], eax
    or	    eax, eax
    jz	    .Abort
    jmp     .Done
  .Abort:
    ReportErrorToUser BB_StartUpFailed, BB_DefaultWindowTitle
    jmp     .End
  .Done:
    mov     [BlackBox.Flags], BB_IsOperable
    stdcall BB_NewWindow, 0
  .End:
    popfd
    popa
  return

  proc BB_ShutDown, Message

    enter
    pusha
    pushfd
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .Abort
    invoke  CloseHandle, [BlackBox.File.Handle]
    lea     ebx, [BlackBox.Windows]
    mov     ecx, BBWindow_MaxNumber
    .CloseWindows_LoopTop:
      cmp     [EBX_BBWindow.Handle], 0
      jz      .ClosedAlready
	invoke	DestroyWindow, [EBX_BBWindow.Handle]
      .ClosedAlready:
      add     ebx, Type_BBWindow.sizeof
      dec     ecx
      jnz     .CloseWindows_LoopTop
    invoke  DeleteObject, [EBX_BBWindow.Font.Handle]
  .Abort:
    popfd
    popa
  return

  proc BB_NewWindow, TitleStr

    enter
    pusha
    pushfd
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    mov     [EBX_BBWindow.WndInfo.Left], 0
    mov     [EBX_BBWindow.WndInfo.Top], 0
    mov     eax, BBWindow_CharsAcross * BBFont_Width
    mov     [EBX_BBWindow.WndInfo.Right], eax
    mov     eax, BBWindow_DfltLinesOfText * BBFont_Height
    mov     [EBX_BBWindow.WndInfo.Bottom], eax
    mov     [EBX_BBWindow.WndInfo.State], 0
    mov     [EBX_BBWindow.WndInfo.Style], \
	      WS_VISIBLE or WS_OVERLAPPED or WS_CAPTION or \
	      WS_SYSMENU or WS_MINIMIZEBOX
    mov     eax, [BlackBox.CurWndPos.x]
    add     [EBX_BBWindow.WndInfo.Left], eax
    add     [EBX_BBWindow.WndInfo.Right], eax
    mov     eax, [BlackBox.CurWndPos.y]
    add     [EBX_BBWindow.WndInfo.Top], eax
    add     [EBX_BBWindow.WndInfo.Bottom], eax
    lea     eax, [EBX_BBWindow.WndInfo]
    invoke  AdjustWindowRect, eax, \
	      [EBX_BBWindow.WndInfo.Style], NULL
    or	    eax, eax
    jnz     .AdjustWndRectOkay
      ReportErrorToUser BB_AdjustWndRectFailed, \
	BB_DefaultWindowTitle
      jmp     .ProcFailed
    .AdjustWndRectOkay:
    mov     eax, [EBX_BBWindow.WndInfo.Right]
    sub     eax, [EBX_BBWindow.WndInfo.Left]
    mov     [EBX_BBWindow.WndInfo.Width], eax
    mov     eax, [EBX_BBWindow.WndInfo.Bottom]
    sub     eax, [EBX_BBWindow.WndInfo.Top]
    mov     [EBX_BBWindow.WndInfo.Height], eax
    mov     esi, BB_DefaultWindowTitle
    lea     eax, [TitleStr]
    or	    eax, eax
    cmovnz  esi, eax
    invoke  CreateWindow, BB_ClassName, esi, \
	      [EBX_BBWindow.WndInfo.Style], \
	      [EBX_BBWindow.WndInfo.XPos], \
	      [EBX_BBWindow.WndInfo.YPos], \
	      [EBX_BBWindow.WndInfo.Width], \
	      [EBX_BBWindow.WndInfo.Height], \
	      NULL, NULL, [BlackBox.AppHandle], NULL
    mov     [EBX_BBWindow.Handle], eax
    or	    eax, eax
    jnz     .CreateWindowOkay
      ReportErrorToUser BB_CreateWindowFailed, \
	BB_DefaultWindowTitle
      jmp     .ProcFailed
    .CreateWindowOkay:
    add     [BlackBox.CurWndPos.x], BBWindow_NextWndXDelta
    add     [BlackBox.CurWndPos.y], BBWindow_NextWndYDelta
    inc     [BlackBox.TopWindowNumber]
    jmp     .Done
  .ProcFailed:
    ReportErrorToUser BB_WindowCreationFailed, \
      BB_DefaultWindowTitle
  .Done:
  .End:
    popfd
    popa
  return

  proc BB_ScrapWindow

    enter
    pusha
    pushfd
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    lea     ebx, [BlackBox.Windows]
    mov     eax, Type_BBWindow.sizeof
    mul     [BlackBox.TopWindowNumber]
    add     ebx, eax
    invoke  DestroyWindow, [EBX_BBWindow.Handle]
    sub     [BlackBox.CurWndPos.x], BBWindow_NextWndXDelta
    sub     [BlackBox.CurWndPos.y], BBWindow_NextWndYDelta
    cld
    lea     ebx, [BlackBox.WatchedVarArray]
    mov     ecx, BB_VarListMaxLength
    mov     eax, [BlackBox.TopWindowNumber]
    .ClearAssocVars_LoopTop:
      cmp     [EBX_BBDataInfo.AssocWndNumber], eax
      jne     .GoToNextSlot
	mov	[EBX_BBDataInfo.Flags], 0
      .GoToNextSlot:
      add     ebx, Type_BBDataInfo.sizeof
      dec     ecx
      jnz     .ClearAssocVars_LoopTop
    dec     [BlackBox.TopWindowNumber]
  .End:
    popfd
    popa
  return

  proc BB_RecProcEntry, ProcNameStr

    enter
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    pusha
    pushfd
    cld
    mov     al, $0
    mov     ecx, BB_ProcNameMaxLength
    lea     edi, [ProcNameStr]
    repne scasb
    mov     eax, BB_ProcNameMaxLength
    sub     eax, ecx
    mov     ecx, eax
    lea     esi, [ProcNameStr]
    lea     edi, [BlackBox.ProcNameStack]
    mov     eax, BB_ProcNameMaxLength
    mul     [BlackBox.ProcNameStackPos]
    add     edi, eax
    rep movsb
    inc     [BlackBox.ProcNameStackPos]
    or	    [BlackBox.Flags], BB_StartedProc
    popfd
    popa
  .End:
  return

  proc BB_RecProcReturn

    enter
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    dec     [BlackBox.ProcNameStackPos]
    or	    [BlackBox.Flags], BB_FinishedProc
  .End:
  return

  proc BB_AddVar, VarName, DataType, NameStr

    enter
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    pusha
    pushfd
    cld
    lea     ebx, [BlackBox.WatchedVarArray]
    mov     ecx, BB_VarListMaxLength
    .CheckSlot:
      test    [EBX_BBDataInfo.Flags], BBVarList_UsedSlot
      jz      .SlotLookUpDone
      dec     ecx
      jz      .Abort
      add     ebx, Type_BBDataInfo.sizeof
      jmp     .CheckSlot
    .SlotLookUpDone:
    mov     eax, [DataType]
    or	    eax, BBVarList_UsedSlot
    mov     [EBX_BBDataInfo.Flags], eax
    mov     eax, [BlackBox.TopWindowNumber]
    mov     [EBX_BBDataInfo.AssocWndNumber], eax
    lea     eax, [VarName]
    mov     [EBX_BBDataInfo.VarAddress], eax
    mov     al, $0
    mov     ecx, BB_VarNameMaxLength
    lea     edi, [NameStr]
    repne scasb
    lea     esi, [NameStr]
    lea     edi, [EBX_BBDataInfo.VarName]
    rep movsb
    inc     [BlackBox.WatchedVarCount]
    popfd
    popa
  .Abort:
  .End:
  return

  proc BB_DropVar, VarName

    enter
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    pusha
    pushfd
    cld
    lea     edi, [BlackBox.WatchedVarArray]
    mov     ecx, BB_VarListMaxLength
    mov     eax, [BlackBox.TopWindowNumber]
    .FindVar_LoopTop:
      cmp     [EDI_BBDataInfo.AssocWndNumber], eax
      jne     .MoveToNextSlot
	lea	edx, [VarName]
	cmp	[EDI_BBDataInfo.VarAddress], edx
	je	.ClearVar
      .MoveToNextSlot:
      add     edi, Type_BBDataInfo.sizeof
      dec     ecx
      jz      .Abort
      jmp     .FindVar_LoopTop
    .ClearVar:
    mov     [EDI_BBDataInfo.Flags], 0
    mov     [EDI_AssocWndNumber], -1
    mov     [EDI_VarAddress], 0
    xor     eax, eax
    mov     ecx, BB_VarNameMaxLength
    rep stosb
  .Abort:
    popfd
    popa
  .End:
  return

  proc BB_Update, Flags, Message

    enter
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    SAVEGENREGS BlackBox.GenRegs
    pusha
    pushfd
    mov     eax, [ebp]
    mov     [BlackBox.EFlagsReg], eax
    test    [BlackBox.Flags], BB_ProcFlagMask
    jz	    .ProcData

    test    [BlackBox.Flags], BB_StartedProc
    jz	    .Write_LeftProc
      stdcall BB_DrawFill, BlackBox.TextBuffer, \
		BBStrs_EnteredProcFill
      mov     [BlackBox.TextBfrTotalLines], eax
      stdcall BB_PrintStrs, BlackBox.TextBuffer, \
		BBStrs_EnteredProcText
      lea     esi, [BlackBox.ProcNameStack]
      mov     eax, BB_ProcNameMaxLength
      mul     [BlackBox.ProcNameStackPos]
      add     esi, eax
      lea     edi, [BlackBox.TextBuffer]
      add     edi, $2 * BBDisplay_MaxWidth + $14
      mov     ecx, BBDisplay_MaxWidth - $14
      rep movsb
      jmp     .PrintGlobalBreakNumber
    .Write_LeftProc:
      stdcall BB_DrawFill, BlackBox.TextBuffer, \
		BBStrs_LeftProcFill
      mov     [BlackBox.TextBfrTotalLines], eax
      stdcall BB_PrintStrs, BlackBox.TextBuffer, \
		BBStrs_LeftProcText
      lea     esi, [BlackBox.ProcNameStack]
      mov     eax, BB_ProcNameMaxLength
      mul     [BlackBox.ProcNameStackPos]
      add     eax, BB_ProcNameMaxLength
      add     esi, eax
      lea     edi, [BlackBox.TextBuffer]
      add     edi, $2 * BBDisplay_MaxWidth + $11
      mov     ecx, BBDisplay_MaxWidth - $11
      rep movsb
    .PrintGlobalBreakNumber:
    lea     edi, [BlackBox.TextBuffer]
    add     edi, BBDisplay_MaxWidth + $13
    DWORD_to_HDStr BlackBox.BreakNumber, edi
    jmp     .OutpInfo

  .ProcData:
    mov     [BlackBox.TextBfrTotalLines], 0
    mov     [BlackBox.TextPos.XPos], 0
    mov     [BlackBox.TextPos.YPos], 0
    lea     edi, [BlackBox.TextBuffer]
    test    [BlackBox.Flags], BB_ProcFlagMask
    jnz     .ClearProcFlags
      stdcall BB_DrawFill, BlackBox.TextBuffer, \
		BBStrs_UpdateSeparator
      inc     [BlackBox.TextBfrTotalLines]
      add     edi, BBDisplay_MaxWidth
      jmp     .PrintTopText
    .ClearProcFlags:
      and     [BlackBox.Flags], not BB_ProcFlagMask
    .PrintTopText:
    stdcall BB_DrawFill, edi, BBStrs_UpdateTopFill
    add     [BlackBox.TextBfrTotalLines], eax
    stdcall BB_PrintStrs, edi, BBStrs_UpdateTopText
    mov     eax, edi
    add     eax, BBDisplay_MaxWidth + $13
    DWORD_to_HDStr BlackBox.BreakNumber, eax
    lea     esi, [BlackBox.SubBreakNumbers]
    mov     eax, $4
    mul     [ProcNameStackPos]
    add     esi, eax
    mov     eax, edi
    add     eax, BBDisplay_MaxWidth + $32
    DWORD_to_HDStr esi, edi
    cmp     [Message], $0
    jne     .ScrapOldMessage
    test    [Flags], BB_ScrapMessage
    jz	    .EndOfMessageScrapping
    .ScrapOldMessage:
      lea     ebx, [BlackBox.Windows]
      mov     eax, Type_BBWindow.sizeof
      mul     [BlackBox.TopWindowNumber]
      add     ebx, eax
      and     [EBX_BBWindow.Flags], not BB_ScrapMessage
      mov     al, $0
      lea     edi, [BlackBox.Message]
      mov     ecx, BBWindow_MsgBufferLength
      rep stosb
    .EndOfMessageScrapping:
    lea     ebx, [BlackBox.Windows]
    mov     eax, Type_BBWindow.sizeof
    mul     [BlackBox.TopWindowNumber]
    add     ebx, eax
    cmp     [Message], $0
    je	    .CheckOnUsingOldMsg
      mov     eax, [Flags]
      and     eax, BB_KeepMessage
      or      [EBX_BBWindow.Flags], eax
      lea     esi, [Message]
      .CheckChar_LoopTop:
	cmp	byte [esi], ' '
	jae	.GoToNextChar
	  cmp	  byte [esi], $0A
	  jne	  .NotLineFeedChar
	    inc     [BlackBox.MessageLineCount]
	    jmp     .GoToNextChar
	  .NotLineFeedChar:
	  cmp	  byte [esi], $0
	  je	  .OutpMessage
	.GoToNextChar:
	inc	esi
	lea	eax, [Message]
	add	eax, BBWindow_MsgBufferLength
	cmp	esi, eax
	jb	.CheckChar_LoopTop
      jmp     .OutpMessage
    .CheckOnUsingOldMsg:
      test    [EBX_BBWindow.Flags], BB_KeepMessage
      jz      .PrintBodyText
    .OutpMessage:
    lea     esi, [BBStrs_UpdateMsgFill]
    mov     eax, [BlackBox.MessageLineCount]
    mov     [esi + 1], eax
    stdcall BB_DrawFill, BlackBox.TextBuffer, \
	      BBStrs_UpdateMessageFill
    lea     esi, [BlackBox.Message]
    lea     edi, [BlackBox.TextBuffer]
    mov     [BlackBox.TextPos.XPos], 0
    mov     eax, [BlackBox.TextBfrTotalLines]
    mov     [BlackBox.TextPos.YPos], eax
    .CopyMessageStrFrag_LoopTop:
      .PassCtrlChars_LoopTop:
	cmp	[esi], ' '
	jae	.PassCtrlChars_LoopEnd
	inc	esi
	jmp	PassCtrlChars_LoopTop
      .PassCtrlChars_LoopEnd:
      mov     ebx, esi
      xor     ecx, ecx
      .FindBlankLineFeedOrDash_LoopTop:
	cmp	[ebx], ' '
	jae	.CheckForSpaceOrDash
	  cmp	  [ebx], $0A
	  je	  .DoLineFeedAndReturn
	  cmp	  [ebx], $0
	  je	  .CopyStringFragment
	  inc	  ebx
	  jmp	  .FindBlankLineFeedOrDash_LoopTop
	.CheckForSpaceOrDash:
	  cmp	  [ebx], ' '
	  jne	  .NotSpaceChar
	    inc     ecx
	    mov     eax, [BlackBox.TextPos.XPos]
	    add     eax, ecx
	    cmp     eax, BBDisplay_MaxWidth
	    jae     .DoLineFeedAndReturn
	    jmp     .CopyStringFragment
	  .NotSpaceChar:
	  cmp	  [ebx], '-'
	  jne	  .NotDashChar
	    inc     ecx
	    mov     eax, [BlackBox.TextPos.XPos]
	    add     eax, ecx
	    cmp     eax, BBDisplay_MaxWidth
	    jae     .DoLineFeedAndReturn
	    jmp     .CopyStringFragment
	  .NotDashChar:
	.MoveToNextChar:
	inc	ebx
	inc	ecx
	mov	eax, [BlackBox.TextPos.XPos]
	add	eax, ecx
	cmp	eax, BBDisplay_MaxWidth
	jb	.FindBlankLineFeedOrDash_LoopTop
      .DoLineFeedAndReturn:
	mov	[BlackBox.TextPos.XPos], 0
	inc	[BlackBox.TextPos.YPos]
	cmp	[BlackBox.TextPos.YPos], BBWindow_MsgBfrMaxLines
	je	.CopyMessageStrFrag_LoopDone
	lea	edi, [BlackBox.TextBuffer]
	mov	eax, BBDisplay_MaxWidth
	mul	[BlackBox.TextPos.YPos]
	add	edi, eax
      .CopyStringFragment:
      add     [BlackBox.TextPos.XPos], ecx
      rep movsb
      cmp     esi, $0
      jne     .CopyMessageStrFrag_LoopTop
    .CopyMessageStrFrag_LoopDone:
    inc     [BlackBox.TextPos.YPos]
    mov     eax, [BlackBox.TextPos.YPos]
    add     [BlackBox.TextBfrTotalLines], eax
  .PrintBodyText:
    lea     edi, [BlackBox.TextBuffer]
    mov     eax, BBDisplay_MaxWidth
    mul     [BlackBox.TextBfrTotalLines]
    add     edi, eax
    stdcall BB_DrawFill, edi, BBStrs_UpdateBodyFill
    add     [BlackBox.TextBfrTotalLines], eax
    stdcall BB_PrintStrs, edi, BBStrs_UpdateBodyText
    mov     eax, edi
    add     eax, $2 * BBDisplay_MaxWidth + $8
    DWORD_to_HDStr BlackBox.GenRegs.EAX, eax
    add     eax, $10
    DWORD_to_HDStr BlackBox.GenRegs.EBX, eax
    add     eax, $10
    DWORD_to_HDStr BlackBox.GenRegs.ECX, eax
    add     eax, $10
    DWORD_to_HDStr BlackBox.GenRegs.EDX, eax
    add     eax, $10
    DWORD_to_HDStr BlackBox.GenRegs.ESP, eax
    add     eax, $10
    DWORD_to_HDStr BlackBox.GenRegs.EBP, eax
    add     eax, $10
    DWORD_to_HDStr BlackBox.GenRegs.ESI, eax
    add     eax, $10
    DWORD_to_HDStr BlackBox.GenRegs.EDI, eax
    add     edi, $5 * BBDisplay_MaxWidth
    test    [BlackBox.EFlagsReg], EFlags_Carry
    jnz     .SayCarryIsON
      mov     [edi + $F], BBStrs_OFF_DWORD
      jmp     .TestForSign
    .SayCarryIsON:
      mov     [edi + $F], BBStrs_ON_DWORD
    .TestForSign:
    test    [BlackBox.EFlagsReg], EFlags_Sign
    jnz     .SaySignIsON
      mov     [edi + $26], BBStrs_OFF_DWORD
      jmp     .TestForOverflow
    .SaySignIsON:
      mov     [edi + $26], BBStrs_ON_DWORD
    .TestForOverflow:
    test    [BlackBox.EFlagsReg], EFlags_Overflow
    jnz     .SayOverflowIsON
      mov     [edi + $3C], BBStrs_OFF_DWORD
      jmp     .TestForZero
    .SayOverflowIsON:
      mov     [edi + $3C], BBStrs_ON_DWORD
    .TestForZero:
    add     edi, BBDisplay_MaxWidth
    test    [BlackBox.EFlagsReg], EFlags_Zero
    jnz     .SayZeroIsON
      mov     [edi + $F], BBStrs_OFF_DWORD
      jmp     .TestForDirection
    .SayZeroIsON:
      mov     [edi + $F], BBStrs_ON_DWORD
    .TestForDirection:
    test    [BlackBox.EFlagsReg], EFlags_Direction
    jnz     .SayDirectionIsON
      mov     [edi + $26], BBStrs_OFF_DWORD
      jmp     .OutpVarList
    .SayDirectionIsON:
      mov     [edi + $26], BBStrs_ON_DWORD
    .OutpVarList:
    add     edi, $3 * BBDisplay_MaxWidth
    lea     esi, [BlackBox.WatchedVarArray]
    mov     ecx, [BlackBox.WatchedVarCount]
    .OutpVarList_LoopTop:
      BB_ClearLine edi
      push    ecx esi edi
      lea     esi, [ESI_BBDataInfo.VarName]
      add     edi, $2
      mov     ecx, BB_VarNameMaxLength
      rep movsb
      pop     edi esi ecx
      mov     [edi + $36], '='
      mov     ebx, [ESI_BBDataInfo.VarAddress]
      mov     eax, [ESI_BBDataInfo.Flags]
      and     eax, BB_SizeMask
      cmp     eax, BB_2Byte
      jb      .LoadByte
      je      .LoadWord
      ja      .LoadDWord
      .LoadByte:
	mov	al, [ebx]
	jmp	.RecordAsHDText
      .LoadWord:
	mov	ax, [ebx]
	jmp	.RecordAsHDText
      .LoadDWord:
	mov	eax, [ebx]
      .RecordAsHDText:
      mov     ebx, edi
      add     ebx, $38
      DWORD_to_HDStr eax, ebx
      add     esi, Type_BBDataInfo.sizeof
      add     edi, BBDisplay_MaxWidth
      dec     ecx
      jnz     OutpVarList_LoopTop
    BB_ClearLine edi
    mov     eax, [BlackBox.WatchedVarCount]
    add     [BlackBox.TextBfrTotalLines], eax
    inc     [BlackBox.TextBfrTotalLines]

  .OutpInfo:
    test    [BlackBox.File.Flags], BBFile_IsUsable
    jz	    .EndOfFileWriting
      invoke  WriteFile, [BlackBox.File.Handle], \
		[BlackBox.TextBuffer], \
		[BlackBox.File.BytesToWrite], \
		[BlackBox.File.BytesWritten], NULL
      or      eax, eax
      jnz     .FileWriteOkay
	ReportErrorToUser BB_WriteToFileFailed, \
	  BB_DefaultWindowTitle
	jmp	.EndOfFileWriteCheck
      .FileWriteOkay:
	mov	eax, [BlackBox.File.BytesWritten]
	add	[BlackBox.File.CurOffset], eax
      .EndOfFileWriteCheck:
      mov     eax, BBDisplay_MaxWidth
      mul     [BlackBox.TextBfrTotalLines]
      add     [BlackBox.File.CurOffset], eax
    .EndOfFileWriting:
    cmp     [BlackBox.TopWindowNumber], -1
    je	    .EndOfWindowOutp
      test    [BlackBox.Flags], BB_ProcFlagMask
      jz      .OutputDataReport
	lea	esi, [BlackBox.TextBuffer]
	add	esi, BBDisplay_MaxWidth
	stdcall BB_OutputText, [BlackBox.TopWindowNumber], \
		  $0, esi
	add	edi, BBDisplay_MaxWidth
	stdcall BB_OutputText, [BlackBox.TopWindowNumber], \
		  $1, esi
	jmp	.EndOfWindowOutp
      .OutputDataReport:
	xor	ecx, ecx
	lea	esi, [BlackBox.TextBuffer]
	cmp	esi, '-'
	jne	.SkipOneLine
	  add	  esi, BBDisplay_MaxWidth
	.SkipOneLine:
	add	esi, BBDisplay_MaxWidth
	.OutpTextLines_LoopTop:
	  stdcall BB_OutputText, [BlackBox.TopWindowNumber], \
		    ecx, esi
	  add	  esi, BBDisplay_MaxWidth
	  inc	  ecx
	  cmp	  ecx, [BlackBox.TextBfrTotalLines]
	  jne	  .OutpTextLines_LoopTop
    .EndOfWindowOutp:
    .GetKey:
    if defined BB_AbortProgram
      invoke  GetKeyState, BB_AbortProgramKey
      and     eax, VKeyState
      jnz     ..BB_AbortProgram
    if defined BB_KillProgram
      invoke  GetKeyState, BB_KillProgramKey
      and     eax, VKeyState
      jnz     ..BB_KillProgram
    end if
      invoke  GetKeyState, BB_ResumeKey
      and     eax, VKeyState
      jz      .GetKey
    test    [BlackBox.Flags], BB_ProcFlagMask
    jnz     .ProcData

  .End:
    popfd
    popa
  return

  proc BB_DrawFill, Address, FillDescStr

    enter
    push    ebx esi edi ecx
    mov     edi, [Address]
    mov     esi, [FillDescStr]
    xor     ebx, ebx
    xor     ecx, ecx
    mov     cl, [esi]
    inc     esi
    .ProcessFill_LoopTop:
      push    ecx
      mov     cl, [esi]
      inc     esi
      add     ebx, ecx
      .FillLine_LoopTop:
	push	ecx
	mov	cl, [esi]
	inc	esi
	mov	al, [esi]
	inc	esi
	rep stosb
	pop	ecx
	dec	ecx
	jnz	.FillLine_LoopTop
      pop     ecx
      dec     ecx
      jnz     .ProcessFill_LoopTop
    mov     eax, ebx
    pop     ecx edi esi ebx
  return

  proc BB_PrintStrs, Address, StrList

    enter
    push    esi edi ecx
    mov     edi, [Address]
    mov     esi, [StrList]
    xor     eax, eax
    xor     ecx, ecx
    mov     cl, [esi]
    inc     esi
    .ProcessString_LoopTop:
      push    ecx
      mov     al, $0
      lea     edi, [ESI_BBTextData.ZString]
      mov     ecx, BBDisplay_MaxWidth
      repne scasb
      mov     al, BBDisplay_MaxWidth
      mul     [ESI_BBTextData.LineNumber]
      mov     edi, [Address]
      add     edi, eax
      add     edi, [ESI_BBTextData.StartPos]
      lea     esi, [ESI_BBTextData.ZString]
      rep movsb
      pop     ecx
      dec     ecx
      jnz     .ProcessString_LoopTop
    pop     ecx edi esi
  return

  proc BB_OutputText, WindowId, LineNumber, String

    enter
    push    ebx
    test    [BlackBox.Flags], BB_IsOperable
    jz	    .End
    lea     ebx, [BlackBox.Windows]
    mov     eax, Type_BBWindow.sizeof
    mul     [WindowId]
    add     ebx, eax
    mov     eax, [EBX_BBWindow.Handle]
    or	    eax, eax
    jz	    .AbortOutpToWindow
      invoke  GetDC, [EBX_BBWindow.Handle]
      or      eax, eax
      jz      .AbortOutpToWindow
      mov     [EBX_BBWindow.DCHandle], eax
      invoke  SelectObject, [EBX_BBWindow.DCHandle], \
		[BlackBox.Font.Handle]
      mov     eax, BBFont_Height
      mul     [LineNumber]
      invoke  TextOut, [EBX_BBWindow.DCHandle], eax, \
		BBFont_Width, String, BBDisplay_MaxWidth
      invoke  ReleaseDC, [EBX_BBWindow.Handle], \
		[EBX_BBWindow.DCHandle]
    .AbortOutpToWindow:
    .Done:
    .End:
    pop     ebx
  return

; END OF FILE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;