1
0
mirror of https://github.com/wfjm/w11.git synced 2026-01-25 04:05:39 +00:00
wfjm.w11/tools/tcl/rw11/shell.tcl
wfjm f903bda7b4 Workaround for tclreadline and after interference
- using in tcl 8.6 a timed `after` together with `tclreadline` injects
  spurious text of the form `after#nnn` into the readline input buffer.
- the `rw11/shell` overloads `unknown`, inviting for a bloody HACK which
  simply checks for such prefixes, scrubs them, and retries the command.
2018-10-29 11:57:38 +01:00

707 lines
21 KiB
Tcl

# $Id: shell.tcl 1058 2018-10-21 21:46:26Z mueller $
#
# Copyright 2015-2018 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
#
# This program is free software; you may redistribute and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for complete details.
#
# Revision History:
# Date Rev Version Comment
# 2018-10-21 1058 2.2.5 add after#\d+ scrubber (a real HACK, sorry)
# 2017-04-23 885 2.2.4 adopt .cm* to new interface
# 2017-04-22 883 2.2.3 integrate rbmon: add .rme,.rmd,.rmf,.rml
# 2017-04-16 879 2.2.2 rename .cres->.crst and .cr->.cres (more intuitive)
# 2017-04-09 872 2.2.1 adopt .ime to new interface
# 2017-01-02 837 2.2 code re-shuffle; add cpu status in prompt
# 2016-12-31 834 2.1 add '@' command
# 2016-12-30 833 2.0 major overhaul
# 2015-12-23 717 1.1 add e,g,d commands; fix shell_tin
# 2015-07-12 700 1.0 Initial version
#
package provide rw11 1.0
package require rlink
package require rwxxtpp
package require ibd_ibmon
package require rbmoni
namespace eval rw11 {
variable shell_depth 0; # recursion stopper
variable shell_cpu "cpu0"; # current cpu command
variable shell_cpu_stat ""; # cpu status
variable shell_attnhdl_added 0
variable shell_eofchar_save {puts {}}
#
# shell_start: start rw11 shell --------------------------------------------
#
proc shell_start {} {
variable shell_cpu
variable shell_attnhdl_added
variable shell_eofchar_save
variable shell_cpu_stat
global tirri_interactive
# quit if shell already active
if {[llength [info procs ::rw11::unknown_save]] > 0} {
error "rw11 shell already started"
}
# set unknown handler
rename ::unknown ::rw11::unknown_save
rename ::rw11::shell_forward ::unknown
# check that attn handler is installed
if {!$shell_attnhdl_added} {
rls attn -add 0x0001 { rw11::shell_attncpu }
set shell_attnhdl_added 1
}
# redefine ti_rri prompt and eof handling
if { $tirri_interactive } {
# setup new prompt (save old one...)
rename ::tclreadline::prompt1 ::rw11::shell_prompt1_save
namespace eval ::tclreadline {
proc prompt1 {} {
return "${rw11::shell_cpu_stat}${rw11::shell_cpu}> "
}
}
# disable ^D (and save old setting)
set shell_eofchar_save [::tclreadline::readline eofchar]
::tclreadline::readline eofchar {::rw11::shell_eofchar}
}
return
}
#
# shell_stop: stop rw11 shell ----------------------------------------------
#
proc shell_stop {} {
variable shell_eofchar_save
global tirri_interactive
if {[llength [info procs ::rw11::unknown_save]] == 0} {
error "rw11 shell not started"
}
rename ::unknown ::rw11::shell_forward
rename ::rw11::unknown_save ::unknown
# restore ti_rri prompt and eof handling
if { $tirri_interactive } {
rename ::tclreadline::prompt1 {}
rename ::rw11::shell_prompt1_save ::tclreadline::prompt1
::tclreadline::readline eofchar $shell_eofchar_save
}
return
}
#
# shell_forward: rw11 shell forwarder (will be renamed to ::unknown) -------
#
proc shell_forward args {
# in tcl 8.6 tclreadline and after interferes in some strange manner
# commands are sometime prefixed with 'after#nnn'. This BLOODY HACK
# looks for commands starting with 'after#\d+', scrubs of prefix, and
# executes them via uplevel.
set cmd [lindex $args 0]
set cmdo $cmd
regsub -all {^after#\d+} $cmd {} cmd
if {$cmd ne $cmdo} {
if {$cmd eq ""} { return "" }
lset args 0 $cmd
uplevel 1 [list {*}$args]
return ""
}
uplevel 1 [list rw11::shell {*}$args]
}
#
# shell_eofchar: eofchar handler -------------------------------------------
#
proc shell_eofchar args {
cpu0 cp -rstat cpustat
if {[regget rw11::CP_STAT(go) $cpustat]} {
puts \
"cpu0 running, ^D disabled. Use .q to quit shell or .qq to bail out"
return
}
tirri_exit
return
}
#
# shell_attncpu: cpu attn handler ------------------------------------------
#
proc shell_attncpu {} {
puts "CPU attention"
puts [cpu0 show -r0ps]
shell_update_cpu_stat
puts -nonewline [::tclreadline::prompt1]
flush stdout
return
}
#
# shell: rw11 shell --------------------------------------------------------
#
proc shell args {
variable shell_depth
variable shell_cpu
set rval {}
set cname [lindex $args 0]
set cargs [lreplace $args 0 0]
# handle @<command-file>
if {[regexp -- {^\@(.+)$} $cname matched fname]} {
if {![file exists $fname]} {
error "shell-E: file 'fname' not found"
}
if {[regexp -- {^(.+)\.scmd$} $fname]} {
shell_simh $fname
} else {
source $fname
}
return
}
switch $cname {
.e {set rval [shell_exa {*}$cargs]}
.g {set rval [shell_get {*}$cargs]}
.d {set rval [shell_dep {*}$cargs]}
.c0 -
.c1 -
.c2 -
.c3 {set rval [shell_setcpu $cname]}
.cs {set rval [shell_cs {*}$cargs]}
.cres {set rval [shell_cres {*}$cargs]}
.csus {set rval [shell_csus {*}$cargs]}
.csto {set rval [shell_csto {*}$cargs]}
.crst {set rval [shell_crst {*}$cargs]}
.csta {set rval [shell_csta {*}$cargs]}
.bs {set rval [rw11::hb_set $shell_cpu {*}$cargs]}
.br {set rval [rw11::hb_remove $shell_cpu {*}$cargs]}
.bl {set rval [rw11::hb_list $shell_cpu {*}$cargs]}
.cme {set rval [shell_cme {*}$cargs]}
.cmd {set rval [shell_cmd {*}$cargs]}
.cml {set rval [shell_cml {*}$cargs]}
.ime {set rval [shell_ime {*}$cargs]}
.imd {set rval [shell_imd {*}$cargs]}
.imf {set rval [shell_imf {*}$cargs]}
.iml {set rval [shell_iml {*}$cargs]}
.rme {set rval [shell_rme {*}$cargs]}
.rmd {set rval [shell_rmd {*}$cargs]}
.rmf {set rval [shell_rmf {*}$cargs]}
.rml {set rval [shell_rml {*}$cargs]}
. {set rval [shell_cls {*}$cargs]}
? {set rval [shell_clb {*}$cargs]}
?m {set rval [shell_clm {*}$cargs]}
?u {set rval [shell_clu {*}$cargs]}
?? {set rval [shell_cll {*}$cargs]}
( {set rval [shell_ti {*}$cargs]}
< {set rval [shell_tin {*}$cargs]}
.hr {set rval [shell_hr {*}$cargs]}
.h {set rval [shell_h {*}$cargs]}
.ha {set rval [shell_ha {*}$cargs]}
.q {set rval [shell_q {*}$cargs]}
.qq {set rval [shell_qq {*}$cargs]}
default {
if {$shell_depth > 1} {
error "nested entry to shell for \"$cname\""
}
incr shell_depth
uplevel 1 [list rw11::unknown_save {*}$args]
}
}
set shell_depth 0
return $rval;
}
#
# shell_setcpu: set current cpu --------------------------------------------
#
proc shell_setcpu {cname} {
variable shell_cpu
set cpucmd "cpu[string range $cname 2 2]"
if {[llength [info commands $cpucmd]] == 0} {
error "'$cpucmd' not available"
}
set shell_cpu $cpucmd
return
}
#
# shell_cs: cpu step -------------------------------------------------------
#
proc shell_cs {{nstep 1}} {
variable shell_cpu
set rval {}
for {set i 0} {$i < $nstep} {incr i} {
rw11::hb_clear $shell_cpu
$shell_cpu cp -step
if {$i > 0} {append rval "\n"}
append rval [$shell_cpu show -pcps]
$shell_cpu cp -rstat stat
if {[regget rw11::CP_STAT(rust) $stat] != $rw11::RUST_STEP} {break}
}
shell_update_cpu_stat
return $rval
}
#
# shell_cres: cpu resume ---------------------------------------------------
#
proc shell_cres {} {
variable shell_cpu
variable shell_cpu_stat
set shell_cpu_stat "g:";
rw11::hb_clear $shell_cpu
$shell_cpu cp -resume
return
}
#
# shell_csus: cpu suspend --------------------------------------------------
#
proc shell_csus {} {
variable shell_cpu
$shell_cpu cp -suspend
shell_update_cpu_stat
return
}
#
# shell_csto: cpu stop -----------------------------------------------------
#
proc shell_csto {} {
variable shell_cpu
$shell_cpu cp -stop
shell_update_cpu_stat
return
}
#
# shell_crst: cpu reset ----------------------------------------------------
#
proc shell_crst {} {
variable shell_cpu
$shell_cpu cp -stop
$shell_cpu cp -creset
shell_update_cpu_stat
return
}
#
# shell_csta: cpu start ----------------------------------------------------
#
proc shell_csta {{pc -1}} {
variable shell_cpu
variable shell_cpu_stat
set shell_cpu_stat "g:";
if {$pc == -1} {
$shell_cpu cp -start
} else {
$shell_cpu cp -stapc $pc
}
return
}
#
# shell_cme: cmon enable ---------------------------------------------------
#
proc shell_cme {{mode ""}} {
variable shell_cpu
if {![shell_test_cpurmap $shell_cpu "cme" "cm.cntl" "dmcmon"]} {return}
rw11::cme $shell_cpu $mode
return
}
#
# shell_cmd: cmon disable --------------------------------------------------
#
proc shell_cmd {} {
variable shell_cpu
if {![shell_test_cpurmap $shell_cpu "cmd" "cm.cntl" "dmcmon"]} {return}
rw11::cm_stop $shell_cpu
return
}
#
# shell_cml: cmon list -----------------------------------------------------
#
proc shell_cml {{nent -1}} {
variable shell_cpu
if {![shell_test_cpurmap $shell_cpu "cml" "cm.cntl" "dmcmon"]} {return}
return [rw11::cml $shell_cpu $nent]
}
#
# shell_ime: ibmon enable --------------------------------------------------
#
proc shell_ime {{mode "lrc"}} {
variable shell_cpu
if {![shell_test_cpurmap $shell_cpu "ime" "im.cntl" "ibmon"]} {return}
ibd_ibmon::ime $shell_cpu $mode
return
}
#
# shell_imd: ibmon diasable -------------------------------------------------
#
proc shell_imd {} {
variable shell_cpu
if {![shell_test_cpurmap $shell_cpu "imd" "im.cntl" "ibmon"]} {return}
ibd_ibmon::stop $shell_cpu
return
}
#
# shell_imf: ibmon filter ---------------------------------------------------
#
proc shell_imf {{lo ""} {hi ""}} {
variable shell_cpu
if {![shell_test_cpurmap $shell_cpu "imf" "im.cntl" "ibmon"]} {return}
ibd_ibmon::imf $shell_cpu $lo $hi
return
}
#
# shell_iml: ibmon list -----------------------------------------------------
#
proc shell_iml {{nent -1}} {
variable shell_cpu
if {![shell_test_cpurmap $shell_cpu "iml" "im.cntl" "ibmon"]} {return}
set mondat [ibd_ibmon::read $shell_cpu $nent]
if {![llength $mondat]} {return}
return [ibd_ibmon::print $shell_cpu $mondat]
}
#
# shell_rme: rbmon enable --------------------------------------------------
#
proc shell_rme {{mode ""}} {
if {![shell_test_rlcamap "rme" "rm.cntl" "rbmon"]} {return}
rbmoni::rme $mode
return
}
#
# shell_rmd: rbmon diasable -------------------------------------------------
#
proc shell_rmd {} {
if {![shell_test_rlcamap "rmd" "rm.cntl" "rbmon"]} {return}
rbmoni::stop
return
}
#
# shell_rmf: rbmon filter ---------------------------------------------------
#
proc shell_rmf {{lo ""} {hi ""}} {
if {![shell_test_rlcamap "rmf" "rm.cntl" "rbmon"]} {return}
rbmoni::rmf $lo $hi
return
}
#
# shell_rml: rbmon list -----------------------------------------------------
#
proc shell_rml {{nent -1}} {
if {![shell_test_rlcamap "rml" "rm.cntl" "rbmon"]} {return}
set mondat [rbmoni::read $nent]
if {![llength $mondat]} {return}
return [rbmoni::print $mondat]
}
#
# shell_cls: cpu short status ----------------------------------------------
#
proc shell_cls {} {
variable shell_cpu
return [$shell_cpu show -pcps]
}
#
# shell_clb: cpu brief status ---------------------------------------------
#
proc shell_clb {} {
variable shell_cpu
return [$shell_cpu show -r0ps]
}
#
# shell_clm: mmu status ----------------------------------------------------
#
proc shell_clm {} {
variable shell_cpu
return [$shell_cpu show -mmu]
}
#
# shell_clu: ubmap status --------------------------------------------------
#
proc shell_clu {} {
variable shell_cpu
return [$shell_cpu show -ubmap]
}
#
# shell_cll: cpu long status -----------------------------------------------
#
proc shell_cll {} {
variable shell_cpu
set rval [shell_clb]
append rval "\n" [shell_clm]
append rval "\n" [shell_clu]
return $rval
}
#
# shell_ti: tta0 input (no cr at end) --------------------------------------
#
proc shell_ti args {
variable shell_cpu
set str [join $args " "]
"${shell_cpu}tta0" type $str
return
}
#
# shell_tin: tta0 input (with cr at end) -----------------------------------
#
proc shell_tin args {
variable shell_cpu
set str [join $args " "]
append str "\r"
"${shell_cpu}tta0" type $str
return
}
#
# shell_hr: list of and help on ibus register -------------------------------
#
proc shell_hr {{spec "*"} {am ""}} {
variable shell_cpu
set nreg 0
set rval "name : hex oct"
if {$am ne "" && ![regexp {^[rl][rw]$} $am]} {
error ".hr-E: bad am '$am', only \[rl\]\[rw\] allowed"
}
foreach item [$shell_cpu imap] {
set val [lindex $item 0]
set nam [lindex $item 1]
if {[string match $spec $nam]} {
incr nreg
append rval [format "\n%-10s : 0x%04x %06o" $nam $val $val]
set rdsc [rw11util::regmap_get $nam $am]
if {$rdsc ne ""} {
append rval "\n"
append rval [regdsc_print $rdsc]
}
}
}
if {!$nreg} {
append rval [format "\n%-10s : -- no matches found --" $spec]
}
return $rval
}
#
# shell_q: quit shell ------------------------------------------------------
#
proc shell_q args {
puts "shell-I: use rw11::shell_start to restart shell"
shell_stop
}
#
# shell_qq: quit ti_rri unconditionally ------------------------------------
#
proc shell_qq args {
tirri_exit
}
#
# shell_h: shell help text -------------------------------------------------
#
proc shell_h args {
variable shell_cpu
append rval "CPU state:"
foreach i {0 1 2 3} {
if {[llength [info commands "cpu${i}"]] > 0} {
append rval "\n .c${i} ; select cpu${i}"
}
}
append rval "\n .cs ?nstep? ; cpu step"
append rval "\n .cres ; cpu resume"
append rval "\n .csus ; cpu suspend"
append rval "\n .csto ; cpu stop"
append rval "\n .csta ?addr? ; cpu start"
append rval "\n .crst ; cpu reset"
append rval "\n . ; short cpu state (pc+psw)"
append rval "\n ? ; brief cpu state (all regs)"
append rval "\n ?m ; mmu status"
append rval "\n ?u ; ubmap status"
append rval "\n ?? ; full cpu state (with mmu+ubmap)"
append rval "\nmemory and register access:"
append rval "\n .e aspec ; examine, return as text"
append rval "\n .g aspec ; get, return as tcl list"
append rval "\n .d aspec vals ; deposit"
append rval "\n ; see .ha for help on aspec format"
append rval "\nCPU hardware breakpoint:"
append rval "\n .bs ind typ lo hi ; set bpt"
append rval "\n .br ?ind? ; remove bpt"
append rval "\n .bl ; list bpt"
if {[$shell_cpu get hascmon]} {
append rval "\nCPU monitor:"
append rval "\n .cme ?mode? ; cmon enable; mode:n?\[isS\]?"
append rval "\n .cmd ; cmon disable"
append rval "\n .cml ?nent? ; cmon list"
}
if {[$shell_cpu get hasibmon]} {
append rval "\nibus monitor:"
append rval "\n .ime ; ibmon enable; mode: \[lrcnRW\]*"
append rval "\n .imd ; ibmon disable"
append rval "\n .imf ?lo? ?hi? ; ibmon filter"
append rval "\n .iml ?nent? ; ibmon list"
}
if {[rlc get hasrbmon]} {
append rval "\nrbus monitor:"
append rval "\n .rme ; rbmon enable; mode: \[nRW\]*"
append rval "\n .rmd ; rbmon disable"
append rval "\n .rmf ?lo? ?hi? ; rbmon filter"
append rval "\n .rml ?nent? ; rbmon list"
}
append rval "\nconsole (tta0) direct input:"
append rval "\n ( ?text? ; tta0 input without cr"
append rval "\n < ?text? ; tta0 input with cr"
append rval "\nmiscellaneous:"
append rval "\n .hr ?name? ?am? ; list ibus registers; am: \[rl\]\[rw\]"
append rval "\n .h ; this help text"
append rval "\n .q ; quit shell, return to ti_rri"
append rval "\n .qq ; quit ti_rri unconditionally"
return $rval
}
#
# shell_ha: shell aspec help text ------------------------------------------
#
proc shell_ha args {
set rval "address specifier format for .e .g and .d:"
append rval "\n .e addr/opt/opt/... --> returns text"
append rval "\n .g addr/opt/opt/... --> returns value list"
append rval "\n .d addr/opt/opt/... vals"
append rval "\n"
append rval "\naddr format"
append rval "\n nnnn - number (note that tcl default is decimal !!)"
append rval "\n name - register name lookup in imap"
append rval "\n name+nn - name with offset"
append rval "\n rx - with r0,..,r7,sp,pc and also ps"
append rval "\n @rx - indirect: with r0,..,r7,sp,pc"
append rval "\n (rx) - indirect"
append rval "\n nnn(rx) - indirect with offset"
append rval "\n"
append rval "\nopt format (multiple opt's allowed)"
append rval "\n nnn - repeat count (decimal, in words)"
append rval "\n l - for iopage access: loc (as seen by CPU)"
append rval "\n r - for iopage access: rem (as seen by rlink)"
append rval "\n p - for memory access: physical (16bit)"
append rval "\n e - for memory access: extended (22 bit)"
append rval "\n u - for memory access via ubmap (22 bit)"
append rval "\n MS - for memory access via mmu mode=M and space=S"
append rval "\n - M (mode) as c,p,k,s,u for cm,pm,kern,sup,user"
append rval "\n - S (space) as i,d for instruction,data"
append rval "\n i - print as intructuion with dasm"
append rval "\n a - print as ascii"
append rval "\n d - print as decimal"
append rval "\n x - print as hex"
append rval "\n"
append rval "\nexamples:"
append rval "\n .e rpa.cs1 - register rhrp.cs1"
append rval "\n .e rpa.cs1/12/r - 12 regs starting rpa.cs1, rlink view"
append rval "\n .e @pc/8/ci/i - use pc, mmu in ci mode, 8 words as instructions"
append rval "\n .e @r0/8/cd - use r0. mmu in cd mode, show 8 words"
return $rval
}
#
# shell_test_cpurmap: test whether cpu option available ---------------------
#
proc shell_test_cpurmap {cpu cmd regnam optnam} {
if {[$cpu rmap -testname $regnam]} {
return 1;
}
puts "shell-W: '$cmd' command ignored, '$optnam' CPU option not available"
return 0;
}
#
# shell_test_rlcamap: test whether rbus option available --------------------
#
proc shell_test_rlcamap {cmd regnam optnam} {
if {[rlc amap -testname $regnam]} {
return 1;
}
puts "shell-W: '$cmd' command ignored, '$optnam' rbus option not available"
return 0;
}
#
# shell_update_cpu_stat ----------------------------------------------------
#
proc shell_update_cpu_stat {} {
variable shell_cpu_stat
set shell_cpu_stat ""
foreach i {0 1 2 3} {
if {[llength [info commands "cpu${i}"]] > 0} {
cpu${i} cp -rreg "stat" cp_stat
set cpu_rust [regget rw11::CP_STAT(rust) $cp_stat]
if {$cpu_rust == $rw11::RUST_INIT} { set cpu_rcode "I" } \
elseif {$cpu_rust == $rw11::RUST_HALT} { set cpu_rcode "H" } \
elseif {$cpu_rust == $rw11::RUST_RESET} { set cpu_rcode "R" } \
elseif {$cpu_rust == $rw11::RUST_STOP} { set cpu_rcode "S" } \
elseif {$cpu_rust == $rw11::RUST_STEP} { set cpu_rcode "+" } \
elseif {$cpu_rust == $rw11::RUST_SUSP} { set cpu_rcode "s" } \
elseif {$cpu_rust == $rw11::RUST_HBPT} { set cpu_rcode "b" } \
elseif {$cpu_rust == $rw11::RUST_RUNS} { set cpu_rcode "g" } \
else { set cpu_rcode "E" }
append shell_cpu_stat $cpu_rcode
}
}
append shell_cpu_stat ":"
}
}