![]() |
Forum Index : Microcontroller and PC projects : OOP (object oriented programming) in MMBASIC (kind of)
![]() ![]() |
|||||
Author | Message | ||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3309 |
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 KingdomPosts: 7499 |
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: CanadaPosts: 1121 |
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: GermanyPosts: 72 |
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: AustraliaPosts: 7 |
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 ![]() 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: CanadaPosts: 527 |
Welcome Rando! 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 StatesPosts: 307 |
Thank you for saying this for me. |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10066 |
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 |
||||
![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |