Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 09:16 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 : CMM2: initialising large arrays without DATA or files

     Page 1 of 2    
Author Message
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 08:33pm 12 May 2021
Copy link to clipboard 
Print this post

Hi folks,

I'm writing some library code that needs to to initialise large arrays but I don't want to do it from DATA statements (1) and I don't want to read it from files (2).

1) Using DATA statements in library code seems to be inherently unsafe because unless you impose restrictions on the client you can't prevent it from reading the DATA in a random library .inc file instead of its own DATA.

2) Reading data from files in library code isn't much better because it relies on the files being in a fixed location, or the client telling the library where its files are.

Currently this is the best I've come up with, but I'm hoping for a better solution:


Function crypt.md5$(s$)
   ' Per-round shift amounts
   Local r%(array.new%(64))
   Local i% = Mm.Info(Option Base)
   array.insert_ints(r%(), i%, 8, 7, 12, 17, 22, 7, 12, 17, 22) : Inc i%, 8
   array.insert_ints(r%(), i%, 8, 7, 12, 17, 22, 7, 12, 17, 22) : Inc i%, 8
   array.insert_ints(r%(), i%, 8, 5,  9, 14, 20, 5,  9, 14, 20) : Inc i%, 8
   array.insert_ints(r%(), i%, 8, 5,  9, 14, 20, 5,  9, 14, 20) : Inc i%, 8
   array.insert_ints(r%(), i%, 8, 4, 11, 16, 23, 4, 11, 16, 23) : Inc i%, 8
   array.insert_ints(r%(), i%, 8, 4, 11, 16, 23, 4, 11, 16, 23) : Inc i%, 8
   array.insert_ints(r%(), i%, 8, 6, 10, 15, 21, 6, 10, 15, 21) : Inc i%, 8
   array.insert_ints(r%(), i%, 8, 6, 10, 15, 21, 6, 10, 15, 21)

   ' Binary integer part of the sines of integers (Radians) as constants
   Local K%(array.new%(64))
   i% = Mm.Info(Option Base)
   array.insert_ints(K%(), i%, 4, &hd76aa478, &he8c7b756, &h242070db, &hc1bdceee) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &hf57c0faf, &h4787c62a, &ha8304613, &hfd469501) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &h698098d8, &h8b44f7af, &hffff5bb1, &h895cd7be) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &h6b901122, &hfd987193, &ha679438e, &h49b40821) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &hf61e2562, &hc040b340, &h265e5a51, &he9b6c7aa) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &hd62f105d, &h02441453, &hd8a1e681, &he7d3fbc8) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &h21e1cde6, &hc33707d6, &hf4d50d87, &h455a14ed) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &ha9e3e905, &hfcefa3f8, &h676f02d9, &h8d2a4c8a) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &hfffa3942, &h8771f681, &h6d9d6122, &hfde5380c) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &ha4beea44, &h4bdecfa9, &hf6bb4b60, &hbebfbc70) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &h289b7ec6, &heaa127fa, &hd4ef3085, &h04881d05) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &hd9d4d039, &he6db99e5, &h1fa27cf8, &hc4ac5665) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &hf4292244, &h432aff97, &hab9423a7, &hfc93a039) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &h655b59c3, &h8f0ccc92, &hffeff47d, &h85845dd1) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &h6fa87e4f, &hfe2ce6e0, &ha3014314, &h4e0811a1) : Inc i%, 4
   array.insert_ints(K%(), i%, 4, &hf7537e82, &hbd3af235, &h2ad7d2bb, &heb86d391)

   ' This function is not finished ;-)
End Function

Sub array.insert_ints(a%(), lb%, num%, i0%, i1%, i2%, i3%, i4%, i5%, i6%, i7%, i8%, i9%)
   If num% < 1 Or num% > 10 Then Error "num% out of bounds 1..10"
   a%(lb%) = i0%
   If num% > 1 Then a%(lb% + 1) = i1% Else Exit Sub
   If num% > 2 Then a%(lb% + 2) = i2% Else Exit Sub
   If num% > 3 Then a%(lb% + 3) = i3% Else Exit Sub
   If num% > 4 Then a%(lb% + 4) = i4% Else Exit Sub
   If num% > 5 Then a%(lb% + 5) = i5% Else Exit Sub
   If num% > 6 Then a%(lb% + 6) = i6% Else Exit Sub
   If num% > 7 Then a%(lb% + 7) = i7% Else Exit Sub
   If num% > 8 Then a%(lb% + 8) = i8% Else Exit Sub
   If num% > 9 Then a%(lb% + 9) = i9% Else Exit Sub
End Sub

' Gets the upper-bound that should be used to dimension an array of the given
' capacity, irrespective of OPTION BASE.
'
' e.g. To create a string array that can hold 10 elements:
'        Dim my_array$(array.new%(10))
Function array.new%(capacity%)
   array.new% = capacity% + Mm.Info(Option Base) - 1
End Function



Best wishes,

Tom
Edited 2021-05-13 06:48 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 08:46pm 12 May 2021
Copy link to clipboard 
Print this post

Possibly the answer is to either brute force it, e.g.


   r%(i% + 0) = 7 : r%(i% + 1) = 12 : r%(i% + 2) = 17 : r%(i% + 3) = 22
   ...


Or use a CSUB

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 08:47pm 12 May 2021
Copy link to clipboard 
Print this post

Deleted
Edited 2021-05-13 06:48 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 09:08pm 12 May 2021
Copy link to clipboard 
Print this post

Could you poke the data into strings?
Mick

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

Joined: 11/12/2012
Location: United Kingdom
Posts: 10310
Posted: 09:46pm 12 May 2021
Copy link to clipboard 
Print this post

Put the data into CSUB format and then use peek(cfunaddr, peek(VARADDR and MEMORY COPY
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 11:06pm 12 May 2021
Copy link to clipboard 
Print this post

  matherp said  Put the data into CSUB format and then use peek(cfunaddr, peek(VARADDR and MEMORY COPY


Sounds like fun, I will definitely try that out.

What is the intended purpose of Peek(CFunAddr foo) ? or is this it, to allow access to arbitrary binary data encoded in the program.

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
epsilon

Senior Member

Joined: 30/07/2020
Location: Belgium
Posts: 255
Posted: 07:21am 13 May 2021
Copy link to clipboard 
Print this post

I'm missing the point about the DATA statements.
E.g. If I would put the code below in a library, what makes it inherently unsafe?


DIM CRSR_INS_SPRITE%(COL_WIDTH%*ROW_HEIGHT%-1) 'Insert cursor sprite
CRSR_INS_SPRITE_DATA:
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0
 DATA FG_COLOR%, FG_COLOR%, 0, 0, 0, 0, 0, 0

SUB initInsSprite
 LOCAL ii%

 RESTORE CRSR_INS_SPRITE_DATA
 FOR ii%=0 TO COL_WIDTH%*ROW_HEIGHT%-1
     READ CRSR_INS_SPRITE%(ii%)
 NEXT ii%
END SUB

initInsSprite

Epsilon CMM2 projects
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 08:49am 13 May 2021
Copy link to clipboard 
Print this post

  epsilon said  I'm missing the point about the DATA statements.
E.g. If I would put the code below in a library, what makes it inherently unsafe?


There is nothing in the BASIC language that enforces that all DATA sections are preceded by labels and that all code that reads data calls RESTORE <label> first. As a result naive/legacy client code may in ignorance read your DATA instead of their own.

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
epsilon

Senior Member

Joined: 30/07/2020
Location: Belgium
Posts: 255
Posted: 09:15am 13 May 2021
Copy link to clipboard 
Print this post

  thwill said  There is nothing in the BASIC language that enforces that all DATA sections are preceded by labels and that all code that reads data calls RESTORE <label> first. As a result naive/legacy client code may in ignorance read your DATA instead of their own.


Got it. Thanks for clarifying!
Epsilon CMM2 projects
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 10:49am 13 May 2021
Copy link to clipboard 
Print this post

I've not tried this...  RESTORE with no argument selects the first DATA statement *in the program*. Does that still apply in this case though? Would a DATA statement in the library actually be selected?
Mick

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

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 10:59am 13 May 2021
Copy link to clipboard 
Print this post

  Mixtel90 said  I've not tried this...  RESTORE with no argument selects the first DATA statement *in the program*. Does that still apply in this case though? Would a DATA statement in the library actually be selected?


In all honesty I didn't try it. But when I say library I'm talking about .inc file and from everything Peter has said in the last 12+ months the pre-processing step just flattens everything, so once it hits the flash there is no distinction between code that came from a .inc file and that in the .bas file ... though it must be maintaining the origin somewhere to show in error messages.

Of course I may be completely mistaken in my assumption in which case Peter will no doubt put me straight ... though in this case I think not

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 11:25am 13 May 2021
Copy link to clipboard 
Print this post

For the Micromite it depends on what "order" the library is relative to the program space. If the library comes *after* the program space then there's no problem - just use RESTORE <datalabel> in the library then RESTORE to reset the pointer to the first DATA statement in the program. That would be in an ideal world. :)

I've never used inc files, but I assume they would come *before* the program. However, they aren't likely to be used with legacy programs any way, are they? Just make sure that RESTORE always has an argument.
Edited 2021-05-13 21:29 by Mixtel90
Mick

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

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 11:34am 13 May 2021
Copy link to clipboard 
Print this post

  Mixtel90 said  It depends on what "order" the library is relative to the program space. If the library comes *after* the program space then there's no problem - just use RESTORE <datalabel> in the library then RESTORE to reset the pointer to the first DATA statement in the program. That would be in an ideal world. :)


Yes, but:

1. Most developers familiar with #include from other languages are in the habit of putting those statements first.

2. You put the onus on the client code to call RESTORE to reset the pointer, naive/legacy code doesn't.

3. This problem:
 library.one()
 RESTORE
 READ some data ...
 library.two()
 READ some more data ... which will fail because it is trying to read beyond the library data!
 END

 ' some data
 DATA ...

 ' some more data
 DATA ...

 ' The following comes from a #Include
 SUB library.one()
 END SUB

 SUB library.two()
   RESTORE library.data
   READ library.data ...
 END SB

 library.data:
 DATA ...


Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 11:35am 13 May 2021
Copy link to clipboard 
Print this post

  Mixtel90 said  Just make sure that RESTORE always has an argument.


Yes, that's one possible fix, impose that restriction on any client code using your library. I was investigating whether there were other options.

Edit: just in case it isn't clear the client code is not necessarily code written by me, hence wanting a robust solution that doesn't depend on someone reading any accompanying documentation.
Edited 2021-05-13 21:36 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 11:43am 13 May 2021
Copy link to clipboard 
Print this post

I see that #INCLUDE inserts the file *at that point*, so using it at the end of the program would put your DATA statements at the end. You could then use RESTORE <name> anywhere to process them and RESTORE to point to the first program DATA.

It's been a long time since RESTORE without a label or line number has been acceptable programming really. It's not something I commonly did on the TRS-80.  :)

Micromite: Library DATA statements will (apparently) be processed before program DATA statements, so it's not a perfect world. lol
Edited 2021-05-13 21:52 by Mixtel90
Mick

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

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 11:52am 13 May 2021
Copy link to clipboard 
Print this post

  Mixtel90 said  I see that #INCLUDE inserts the file *at that point*, so using it at the end of the program would put your DATA statements at the end. You could then use RESTORE <name> anywhere to process them and RESTORE to point to the first program DATA.


Yes, the BEGINNING of the first program data. But see my 3rd point above. What it comes down to is that there is a single global resource, the data pointer, that naive client code might not expect "library" code to manipulate.

Likewise there is a problem with "library" code and open file descriptors (another global resource). i.e. there is no file descriptor that a piece of library code can use that I can't guarantee the client code doesn't already have open.

You could also argue the same for global variables, fortunately MMBasic allows "." in a variable name which I'm using to provide a poor persons namespacing facility, i.e. everything declared in the "crypt.inc" as the prefix "crypt."

Note that I'm not complaining about any of this, BASIC is what it is, and MMBasic is a superior version of that. All I'm doing is fishing for other ideas and opinions whilst I decide how to handle it.

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 11:57am 13 May 2021
Copy link to clipboard 
Print this post

  Mixtel90 said  It's been a long time since RESTORE without a label or line number has been acceptable programming really. It's not something I commonly did on the TRS-80.  :)


I can't remember what I was doing that far back, I certainly wasn't trying to push BBC & Sinclair BASIC (and later AMOS) as hard then as I'm pushing MMBasic now.

Best wishes,

Tom
Edited 2021-05-13 21:58 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 12:19pm 13 May 2021
Copy link to clipboard 
Print this post

Personally, I think I'd opt for a single #INCLUDE at the beginning that pulls the data from a file with the proviso that the data file must be in the same directory as the program file. The #INCLUDE file has to be in the same directory anyway. Once the file is loaded into somewhere or other you can close the file descriptor and leave it free for the client program.
Mick

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

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 12:32pm 13 May 2021
Copy link to clipboard 
Print this post

  Mixtel90 said  ... The #INCLUDE file has to be in the same directory anyway ...


Not true, #INCLUDE statements can reference both absolute and relative paths. Thus there isn't any sane mechanism (you need to manually parse the current .bas file to find the #INCLUDE statements) for a .inc file to determine where it came from and thus locate a data file that it depends upon. So I believe that either data has to be packed into the code, or a library has to sit at a fixed known location, or the client code has to inform the library where it can find its own data.

Best wishes,

Tom
Edited 2021-05-13 22:38 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 12:56pm 13 May 2021
Copy link to clipboard 
Print this post

Ah... I see... Perhaps that's the condition of using your library that you need to specify, that the INC and DAT files are in the same directory as the program.

The problem with embedding the data, unless you use DATA statements, would seem to be that any changes to it require a new version of the program - the two can never be separated. A file is definitely the most flexible.
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
     Page 1 of 2    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025