that is, given something like 0800.1234.5678 I want to flip each byte
in terms of how they byte is read, so 0x08, or in binary 00001000
becomes 00010000 - i.e 0x10
Doing things like expr ~0x08 gives me -9 as an output though, and I
don't understand this. I've done a trawl for how bitwise NOT is
supposed to work with TCL and can't find anything. Can anyone help?
Many thanks
Danny
Values in Tcl are signed, and since you've flipped the top (sign) bit,
you've gone from a positive to a negative number. Easy!
% format "0x08 = %08x, ~0x08 = %08x" 0x08 [expr ~0x08]
0x08 = 00000008, ~0x08 = fffffff7
Donal.
Given that I want to see it unsigned, can I use some sort of format
statement to print out the result unsigned ?
cheers
Danny
I'm obviously going brain-dead. I don't want to bit flip - I want to
read it the other way - little endian .v. big endian if you like. I
have no idea why I started down doing logical NOTs.
That is - I want a nice easy operator which will take something like
x7, understand that it is 0111, read it in the opposite direction so
1110 and convert that to xD
How easy is this?
thanks for your time - and interesting about the logical NOT even if I
don't need it at the minute,
cheers
Danny
set val "\001\002\003\004"
binary scan $val I bigendian
binary scan $val i littleendian
puts [format "big: %08x little: %08x" $bigendian $littleendian ]
uwe
look at binary format/scan - the operand you use can specify either
big endian or little endian.
Bruce
Uwe Klein wrote:
> [binary scan] is your friend.
OK, there is some miscommunication and misunderstanding going on here.
First off. If danny really wants what he is describing then binary scan
won't help much.
Now for the basics:
Big endian and little endian are generally byte-wise in nature. That
is, what most people refer to big endian and little endian have the
same binary representation if the data is a single byte.
For example, the 16 bit big endian number 0x1234 when converted to
little endian is 0x3412. Notice it is NOT 0x4321. Neither is it 0x2c48.
That is, the bytes are intact and not reversed but the byte order is
reversed. A 32 bit example would be big endian 0x12345678 to little
endian 0x78563412.
What danny is asking is how to convert 0x1234 into 0x2c48 which
reverses the bit order and not the byte order. As far as I recall, no
computer in the history of mankind have ever been designed to do this.
Also, no programming language in the history of computing have ever
been designed to handle this.
I had this same problem once because I wired up a digital to analog
converter backwards on the bus. The only way to do this is to do some
bit-bangging. That is to check each bit of the input and set the
appropriate bit in the output.
Something like this:
proc bitflip {number} {
# assuming all numbers are 32 bits.
set output 0
for {set x 0} {$x < 32} {incr x} {
if {[expr {$number & (1 << $x)}] != 0} {
set output [expr {$output | (0x8000 >> $x)}]
}
}
return output
}
You can do this with binary scan/format bit wise operators
proc bitflip { number } {
binary scan $number B* bin
return [ binary format b* $bin ]
}
NOTE: The case of the b's.
NOTE: This works BYTE wise, for the number of bytes you put in :
I.e. bitflip "\x01\x02" will return "\x80\x40"
Thanks for listening...
Paul.
binary scan would still be usefull in various permutations,
but OK it is not _one step_ .
One will have to have an eye on byteorder while disbyting
and then scan the bytes with B/b as apropriate.
rebuilding would be single step : fmt:B32 .
uwe
PS: i have used something like this in talking to a PIC.
a parameter would be sent as inverted bit16 because then
the controller would just have to compare this value to
an internal runtime counter.
sleb...@yahoo.com wrote:
> Also, no programming language in the history of computing have ever
> been designed to handle this.
Quokka wrote:
> You can do this with binary scan/format bit wise operators
I stand corrected. It seems that tcl can do it natively (sort of).
I keep getting amazed by tcl.
This is really nice. But keep in mind that binary scan/format converts
from binary strings to human readable representation and vice versa.
Doing binary scan b/B does not give you a number but a string made from
ones and zeroes. To convert it back to a number you'd have to do binary
format to convert it back to raw binary data and then binary scan a
second
time to convert it back to a number.
Yep - you're right - I wasn't being very clear.
The application for this is to do with converting how MAC addresses are
represented on Ethernet and on TokenRing. One is described as
canonical and the other as non-canonical. Basically in one, the bytes
are read a bit at a time from left to right. In the other, the bytes
are read a bit at a time in the opposite direction. When a MAC address
is specified in one domain (e.g. Token Ring), and you're in the other
domain (e.g Ethernet) you have to convert between them. It's not a
massively difficult exercise but you're still doing so on a 48 bit
number and there's potential for mistakes. If I could code it very
simply, that would be superb.
Cisco routers now, in later versions of IOS, have a version of a tcl
shell built into it, so I was hoping to use this.
The script you've given me helps me convert between decimal 8 and its
equivalent in the opposite direction. To go back to an eariler post in
this thread, It seems that the tcl I have built into my router here
won't let me type in "binary scan $val I bigendian" - it doesn't like
the words binary scan - at least not at the command line and if I put
this into a proc{} I can't seem to call it successfully.
To rob another post, if I use the proc
proc bitflip { number } {
binary scan $number B* bin
return [ binary format b* $bin ]
}
How do I call this from a tcl shell? For example:
puts [ eval bitflip 81 ]
doesn't work, or at least not in the shell I'm playing with.
What'm I missing in my understanding?
cheers
Danny
We're generally fairly effective at helping people with an appe-
tite for simple coding. We'll get through this.
What version of Tcl does IOS give you? My guess is that it's
8.0, which lacked [binary format] (I believe--we'll look it up
eventually). At the Tcl prompt, request
info patchlevel
or
info tclversion
set IP 0x1248EDB7
set PI 0x0
for { set i 0 } { $i < 32 } { incr i } {
set PI [expr ( $PI << 1 ) | ( ($IP >> $i ) & 1) ]
}
puts [format %08x $IP ]
puts [format %08x $PI ]
uwe
Thankyou - I appreciate that. While I'm not really a programmer, I'm
not without understanding of how to do simple stuff so hopefully I
should be able to respond reasonably well to need to provide
information if you can guide me.
> What version of Tcl does IOS give you? My guess is that it's
> 8.0, which lacked [binary format] (I believe--we'll look it up
> eventually). At the Tcl prompt, request
>
> info patchlevel
> or
> info tclversion
That's interesting. I assume the current version of tcl is
significantly newer than 8.0 (quick search suggests 8.4.10 is current)
- well:
info patchlevel gives 104
info tclversion gives 7.1
I'm running IOS 12.2(15)T14 - I know this is a programming world in
here, so I can probably risk not offending too many people by saying
this isn't the most bang up to date version of IOS but it isn't too
old. I don't have a latest ios image to play with right now - I'll get
hold of one if it's worthwhile and see what version of tcl is on that.
For the specific context of what I need though, the 12.2(x)T releases
are what I need to be able to work with, for now at least.
cheers!
Danny
What I'm working with is 48 bit numbers - Ethernet/TokenRing MAC
addresses to be precise. To make it more complicated they will often
begin with leading 0s.
Can you walk me through the algorithm. I see that you're setting the
variable IP in this and using i as a counter in the for loop. I can see
that you're incrementing it between 0 and 31. I see that within the
loop you're setting PI up and I can see you're using bitwise operators
to do so. I think what you're doing is a bitwise shift on IP and a
bitwise AND with 1 - presumably this starts with the least significant
bit of IP and moves towards the MSB, setting the LSB into the MSB of PI
and so on, so long as that bit of IP is set to 1. Why is the bitwise
AND needed?
What I'm more unsure about is the puts statements. The %08x looks like
a length field - presumably the 08 is a decimal value telling me that
what I'm about to puts is an eight byte value. If I want to work with
a 12 byte value (as I do), I'd expect to use %12x. I've just used my
current working example as "set ip 0x00070eb96d59" and doing either of
puts [format %08x $IP]
puts [format %12x $IP]
gives me as output:
FFFFFFFF
Not that useful!
If I do "puts $IP" I still get the correct value (with the 0x) of
0x00070eb96d59
I'm intrigued that "puts $pi" does nothing of the kind - it prints out
a large -ve decimal number - why are they formatted differently? Is
the format held in the number somehow, or is this all due to everything
being held as strings?
So - questions :
how do I increase the length of the output formatter to output six byte
numbers rather than four?
how do I get this to work on one byte at a time through the six byte
number ? I assume another for loop around your example would do this
if I just read the number one byte at once, but I'm not sure how to
split it up!
That is, given a number (dots used for clarity only)
x0003.0D10.320D
I want to work on x00 to get x00 and print that out, then x03 to get
xC0 and print that etc so the output is x00C0.B008.4CB0 rather than
xB04C.08B0.C000
By the way - I'm enjoying this - it's interesting!
cheers
Danny
If time is of essence for the bitflip/mirror etc. operations we can do
set cache {}
foreach {set i 0} {$i < 256} {incr i} {lappend cache [_true_bitflip
$i]}
and then define
proc bitflip i {global cache ; lindex $cache $i}
I.e. do the costly bitoperations once, and then simply look up the
results in our cache when we need them.
If the data is more sparse, or the key more complex an array will serve
as well.
re my example: you would have to use tcl with "large int" support
set MAC 0x001248EDB70FF ;# this is the 48bit incoming addr.
;# stored in a "large int"
set CAM 0x0 ;# this is the 48bit outgoing addr.
;# stored in a "large int"
for { set i 0 } { $i < 48 } { incr i } {
# get the i'th bit from incoming
# starting with the lsb/0 ending with msb/47
set bit_in [ expr ( ($MAC >> $i ) & 1) ]
# shift the contructed outgoing value 1 bit left
set CAM [ expr $CAM << 1 ]
# insert the indexed bit in lsb position into the
# constructed value
set CAM [expr $CAM | $bit_in ]
}
# show what we have done:
puts [format %012x $IP ]
puts [format %012x $PI ]
now if you get the addresses as "ascii" strings i.e. something like:
"00213a5b7821"
we can do it in a different way:
# predetermine the 16 possible
# patterns for a hex nibble "bitinversion"
array set conv {
0 0 1 8 2 4 3 c
4 2 5 a 6 6 7 e
8 1 9 9 a 5 b d
c 3 d b e 7 f f
}
# hope i did not make a mistake here;-)
set MAC 00213a5b7821 ;# incoming
set CAM {} ;# outgoing
set MAC [ string tolower $MAC ] ; fix things like A B C ..
if {[string length $MAC ] < 12 } {
puts stderr "err: not enough nibbles"
}
for { set i 11 } { $i >= 0 } { incr i -1} {
set hex [ string index $MAC $i ]
append CAM $conv($hex)
}
# show what we have done:
puts $MAC
puts $CAM
People, this is tcl 7.1 so I don't think bignums are supported. Also,
if memory serves me right, arrays are not available either. This is OLD
tcl guys, remember how we used to do things back then?
If arrays are available then Uwe Klein's solution works perfectly.
Otherwise you have to apply a bitflip proc to each byte instead of
looking up an array.
>># patterns for a hex nibble "bitinversion"
>>
>>array set conv {
>> 0 0 1 8 2 4 3 c
>> 4 2 5 a 6 6 7 e
>> 8 1 9 9 a 5 b d
>> c 3 d b e 7 f f
>>}
>
>
> People, this is tcl 7.1 so I don't think bignums are supported. Also,
> if memory serves me right, arrays are not available either. This is OLD
> tcl guys, remember how we used to do things back then?
Arrays were around when TCL was still being chiseled in stone tablets
(they are discussed in Ousterhout's book, but [array set] is not).
>
> If arrays are available then Uwe Klein's solution works perfectly.
> Otherwise you have to apply a bitflip proc to each byte instead of
> looking up an array.
Creating a list rather than array, converting the nibble from hex to
deciaml, and using [lindex] could work too.
Gerry
would this work in The Carvedinstone Language ( i.e. tcl prehistoric?
uwe
> foreach {hex xeh} $hexies {
Not available in Tcl7
I still use Tcl7.3 in a VXworks program, but I don't know
how different 7.1 is from that.
(OK, it is 0.2 different!)
--
Donald Arseneau as...@triumf.ca
I met tcl over some sweat in a lab at 8.0
and never looked back since ;-)
uwe,
who really hasn't used any tcl version before 8.0
> Donald Arseneau wrote:
> > Uwe Klein <uwe_klein_...@t-online.de> writes:
> >
> >>foreach {hex xeh} $hexies {
> > Not available in Tcl7
> > I still use Tcl7.3 in a VXworks program, but I don't know
> > how different 7.1 is from that.
> > (OK, it is 0.2 different!)
>
> I met tcl over some sweat in a lab at 8.0
> and never looked back since ;-)
I think Tcl8 may even have been out before I got involved with the vxworks
stuff.
Anyway, I should clarify: foreach is available, but only in its simplest
form -- foreach var list code
--
Donald Arseneau as...@triumf.ca
Appreciate your willingness to help Uwe - sadly foreach seems to fail.
Once I enter the '}' after the foreach.. bits I get
can't read "hex": no such variable
This really *is* old, isn't it?
I'll try to see what version of tcl is supported on a newer version of
the operating system, but for my purposes I'm stuck with this version
and tcl 7.1 !
cheers
Danny
someone said further up in the thread that
the alluvial foreach could only swing one
stone at a time:
set cnt 0
foreach tok $hexies {
if {$cnt % 2} {
set xeh $tok
set $hex $xeh
} else {
set hex $tok
}
incr cnt
}
would this work?
uwe, now heading to his clapprig bettgestell
for some refreshing sleep.
This set me in a direction which worked for me :
proc flip MAC {
set hexies(0) 0
set hexies(1) 8
set hexies(2) 4
set hexies(3) c
set hexies(4) 2
set hexies(5) a
set hexies(6) 6
set hexies(7) e
set hexies(8) 1
set hexies(9) 9
set hexies(a) 5
set hexies(b) d
set hexies(c) 3
set hexies(d) b
set hexies(e) 7
set hexies(f) f
set CAM ""
for {set i 1} {$i < 13} {incr i 1} {
if {$i % 2} {
set j [expr $i +1]
set CAM $CAM$hexies([ string index $MAC $i ])
set CAM $CAM$hexies([ string index $MAC [expr $i -1] ])
}
}
return $CAM
}
Called with
puts [ flip 080012349876 ]
It's rather crude, but does at least work although relies upon me
working hexies out in the first place - granted not very difficult.
I'm sure if I think about it a bit harder I could put all of hexies
into a string and use string index, but I'll leave that for another
occasion.
Not much bit swapping in this though :-)
cheers
Danny