Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 00:20 10 May 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 : OOP (object oriented programming) in MMBASIC (kind of)

     Page 3 of 3    
Author Message
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3309
Posted: 04:05pm 24 Mar 2025
Copy link to clipboard 
Print this post

I looked at Pascal when it first came out for microprocessors. Oops, no ability to read or write a file. I guess, re running "the London International Financial Futures exchange", that that has changed, but I haven't looked at it since earliest days.

I wrote a lot of Fortran, including word processing for an early Xerox mainframe laser printer with proportional fonts and justification and proportional spacing control. Fortran was good because it could handle individual bytes (i.e. characters). The text involved was over a million characters, and the data, for efficiency's sake, had to be all in memory at once.

The memory requirement was such that on the IBM 370 machine it was running on, it could only be run at night with nothing else running. One minute to read in and format all the text, then another minute to spit out a dozen or more Senator's voting record for every vote that they had ever taken, with vote descriptions ordered by subject matter, with the vote position plugged in for each Senator in order. Then another similar run for the next group of Senators all sworn in at the same time. This was a once-a-year process. The output was 100 pages or more per Senator.

No OOP for me, though I agree that structures are nice. Haven't needed them on the mites, but if I thought I did, the work-arounds would probably be fine.
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7499
Posted: 04:35pm 24 Mar 2025
Copy link to clipboard 
Print this post

Nascom-1, CP/M? Don't make I larff....  :)
You might like this. :)

The Nascom-1, as supplied, was a 11" x 8" PCB and a keyboard. If you bought it as a kit it came with bags of components. There were two instruction books, the "Easy" book was how to build it and the "Hard" book was how to program. The power supply was an optional extra and you had to provide your own TV and cassette recorder. You had a machine code monitor and (IIRC) 960 bytes of user RAM to go with the Z80 processor. It took a while for the Zeap assembler to appear so everything was written using the machine code monitor (NASBUG usually). We had such famous games as "Lollypop Lady Trainer" (a bit like Frogger but more bloodthirsty as an ambulance comes past to remove the run-down corpse).

Expansion needed a bus and a bus expander card to link it to the Nascom via a ribbon cable. This was usually mounted in a 19" rack. After that you could add some RAM or EPROM cards to the system. The Nascom-1 was hard-wired to start the monitor at 0000H and there was no RAM there so CP/M was impossible. You had to map external memory to start there, hack the PCB and change the monitor to the later NAS-SYS. That could, if you were very lucky, allow you to boot NAS-SYS which would then copy itself to high memory and page itself out to leave RAM at 0000H.

Oh.... CP/M needs disks... Ah... That needs more (very, very expensive) bits.

The Nascom-2 made a lot of the above somewhat easier. It coupled directly to the motherboard and came with NAS-SYS, but you still only got 16K of RAM and needed external expansion. It also had BASIC! You had to change a boot rom to reconfigure the memory map for CP/M.

There were several sort-of-competing disk systems, most of which were proprietary and couldn't run (the very expensive) CP/M on the (very, very expensive) system.

Ironically, once Nascom had disappeared it was replaced by the Gemini system which *did* run CP/M and , although it was expensive, still worked out a bit cheaper than even a Nascom-2. It was still way too expensive for me though.

I had to wait until I got a Tandy Model I before I got my first floppy disk drive.
Mick

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

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1121
Posted: 04:46pm 24 Mar 2025
Copy link to clipboard 
Print this post

In MMBasic, we have 3 data types: INTEGER, FLOAT and STRING. (Did some version of flag bits appear recently?) The first two are 8 bytes long each, and a string can be 8 bytes, 255 bytes or, in arrays, an arbitrary size.

INTEGER and FLOAT convert easily, STRINGs less so.

Suppose we had the ability to do something like this:
Dim STRING mydata

mydata(1) = 1234    ' force an integer into the first 8 bytes/chars of string
mydata(9) = 2 * PI  ' force a float into next 8 bytes of string
mydata(17) = SQR(2) ' 8 more bytes for another float
mydata(25) = "Useful math constants" ' add a string starting from the 25th posn

The key here is the ease with which a numerical value can be stuffed into and retrieved from a (portion of a) string. There is immense opportunity for messing up data by referencing the wrong string position, but hey, with great power comes great responsibility.

Now, for the above example, the string index value could be stored in a CONST, allowing the "fields" in the string to be named.
Ex:
CONST root2 = 17
diagonal = side * mydata(root2)

The final icing, for those who need STRUCT.MEMBER format, would be the ability to refer to the array elements with a "." character (or equivalent.)
diagonal = side * mydata.root2

Perhaps a string used this way _must_ be specified with a "$" character so that "name$.index" (no space allowed) clearly indicates a position in the string. Also, there must be a way to specify the maximum number of characters being being put into a portion of the structured string.
CONST title = 25
mydata$.title,32 = "Useful math constants"   ' max 32 characters fit here


The above is fine and dandy for me to say, but I have no idea how difficult the reality is. Just a thought...
Visit Vegipete's *Mite Library for cool programs.
 
bfwolf
Regular Member

Joined: 03/01/2025
Location: Germany
Posts: 72
Posted: 05:36pm 24 Mar 2025
Copy link to clipboard 
Print this post

  matherp said  
  Quote  Not to mention that PASCAL was more suited to IT and math students and professors than to "real programs."


Strange that since it ran the London International Financial Futures exchange with 100s of thousand transactions a day hitting a relational database on a DEC Vax cluster.

Most of my real programming was in Pascal in that environment


Okay, I confess, I hadn't even thought of such applications.
But of course: Due to its robustness and speed, its mathematical orientation, and the RECORD data type, which corresponds 1:1 to records in databases, it is naturally very well suited for such use cases.

Financial mathematics was never part of my business as a programmer. After university, I wrote software exclusively for embedded systems. And unfortunately, it's not suitable for that because (due to its high-level orientation) it doesn't allow direct access to the hardware.

Incidentally, I also had my first Pascal experiences at university on a DEC VAX 11/780. But back then, 40 of us students had to share the one VAX across 40 DEC VT100 terminals, with the shells running at a low priority so we wouldn't slow down the university administration system, which also ran on the VAX. This meant that the compiler run for a program with four typewriter pages size took 10 minutes or more. And at the end you could see in the compiler report that you had been given 2s of CPU time!

And I forgot about "Turbo Pascal", which dominated the development of software on PCs over a decade. But "Turbo Pascal" with it's "unit" concept was more similar to Modula-2 than to "Standard Pascal".

bfwolf
 
some_rando
Newbie

Joined: 25/03/2025
Location: Australia
Posts: 7
Posted: 11:42pm 25 Mar 2025
Copy link to clipboard 
Print this post

Let me show you a little bit of code ...

Sub MM.Prompt
Local pn$ length 8 = "asm01"
Local esc$ length 1 = Chr$(27)
Local c.normal$ length 3 = esc$ + "[m"
Local c.underline$ length 4 = esc$ + "[4m"
Local id$ length 16 = MM.Info(ID)

 Select Case id$
 Case "E66118C4E36"
   id$ = "moe"
 Case "E6612483CB0"
   id$ = "dallax"
 Case "E6612483CB2"
   id$ = "curly"
 Case "E6614C311B6"
   id$ = "larry"
 Case "E6616407E35"
   id$ = "larry1"
 Case "E6616407E37"
   id$ = "ripley"
 Case "E4611847675"
   id$ = "dallas"
 Case "E6616407E36"
   id$ = "dallas"
 Case "E6616408432"
   id$ = "dallas2"
 Case "4B02317F8D3"
   id$ = "Dallas"
 End Select
 Print pn$ + " " + c.underline$ + id$ + c.normal$ "> ";
End Sub


This code segment is a small present to celebrate my first post. I have been a long time lurker of course . "pn$" is the current program name, which I update every edit. I have Geoff Grahams 2014 ASCII video terminal connected to Ripley (a Pico) running terminal switching between three target Pico/Pico2's which have MMBasic installed. I try to work on a single program on each Pico. My goal is a self-hosting system, and I am also off-grid, so the idea is to satisfy my computing and control tasks in a low power environment with some of the system running 24/7. The above code when attached to each program helps me to navigate between the active sessions.


Sub list.list(s$(), len%)
Local I%
 If len% Then
   For I% = 1 To len%
     machine.log.message(s$(I%))
   Next I%
 EndIf
End Sub

' Error checking is done in the wrapper
' Alters tos as a side effect, since MMBasic arguments
' are by reference.
Function list.push$(a$, s$(), tos%)
 Inc tos%
 s$(tos%) = a$
 list.push$ = s$(tos%)
End Function

' Error checking is done in the wrapper
' Alters tos as a side effect, since MMBasic arguments
' are by reference.
Function list.pop$(s$(), tos%)
 list.pop$ = s$(tos%)
 tos% = tos% - 1
End Function


This is an abstract class written in MMBasic. It is inherited by other classes, and never intended to be called directly. Also, it does not contain any data.
 Although MMBasic does not support OOP at an interpretive level, we can bend and shape things to meet our needs by being disciplined, and writing in an object oriented style. The only thing that really changes is we exploit the ability of MMBasic to recognise "." and "_" characters as part of an identifier. Choose identifiers to reflect the class structure, remembering that they are of limited length. The main difference from a computing language with inherent support for OOP is that the MMBasic interpreter cannot provide any compile time checking, or run time checking, but that's okay because we are young and free and can run with the wind  .


'
' Symbol Table
'

' 12Feb25 MPS - The symbol table class inherits
' stack methods, and Tiny10's Lookup, Locate,
' InTable, and AddEntry methods. Oddly, the first
' three when translated to MMBasic appear to be
' synonyms.
'   Also, Tiny used an array of strings, and parallel
' array of single characters to hold the identifier,
' and type respectively. MMBasic may as well use a
' single string with field 1 the identifier,
' field 2 the type, and field 3 the value. No single char limitation.

' type Symbol = string[8];
' Const MaxEntry = 100;
' VAR ST   : array[1..MaxEntry] of Symbol;
'     SType: array[1..MaxEntry] of char;
' Then a type is used to create a pointer and pass
' it around ...
'     SymTab = array[1..1000] of Symbol;
'     TabPtr = ^SymTab;

Dim symtab.unread$
Dim symtab.stack_pointer% = 0
Dim symtab.stack_depth% = 100
Dim symtab.stack$(symtab.stack_depth%)

Sub symtab.init(s$)
 symtab.unread$ = s$
 symtab.stack_clear()
End Sub

' See K&R, _C Programming Language_, 1978 P75
Sub symtab.stack_clear()
 symtab.stack_pointer% = 0
End Sub

Sub symtab.list()
 list.list(symtab.stack$(), symtab.stack_pointer%)
End Sub

Function symtab.push$(a$)
 If symtab.stack_pointer% < symtab.stack_depth% Then
   symtab.push$ = list.push$(a$,symtab.stack$(),symtab.stack_pointer%)
 Else
   machine.log.message("error: symtab stack full")
   symtab.stack_clear()
   symtab.push$ = ""
 EndIf
End Function

Function symtab.pop$()
 If symtab.stack_pointer% Then
   symtab.pop$ = list.pop$(symtab.stack$(),symtab.stack_pointer%)
 Else
   machine.log.message("error: symbol table empty")
   symtab.stack_clear()
   symtab.pop$ = ""
 EndIf
End Function

Function symtab.top_of_stack$()
 If symtab.stack_pointer% Then
   symtab.top_of_stack$ = symtab.stack$(symtab.stack_pointer%)
 Else
   machine.log.message("error: symbol table also empty")
   symtab.stack_clear()
   symtab.top_of_stack$ = ""
 EndIf
End Function

' Tiny - symtab.Lookup(T: TabPtr; s: string; n: integer): integer;
' Lookup, Locate, and InTable are synonyms?
Function symtab.Lookup%(sym$)
Local index% = symtab.stack_pointer%
Local s$ = Field$(sym$,1,":")
Local found% = 0
 Do While (index% > 0) And (Not found%)
   If s$ = symtab.GetIdentifier$(index%) Then
     Inc found%
   Else
     index% = index% - 1
   EndIf
 Loop
 symtab.Lookup% = index%
End Function

' Locate a symbol in the symbol table
' Returns the index of the entry.  Zero if Not present.
' Translated Tiny - Lookup and Locate are synonyms?
Function symtab.Locate%(s$)
  symtab.Locate% = symtab.Lookup(s$)
End Function

' Look For Symbol in Table
Function symtab.InTable%(s$)
  symtab.InTable% = symtab.Lookup%(s$)
End Function

' s$ is the new symbol name, t$ it's type, and v$ it's initial value
Sub symtab.AddEntry(s$, t$, v$)
Local buffer$
 If symtab.InTable%(s$) Then
   cradle.Abort("Duplicate Identifier " + s$)
 Else
   buffer$ = symtab.push$(s$ + ":" + t$ + ":" + v$)
 EndIf
End Sub

Sub symtab.ChangeValue(index%, new_value$)
Local entry$ = symtab.stack$(index%)
Local identifier$ = Field$(entry$, 1, ":")
Local type$ = Field$(entry$, 2, ":")
 symtab.stack$(index%) = identifier$ + ":" + type$ + ":" + new_value$
End Sub

Function symtab.GetIdentifier$(index%)
Local entry$ = symtab.stack$(index%)
Local identifier$ = Field$(entry$, 1, ":")
 symtab.GetIdentifier$ = identifier$
End Function

Function symtab.GetType$(index%)
Local entry$ = symtab.stack$(index%)
Local type$ = Field$(entry$, 2, ":")
 symtab.GetType$ = type$
End Function

Function symtab.GetValue$(index%)
Local symbol$ = symtab.stack$(index%)
Local value$ = Field$(symbol$, 3, ":")
 symtab.GetValue$ = value$
End Function


 Okay, so this is an inherited class, a symbol table. In demonstrates a few other OOP concept, because there is a touch of multiple inheritance going on here.
 Furthermore, MMBasic has powerful string handling facilities which we can utilise to provide PASCAL "records" with a little bit of work. The Field$ function is used to pull out individual components, in this case delimited by a colon.
 Likewise, if you want to instantiate multiple instances of a class, you will need to make your own arrangements to keep track of which one is active. Others have already mentioned some ways to go about this in the thread.

Limitations
 Having adopted some of these ideas, the limitations that I have encountered are around the number of function/sub routines (256 for Pico, 512 for Pico2), and code space (roughly 128kB).
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 527
Posted: 02:53am 26 Mar 2025
Copy link to clipboard 
Print this post

Welcome Rando!

  some_rando said  Having adopted some of these ideas, the limitations that I have encountered are around the number of function/sub routines (256 for Pico, 512 for Pico2), and code space (roughly 128kB).


I know you're concerned about power draw, but have you looked at the CMM2? It's still fairly frugal on power (specially compared to a modern PC) but has quite a bit more memory (512K program, 32MB graphics/data). As for the limitation on functions/subroutines, you can dump variables to disk, then load a new program (read the variables and then continue). The speed of the SD card access is fast enough that you'll hardly miss a beat.

The limitations are part of the fun of it for me. I get that you're using your MMbasic for "Serious" stuff, but I'm just using it for fun. Trying to push the limits of the system feels very much like things did in the 80's. I have to say that I miss that feeling of "real programming" with modern computers.
Edited 2025-03-26 12:56 by PeteCotton
 
pwillard
Guru

Joined: 07/06/2022
Location: United States
Posts: 307
Posted: 11:33am 26 Mar 2025
Copy link to clipboard 
Print this post

  Quote  Oh, no, please no oop in mmbasic, it's the worst invention ever made.


Thank you for saying this for me.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10066
Posted: 12:41pm 26 Mar 2025
Copy link to clipboard 
Print this post

  Quote   it's the worst invention ever made.


I disagree, I just wish I was competent in it/c++

If interested watch some of One lone coder videos. The productivity he achieves with C++/OO is astonishing. My measure of productivity being amount of functionality created for amount of code written.
Edited 2025-03-26 22:44 by matherp
 
     Page 3 of 3    
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