Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 04:41 10 Jan 2026 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 RP2350 V6.01.00EXP with user-defined structures

     Page 3 of 7    
Author Message
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9819
Posted: 02:05am 30 Dec 2025
Copy link to clipboard 
Print this post

My guess would seem to boil down to the statement: "It's easy, once you know how."    

To me, structures seem like a good way to manage database-type programs, and I do use/write those kinds of programs, where data is(normally) just stored as files on the SD card, or as a whole bunch of integers and strings.

Sounds like the old Windows cardfile program: (circa Windoze 3.11 and Windoze 95)


TYPE CARD
 NAME as String
 PHONE as Integer
 ADDRESS as String
 EMAIL as String
END TYPE

...

DIM CARDFILE(500) As CARD 'Define a new "Cardfile" variable, with up to 500 "Cards" in it


...then the ONE SINGLE variable can hold ALL FOUR different data types in each element of the variable, yes? PRINT CARDFILE(15) would print the 15th card on the screen, consisting of all four variables for that "Card", yes?

...probably "No."
It takes me a while to cotton onto some things!  

I am just using the old cardfile concept here - simple database for holding various bits of information, and each group of information is a "Card" in the cardfile.  I'm using that reference, as it is familiar to me, as I used to use old cardfile a-lot back in the day!

I think I understand that aspect now, I just need to re-read and understand how the IF/THEN searching thing works a bit better.
Smoke makes things work. When the smoke gets out, it stops!
 
toml_12953
Guru

Joined: 13/02/2015
Location: United States
Posts: 540
Posted: 04:15am 30 Dec 2025
Copy link to clipboard 
Print this post

  JohnS said  
  toml_12953 said  
  JohnS said  The Zeno way isn't really helping understanding or maintenance much, as the code needs to know the r & c (in your example) are arrays.  You may as well just declare them as such and if anything make the code clearer.

John


The example I gave was greatly simplified to show just the essence of the problem.

A better example would help.  As it is, what you showed isn't the kind of thing structures are for.

John


You're missing the point. Don't analyze the code. It's just to show a capability in most other languages (C, Pascal, Lua, JavaScript and Zeno to name a few) that's missing from MMBasic.
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 4191
Posted: 07:58am 30 Dec 2025
Copy link to clipboard 
Print this post

  toml_12953 said  
  JohnS said  
  toml_12953 said  
  JohnS said  The Zeno way isn't really helping understanding or maintenance much, as the code needs to know the r & c (in your example) are arrays.  You may as well just declare them as such and if anything make the code clearer.

John


The example I gave was greatly simplified to show just the essence of the problem.

A better example would help.  As it is, what you showed isn't the kind of thing structures are for.

John


You're missing the point. Don't analyze the code. It's just to show a capability in most other languages (C, Pascal, Lua, JavaScript and Zeno to name a few) that's missing from MMBasic.

It's not especially useful (*) and it's not a structure.

The equivalent of your example in MMBasic is just to declare two arrays.

I'm sure Peter could special-case your example, which is really just type renaming not a structure, at the cost of some internal complication.

(*) except in C where you can somewhat tame a horrible type involving pointers, particularly pointers to functions where the C syntax is to be kind a bit challenging - and this is another thing that MMBasic can't do (and likely shouldn't)

John
Edited 2025-12-30 18:09 by JohnS
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 8430
Posted: 08:32am 30 Dec 2025
Copy link to clipboard 
Print this post

Structs looks like a lovely way to handle maps in adventure-type games. Location/room based adventures are basically databases anyway.

Stan:
The great thing about MMBasic is that it has incredible flexibility just in case you want to use it. I'm very unlikely to use the astronomical stuff, regular expressions, VGA222, MOD files, GPS connection, PIO programming or turtle graphics even though Peter has put in a hell of a lot of work on them. But, whether I use them or not is immaterial - they are there just in case I do need them at some unknown point in the future. They are also there should I wish to run someone else's software that does use them.  :)  They are there but invisible and that's not a problem at all. I can even ignore SUBs and FUNCTIONs and program using GOTO and GOSUB and the LET command if I'm feeling masochistic. :)  I would suggest that if you are concerned about any slight performance hit that you don't use USB and go back to PS2 in addition to using PICO 2 (RP2350A or a board using RP2350B without PSRAM) instead of the original PICO.
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10838
Posted: 08:34am 30 Dec 2025
Copy link to clipboard 
Print this post

  Quote  So you could probably get by with uint32_t alignment when inserting padding bytes, which would save memory in RAM and the file! Unless you want uint64_t alignment in general, so that MMBasic is "future-proof" for 64-bit systems with mandatory alignment?

uint64_t alignment is necessary to also support the CMM2 which has 64-bit load and store instructions and 64-bit floating point. Therefore the code should always use full 64-bit alignment even though it isn't necessary for the M0+ processor in the RP2040/RP2350
 
flip
Senior Member

Joined: 18/07/2016
Location: Australia
Posts: 119
Posted: 08:54am 30 Dec 2025
Copy link to clipboard 
Print this post

Hi Peter, all
Structs are a great inclusion - I'm having great fun with it on the new HDMIUSB from November Silicon Chip.

It seems though it only works with Files opened for INPUT or OUTPUT.
I can't use with a RANDOM access file, so can't seek to a position to read a specific record.

Is there something I've missed?

Regards Phil

PS. This is options, program and the Commands entered to test
'LIST OPTIONS
OPTION PICO OFF
OPTION RESOLUTION 1280x720 @ 372000KHz
OPTION DISPLAY 60, 160
OPTION HDMI PINS  1, 3, 5, 7
OPTION SDCARD GP29, GP30, GP31, GP32
OPTION KEYBOARD REPEAT 400,50
OPTION AUDIO I2S GP10,GP22', ON PWM CHANNEL 11
OPTION RTC AUTO ENABLE
OPTION COUNT GP0,GP1,GP2,GP3
OPTION MODBUFF ENABLE  512
OPTION PLATFORM HDMIUSBI2S
OPTION HEARTBEAT PIN GP25
> List
Type tBox
 bLeft As integer
 bTop As Integer
 bWidth As Integer
 bHeight As Integer
 bFore As Integer
 bBack As Integer
 bHiFore As Integer
 bHiBack As Integer
 bHeading As String
 bFont As integer
End Type
Dim Boxes(255) As tBox

Sub DrawBox(bID As Integer)
 Box Boxes(bID).bLeft*MM.FONTWIDTH-2,Boxes(bID).bTop*MM.FONTHEIGHT-2,Boxes(bID).bWidth*MM.FONTWIDTH+4,Boxes(bID).bHeight*MM.FONTHEIGHT+4,,1,0
 Text Boxes(bID).bLeft*MM.FONTWIDTH,Boxes(bID).bTop*MM.FONTHEIGHT,Boxes(bID).bHeading
 Text Boxes(bID).bLeft*MM.FONTWIDTH,(Boxes(bID).bTop+1)*MM.FONTHEIGHT,"L"+Str$(Boxes(bID).bLeft)+",T"+Str$(Boxes(bID).bTop)
 Text Boxes(bID).bLeft*MM.FONTWIDTH,(Boxes(bID).bTop+2)*MM.FONTHEIGHT,"W"+Str$(Boxes(bID).bWidth)+",H"+Str$(Boxes(bID).bHeight)
 TILE Boxes(bID).bLeft,Boxes(bID).bTop,RGB(green),RGB(magenta),Boxes(bID).bWidth*MM.FONTWIDTH\8,1
 TILE Boxes(bID).bLeft,Boxes(bID).bTop+1,RGB(yellow),RGB(blue),Boxes(bID).bWidth*MM.FONTWIDTH\8,Boxes(bID).bHeight-1
End Sub

Sub DRBs(n%) 'Draw Random Boxes
 If MM.FONTHEIGHT<> MM.Info(TILE HEIGHT) Then
   If MM.FONTHEIGHT>7 Then TILE HEIGHT MM.FONTHEIGHT Else TILE HEIGHT 8
 EndIf
 If n%<1 Then n%=1
 If n%>255 Then n%=255
 For i=0 To n%
   Boxes(i).bTop=Rnd*(MM.HEIGHT-5)
   Boxes(i).bHeight=2+Rnd*Rnd*(MM.HEIGHT-Boxes(i).bTop-4)
   Boxes(i).bLeft=Rnd*(MM.WIDTH-10)
   Boxes(i).bWidth=4+Rnd*Rnd*(MM.WIDTH-Boxes(i).bLeft-6)
   Boxes(i).bHeading="Box "+Str$(i)
   DrawBox i
 Next i
End Sub

'File save method for Screen Definitions
RUN
DRBs 255 ' draw 255 random boxes
B:
CHDIR "Screens"
Open "st.bas" for output as 1
Struct Save #1, Boxes()
Close #1

' Display method for previously SAVED screens (Boxes())
B:
CHDIR "Screens"
RUN ' to DIM the Boxes() array
Tile height 12
timer=0:open "st.bas" for INPUT as 1:struct load #1,Boxes():for i=0 to 255:DrawBox i:next i:close 1:? timer
'272.81 to restore array from B:and draw screens

(Changing the last line from 'INPUT' to 'RANDOM' causes a 'nothing' array to be loaded)
Edited 2025-12-30 19:05 by flip
 
flip
Senior Member

Joined: 18/07/2016
Location: Australia
Posts: 119
Posted: 09:18am 30 Dec 2025
Copy link to clipboard 
Print this post

Actually RANDOM is OK - It looks like I forgot to do a seek first
It works great for the whole array.

I am still having some difficulty trying to get individual records from a complete save.
The bytes are exactly correct (so I would guess no padding characters are embedded - so I expect it's raw data)

Will have a bit more of a play to see if i can work out my problem!

Regards Phil
ps again: forgot to add the error:
> B:
> CHDIR "/Screens"
> RUN ' to DIM the Boxes() array
> Tile height 12
> timer=0:open "st.bas" for RANDOM as 1:Seek 1,1:struct load #1,Boxes(10):DrawBox 10:close 1:? timer
[16] Box Boxes(bID).bLeft*MM.FONTWIDTH-2,Boxes(bID).bTop*MM.FONTHEIGHT-2,Boxes(bID).bWidth*MM.FONTWIDTH+4,Boxes(bID).bHeight*MM.FONTHEIGHT+4,,1,0
Error : Unknown structure member
>

Edited 2025-12-30 19:46 by flip
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10838
Posted: 09:59am 30 Dec 2025
Copy link to clipboard 
Print this post

V6.01.00EXP3

PicoMiteV6.01.00EXP3.zip

MMBasic_Structures_Manual.pdf


Fixes bug in arrays of structures where the underlying type is not a multiple of 8 byte long
Allows full line comments (REM or ') in TYPE/END TYPE blocs
Fixes bugs in OPTION RESET for a couple of board types
Fixes bug in RAM command usage where a reset would clear the RAM slot
Adds validation to FLASH and RAM commands to stop a lockup when the LOAD subcommand was used on an empty slot
Fixes incorrect naming of VGA222_640 in LIST OPTIONS
Fixes incorrect naming of BLUE_H in LIST PINS
Confirmed STRUCT LOAD/SAVE work with random access files

Volhout, I would really appreciate it if you could give the RP2040 versions a good test with your various programs. Assuming that is all OK I will probably go straight to a release candidate for 6.02.00 with this latest version.

Additional test for test scripts
' ============================================
' TEST 75: Random access file with struct array (write and read in reverse)
' Also tests alignment of array elements where the type definition is not a
' multiple of 8 bytes
' tests comments in type definitions
' ============================================
Print "TEST 75: Random access file with struct array"

' Define a struct type with integer and string for this test
Type FileRecord
 id As INTEGER 'comment
' another comment
 label As STRING length 20
rem yet another comment
End Type

' Create a struct array with test data
Dim fileArr75(4) As FileRecord
For i% = 0 To 4
 fileArr75(i%).id = (i% + 1) * 100
 fileArr75(i%).label = "Record" + Str$((i% + 1) * 100)
Next i%

' Write each struct element to file sequentially
Open "structtest.dat" For random As #1
For i% = 0 To 4
 Struct Save #1, fileArr75(i%)
Next i%
Close #1

' Calculate struct size for seeking (INTEGER=8 bytes, STRING=256 bytes = 264 bytes per record)
Dim structSize75% = Struct(sizeof,"FileRecord")

' Read back in reverse order using SEEK
Dim readArr75(4) As FileRecord
Open "structtest.dat" For Random As #1
For i% = 4 To 0 Step -1
 ' Seek to byte position (1-based, so first record at position 1)
 Seek #1, i% * structSize75% + 1
 Struct Load #1, readArr75(4 - i%)
Next i%
Close #1

' Verify the data was read correctly in reverse
ok% = 1
Dim expectedId%, expectedLabel$,labelNum%
For i% = 0 To 4
 ' readArr75(0) should have fileArr75(4) data, readArr75(1) should have fileArr75(3), etc.
 expectedId% = (5 - i%) * 100
 expectedLabel$ = "Record" + Str$(expectedId%)
 If readArr75(i%).id <> expectedId% Then
   Print "    Index"; i%; ": Expected id"; expectedId%; " got"; readArr75(i%).id
   ok% = 0
 EndIf
 If readArr75(i%).label <> expectedLabel$ Then
   Print "    Index"; i%; ": Expected label '"; expectedLabel$; "' got '"; readArr75(i%).label; "'"
   ok% = 0
 EndIf
 ' Also verify using VAL() that the embedded number is correct
 labelNum% = Val(Mid$(readArr75(i%).label, 7))
 If labelNum% <> expectedId% Then
   Print "    Index"; i%; ": VAL extraction failed, expected"; expectedId%; " got"; labelNum%
   ok% = 0
 EndIf
Next i%

If ok% Then
 Print "  PASS: Random access file write and reverse read with strings"
Else
 Print "  FAIL: Random access file reverse read failed"
EndIf

' Clean up the test file
Kill "structtest.dat"

Edited 2025-12-30 20:06 by matherp
 
bfwolf
Senior Member

Joined: 03/01/2025
Location: Germany
Posts: 131
Posted: 11:30am 30 Dec 2025
Copy link to clipboard 
Print this post

  matherp said  V6.01.00EXP3

PicoMiteV6.01.00EXP3.zip

MMBasic_Structures_Manual.pdf


Fixes bug in arrays of structures where the underlying type is not a multiple of 8 byte long
Allows full line comments (REM or ') in TYPE/END TYPE blocs
Fixes bugs in OPTION RESET for a couple of board types
Fixes bug in RAM command usage where a reset would clear the RAM slot
Adds validation to FLASH and RAM commands to stop a lockup when the LOAD subcommand was used on an empty slot
Fixes incorrect naming of VGA222_640 in LIST OPTIONS
Fixes incorrect naming of BLUE_H in LIST PINS
Confirmed STRUCT LOAD/SAVE work with random access files

...


 

Perhaps you could add some "best practice" recommendations regarding alignment to the Struct Manual?

For example, that one should avoid creating padding bytes without defined content when storing structs as "file records" in files.

This can be achieved by ensuring alignment when arranging the members, or by always making string members ((n*8)-1) incremental.

And if this is undesirable, one can explicitly insert "padding string members" that provide (n*8) alignment and then explicitly initialize them with 0. This prevents "random hidden members" and "nasty surprises" with the contents of the "file records".

I always use this method (explicitly initialized padding members) in my C programs when structs are stored "permanently" somewhere.

Of course, for structs that are "volatile" — that is, they only exist in RAM — this is completely irrelevant!

---

Did you see my post:
https://www.thebackshed.com/forum/ViewTopic.php?TID=18519&P=2#248815

This is about the following:

I would consider renaming the function
'Struct(SIZE, array() [, dimension])' to
'Struct(BOUND, array() [, dimension])',
since the function 'BOUND(array() [, dimension])' already exists for "ordinary arrays". This would simply be more consistent, and it's "awkward" to have to remember two different words for the same thing.

Sure, it's "more of a cosmetic issue" — but still not unimportant when developing a programming language.

And regarding my idea with 'Struct(OFFSETOF, "typeName,memberName")': If you're up for the work...

Many thanks again !!!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10838
Posted: 12:03pm 30 Dec 2025
Copy link to clipboard 
Print this post

  Quote  since the function 'BOUND(array() [, dimension])' already exists for "ordinary arrays". This would simply be more consistent, and it's "awkward" to have to remember two different words for the same thing.

Bound is tokenised so it doesn't work
 
bfwolf
Senior Member

Joined: 03/01/2025
Location: Germany
Posts: 131
Posted: 01:04pm 30 Dec 2025
Copy link to clipboard 
Print this post

  matherp said  
  Quote  since the function 'BOUND(array() [, dimension])' already exists for "ordinary arrays". This would simply be more consistent, and it's "awkward" to have to remember two different words for the same thing.

Bound is tokenised so it doesn't work


Ah, okay, I didn't realize that conflicts could arise.

So, names for which tokens for commands and functions already exist are therefore "off-limits" for "sub-function" names?

How about 'SBOUND' (for Struct-Bound) or 'TBOUND' (for Type-Bound) or something that expresses it gives the "array of structs' bound") ? Just a thought... In any case, it would be nice to have a name that's "similar to BOUND".

Edit:
Just tried 'print bound(people(),1)' in command mode after my little test program was run before: It works !!!  

So it seems that the long-implemented 'BOUND()' function also works with arrays of structs! This could make Struct(SIZE typeName) obsolete !

Greetings..
Edited 2025-12-30 23:13 by bfwolf
 
stanleyella

Guru

Joined: 25/06/2022
Location: United Kingdom
Posts: 2715
Posted: 06:06pm 30 Dec 2025
Copy link to clipboard 
Print this post

first thank you to Peter for his work improving mmbasic.
it's my fault if I don't understand the changes.
is structured basic for large programs? I can't see the point and I think it makes code less easy to follow imho.
it's said you don't have to use it but if it's a better way will all mmbasic now be structured?
 
toml_12953
Guru

Joined: 13/02/2015
Location: United States
Posts: 540
Posted: 06:59pm 30 Dec 2025
Copy link to clipboard 
Print this post

  stanleyella said  first thank you to Peter for his work improving mmbasic.
it's my fault if I don't understand the changes.
is structured basic for large programs? I can't see the point and I think it makes code less easy to follow imho.
it's said you don't have to use it but if it's a better way will all mmbasic now be structured?


Structures don't necessarily make a program structured (ha!) They just allow you to work with records more easily. Here's an example:

Using a structure for coordinates:
TYPE point
 x as float
 y as float
END TYPE

DIM PointArray(100) as point

' Now access the x and y components of the 5th element:
PRINT PointArray(5).x
PRINT PointArray(5).y



Same result without structures:
DIM PointArray(100,2)
CONST x=1
CONST y=2

' Now access the x and y components of the 5th element:
PRINT PointArray(5,x)
PRINT PointArray(5,y)


Accessing different types of data isn't too hard, you just need a separate array for each datatype and keep them synchronized.
Edited 2025-12-31 05:03 by toml_12953
 
stanleyella

Guru

Joined: 25/06/2022
Location: United Kingdom
Posts: 2715
Posted: 08:25pm 30 Dec 2025
Copy link to clipboard 
Print this post

thanks toml_12953 but I still don't get it. your code says what's the point.it don't make sense like I used mmb before

Same result without structures:
DIM PointArray(100,2)
CONST x=1
CONST y=2

' Now access the x and y components of the 5th element:
PRINT PointArray(5,x)
PRINT PointArray(5,y)
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1716
Posted: 08:31pm 30 Dec 2025
Copy link to clipboard 
Print this post

  stanleyella said  thanks toml_12953 but I still don't get it. your code says what's the point.it don't make sense like I used mmb before
...

The point is the self-documentation of the code.
Regards
Michael
causality ≠ correlation ≠ coincidence
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 593
Posted: 08:43pm 30 Dec 2025
Copy link to clipboard 
Print this post

  stanleyella said  is structured basic for large programs? I can't see the point and I think it makes code less easy to follow imho.
it's said you don't have to use it but if it's a better way will all mmbasic now be structured?


I think the name might be causing a bit of confusion. MMBasic is already what I understand as being "Structured Basic". That is, it doesn't need line numbers, it has WHILE, DO loops etc. and functions/subroutines.

The introduction of user-defined structures isn't actually a part of "structured basic". They just happen to share the same name (from my viewpoint). There were lots of structured basics back in the day that didn't have user-defined structures.

Later languages had the ability to use User-Defined Structures, or Records, or User-Defined Types (all the same thing).

While I am strongly against changing MMBasic into a high language (I love using BASIC) - this is one of those circumstances where the feature is complimentary to the existing BASIC and is going to greatly simplify code and make it far more readable.

I know there appears to be a bit of confusion around it currently - but I think you can see from the enthusiastic response from some of us, how massive this is going to be for us.

I know we've been concentrating on arrays of structures (which is great), but they are also very useful for single elements.

Say I am controlling a fuel valve with the following parameters
Type FuelValve
  demand As FLOAT       ' The % open value we are trying to set the valve to
  highAlarm As FLOAT    ' The % opening of the valve that we should never exceed
  lowAlarm AS FLOAT     ' The % opening of the valve that we should never drop below
  feedback AS FLOAT     ' The % open value read back from the valve
  alarmIsActive AS INTEGER   ' If 0 then healthy. if 1 then alarmed
End Type


I can define a single variable as type FuelValve


 Dim valve1 As FuelValve


Now when I want to pass all of that data into a subroutine I just need to pass valve1, and all of the variables/values get passed.

Similarly, if I want to use the the valve1.alarmIsActive integer to check if the valve has exceeded an alarm level, then I can easily set it with something like this:


Function SetAlarmFlag(passedInValve AS FuelValve) As FuelValve
 ' First - set the return variable (SetAlarmFlag) to equal the values in the valve variable passed in
 Struct Copy passedInValve To SetAlarmFlag

 ' Now clear the .alarmIsActive flag (in case it's already set)
 SetAlarmFlag.alarmIsActive = 0

 ' Now see if we need to set the flag
 IF passedInValve.demand > valve.highAlarm THEN SetAlarmFlag.alarmIsActive = 1  
 IF passedInValve.feedback > valve.highAlarm THEN SetAlarmFlag.alarmIsActive = 1
 IF passedInValve.demand < valve.lowAlarm THEN SetAlarmFlag.alarmIsActive = 1
 IF passedInValve.feedback < valve.lowAlarm THEN SetAlarmFlag.alarmIsActive = 1
End Function

If you look at this function, you pass in a FuelValve variable, and it returns FuelValve variable. This can be used to easily update your FuelValve (in this case the .alarmIsActive flag is set to 1 if the demand or feedback is greater than the high alarm, or if they are less than the low alarm).

In your code, you simply need to call this function, and then check the .alarmIsActive flag.


valve1 = SetAlarmFlag(valve1)

IF valve1.alarmIsActive = 1 THEN PRINT "ALARM! We're all going to die!"

Edited 2025-12-31 06:55 by PeteCotton
 
bfwolf
Senior Member

Joined: 03/01/2025
Location: Germany
Posts: 131
Posted: 08:56pm 30 Dec 2025
Copy link to clipboard 
Print this post

@PeteCotton: You were faster than me.. I try to explain it this way:

  stanleyella said  first thank you to Peter for his work improving mmbasic.
it's my fault if I don't understand the changes.
is structured basic for large programs? I can't see the point and I think it makes code less easy to follow imho.
it's said you don't have to use it but if it's a better way will all mmbasic now be structured?


Hi Stan, maybe you read this post of mine?:
https://www.thebackshed.com/forum/ViewTopic.php?TID=18519&P=2#248808

You shouldn't be afraid of the additional possibilities that MMBasic will offer with structs in the future!

I think you also have a misunderstanding between structured programming and data structures - they are two different things:

Structured programming is facilitated and supported through commands like
IF <condition>
<commands>
ELSE
<other commands>
END IF


or

SUB subprogram-name [(parameter-list)]
<commands>
END SUB


but not enforced, when commands like

IF <condition> GOTO <label-or-line-number>

or
GOSUB <label-or-line-number>


are still allowed, which has always been the case with MMBasic.

You can even do structured programming with just GOTO and GOSUB, but it's much more difficult because you then quickly produce "spaghetti code" that others don't understand and after a short time you don't understand either.

So you have the choice to program with GOTO and GOSUB, but "it is recommended" to use 'IF .. ELSE .. END IF' or 'SUB .. END SUB', because this makes it easier to write a program that is easier to understand. Getting these commands offered by the interpreter and using them is “Structured BASIC”.

So now to the data structures:
These offer a better opportunity to jointly manage different informations that belong together. That's all!

The example I gave in the link above: You want to program a data logger that records weather data.

Without data structs:


Dim temperature_degC(1000) As integer
Dim humidity_percent(1000) As integer
Dim airpressure_mbar(1000) As integer
Dim unix_time_sec(1000) As integer ' seconds since 1.1.1970

' And now a Sub, that shall print the temperature and the time..
Sub PrintWeatherData(degC As integer, tUnix As integer)
 Print "We had "; degC; "°C at time"; tUnix; " (unix-sec)"
End Sub

' We execute the Sub by:
PrintWeatherData(temperature_degC(1), unix_time_sec(1))


And now with data structs:

Type weatherData
 temperature_degC As integer
 humidity_percent As integer
 airpressure_mbar As integer
 unix_time As integer ' seconds since 1.1.1970
End Type

Dim weatherDataRecordings(1000) As weatherData

' And now a Sub, that shall print the temperature and the time..
Sub PrintWeatherData(d As weatherData)
 Print "We had "; d.temperature_degC; "°C at time"; d.unix_time; " (unix-sec)"
End Sub

' We execute the Sub by:
PrintWeatherData(weatherDataRecordings(1))

' But once later we decide to have a Sub, that shall print the temperature and the pressure and the time..
Sub PrintWeatherData2(d As weatherData)
 Print "We had "; d.temperature_degC; "°C and "; d.airpressure_mbar; "mbar at time"; d.unix_time; " (unix-sec)"
End Sub

' We execute the Sub by:
PrintWeatherData2(weatherDataRecordings(1))



Perhaps you have now seen and understood from this simple and small example how the functionality of the 'PrintWeatherData' sub can be extended without having to change anything in the list of passed parameters, because they are all connected and contained in the 'weatherData' struct type?

Of course, you are still free to program as shown in the 1st example further above, without data structs!

Greetings, bfwolf
Edited 2025-12-31 07:10 by bfwolf
 
LeoNicolas

Guru

Joined: 07/10/2020
Location: Canada
Posts: 555
Posted: 10:16pm 30 Dec 2025
Copy link to clipboard 
Print this post

This new data structure is super useful to organize complex data structures making the code more readable.

A real example from my Knightmare game, taking these global variables:


' Supports 13 shots at the same time: 0: id | 1: x | 2: y | 3: GPR 1 | 4: GPR 2 | 5: GPR 3
' The three first slots are for the player shots, the other ones for enemies
dim g_shots(12,5)
' Object data: 0: obj id | 1: x | 2: y | 3: life | 4: GPR 1 | 5: GPR 2 | 6: shadow
dim g_obj(20,6)


It is required to remember which position of the array stores the value I want to access. If I want to access an object X and Y position:

g_obj(0,1) and g_obj(0,2)

Now imagine that in the middle of hundreds of lines of code.

If a data type structure is much better. It will be something like this:

g_obj(0).X
g_obj(0).y
g_obj(0).life
Etc
 
toml_12953
Guru

Joined: 13/02/2015
Location: United States
Posts: 540
Posted: 10:30pm 30 Dec 2025
Copy link to clipboard 
Print this post

  stanleyella said  I can get my head around "structured" basic ie simple flow with subs, no goto, but not structures. functions never used. I can't see any of this is would make any code I wrote better if I used structures.
I use option explicit and declare var types as.
I'm willing to learn though.


You may never need structures at all. It all depends on the type of programming you do. If you don't deal with records, then structures might not have much impact on you and you can ignore them. Us old COBOL programmers couldn't live without them!

Don't confuse structures with structured programming. Rather think of structures as User Defined Datatypes.

All it is is a way to define different datatypes. It's useful to organize data into records of related information. Suppose you have a business with 500 employees. You want to keep one record for each employee for tax purposes. You could create multiple arrays, each with one piece of data such as name, home address, DOB, SSN, spouse's name, home phone, cell phone, hourly rate, hire date. Do you see how this could create a lot of overhead to keep track of all these things? With structures, we can keep all the data in one record and deal with it all in a few statements.

Type EmpRec
 EmpName as string * 20
 HomeAddress as string * 60
 DOB as string * 10
 SSN as string * 10
 SpouseName as string * 20
 HomePhone as string * 10
 CellNumber as string * 10
 HourlyRate as float
 HireDate as string *10
END TYPE

DIM EmployeeFile(500) as EmpRec


Now, to access all the data for, say, employee # 47, all you have to deal with is EmployeeFile(47), not 9 different arrays.

EmployeeFile(47).HireDate will give you the hire date of employee #47

Why not use integers for things like SSN and phone numbers? It's good programming practice and highly recommended to only define fields used for calculation as numeric. All other data, even if it consists of all digits should defined as character (string) data.

Sure you could pack all the data into a single string and get at individual fields using MID$ but that code would be a lot harder to read.

What field does MID$(EmployeeFile(47),81,10) refer to?

Now, how about EmployeeFile(47).DOB?

They access the same data but which is easier to read?
Edited 2025-12-31 08:43 by toml_12953
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6426
Posted: 05:18am 31 Dec 2025
Copy link to clipboard 
Print this post

This is an example of using structures.
Written as a learning exercise for my own use but may help some.

The same thing can be done using an extra dimension in the arrays but TYPEs are quicker. (One call instead of two)

There are a few places where an intermediate variable is required.
The LINE command is happy with
line n*2,scalep(readings(n)).t,n*2+2,scalep(readings(n+1)).t,1,rgb(yellow)
but LINE PLOT is not happy. You have to use a normal array.

It is easy to extract just the "temperature" from the typed array using MEMORY COPY and that lets you use all the MATH functions.
A command that lets you do the extraction with fewer steps and without the need to know the structure in detail would be nice to have.

You can run the program without a DHT22 connected.

 '
 OPTION EXPLICIT
 OPTION DEFAULT INTEGER
 OPTION BASE 0
 
TYPE room
 t AS FLOAT
 h AS FLOAT
END TYPE
 
 DIM readings(144) AS room
 DIM n

 ' set up dummy data
 FOR n = 1 TO 144
   READ readings(n).t,readings(n).h
 NEXT n
 
 SETTICK 10000, plotroom, 1 'every 10 seconds. *60 for 10 minutes = 1 day
 plotroom
 DO
   ' do other things
 LOOP
 
FUNCTION thisreading(p AS INTEGER) AS room
 ' p = pin number for DHT22
 LOCAL FLOAT tmpr, hmid
 DEVICE HUMID p, tmpr ,hmid
 thisreading.t = tmpr
 thisreading.h = hmid
END FUNCTION

SUB plotroom
 LOCAL n, fromaddr, toaddr, starttime AS FLOAT
 LOCAL ydata(144) AS FLOAT
 LOCAL byteSize = Struct(SIZEOF, "room")
 LOCAL pt1 AS room, pt2 AS room
 
 starttime = TIMER
 ' the slow way. even slower if we used a 2 dimension array
 'for n = 1 to 143
 'readings(n) = readings(n+1)
 'next n
 
 ' it would be faster to use MEMORY COPY
 toaddr = PEEK(VARADDR readings())
 fromaddr = toaddr + byteSize
 MEMORY COPY FLOAT fromaddr, toaddr, 144*2
 
 readings(144) = thisreading(22)
 CLS
 ' the slow way:
 'for n = 1 to 143
 'pt1 = scalep(readings(n))
 'pt2 = scalep(readings(n+1))
 'line n*2,pt1.t,n*2+2,pt2.t,1,rgb(red)
 'line n*2,pt1.h,n*2+2,pt2.h,1,rgb(cyan)
 'next n
 
 'the really slow way:
 'for n = 1 to 143
 'line n*2,scalep(readings(n)).t,n*2+2,scalep(readings(n+1)).t,1,rgb(yellow)
 'line n*2,scalep(readings(n)).h,n*2+2,scalep(readings(n+1)).h,1,rgb(cyan)
 'next n
 
 ' the fast way to plot data
 ' use memory copy to extract each member of the type
 ' LINE PLOT doesn't like typed arrays
 ' you can then use all the features of the MATH command
 fromaddr = PEEK(VARADDR readings())
 toaddr = PEEK(VARADDR ydata())
 MEMORY COPY FLOAT fromaddr, toaddr, 145,2 ' get the temperatures
 MATH SCALE ydata(),-4,ydata()
 MATH ADD ydata(),MM.VRES,ydata()
 LINE PLOT ydata(),144,,2,,,RGB(YELLOW)
 
 MEMORY COPY FLOAT fromaddr+8, toaddr, 145,2 ' get the humidity
 MATH SCALE ydata(),-3,ydata()
 MATH ADD ydata(),MM.VRES,ydata()
 LINE PLOT ydata(),144,,2,,,RGB(CYAN)
 
 struct PRINT readings(144)
 PRINT TIMER - starttime  
END SUB
 
FUNCTION scalep(x AS room) AS room
 scalep.t = MM.VRES - x.t*4
 scalep.h = MM.VRES - x.h*3
END FUNCTION
 
dummydata:
 DATA 21.3,45.7
 DATA 21.5,44.9
 DATA 21.5,43.4
 DATA 21.6,42.2
 DATA 21.6,41.8
 DATA 21.5,41.8
 DATA 21.3,40.9
 DATA 21  ,43
 DATA 21.1,43.9
 DATA 21.3,43.4
 DATA 21.2,43.7
 DATA 21.2,43.5
 DATA 21.4,43.3
 DATA 21.2,43
 DATA 21.2,43.7
 DATA 21.2,44.3
 DATA 21.2,44.3
 DATA 21.3,44.5
 DATA 21.5,43.1
 DATA 21.5,43.5
 DATA 21.4,43.1
 DATA 21.4,43.8
 DATA 21.3,42.8
 DATA 21.2,42.4
 DATA 20.9,43.9
 DATA 20.8,44.6
 DATA 20.7,44.8
 DATA 20.8,44.9
 DATA 20.9,46
 DATA 20.8,46.3
 DATA 20.7,47
 DATA 20.8,47.7
 DATA 21.2,46.8
 DATA 21.3,46.4
 DATA 20.9,48.5
 DATA 20.8,49.2
 DATA 20.7,49.5
 DATA 20.6,49.8
 DATA 20.5,49.9
 DATA 20.5,50
 DATA 20.4,49.5
 DATA 20.3,49.9
 DATA 20.3,49.9
 DATA 20.5,48.2
 DATA 20.2,49.4
 DATA 20.1,50.1
 DATA 20  ,50.2
 DATA 20  ,50.2
 DATA 19.9,50.4
 DATA 19.8,50.4
 DATA 19.7,50.7
 DATA 19.7,50.7
 DATA 19.5,50.9
 DATA 19.5,51
 DATA 19.4,51
 DATA 19.4,51.1
 DATA 19.3,51.4
 DATA 19.2,51.4
 DATA 19.2,51.2
 DATA 19.1,51.6
 DATA 19.1,51.7
 DATA 19  ,51.8
 DATA 19  ,51.8
 DATA 18.9,52.2
 DATA 18.9,52.1
 DATA 18.9,52.6
 DATA 18.8,52.6
 DATA 18.8,53
 DATA 18.8,52.8
 DATA 18.7,53.2
 DATA 18.7,53.3
 DATA 18.7,53.2
 DATA 18.6,53.3
 DATA 18.6,53.3
 DATA 18.6,53.4
 DATA 18.6,53.4
 DATA 18.5,53.5
 DATA 18.5,53.9
 DATA 18.5,53.8
 DATA 18.4,53.8
 DATA 18.4,53.7
 DATA 18.4,53.9
 DATA 18.3,54.1
 DATA 18.3,54.1
 DATA 18.2,54
 DATA 18.2,54.1
 DATA 18.2,54.2
 DATA 18.1,54.6
 DATA 18,  54.6
 DATA 18,  54.5
 DATA 18,  54.4
 DATA 18,  54.5
 DATA 17.9,54.6
 DATA 17.9,54.6
 DATA 18.3,53.9
 DATA 18.7,53
 DATA 18.7,52.5
 DATA 18.7,52.5
 DATA 18.6,52.8
 DATA 18.4,53.8
 DATA 18.5,53.8
 DATA 18.7,53.1
 DATA 18.7,53.1
 DATA 18.4,53.8
 DATA 18.5,54.3
 DATA 18.8,53.9
 DATA 18.9,53.6
 DATA 18.7,54.3
 DATA 18.8,54.4
 DATA 18.9,54.3
 DATA 18.7,55
 DATA 18.9,55.8
 DATA 18.9,55.4
 DATA 19,  55.2
 DATA 19.3,54.8
 DATA 19.2,54.9
 DATA 19.3,55.2
 DATA 19.3,55.4
 DATA 19.4,55
 DATA 19.5,54.9
 DATA 19.5,54.9
 DATA 19.7,54.5
 DATA 19.7,54.6
 DATA 19.8,54.5
 DATA 19.9,54.4
 DATA 20,  54.1
 DATA 20.1,54.4
 DATA 20.2,54.4
 DATA 20.2,54.2
 DATA 20.3,53.9
 DATA 20.3,53.9
 DATA 20.3,35.2
 DATA 20.9,53.5
 DATA 21,  53.4
 DATA 21.2,53.1
 DATA 21.5,53.2
 DATA 21.5,52.9
 DATA 21.7,52.7
 DATA 21.8,52.4
 DATA 21.7,52.6
 DATA 21.7,52.4
 DATA 21.8,52.4
 DATA 22,  52.3
 DATA 22.2,52

VK7JH
MMedit
 
     Page 3 of 7    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2026