#!/usr/bin/expectk
################################################################################
#                                                                              #
# Copyright (c) 1999 by Simon Stegmaier                                        #
#                                                                              #
#   Redistribution, with or without modification, is permitted provided that   #
#   the above copyright notice and this notice are retained                    #
#                                                                              #
################################################################################

set dlxpath "/home/tdk/DLX.mips/dlx/bin/dlxsim"
set helpfile "/home/tdk/DLX.mips/dlxgui/dlxgui.hlp"
set cmdhelp "/home/tdk/DLX.mips/dlxgui/dlxcmd.hlp"

################################################################################
# main window                                                                  #
################################################################################
wm title . "DLXGUI Version 1.0"
wm geometry . +50+50
frame .main
pack .main

################################################################################
# menu bar                                                                     #
################################################################################
frame .menu -relief raised -bd 2
pack .menu -in .main -fill x 

menubutton .file -text File -underline 0 -menu .file.menu
menubutton .options -text Options  -underline 0 -menu .options.menu
menubutton .help -text Help -underline 0 -menu .help.menu
pack .file .options -ipadx 10 -side left -in .menu
pack .help -ipadx 10 -side right -in .menu

#
# file menu
#
set m [menu .file.menu -tearoff 0]
$m add command -label New -command New
$m add command -label Open... -command Open
$m add command -label Save -command Save
$m add command -label "Save as..." -command SaveAs
$m add separator
$m add command -label QUIT -command Quit

#
# options menu
#
set m [menu .options.menu -tearoff 0]
$m add cascade -label GPRs -menu .options.menu.sub1
$m add cascade -label FPRs -menu .options.menu.sub2
$m add cascade -label Statistics -menu .options.menu.sub3
$m add separator
$m add command -label Custom... -command Custom

# 
# sub menu GPRs
set s [menu .options.menu.sub1 -tearoff 0]
$s add radio -label binary -value B -variable gpropt -command FillRegs
$s add radio -label decimal -value d -variable gpropt -command FillRegs
$s add radio -label hexadecimal -value x -variable gpropt -command FillRegs

#
# sub menu FPRs
#
set s [menu .options.menu.sub2 -tearoff 0]
$s add radio -label float -value f -variable fpropt -command FillRegs
$s add radio -label double -value d -variable fpropt -command FillRegs

#
# sub menu Stats
#
set s [menu .options.menu.sub3 -tearoff 0]
$s add check -label stalls -onvalue stalls -offvalue "" -variable statst
$s add check -label opcount -onvalue opcount -offvalue "" -variable statop
$s add check -label pending -onvalue pending -offvalue "" -variable statpe
$s add check -label branch -onvalue branch -offvalue "" -variable statbr
$s add check -label hw -onvalue hw -offvalue "" -variable stathw

#
# initial values
#
set gpropt d
set fpropt d
set statst stalls
set statop opcount
set statpe pending
set statbr branch
set stathw hw

#
# help menu
#
set m [menu .help.menu -tearoff 0]
$m add command -label GUI... -command GuiHelp
$m add separator
$m add command -label About... -command About

################################################################################
# DLX frame                                                                    #
################################################################################
frame .dlx
frame .editor
frame .buttons
frame .register
pack .dlx -in .main
pack .editor .buttons .register -fill y -padx 10 -pady 10 -side left -in .dlx

################################################################################
# editor                                                                       #
################################################################################
frame .edtop
frame .edbottom
pack .edtop .edbottom -fill x -in .editor
label .filename -text unknown
pack .filename -side left -in .edtop
scrollbar .edyscroll -command {.edtext yview}
scrollbar .edxscroll -orient horiz -command {.edtext xview}
text .edtext -width 50 -height 40 -yscrollcommand {.edyscroll set}\
    -xscrollcommand {.edxscroll set} -wrap none 
bind .edtext <Button-3> CmdHelp
pack .edyscroll -side right -fill y -in .edbottom
pack .edxscroll -fill x -side bottom -in .edbottom
pack .edtext -in .edbottom

################################################################################
# buttons                                                                      #
################################################################################
label .dummy1
label .dummy2
label .dummy3
label .dummy4
label .dummy5
button .go -text Go -command {Go 0}
bind .go <Button-3> {Go 1}
button .step -text Step -command {Step 0}
bind .step <Button-3> {Step 1}
button .break -text Break -command {Break 0}
bind .break <Button-3> {Break 1}
button .status -text Stats -command Stats
bind .status <Button-3> StatsReset
button .interact -text Interact -command Interact
pack .dummy1 -fill x -ipadx 10 -in .buttons
pack .go -fill x -ipady 10 -ipadx 10 -in .buttons
pack .dummy2 -fill x -ipadx 10 -in .buttons
pack .step -ipady 10 -ipadx 10 -fill x -in .buttons
pack .dummy3 -fill x -ipadx 10 -in .buttons
pack .break -ipady 10 -ipadx 10 -fill x -in .buttons
pack .dummy4 -fill x -ipadx 10 -in .buttons
pack .status -ipady 10 -ipadx 10 -fill x -in .buttons
pack .dummy5 -fill x -ipadx 10 -in .buttons
pack .interact -ipady 10 -ipadx 10 -fill x -in .buttons

################################################################################
# custom and register frames                                                   #
################################################################################
frame .custom
frame .gprs
frame .fprs
pack .custom -in .register
pack .fprs .gprs -side bottom -in .register

#
# custom frame
#
frame .customtop
frame .custombottom
pack .customtop .custombottom -fill x -in .custom
label .customlabel -text "Custom command "
label .customext -text "()"
pack .customlabel .customext -side left -in .customtop
scrollbar .customyscroll -command {.customtext yview}
scrollbar .customxscroll -orient horiz -command {.customtext xview}
text .customtext -width 50 -height 10 -yscrollcommand {.customyscroll set}\
    -xscrollcommand {.customxscroll set} -wrap none -state disabled
bind .customtext <Any-Button> CustomClear
bind .customtext <Enter> {.customtext configure -bg grey95}
bind .customtext <Leave> {.customtext configure -bg grey85}
pack .customyscroll -side right -fill y -in .custombottom
pack .customxscroll -fill x -side bottom -in .custombottom
pack .customtext -in .custombottom

# 
# FPRs frame
#
frame .fprstop
frame .fprsbottom
pack .fprstop .fprsbottom -fill x -in .fprs
label .fprslabel -text "Floating Point Registers"
pack .fprslabel -side left -in .fprstop
scrollbar .fprsyscroll -command {.fprstext yview}
scrollbar .fprsxscroll -orient horiz -command {.fprstext xview}
text .fprstext -width 50 -height 10 -yscrollcommand {.fprsyscroll set}\
  -xscrollcommand {.fprsxscroll set} -wrap none -state disabled
pack .fprsyscroll -side right -fill y -in .fprsbottom
pack .fprsxscroll -fill x -side bottom -in .fprsbottom
pack .fprstext -in .fprsbottom

#
# GPRs frame
#
frame .gprstop
frame .gprsbottom
pack .gprstop .gprsbottom -fill x -in .gprs
label .gprslabel -text "General Purpose Registers"
pack .gprslabel -side left -in .gprstop
scrollbar .gprsyscroll -command {.gprstext yview}
scrollbar .gprsxscroll -orient horiz -command {.gprstext xview}
text .gprstext -width 50 -height 10 -yscrollcommand {.gprsyscroll set}\
  -xscrollcommand {.gprsxscroll set} -wrap none -state disabled 
pack .gprsyscroll -side right -fill y -in .gprsbottom
pack .gprsxscroll -fill x -side bottom -in .gprsbottom
pack .gprstext -in .gprsbottom

################################################################################
# main (procedures in alphabetical order)                                      #
################################################################################

log_user 0

spawn $dlxpath
expect "(dlxsim) "
set custom ""
set filename ""
set tempfile temp.[pid]
set requestAddr 1
set unmodified [.edtext get 1.0 end]

focus .edtext

#
# prints the About... message
#
proc About {} {
  ShowMessage About "DLXGUI Version 1.0\nWritten by Simon Stegmaier\
                       \nstegmasn@tick.informatik.uni-stuttgart.de"
}

#
# calculates the address of a label
#
proc CalcAddress {label} {
  set label [string trimleft [string trimright $label]]

  # 'label' is already an address
  if {![regexp "^(\[a-zA-Z]|_|\\\$)" $label w]} {
    return $label
  } 

  set startline [SearchText "\\.text"]
  set endline [SearchText "$label:"]

  if {$endline == 0} {
    ShowMessage Error "Label '$label' does not exist"
    return
  }

  set lineAddr [CodeAddress]
  set lineNr [expr $startline + 1]
  set line [.edtext get "$lineNr.0 linestart" "$lineNr.0 lineend"]
  while {$lineNr < $endline} {
    if {[CommandLine $line]} {
      set lineAddr [expr $lineAddr + 4]
    }
    set lineNr [expr $lineNr + 1]
    set line [.edtext get "$lineNr.0 linestart" "$lineNr.0 lineend"]
  }

  return $lineAddr
}

#
# loads a file into the dlxsim and asks what it thinks about it
#
proc CheckFile {name} {
  exp_send "load $name\r"
  expect -re "\r\n(.*)\\(dlxsim\\) "

  if {![string compare $expect_out(1,string) ""]} {
    return 1
  } else {
      ShowText Error [join [split $expect_out(1,string) "\r"]]
      return 0
  }
}

#
# show a help text on the selected command
#
proc CmdHelp {} {
  global cmdhelp

  if {[catch {open $cmdhelp r} handle]} {
      ShowMessage Error $handle
      return
  } else {
      if {[catch {set cmd [string toupper [selection get]]}]} {
        return
      }

      set cmd [string trimleft [string trimright $cmd]]
      if {![regexp "^\[A-Z]\[A-Z0-9]*$" $cmd]} {
        return
      }

      if {[regexp "\n($cmd\[^\n]*)\n(\[^\n]*)" [read $handle]\
                                                     w syntax help]} {
          ShowMessage "Command Help" "$syntax\n\n$help"
      } 
  }
}  

#
# returns address of code area
#
proc CodeAddress {} {
  set lineNr [SearchText "\\.text"]
  if {$lineNr == 0} {
    ShowMessage Warning ".text directive missing\n(no code area)"
    return
  }

  set line [.edtext get "$lineNr.0" "$lineNr.end"]

  if {![regexp "\\.text\[ \t]+(\[xX0-9a-fA-F]+)" $line w addr]} {
    ShowMessage Error "Please specify an address\nfor the code area\
                       (following .text)"
    return
  }
  
  return $addr
}

#
# allows the user to specify a start address;
# address following .text is default value 
#
proc CodeStart {} {
  set codestart [CalcAddress [GetString Address "Enter address:"\
                                                          [CodeAddress]]]

  if {![string compare $codestart ""]} {
      return
  } else {
      return $codestart
  }
}

#
# checks if a source code line is a line with a dlx-command or
# just a line with only whitespace, only a label (perhaps
# followed by a comment) or just a comment
#
proc CommandLine {line} {
  if {[regexp "^\[ \t]*\$" $line w] ||\
      [regexp "^\[ \t]*\[a-zA-Z_\\\$]\[^:;]*:\[ \t]*;*\$" $line w] ||\
      [regexp "^\[ \t]*;" $line w]} {
      return 0
  } else {
      return 1
  }
}

#
# prompts for a custom command
#
proc Custom {} {
  global custom
  set custom [GetString "Custom Command" "Enter custom command:" $custom]
  .customext configure -text "($custom)"
  ExeCustom
}

#
# clear the custom window
#
proc CustomClear {} {
  .customtext configure -state normal
  .customtext delete 1.0 end
  .customtext configure -state disabled
}

#
# executes a custom command and displays the result
#
proc ExeCustom {} {
  global custom

  if {[string length $custom] == 0} {
    return
  } else {
    exp_send "$custom\r"
    expect -re "\r\n(.*)\\(dlxsim\\) "
    .customtext configure -state normal
    .customtext insert end "[join [split $expect_out(1,string) "\r"]]\n"
    .customtext see end
    .customtext configure -state disabled
  }
}

#
# reads the contents of the gpr and fpr registers and updates the frames
#
proc FillRegs {} {
  global gpropt fpropt
  
  .gprstext configure -state normal  
  .gprstext delete 1.0 end
  for {set i 0} {$i < 31} {incr i 2} {
    exp_send "get r$i $gpropt\r"
    expect -re "(r\[0-9]+:.*)\r\n\\(dlxsim\\) "
    .gprstext insert end "$expect_out(1,string)\t"
    exp_send "get r[expr $i + 1] $gpropt\r"
    expect -re "(r\[0-9]+:.*)\r\n\\(dlxsim\\) "
    .gprstext insert end "$expect_out(1,string)\n"
  } 
  .gprstext configure -state disabled  
  
  .fprstext configure -state normal
  .fprstext delete 1.0 end
  for {set i 0} {$i < 31} {incr i 2} {
    exp_send "fget f$i $fpropt\r"
    expect -re "(f\[0-9]+:.*)\r\n\\(dlxsim\\) "
    .fprstext insert end "$expect_out(1,string)\t"
    if {![string compare $fpropt d]} {
      if {[expr ($i+2) % 4] == 0} {
        .fprstext insert end "\n"
      } else {
        .fprstext insert end "\t"
      }
      continue
    }
    exp_send "fget f[expr $i + 1] $fpropt\r"
    expect -re "(f\[0-9]+:.*)\r\n\\(dlxsim\\) "
    .fprstext insert end "$expect_out(1,string)\n"
  } 
  .fprstext configure -state disabled
} 

#
# prompts for a string and return it
#
proc GetString {title prompt entrydef} {
  global ok result
 
  set oldfocus [focus]

  set result $entrydef
  set f [toplevel .t_GetString -bd 10]
  wm title $f $title
  # position window outside of visible area
  wm geometry $f +10000+10000

  set t [frame $f.top -bd 10]
  pack $t -fill x
  label $t.l -text "$prompt  "
  entry $t.entry -textvariable result
  pack $t.l -side left
  pack $t.entry -side right -fill x -expand 1

  bind $t.entry <Return> "set ok 1"
  bind $t.entry <Control-c> "set ok 0"

  set b [frame $f.buttons -bd 10]
  pack $b -fill x
  button $b.ok -text OK -command "set ok 1"
  button $b.cancel -text Cancel -command "set ok 0"
  pack $b.ok -side left 
  pack $b.cancel -side right

  $t.entry select range 0 end
  $t.entry icursor 0

  tkwait visibility $f
  PosWindow . $f
  focus $t.entry
  grab $f
  tkwait variable ok
  grab release $f
  destroy $f
  focus $oldfocus
  if {$ok == 1} {
    return $result
  } else {
    return ""
  }
}

#
# allows the user to specify breakpoint numbers;
#
proc Numbers {} {
  set numbers [CalcAddress [GetString "Delete Breakpoint" "Enter number:" ""]]
  if {![string compare $numbers ""]} {
      return
  } else {
      return $numbers
  }
}

#
# allows the user to specify a breakpoint address;
#
proc Breakpoint {} {
  set address [CalcAddress [GetString "Set Breakpoint" "Enter address:" ""]]

  if {![string compare $address ""]} {
      return
  } else {
      return $address
  }
}

#
# sets/deletes a breakpoint
#
proc Break {delete} {
  # return if no source fileloaded
  if {![SourceLoaded]} {
    return
  }

  if {$delete} {
    exp_send "stop info\r"
    expect -re "\r\n(.*)\r\n\\(dlxsim\\) "
    set breakpoints $expect_out(1,string) 
   
    ShowMessage Breakpoints $breakpoints
    if {![string compare $breakpoints "No stops are currently set."]} {
        return
    } 

    set numbers [Numbers]
    if {![string compare $numbers ""]} {
        return
    } else {
        exp_send "stop delete $numbers\r"
        expect -re "\r\n(.*)\\(dlxsim\\) "
        if {[string compare $expect_out(1,string) ""]} {
          ShowMessage Info $expect_out(1,string)
        }
    }
  } else {
      set address [Breakpoint]
      if {![string compare $address ""]} {
          return
      } else {
          exp_send "stop at $address\r"
          expect -re "\r\n(.*)\\(dlxsim\\) "
          if {[string compare $expect_out(1,string) ""]} {
            ShowMessage Info $expect_out(1,string)
          }
      }
  }
}       
         

#
# runs a DLX program
#
proc Go {addr} {
  global tempfile requestAddr
    
  # return if no source file is loaded
  if {![SourceLoaded]} {
    return
  } 
  
  # return if code area has no address
  if {![string compare [CodeAddress] ""]} {
    return
  }

  # ask for an address if the user asked for it or if the user
  # has just load a new source file 
  if {$addr || $requestAddr} {
    MakeCopy
    if {[CheckFile $tempfile]} {
      set codestart [CodeStart]
      if {![string compare $codestart ""]} {
        return
      } else {
          exp_send "go $codestart\r"
      }
    } else {
      return
    }
  } else {
    exp_send "go\r"
  } 
  
  expect -re "\r\n(.*)\r\n\\(dlxsim\\) "
  set out $expect_out(1,string)
  ExeCustom
  FillRegs
    
  if {[regexp "^stop" $out]} {
      if {[regexp "stop.*pc = (.*)\\+(.*):.*" $out w label offset]} {
          SetMarker [expr [CalcAddress $label] + $offset] 
      } elseif {[regexp "stop.*pc = (\[0-9].*):.*" $out w address]} {
          SetMarker $address
      } elseif {[regexp "stop.*pc = (.*):.*" $out w label]} {
          SetMarker [CalcAddress $label]
      } 
      ShowMessage Info $out 
      set requestAddr 0
  } else {
      SetMarker [CodeAddress]
      ShowMessage Info $out
      set requestAddr 1
  }
}   

#
# displays contents of helpfile
#
proc GuiHelp {} {
  global helpfile

  if {[catch {open $helpfile r} handle]} {
      ShowMessage Error $handle
      return
  } else {
      ShowText "GUI Help" [read $handle]
  }
}

#
# this procedure will be called when the user presses the interact button
#
proc Interact {} {
  global m t 
  
  set oldfocus [focus] 
  
  set f [toplevel .t_Interact -bd 10]
  wm title $f Interact
  # position window outside of visible area
  wm geometry $f +10000+10000 
    
  set t [frame $f.top -bd 10]
  pack $t -fill x 
  label $t.l -text "Enter command: "
  entry $t.e -textvariable cmd
  $t.e select range 0 end
  pack $t.l -side left 
  pack $t.e -side right -fill x -expand 1

  bind $t.e <Return> {InteractExe $m.t $t.e $cmd}
  bind $t.e <Control-c> "set close 0"

  set m [frame $f.middle]
  pack $m
  text $m.t -width 80 -height 40 -yscrollcommand {$m.yscroll set}\
            -xscrollcommand {$m.xscroll set} -wrap none 
  scrollbar $m.yscroll -command {$m.t yview}
  scrollbar $m.xscroll -orient horiz -command {$m.t xview}
  pack $m.yscroll -side right -fill y
  pack $m.xscroll -side bottom -fill x
  pack $m.t
      
  set b [frame $f.bottom -bd 10] 
  pack $b -fill x 
  button $b.execute -text Execute -command {InteractExe $m.t $t.e $cmd}
  button $b.close -text Close -command "set close 0"
  pack $b.execute -side left 
  pack $b.close -side right 
      
  tkwait visibility $f
  PosWindow . $f
  focus $t.e
  grab $f
  tkwait variable close
  grab release $f
  destroy $f
  focus $oldfocus
  FillRegs
}

#
# executes a dlxsim-command (Interact)
#
proc InteractExe {textWidget entryWidget cmd} {
  if {[regexp "^\[ \t]*\$" $cmd w]} {
    return
  } elseif {[regexp "step|go" $cmd w]} {
      $textWidget configure -state normal
      $textWidget insert end "*** command ignored (modifies pc) ***\n\n" 
      $textWidget see end
      $textWidget configure -state disabled

      $entryWidget select range 0 end
      $entryWidget icursor 0

      return
  } elseif {[regexp "quit" $cmd w]} {
      $textWidget configure -state normal
      $textWidget insert end "*** command ignored (quits dlxsim) ***\n\n"
      $textWidget see end
      $textWidget configure -state disabled
 
      $entryWidget select range 0 end
      $entryWidget icursor 0  

      return
  }
 
  exp_send "$cmd\r"
  expect -re "\r\n(.*)\\(dlxsim\\) "

  $textWidget configure -state normal

  if {![string compare $expect_out(1,string) ""]} {
      $textWidget insert end "*** command didn't produce output ***\n\n"
  } else {
      $textWidget insert end "[join [split $expect_out(1,string) "\r"]]\n"
  }

  $textWidget see end 
  $textWidget configure -state disabled

  $entryWidget select range 0 end
  $entryWidget icursor 0
}  

#
# saves the text currently displayed in the editor to a temporary file
#
proc MakeCopy {} {
  global tempfile
    
  if [catch {open $tempfile w} handle] {
      ShowMessage Error $handle 
      return
  } else {
      puts $handle [.edtext get 1.0 end]
      close $handle
  } 
} 

#
# clear the editor window to edit a new file
#
proc New {} {
  global filename unmodified

  # return if user wants to save his files before editing a new one
  if {[SaveFirst]} {
    return
  }

  set filename ""
  .filename configure -text unknown
  .edtext delete 1.0 end
  set unmodified [.edtext get 1.0 end]
}

#
# allows user to enter a filename; loads file into editor if valid
#
proc Open {} {
  global filename requestAddr unmodified

  # return if user wants to save his files before opening a new one
  if {[SaveFirst]} {
    return
  }

  set temp [GetString "Open File" "Enter Filename:" ""]
  if {[string length $temp] > 0} {
    if [catch {open $temp r} handle] {
        ShowMessage Error $handle
        return
    } else {
        set filename $temp
        .edtext delete 1.0 end
        .edtext insert end [read $handle]
        close $handle

        set unmodified [.edtext get 1.0 end]

        if {[CheckFile $filename]} {
          SetMarker [CodeAddress]
        }

        .filename configure -text $filename
        FillRegs
        ExeCustom
        set requestAddr 1
    }
  }
}


#  
# positions a window in another window
# NOTE: wait for the new window to be mapped before calling this
#       procedure or you will get the initial width and height 1
# 
proc PosWindow {old new} {
  regexp "(.*)x(.*)\[+-](.*)\[+-](.*)" [wm geometry $old] ow odx ody ox oy 
  regexp "(.*)x(.*)\[+-](.*)\[+-](.*)" [wm geometry $new] nw ndx ndy nx ny

  set x [expr $ox + ($odx-$ndx)/2]
  set y [expr $oy + ($ody-$ndy)/2]
    
  wm geometry $new +$x+$y
}     

#
# quits the program
#
proc Quit {} {
  global tempfile 
 
  # return if user wants to save his files before leaving
  if {[SaveFirst]} {
    return
  }

  # remove temporary files before leaving
  if {[file exists $tempfile]} {
    file delete $tempfile
  }
  
  exit
}

#
# saves the text currently displayed in the editor
#
proc Save {} {
  global filename unmodified

  if {[string compare $filename ""]} {
      if [catch {open $filename w} handle] {
          ShowMessage Error $handle
          return
      } else {
          puts $handle [.edtext get 1.0 end]
          close $handle
          set unmodified [.edtext get 1.0 end]
      }
  } else {
      SaveAs
  }
}

#
# saves the text currently displayed in the editor to the file specified by
# the user 
#
proc SaveAs {} {
  global filename unmodified

  set temp [GetString "Save As" "Enter Filename:" $filename]
  if {[string compare $temp ""]} {
    if [catch {open $temp w} handle] {
        ShowMessage Error $handle
        return
    } else {
        set filename $temp
        puts $handle [.edtext get 1.0 end]
        close $handle
        .filename configure -text $filename
        set unmodified [.edtext get 1.0 end]
    }
  }
}

#
# returns true if the user wants to save his files before continueing 
# if a save is not necessary or not wanted returns false
#
proc SaveFirst {} {
  global unmodified

  if {[string compare $unmodified [.edtext get 1.0 end]]} {
      return [ expr 1 - [ShowOkCancel "Source Modified"\
                                      "No write since last change.\n\
                                       Would you still like to continue?"]]
  } else {
      return 0
  }
}

#
# returns line number of line beginning with 'text' in text widget
#
proc SearchText {text} {
  set numLines [.edtext index end]
  set lineNum 0

  for {set i 1} {$i <= $numLines} {incr i} {
    set line [.edtext get "$i.0" "$i.end"]
    if {[regexp "^\[ \t]*$text" $line w]} {
      set lineNum $i
      break
    }
  }

  return $lineNum
}

#
# sets the marker to the line specified by address
#
proc SetMarker {addr} {
  set lineNr [SearchText "\\.text"]
  set lineNr [expr $lineNr + 1]
  set lineAddr [CodeAddress]

  # read until addr
  while {$lineAddr < $addr} {  
    if {[CommandLine [.edtext get "$lineNr.0 linestart"\
                                  "$lineNr.0 lineend"]]} {
      set lineAddr [expr $lineAddr + 4]
    }
    set lineNr [expr $lineNr + 1]
  }  
 
  # skip non-command lines 
  while {![CommandLine [.edtext get "$lineNr.0 linestart"\
                                    "$lineNr.0 lineend"]]} {
    set lineNr [expr $lineNr + 1]
  }

  # remove old marker
  .edtext tag delete next

  # set new marker
  .edtext tag add next "$lineNr.0 linestart" "$lineNr.0 lineend"
  .edtext tag configure next -foreground red
  .edtext see $lineNr.0
} 

#
# prints a message
#
proc ShowMessage {title message} {
  global ok

  set oldfocus [focus]

  bell
  set f [toplevel .t_ShowMessage -bd 10]
  wm title $f $title
  # position window outside of visible area
  wm geometry $f +10000+10000

  set t [frame $f.top -bd 10]
  pack $t
  label $t.l -text $message
  pack $t.l -side top -fill x

  set b [frame $f.buttons -bd 10]
  pack $b
  button $b.ok -text OK -command "set ok 1"
  bind $b.ok <Return> "set ok 1"
  pack $b.ok -expand 1

  tkwait visibility $f
  PosWindow . $f
  focus $b.ok
  grab $f
  tkwait variable ok
  grab release $f
  destroy $f
  focus $oldfocus
}

#
# prints a message with *two* buttons
#
proc ShowOkCancel {title message} {
  global ok
  
  set oldfocus [focus]
  
  bell
  set f [toplevel .t_ShowMessage -bd 10]
  wm title $f $title
  # position window outside of visible area
  wm geometry $f +10000+10000
  
  set t [frame $f.top -bd 10]
  pack $t
  label $t.l -text $message
  pack $t.l -side top -fill x
  
  set b [frame $f.buttons -bd 10]
  pack $b -fill x
  button $b.ok -text OK -command "set ok 1"
  button $b.cancel -text Cancel -command "set ok 0"
  bind $b.ok <Return> "set ok 1"
  bind $b.cancel <Control-c> "set ok 0"
  pack $b.ok -side left
  pack $b.cancel -side right
  
  tkwait visibility $f
  PosWindow . $f
  focus $b.ok
  grab $f
  tkwait variable ok
  grab release $f
  destroy $f
  focus $oldfocus
  return $ok
}

#
# displays a text
#
proc ShowText {title text} {
  global g

  set oldfocus [focus]

  set f [toplevel .t_ShowText -bd 10]
  wm title $f $title
  # position window outside of visible area
  wm geometry $f +10000+10000

  set g [frame $f.top]
  pack $g
  text $g.t -width 80 -height 40 -yscrollcommand {$g.yscroll set}\
       -xscrollcommand {$g.xscroll set} -wrap none 
  scrollbar $g.yscroll -command {$g.t yview}
  scrollbar $g.xscroll -orient horiz -command {$g.t xview}
  pack $g.yscroll -side right -fill y
  pack $g.xscroll -side bottom -fill x
  pack $g.t

  set b [frame $f.bottom -bd 10]
  pack $b
  button $b.ok -text OK -command "set ok 1"
  pack $b.ok

  $g.t insert end $text
  $g.t configure -state disabled

  tkwait visibility $f
  PosWindow . $f
  focus $b.ok
  grab $f
  tkwait variable ok
  grab release $f
  destroy $f
  focus $oldfocus
}

#
# checks if a source file is already loaded
#
proc SourceLoaded {} {
  global filename

  if {[string length $filename] == 0} {
    ShowMessage Error "No program to execute"
    return 0
  } else {
    return 1
  }
}

#
# reads dlxsim status information and displays it
#
proc Stats {} {
  global statst statop statpe statbr stathw
    
  exp_send "stat $statst $statop $statpe $statbr $stathw\r"
  expect -re "stat\[^\r]*\r\n(.*\r\n)\\(dlxsim\\) "
    
  ShowText Statistics [join [split $expect_out(1,string) "\r"]]
}   

#
# resets dlxsim status information
#
proc StatsReset {} {
  exp_send "stats reset\r"
  expect "(dlxsim) "
  ShowMessage Info "Statistics reset accomplished"
}   

#
# executes a single instruction
#
proc Step {addr} {
  global tempfile requestAddr
    
  # return if no source file ist loaded
  if {![SourceLoaded]} {
    return
  } 
 
  # return if code area has no address
  if {![string compare [CodeAddress] ""]} {
    return
  }
 
  # prompt for an address if the user asked for it or if the user
  # presses the step button the first time
  if {$addr || $requestAddr} {
      MakeCopy
      if {[CheckFile $tempfile]} {
          set codestart [CodeStart]
          if {![string compare $codestart ""]} {
              return
          } else {
              exp_send "step $codestart\n"
              set requestAddr 0
          }
      } else {
          return
      }
  } else {
      exp_send "step\r"
  } 
  
  expect -re "\r\n(.*)\r\n\\(dlxsim\\) "
  ExeCustom
  FillRegs
    
  if {[regexp ".*pc = (.*)\\+(.*):.*" $expect_out(1,string) w label offset]} {
      SetMarker [expr [CalcAddress $label] + $offset] 
  } elseif {[regexp ".*pc = (\[0-9].*):.*" $expect_out(1,string) w address]} {
      SetMarker $address
  } elseif {[regexp ".*pc = (.*):.*" $expect_out(1,string) w label]} {
      SetMarker [CalcAddress $label]
  } else {
      SetMarker [CodeAddress]
      ShowMessage Info $expect_out(1,string)
      set requestAddr 1
  } 
} 
