Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 09:11 01 Aug 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : MMBasic: New in-built functions

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10310
Posted: 10:58am 05 May 2019
Copy link to clipboard 
Print this post

Working with Geoff, and coming soon to all versions of MMBasic, two useful new functions have been created. These make it easy to save and load binary data from file and convert data to and from sensors like the BMP180 family. For File I/O the size of data saved can be easily tuned to match the requirement ensuring file sizes are kept to a minimum. The functions also make it easy to work with things like flash memory chips more efficiently than with ascii data.







Test program attached

'
'write out to file numbers in the range 0 to 65535 using 2 bytes per number
'
open "bin.tst" for output as #1
for j%=0 to 65535
print #1,bin2str$(uint16,j%);
next j%
close #1
'Read the file and check data accuracy
open "bin.tst" for input as #1
for j%=0 to 65535
k%=str2bin(uint16,input$(2,1))
if j%<>k% then print "error k = ", k%," j = ",j%
next j%
close #1
'
' Demo all modes of conversion
'
c%=123
a$=Bin2str$(int8,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print ""
b%=Str2bin(int8,a$)
If b%<>c% Then Print "error in int8"
c%=123
a$=Bin2str$(int8,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(int8,a$,big)
If b%<>c% Then Print "error in int8, big"
c%=223
a$=Bin2str$(uint8,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint8,a$)
If b%<>c% Then Print "error in uint8"
c%=223
a$=Bin2str$(uint8,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint8,a$,big)
If b%<>c% Then Print "error in uint8, big"
'
c%=11123
a$=Bin2str$(int16,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print ""
b%=Str2bin(int16,a$)
If b%<>c% Then Print "error in int16"
c%=11123
a$=Bin2str$(int16,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(int16,a$,big)
If b%<>c% Then Print "error in int16, big"
c%=51223
a$=Bin2str$(uint16,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint16,a$)
If b%<>c% Then Print "error in uint16"
c%=51223
a$=Bin2str$(uint16,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint16,a$,big)
If b%<>c% Then Print "error in uint16, big"
'
c%=2147483600
a$=Bin2str$(int32,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print ""
b%=Str2bin(int32,a$)
If b%<>c% Then Print "error in int32"
c%=2147483600
a$=Bin2str$(int32,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(int32,a$,big)
If b%<>c% Then Print "error in int32, big"
c%=3147483600
a$=Bin2str$(uint32,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint32,a$)
If b%<>c% Then Print "error in uint32"
c%=3147483600
a$=Bin2str$(uint32,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint32,a$,big)
If b%<>c% Then Print "error in uint32, big"
'
c%=9223372036854775800
a$=Bin2str$(int64,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print ""
b%=Str2bin(int64,a$)
If b%<>c% Then Print "error in int64"
c%=9223372036854775800
a$=Bin2str$(int64,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(int64,a$,big)
c%=&HEEDDCCBBAA998877
a$=Bin2str$(uint64,c%)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint64,a$)
If b%<>c% Then Print "error in uint64"
c%=&HEEDDCCBBAA998877
a$=Bin2str$(uint64,c%,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
b%=Str2bin(uint64,a$,big)
If b%<>c% Then Print "error in uint64, big"
'
f=pi
a$=Bin2str$(double,f)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
g=Str2bin(double,a$)
If f<>g Then Print "error in double"
f=pi
a$=Bin2str$(double,f,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
g=Str2bin(double,a$,big)
If f<>g Then Print "error in double, big"
f=32768.5 'should be exact in single precision
a$=Bin2str$(single,f)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
g=Str2bin(single,a$)
If f<>g Then Print "error in single"
f=32768.5 'should be exact in single precision
a$=Bin2str$(single,f,big)
Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print ""
g=Str2bin(single,a$,big)
If f<>g Then Print "error in single, big"
'
' BMP180 example using STR2BIN
'
OPTION EXPLICIT
OPTION DEFAULT FLOAT
const i2caddr=&b1110111
const MS7=7 'set default wait period
const signed=1
const unsigned=0
'
dim i2cin$ length 8 'max size for integer conversion
dim UT%,UP%
dim ac1%,ac2%,ac3,ac4%,ac5%,ac6%,b1%,b2%,mb%,mc%,md% 'bmp180 parameters
dim x1%,x2%,b5%,b6%,x3,b3%,b4%,b7%,OSS%
dim temperature%,pressure%
DIM altitude,QNH,pressureinHpa
dim OSSdata%(4)
dim OSSscale%(4)
on error skip 1
I2C OPEN 400,1000
init:
OSS%=1 'set oversampling ratio
' OSS%=0 ' Uncomment this line to check algorithm against datasheet
OSSdata%(0)=&H34 'commands to sample pressure% with different levels of oversampling
OSSdata%(1)=&H74
OSSdata%(2)=&Hb4
OSSdata%(3)=&HF4
OSSscale%(0)=1 'scale factors for calcs when oversampled
OSSscale%(1)=2
OSSscale%(2)=4
OSSscale%(3)=8
'
I2C WRITE i2caddr,1,1,&HAA 'send read calibration data command
I2C READ i2caddr,0,22,i2cin$ 'read in calibration data
ac1%=str2bin(int16,mid$(i2cin$,1,2),big)
ac2%=str2bin(int16,mid$(i2cin$,3,2),big)
ac3=str2bin(int16,mid$(i2cin$,5,2),big)
ac4%=str2bin(uint16,mid$(i2cin$,7,2),big)
ac5%=str2bin(uint16,mid$(i2cin$,9,2),big)
ac6%=str2bin(uint16,mid$(i2cin$,11,2),big)
b1%=str2bin(int16,mid$(i2cin$,13,2),big)
b2%=str2bin(int16,mid$(i2cin$,15,2),big)
mb%=str2bin(int16,mid$(i2cin$,17,2),big)
mc%=str2bin(int16,mid$(i2cin$,19,2),big)
md%=str2bin(int16,right$(i2cin$,2),big)
'
' Uncomment this block to check algorithm against datasheet
'
' ac1%=408
' ac2%=-72
' AC3=-14383
' ac4%=32741
' ac5%=32757
' ac6%=23153
' b1%=6190
' b2%=4
' mb%=-32768
' mc%=-8711
' md%=2868
main:
I2C WRITE i2caddr,0,2,&HF4,&H2E 'send temp conversion
pause MS7 'wait for temperature% conversion
I2C WRITE i2caddr,1,1,&HF6 'send read data
I2C READ i2caddr,0,2,i2cin$ 'read 2 bytes
UT%=str2bin(uint16,i2cin$,big)
' UT%=27898 ' Uncomment this line to check algorithm against datasheet
I2C WRITE i2caddr,0,2,&HF4,ossdata%(oss%) 'send pressure% conversion
pause (oss%+1)*ms7 'wait for the p ressure% conversion
I2C WRITE i2caddr,1,1,&HF6 'send read data
I2C READ i2caddr,0,3,i2cin$ 'read 3 bytes
i2cin$=chr$(0)+i2cin$ 'convert to big endian unsigned 4 byte integer
UP%=str2bin(uint32,i2cin$,big)
UP%=UP%>>(8-oss%) 'scale the oUT%pUT% by the numb%er of unused bits in the xlsb byte
' UP%=23843' Uncomment this line to check algorithm against datasheet
calc_temp()
calc_pressure()
pressureinHpa=pressure%/100
print "Temperature = ",str$(temperature%/10,4,1)," Deg C"
print "Local pressure = ",str$(pressure%/100,4,1)," Hectopascal/mb"
end
'
' calc_temperature%: calculate the temperature% from the raw temperature% given the calibration parameters
'
sub calc_temp:
x1%=(UT%-ac6%)*ac5%\powerof2(15)
x2%=mc%*powerof2(11)/(x1%+md%) 'This needs to be a floating divide to match the datasheet
b5%=x1%+x2%
temperature%=(b5%+8)\powerof2(4)
end sub
'
' calc_pressure: calculate the pressure% from the raw pressure% given the calibration parameters and temperature% oUT%pUT%
'
sub calc_pressure
b6%=b5%-4000
x1%=(b2%*(b6%*b6%/powerof2(12)))\powerof2(11)
x2%=ac2%*b6%\powerof2(11)
x3=x1%+x2%
b3%=(((ac1%*4+x3)*ossscale%(oss%))+2)\4
x1%=AC3*b6%\POWEROF2(13)
x2%=(b1%*(b6%*b6%/POWEROF2(12)))\POWEROF2(16)
x3=((x1%+x2%)+2)\4
b4%=ac4%*(abs(x3+32768))\powerof2(15)
b7%=abs(UP%-b3%)*(50000\ossscale%(oss%))
pressure%=(b7%*2)\b4%
x1%=(pressure%\powerof2(8))*(pressure%\powerof2(8))
x1%=(x1%*3038)\powerof2(16)
x2%=(-7357*pressure%)\powerof2(16)
pressure%=pressure%+(x1%+x2%+3791)\powerof2(4)
end sub
'
Function powerof2(i as integer) as integer
powerof2=(1<<i)
End Function



Edited by matherp 2019-05-06
 
CircuitGizmos

Guru

Joined: 08/09/2011
Location: United States
Posts: 1427
Posted: 02:39pm 05 May 2019
Copy link to clipboard 
Print this post

That is fantastic and very timely!

Micromites and Maximites! - Beginning Maximite
 
seco61
Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 205
Posted: 10:09pm 05 May 2019
Copy link to clipboard 
Print this post

Hi Peter.

Just curious - the current output of the "list commands" and "list functions" on the v5.05.06 H7 code shows 127 of each. I thought the current limit was 128 functions & commands - how do you add 2 extra functions?

Mind you, I am very much looking forward to these new functions...

Regards

Gerard

Regards

Gerard (vk3cg/vk3grs)
 
seco61
Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 205
Posted: 10:14pm 05 May 2019
Copy link to clipboard 
Print this post

Or are the operators treated separately?

Regards

Gerard (vk3cg/vk3grs)
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3292
Posted: 02:21am 06 May 2019
Copy link to clipboard 
Print this post

Yes, commands and functions+operators have separate token tables with each capable of holding 127 keywords/operators. When MMBasic loads a program it will substitute the appropriate token for each keyword so that the program executes much faster.

In the early days a provision for 254 tokens was more than generous, if I remember correctly only 100 were needed for the first version. But as time marched on more features were added and the tables are now close to full. Peter's ports with even more features has filled the tables.

There are a number of solutions but they all require an extensive rewrite of the interpreter's core which in turn will inevitably create a hoard of new bugs. Because of my aversion to bugs and desire to keep MMBasic stable I have held this day off by being careful when adding new keywords, reserving them for only the most deserving of features.

GeoffEdited by Geoffg 2019-05-07
Geoff Graham - http://geoffg.net
 
vegipete

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1132
Posted: 09:13pm 06 May 2019
Copy link to clipboard 
Print this post

Yeah, no one will ever need more than 640K of RAM.

Could you assign the last token as a sort of shift key or ESC code, indicating a 2 byte token? (Or 2 of them, one for keywords, the other for operators.) A bit like ANSI terminal escape codes.
Visit Vegipete's *Mite Library for cool programs.
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2442
Posted: 01:56am 07 May 2019
Copy link to clipboard 
Print this post

i presume that LIST COMMANDS and LIST FUNCTIONS is unlikely to make it into the MX170 due to space constraints. however, what would be really neat would be if there were a couple of pointers to the tables so one could easily write a short basic program to retrieve the text data. for instance MM.CMDTABLE and MM.FUNTABLE.


cheers,
rob :-)Edited by robert.rozee 2019-05-08
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3292
Posted: 03:48am 07 May 2019
Copy link to clipboard 
Print this post

  vegipete said  Could you assign the last token as a sort of shift key or ESC code, indicating a 2 byte token?

Yes, that is the best solution but it will require major changes to the core of the interpreter with the associated issues.

  robert.rozee said  if there were a couple of pointers to the tables one could easily write a short basic program to retrieve the text data. for instance MM.CMDTABLE and MM.FUNTABLE

Entirely possible but MM.CMDTABLE + MM.FUNTABLE would take another two tokens. A better solution would be to extend the PEEK() function. My only problem is wondering why would you want to do this? The documentation lists all the commands/functions/operators in much better detail.


Geoff Graham - http://geoffg.net
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2442
Posted: 02:07pm 07 May 2019
Copy link to clipboard 
Print this post

  Geoffg said  Entirely possible but MM.CMDTABLE + MM.FUNTABLE would take another two tokens. A better solution would be to extend the PEEK() function. My only problem is wondering why would you want to do this? The documentation lists all the commands/functions/operators in much better detail.


it is definitely not worth using up another two tokens for, or any significant flash space, as very few people would use the feature. it would just be nice to be able to generate command and function lists directly from the loaded firmware to use in creating an index.

remember ages ago i was tinkering with something similar, that searched through the mmbasic source code and generated a similar list? this was part of an idea to automatically create a documentation database - a project that i've had on the back-burner for quite a while!

i take it extending PEEK would not entail using more tokens?

+1 for vegipete's idea of introducing a shift token


cheers,
rob :-)Edited by robert.rozee 2019-05-09
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2442
Posted: 03:37pm 07 May 2019
Copy link to clipboard 
Print this post

just a thought: introducing one or more shift tokens would allow merging commands and functions into a single token space, ie there would be no overlap of token values between commands and functions. this may simplify the interpreter.

the simplest way of handling the shifting may be to convert tokens immediately (when interpreting) into 16-bit unsigned integers. that is:

0x80 to 0xEF = 1-byte tokens
0xF0 to 0xFE = shift tokens

decode using:
if token < 0xF0 then
token16 = token
else
shift = token
token = get_next_token()
token16 = token + ((shift - 0xEF) * 0x70)


this gives you a little under 1800 tokens to play with, ranging from 0x0080 to 0x077F.


cheers,
rob :-)
Edited by robert.rozee 2019-05-09
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025