riscv-openocd-wch/jimtcl/binary.tcl

279 lines
6.1 KiB
Tcl

# Implements the 'binary scan' and 'binary format' commands.
#
# (c) 2010 Steve Bennett <steveb@workware.net.au>
#
# See LICENCE in this directory for licensing.
package require pack
package require regexp
proc binary {cmd args} {
tailcall "binary $cmd" {*}$args
}
proc "binary format" {formatString args} {
set bitoffset 0
set result {}
# This RE is too unreliable...
foreach {conv t u n} [regexp -all -inline {([^[:space:]])(u)?([*0-9]*)} $formatString] {
switch -exact -- $t {
a -
A {
set value [binary::nextarg args]
set sn [string bytelength $value]
if {$n ne "*"} {
if {$n eq ""} {
set n 1
}
if {$n > $sn} {
# Need to pad the string with spaces or nulls
append value [string repeat [dict get {A " " a \x00} $t] $($n - $sn)]
}
} else {
set n $sn
}
if {$n} {
set bitoffset [pack result $value -str $(8 * $n) $bitoffset]
}
}
x {
if {$n eq "*"} {
return -code error {cannot use "*" in format string with "x"}
}
if {$n eq ""} {
set n 1
}
loop i 0 $n {
set bitoffset [pack result 0 -intbe 8 $bitoffset]
}
}
@ {
if {$n eq ""} {
return -code error {missing count for "@" field specifier}
}
if {$n eq "*"} {
set bitoffset $(8 * [string bytelength $result])
} else {
# May need to pad it out
set max [string bytelength $result]
append result [string repeat \x00 $($n - $max)]
set bitoffset $(8 * $n)
}
}
X {
if {$n eq "*"} {
set bitoffset 0
} elseif {$n eq ""} {
incr bitoffset -8
} else {
incr bitoffset $($n * -8)
}
if {$bitoffset < 0} {
set bitoffset 0
}
}
default {
if {![info exists ::binary::scalarinfo($t)]} {
return -code error "bad field specifier \"$t\""
}
# A scalar (integer or float) type
lassign $::binary::scalarinfo($t) type convtype size prefix
set value [binary::nextarg args]
if {$type in {bin hex}} {
set value [split $value {}]
}
set vn [llength $value]
if {$n eq "*"} {
set n $vn
} elseif {$n eq ""} {
set n 1
set value [list $value]
} elseif {$vn < $n} {
if {$type in {bin hex}} {
# Need to pad the list with zeros
lappend value {*}[lrepeat $($n - $vn) 0]
} else {
return -code error "number of elements in list does not match count"
}
} elseif {$vn > $n} {
# Need to truncate the list
set value [lrange $value 0 $n-1]
}
set convtype -$::binary::convtype($convtype)
foreach v $value {
set bitoffset [pack result $prefix$v $convtype $size $bitoffset]
}
# Now pad out with zeros to the end of the current byte
if {$bitoffset % 8} {
set bitoffset [pack result 0 $convtype $(8 - $bitoffset % 8) $bitoffset]
}
}
}
}
return $result
}
proc "binary scan" {value formatString {args varName}} {
# Pops the next arg from the front of the list and returns it.
# Throws an error if no more args
set bitoffset 0
set count 0
# This RE is too unreliable...
foreach {conv t u n} [regexp -all -inline {([^[:space:]])(u)?([*0-9]*)} $formatString] {
set rembytes $([string bytelength $value] - $bitoffset / 8)
switch -exact -- $t {
a -
A {
if {$n eq "*"} {
set n $rembytes
} elseif {$n eq ""} {
set n 1
}
if {$n > $rembytes} {
break
}
set var [binary::nextarg varName]
set result [unpack $value -str $bitoffset $($n * 8)]
incr bitoffset $([string bytelength $result] * 8)
if {$t eq "A"} {
set result [string trimright $result]
}
}
x {
# Skip bytes
if {$n eq "*"} {
set n $rembytes
} elseif {$n eq ""} {
set n 1
}
if {$n > $rembytes} {
set n $rembytes
}
incr bitoffset $($n * 8)
continue
}
X {
# Back up bytes
if {$n eq "*"} {
set bitoffset 0
continue
}
if {$n eq ""} {
set n 1
}
if {$n * 8 > $bitoffset} {
set bitoffset 0
continue
}
incr bitoffset -$($n * 8)
continue
}
@ {
if {$n eq ""} {
return -code error {missing count for "@" field specifier}
}
if {$n eq "*" || $n > $rembytes + $bitoffset / 8} {
incr bitoffset $($rembytes * 8)
} elseif {$n < 0} {
set bitoffset 0
} else {
set bitoffset $($n * 8)
}
continue
}
default {
if {![info exists ::binary::scalarinfo($t)]} {
return -code error "bad field specifier \"$t\""
}
# A scalar (integer or float) type
lassign $::binary::scalarinfo($t) type convtype size prefix
set var [binary::nextarg varName]
if {$n eq "*"} {
set n $($rembytes * 8 / $size)
} else {
if {$n eq ""} {
set n 1
}
}
if {$n * $size > $rembytes * 8} {
break
}
if {$type in {hex bin}} {
set u u
}
set convtype -$u$::binary::convtype($convtype)
set result {}
loop i 0 $n {
set v [unpack $value $convtype $bitoffset $size]
if {$type in {bin hex}} {
append result [lindex {0 1 2 3 4 5 6 7 8 9 a b c d e f} $v]
} else {
lappend result $v
}
incr bitoffset $size
}
# Now skip to the end of the current byte
if {$bitoffset % 8} {
incr bitoffset $(8 - ($bitoffset % 8))
}
}
}
uplevel 1 [list set $var $result]
incr count
}
return $count
}
# Pops the next arg from the front of the list and returns it.
# Throws an error if no more args
proc binary::nextarg {&arglist} {
if {[llength $arglist] == 0} {
return -level 2 -code error "not enough arguments for all format specifiers"
}
set arglist [lassign $arglist arg]
return $arg
}
set binary::scalarinfo {
c {int be 8}
s {int le 16}
t {int host 16}
S {int be 16}
i {int le 32}
I {int be 32}
n {int host 32}
w {int le 64}
W {int be 64}
m {int host 64}
h {hex le 4 0x}
H {hex be 4 0x}
b {bin le 1}
B {bin be 1}
r {float fle 32}
R {float fbe 32}
f {float fhost 32}
q {float fle 64}
Q {float fbe 64}
d {float fhost 64}
}
set binary::convtype {
be intbe
le intle
fbe floatbe
fle floatle
}
if {$::tcl_platform(byteOrder) eq "bigEndian"} {
array set binary::convtype {host intbe fhost floatbe}
} else {
array set binary::convtype {host intle fhost floatle}
}