Displaying Hex Numbers

retepsnikrep

Senior Member
Displaying hex numbers? I'm being thick I think :confused:

I'm using the code from the manual for "readowsn" to get the serial numbers for a DS18b20 sensor, but I don't want to use debug to display it, I want to output the hex numbers to my serial display using serout.

Can that be done? I did try a search and find it's easy to display the ascii value but I want the hex result displayed.

The point is the end user will note down these hex numbers and then use them in the 'multiple temp sensor on one input' reading code to address specific sensors.

http://www.picaxeforum.co.uk/showthread.php?t=8171&highlight=DS18b20

Thanks Peter
 

westaust55

Moderator
Dispalying hex values

If you want to display the vale of a variable in Hex as opposed to decimal then I believe you need a routine like this:

Code:
SYMBOL  value  = b5
SYMBOL  first  = b6
SYMBOL  second = b7 

; read in your value here

first = value / 16
IF first < 10 THEN
  first = first + $30  ; for digits 0 to 9
ELSE
  first = first + $37  ; for chars  A to F
ENDIF

second = value // 16
IF second < 10 THEN
  second = second + $30  ; for digits 0 to 9
ELSE
  second = second + $37  ; for chars  A to F
ENDIF

SEROUT 7, N2400, (first, second, LF,CR)

: goto where ever next
You can always prefix the value with the standard "$" symbol (or alternatively with "0x") if you wish to signify it is a hex number.
 
Last edited:

retepsnikrep

Senior Member
Thanks you for that speedy answer, a further thought can the owin /owout use decimals as the variables to be sent? The example 's given are all using $XX hex numbers.

e.g. owout 1,%1001,($CC,$44)
 

westaust55

Moderator
Hex and other bases for numbers

Yes - as you have found out.

You can represent a value in decimal, binary, or two formats for hexadecimal.

try this code in the PE simulator:
Code:
b0 = 0x44          ; hex version 1
b1 = $44           ; hex version 2
b2 = 68             ; decimal
b3 = %01000100 ; binary

serout 7, N2400, (#b0," ", #b1," ", #b2," ", #b3)
all give the same answer
 

cpedw

Senior Member
Westaust,
In your first code sample, why is the offset for letters $37 for the less significant byte, but $41 for te more significant? Should it not be $41 both times?
Derek
 

westaust55

Moderator
Westaust,
In your first code sample, why is the offset for letters $37 for the less significant byte, but $41 for te more significant? Should it not be $41 both times?
Derek
Ooops, yes there is a typo, but $37 is the correct number.

When calculating the hex value, the first offset is $30.
This is because the ASCII value for a "0" is $30 thru to $39 for the digit "9"

For the rest of the hexadecimal range (A to F), the ASCII value for "A" is $41 = decimal 65 , but we already have the value of decimal 10 ($0A) so we add 65 - 10 = 55 or $37 if it is in the range 10 to 15

Sorry about the confusion.

EDIT: have gone back and fixed the error to avoid anyone have problems in future.
 

hippy

Ex-Staff (retired)
The most optimal ( smallest, fastest ) coding I've come up with for a two digit hex number is ...

Code:
first = value / 16 + "0"
If first > "9" Then
  first = first + 7
End If

second = value & $0F + "0"
If second > "9" Then
  second = second + 7
End If
 

radiosparks

Member
Hippy always has the fastest and minimalist code ever. Works for me.

25852

An OLED (SSD1315) dump of a few bytes on Page 0 from the 24LC16B connected via I2C to a 08M2.
 

Aries

New Member
This was a little routine I wrote to convert a value in b0 into two printable hex characters in b1 and b2:
Code:
MakeHexByte:
' original first line contained redundant &$F0
'    b2 = b0 & $F0 / 16

' replaced by this:
    b2 = b0 / 16
    gosub MakeHexDigit
    b1 = b2
    b2 = b0 & $0F

MakeHexDigit:
    b2 = b2 / 10 MAX 1 * 7 + b2 + "0"
    return
On an 08M2 it compiles in 31 bytes.
EDIT: I realised that I had left in a redundant "&$F0" in "b2 = b0 & $F0 / 16" (now shown as comment). When removed, compiled size reduces to 29 bytes.
 
Last edited:

hippy

Ex-Staff (retired)
Hippy has this uncommon ability to write simple yet elegant code.
Years of practice and being willing to invest 'far too much time' in optimising things.

I am sure it helps that I have a background in assembly language programming from the era of very constrained microcontrollers where saving a single instruction was often well worth the effort put in and that was true for the earliest PICAXE if not so essential these days. In many ways it's recycling the tricks of the part.

Code:
first = value / 16 + "0"
If first > "9" Then
  first = first + 7
End If
Though that's what I use myself, I still can't help think there may be a cleverer way to do it, but I can't see any way to do it with a single line. This would be one way with just two lines -
Code:
first = value / 16
first = first / 10 * 7 + first + "0"
Less code space than the IF-ENDIF but likely slower as it uses a division and multiplication.

One could use a LOOKUP, READ or READTABLE as the second line but purely mathematical is what we are really after.

In theory, how to optimally do it, should be the sort of thing ChatGPT could reveal but perhaps not as most code it's been trained on either calls a function to do it, and how it's actually done isn't considered, or uses an array (READ/LOOKUP) or the IF-ENDIF technique. It may however have spotted more novel ways out there than a manual Google search reveals.
 

hippy

Ex-Staff (retired)
b2 = b2 / 10 MAX 1 * 7 + b2 + "0"
You can lose the "MAX 1" as b2 will only ever be 0 to 15, a divide by 10 can only be 0 or 1.

And I've just realised that's then what I suggested above. You beat me to it.

There may be some mileage in ' w0 = b2 * 16 | b2' which turns byte 'aaaa bbbb' into word '0000 aaaa xxxx bbbb' which puts the two digits into the LSB's of 'b1' and 'b0'. That then requires only one '& $0F' and avoids a divide by 16. I think the multiply by 16 would be faster than the divide. Using an X2 one can instead use shifts.

First attempt ...
Code:
b2 = $AE

w0 = b2 * 16 | b2 & $0F0F
b1 = b1 / 10 * 7 + b1 + "0"
b0 = b0 / 10 * 7 + b0 + "0"
SerTxd(#b2, " = $", b1, b0, CR, LF)
Alternatively ...
Code:
w0 = b2 * 16 | b2 & $0F0F + $3030
b1 = b1 / $3A * 7 + b1
b0 = b0 / $3A * 7 + b0
Haven't checked if this would be faster ...
Code:
w0 = b2 * 16 | b2 & $0F0F + $3030
b1 = b1 Min "9" - "9" Max 1 * 7 + b1
b0 = b0 Min "9" - "9" Max 1 * 7 + b0
 

Aries

New Member
You can lose the "MAX 1" as b2 will only ever be 0 to 15, a divide by 10 can only be 0 or 1.
Agreed, and then it uses even less code. My usual concern is code size rather than speed - I frequently run up against the limits of M2s and even X2s, so small size is usually the over-riding consideration.
 

Flenser

Senior Member
My measurements for the instruction counts.
On the PIC microcontrollers there are 4 cycles per instruction so at 4MHz these unstruction count numbers are also the measurements in us.

Code:
w0 = b2 * 16 | b2 & $0F0F + $3030
b1 = b1 / $3A * 7 + b1
b0 = b0 / $3A * 7 + b0
5902 instructions

Code:
w0 = b2 * 16 | b2 & $0F0F + $3030
b1 = b1 Min "9" - "9" Max 1 * 7 + b1
b0 = b0 Min "9" - "9" Max 1 * 7 + b0
6267 instructions
 

AllyCat

Senior Member
Hi,

Flenser's first version compiles to (about) 28 bytes and the second to 36 bytes, which suggests that the former will execute faster and would suit Aries better:
so small size is usually the over-riding consideration.
Basically, we're comparing "/ $3A" (division by 58) with the expression "Min "9" - "9" Max 1" (which has three stages, and compiles to 4 - 5 bytes). Division is one of the "slower" instructions; normally I assume that it takes around 1ms (i.e. 1000 base PIC instruction cycles) but have observed up to 1.5 ms, depending on the numerical values. I would normally expect a "three-stage" expression to execute in just under 1ms, but here it appears to take slightly more.

Cheers, Alan.
 

hippy

Ex-Staff (retired)
Basically, we're comparing "/ $3A" (division by 58) with the expression "Min "9" - "9" Max 1"
That's the bottom line. I figured the looping in the firmware required to do the division would take longer than the linear Min-Subtract-Max sequence.

It does. But in practice the gain is not as much as I had expected. Having to decode just two tokens for the division and six for the Min-Subtract-Max evens things out.

The gain is just a few tens of microseconds which is about 1% for the calculations themselves, even less when considering a program in its entirety.

And, at the end of the day, it turns out the If-Then version is far quicker than both though the timing is much less consistent, is value dependent.
Code:
#Picaxe 20X2

MainLoop:
  Do                      ; Division - Min-Max = Difference    If-Then
    b2 = $00 : Gosub Test ; 2627       2597      30 us         1272
    b2 = $0F : Gosub Test ; 2625       2600      25 us         1641
    b2 = $F0 : Gosub Test ; 2625       2599      26 us         1642
    b2 = $FF : Gosub Test ; 2622       2602      20 us         2000
    Pause 10
  Loop
Code:
Test: ; Division
  w0 = b2 * 16 | b2 & $0F0F + $3030
  High C.0
  b1 = b1 / $3A * 7 + b1
  b0 = b0 / $3A * 7 + b0
  Low C.0
  Return
Code:
Test: ; Min-Subtract-Max
  w0 = b2 * 16 | b2 & $0F0F + $3030
  High C.0
  b1 = b1 Min "9" - "9" Max 1 * 7 + b1
  b0 = b0 Min "9" - "9" Max 1 * 7 + b0
  Low C.0
  Return
Code:
Test: ; If-Then
  w0 = b2 * 16 | b2 & $0F0F + $3030
  High C.0
  If b1 > "9" Then : b1 = b1 + 7 : End If
  If b0 > "9" Then : b0 = b0 + 7 : End If
  Low C.0
  Return
 
Top