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 for Windows - betas
Page 2 of 30 | |||||
Author | Message | ||||
thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4040 |
Some more grist for the mill: 1. On the CMM2 this lists the named file, on MMB4W it does nothing: > LIST "file-that-exists.bas" 2. This crashes MMB4W: > RUN "file-that-exists.bas" Hello World > RUN "file-that-does-not-exist.bas" Failed to open file C:\home-thwill\git_sandbox\github\file-that-does-not-exist.bas CRASHES!!! 3. PEEK(VARHEADER variable) is unimplemented. 4. INC/CAT doesn't handle case where result is > 255 characters, you had this problem and fixed it on the CMM2 where it now reports "String too long": > Dim x$ > x$ = String$(128, "a") > Inc x$, String$(128, "b") > Len(x$) 98 5. DAY$() function crashes given a date before the epoch: CMM2: > ? Day$("31-12-1969") Wednesday MMB4W: > ? Day$("31-12-1969") CRASHES!!! 6. DATETIME$() function can't handle negative argument: CMM2: > ? DateTime$(-1000) 31-12-1969 23:43:20 MMB4W: > ? DateTime$(-1000) Epoch<0 Interestingly the EPOCH() function does seem to work with a date before the epoch: CMM2: > ? Epoch("31-12-1969 23:43:20") -1000 MMB4W: > ? Epoch("31-12-1969 23:43:20") -1000 It's a pity this isn't a paying gig . Best wishes, Tom Edited 2022-03-01 09:42 by thwill Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
goc30 Guru Joined: 12/04/2017 Location: FrancePosts: 427 |
MS Window, from win3.11 to win11 can draw window with or without any border in full screen from EGA to XGA or Full HD, I don't understand why it is not possible in MMBasic. I have somes progs who use full screen without border, like Maps, for 1 to 4 screen. It work perfect with vb6, but now with win10 and win11, it is more difficulte to execute. For that I can be interesting by MMB, but if it is not possible to work in full screen without border, I must search other langage Lazarus or Purebasic exemple |
||||
Goksteroo Senior Member Joined: 15/03/2021 Location: AustraliaPosts: 114 |
Noooooooo..... |
||||
mclout999 Guru Joined: 05/07/2020 Location: United StatesPosts: 469 |
I second that, NOOOOOOOOOO!!!!. |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 9115 |
Thanks Tom/all for the feedback. No updates now for the next 3 days |
||||
thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4040 |
Enjoy your "break" Peter, Probably a variant of number 2 above: > RUN "HelloWorld.bas" Hello World > NEW CRASHES!!! Best wishes, Tom Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4234 |
Hi Peter, Please check the MATH FFT function. For CMM2 we had to fix it, and for MMB4W we seem to run into a similar issue (although not identical). When I take the FFT magnitude from a sinesquare windowed sine wave, and display it in linear scale, it should look like this (CMM2 fft calculation from below program): However, the MMB4W math function shows many spurious peaks. Below graph shows the sinesquare windowed sine signal (top), as well as the fft (lower graph) I use this code to check it (the program can change the windowing function with "w", and change the number of sinewaves for fft with "a" and "z", and save the graph to disk with "s"). 'check fft magnitude function n=1023 'samples cy=10 'cycles sq=0 'sq=1 = square wave. sq=0 = sine wave dim a!(n),fm!(n) w=0 'window type, 0=none do cls 'draw 2 frames using mm.hres mm.vres box 50,0,(mm.hres-51),mm.vres/2-2 box 50,mm.vres/2,mm.hres-51,mm.vres-1 'prepare input signal sinfill if k$="w" then w=(w+1) mod 4 if w then window 'plot input signal plot_in 'do fft math fft magnitude a!(),fm!() 'convert to logaritm 'lin2log 'plot fft plot_fft 'keyboard commands do k$=inkey$ loop until k$<>"" if k$="a" then cy=cy+1 if k$="z" then cy=cy-1:cy=max(cy,2) if k$="s" then save image "fft",0,0,800,600 loop until k$="q" end 'subroutines 'fill the input array a!() with cy cycles of sinewave sub sinfill for i=0 to n a!(i)=sin(2*pi*cy*i/n) if sq=1 then if a!(i)>0 then a!(i)=1 else a!(i)=-1 end if end if next i end sub 'apply a window over the input signal (triangle for now) sub window if w=1 then for i=0 to n/2 'linear a!(i)=(i/n)*a!(i) a!(n-i)=(i/n)*a!(n-i) next i ? @(60,5) "triangle window" else if w=2 then for i=0 to n 'quadratic (do linear twice) a!(i)=(i/n)*a!(i) a!(n-i)=(i/n)*a!(n-i) next i ? @(60,5) "quadratic window" else if w=3 then for i=0 to n 'sine^2 (hann) window a!(i)=sin(pi*i/n)*sin(pi*i/n)*a!(i) next i ? @(60,5) "hann window" end if end sub 'plot the input signal in the upper window sub plot_in 'find minimum and maximum mi!=0:ma!=0 for i=0 to n mi!=min(mi!,a!(i)) ma!=max(ma!,a!(i)) next i '? mi!,ma! 'calculate gain and offset to fit it into the window xgain=(mm.hres-50)/n xoffs=50 ygain=-(mm.vres/2.3)/(ma!-mi!) yoffs=mm.vres/4-ygain*(ma!+mi!)/2 'plot the actual samples using linear interpolation for i=0 to n-1 line i*xgain+xoffs,a!(i)*ygain+yoffs,(i+1)*xgain+xoffs,a!(i+1)*ygain+yoffs next i 'add legend ? @(mm.hres/2,5) "input linear" ? @(5,5) str$(ma!,2,2) ? @(5,mm.vres/2-20) str$(mi!,2,2) end sub 'plot the fft output in the lower window sub plot_fft 'find min and max and where max is mi!=1e6:ma!=-1e6 for i=0 to n/2 'fft output is symetrical mi!=min(mi!,fm!(i)) ma!=max(ma!,fm!(i)) if ma!=fm!(i) then cf=i':? cf next i 'apply some logaritmic scaling 'mi!=-120:ma!=60 'mi!=0:ma!=200 'mi!=max(mi!,-140):ma!=min(ma!,120) 'for i=0 to n ' fm!(i)=min(ma!,max(mi!,fm!(i))) 'next i 'calculate scaling to fit the window xgain=2*(mm.hres-50)/n xoffs=50 ygain=-(mm.vres/2.3)/(ma!-mi!) yoffs=mm.vres-mi!*ygain-20 'plot the fft graph for i=0 to n/2-2 line i*xgain+xoffs,fm!(i)*ygain+yoffs,(i+1)*xgain+xoffs,fm!(i+1)*ygain+yoffs next i 'add legend ? @(mm.hres/2,mm.vres/2+15) "magintude logarithm" ? @(5,mm.vres/2+5) str$(ma!,2,2) ? @(5,mm.vres-30) str$(mi!,2,2); 'put marker ticks in multiples of centre frequency for i=0 to n/2-2 step cf line i*xgain+xoffs,mm.vres/2,i*xgain+xoffs,mm.vres/2+5 if ((i mod(5*cy)) = 0) then line i*xgain+xoffs,mm.vres/2,i*xgain+xoffs,mm.vres/2+15 end if next i end sub 'convert linear output to logarithmic sub lin2log for i=0 to n/2 fm!(i)= 20*log(fm!(i)+1e-300)/log(10) next i end sub Edited 2022-03-02 17:27 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
KD5ZXG Regular Member Joined: 21/01/2022 Location: United StatesPosts: 53 |
I originally tried sending this to Volhout as PM, but that function appears broken. Appears sent, but the TO: name somehow went blank. Forgive if this response is not entirely on-topic. A sinewave times itself makes for rectified McDonalds arches, but squared in height. Rectification because negative times negative is positive. Times a squarewave +/-1 of same phase and frequency also makes rectified mmmmmm's, but the gain is unity and not squared in magnitude. Both the above outcomes have a positive DC component. If the phases oppose, the DC will be negative. If phases offset, the result flips between both signs. DC can be lowpass filtered to measure phase shift. A sinewave times any other frequency makes a mess that low passes to zilch. Over time, all other frequencies but harmonics will remove themselves from the result. How much you lowpass determines how much mess leaks through, but very narrow lowpass is easy. I have almost described a homodyne detector or lock-in amplifier. Except they usually work in 90 degree pairs, so to produce a two part vector. Let's do that. Instead of multiplying sines and integrating over time, lets multiply only by the sign +1/-1 of a squarewave and integrate. Much easier, we only need add or subtract... Now multiply our sine times a bunch of "sign"waves that differ only in period. Just need an array of countdowns and an array to integrate each result. The sine*sine method is kinda like RMS, except we havn't yet taken the root of the mean squares. Sine*sign is plain old average. Probably relate just like they would for a multimeter. Vectors might need slight adjustment to account for that cheat. I am not mathematical and don't know how FFT works. I'm not sure this alternate makes sense or less work. If I were describing the same thing, I would not know. KD5ZXG Edited 2022-03-03 07:10 by KD5ZXG |
||||
thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4040 |
Picking up from https://www.thebackshed.com/forum/ViewTopic.php?TID=14585&P=7 Thanks again for implementing this Peter. I did wonder at the time whether I was "just being difficult", but when I switched my encryption code (used by SAAINT) to initialise from DATA statements rather than a "misused" CSUB my unit-tests for that code (which were also using DATA) immediately hit this very issue, so not artifical or made up. EDIT: Having "played" with it, I'm inclined to think: 1. Report an error when READ RESTORE called without a prior READ SAVE: "READ RESTORE called before READ SAVE" 2. Have READ RESTORE clear the cached state, and have READ SAVE report an error if the cached state isn't clear: "READ SAVE called twice without intervening READ RESTORE" I think this would help lock the behaviour down and would help avoid breaking any existing programs if in the future it was extended to support multiple levels of READ SAVE (presumably using a stack). IMHO it's a real pity the global data pointer isn't a single atomic value that could simply be PEEKed and POKEed as that would provide ultimate flexibility for those advanced users who actually need this facility. YMMV, Tom Edited 2022-03-03 09:54 by thwill Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
KD5ZXG Regular Member Joined: 21/01/2022 Location: United StatesPosts: 53 |
Somewhere in some other BASIC before (not sure which) was a function that gave the memory address of the variable. I remember poking Z80 into string variables on the trs80, but finding the address wasn't easy. Maybe I searched memory for a known key-string first? A$="FindThis89ABCDEF0123456789ABCDEF" https://rosettacode.org/wiki/Address_of_a_variable QuickBASIC was Y=VARPTR(X) Never seen so many other ways, but anything that works is fine. We may already have a way for finding variables I am unaware of. Can we locate the READ data pointer? Quite another question. What feature is missing that wants direct access to this pointer? Edited 2022-03-03 18:00 by KD5ZXG |
||||
Goksteroo Senior Member Joined: 15/03/2021 Location: AustraliaPosts: 114 |
From Maximite MMBasic help page check out VARADDR and their example of peeking and poking that address is here: sample$ = "The Colour Maximite 2 is the latest in a long series of microcomputers." Mind you, this is for the Maximite version of MM Basic and I haven't tried it on MMB4W. startB = PEEK(VARADDR sample$) PRINT " 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" dump startB FOR k = 0 TO 255 POKE BYTE startB+k,k NEXT k dump startB SUB dump(startblock) LOCAL INTEGER k,b,n LOCAL a$ FOR k = 0 TO &h0f a$ = " " PRINT HEX$(startblock+k*16,8);" "; FOR n = 0 TO 15 b = PEEK(BYTE startBlock+n+k*16) PRINT HEX$(b,2);" "; IF b > 31 AND b < 128 THEN a$ = a$ + CHR$(b) ELSE a$ = a$ + "." ENDIF NEXT n PRINT a$ NEXT k END SUB END |
||||
jirsoft Guru Joined: 18/09/2020 Location: Czech RepublicPosts: 532 |
VARADDR Too late Edited 2022-03-03 19:22 by jirsoft Jiri Napoleon Commander and SimplEd for CMM2 (GitHub), CMM2.fun |
||||
thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4040 |
CMM2 Manual: And there are corresponding POKEs for VAR and VARTBL. PEEK(VARHEADER) is currently unimplemented in MMB4W but I suspect that is just an oversight that Peter will address. This doesn't help with the DATA pointer since that is not an MMBasic variable - and infact is two internal variables which need to be updated as a single atomic unit. There is a fundamental design flaw in the whole BASIC DATA concept, you can't iterate through multiple DATA sections "simultaneously" because there is only one (hidden) pointer. Workarounds include either reading an entire DATA section into memory before operating on it, or designing the format of your DATA such that you can always RESTORE it to the start and then iterate through it to find a point you were at previously. Anway, I have an idea, time to "Fire up the Quattro" - bonus pop culture points for anyone who knows the reference without looking it up. Best wishes, Tom Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4234 |
A sinewave times itself makes for rectified McDonalds arches, but squared in height. Rectification because negative times negative is positive. Times a squarewave +/-1 of same phase and frequency also makes rectified mmmmmm's, but the gain is unity and not squared in magnitude. Both the above outcomes have a positive DC component. If the phases oppose, the DC will be negative. If phases offset, the result flips between both signs. DC can be lowpass filtered to measure phase shift. A sinewave times any other frequency makes a mess that low passes to zilch. Over time, all other frequencies but harmonics will remove themselves from the result. How much you lowpass determines how much mess leaks through, but very narrow lowpass is easy. I have almost described a homodyne detector or lock-in amplifier. Except they usually work in 90 degree pairs, so to produce a two part vector. Let's do that. Instead of multiplying sines and integrating over time, lets multiply only by the sign +1/-1 of a squarewave and integrate. Much easier, we only need add or subtract... Now multiply our sine times a bunch of "sign"waves that differ only in period. Just need an array of countdowns and an array to integrate each result. The sine*sine method is kinda like RMS, except we havn't yet taken the root of the mean squares. Sine*sign is plain old average. Probably relate just like they would for a multimeter. Vectors might need slight adjustment to account for that cheat. I am not mathematical and don't know how FFT works. I'm not sure this alternate makes sense or less work. If I were describing the same thing, I would not know. KD5ZXG Fourier transform works on repetitive signals. (google "wiki FFT"). In theory these signals repeat until eternity. If you take a snapshot of these signals (i.e. 10 sine waves) the beginning and the end of the 10 sine waves represent a distortion of the "eternal signal", they are distortions. To minimize the disruption effect of the beginning and end of the snapshot, each sample in the snapshot is "weighted". It's contribution to the Fourier Transform is made more -or less- important. The "weighting" is called "windowing". There are many types of windows. There is a rectangular window (meaning all samples have the same weight), there is triangular weighting (the first and last samples have zero weight, the centre samples have maximum weight. Samples in between have a weight that is a linear interpolation between first and centre sample. If you execute the program in my post, and press "w" a few times, you see a number of windowing techniques, and the effect on waveform and FFT. One of the weighting standards is the "Hann Window". This is similar to the triangular windowing, but samples between first and centre are weighted with a sine^2 function. Play with the program, and experience. Vohout note: the FFT window shows linear magnitude FFT for demonstration purpose (the lin2log function is commented out). Edited 2022-03-03 20:24 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4040 |
Peter, I offer this for your consideration regarding manipulating the global data pointer. It's been "mangled" due to the changes I've been making to modularise the MMBasic code for MMB4L, but I'm sure you can figure out how to apply it to your implementation: /** PEEK(DATAPOS) */ static void peek_datapos(int argc, char **argv, char *p) { if (argc != 1) ERROR_SYNTAX; uint64_t data_pos = ((NextDataLine - ProgMemory) << 32) + NextData; g_integer_rtn = data_pos; g_rtn_type = T_INT; } /** POKE DATAPOS data_pos% */ static void poke_datapos(int argc, char** argv, char *p) { if (argc != 1) ERROR_ARGUMENT_COUNT; uint64_t data_pos = (uint64_t) getinteger(p); NextDataLine = ProgMemory + (data_pos >> 32); NextData = data_pos & 0xFFFFFFFF; } And example MMBasic: Option Base 0 Option Default None Option Explicit On Restore my_data mad(5) End Sub mad(depth%) If depth% = 0 Then Exit Sub Local data_pos% = Peek(DataPos) ' Cache the data pointer. Local s$ Restore my_data my_label: ' There seems to be an issue with using recursion and DO or FOR loops ? ' Might be MMB4L specific. Read s$ If s$ = "" Then Poke DataPos data_pos% ' Restore the data pointer. Exit Sub EndIf Print Space$(5 - depth%) Str$(depth%) s$ " "; If depth% > 1 Then Print mad(depth% - 1) Goto my_label End Sub my_data: Data "A", "B", "C", "D", "E", "" EDIT: And before anyone starts to "have a go at me" for diverging from "standard" BASIC, we had an extremely flexible way of providing stateless data access by using CSUBs as binary data blobs, until Peter decided it was a hack and wasn't going to be supported by MMB4W . Edited 2022-03-03 21:18 by thwill Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6792 |
The DATA system on the PicoMite isn't a disaster now you can have relative RESTORE points. There's no need for READ loops to get to where you want. You can have as many pointers at you like pointing to the data block. DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA mypointer=1 mypointer20=20 RESTORE mypointer 'restores to first item RESTORE (mypointer+5) '6th item RESTORE (mypointer20+5) '25th item Note that mypointer and mypointer20 are independent as are the values added to them. You only need a RESTORE() to get to any DATA item. Edited 2022-03-03 22:47 by Mixtel90 Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4040 |
If the manual is correct then they aren't relative: RESTORE [line] Resets the line and position counters for the READ statement. If 'line' is specified the counters will be reset to the beginning of the specified line. 'line' can be a line number or label or a variable with these values. If 'line' is not specified the counters will be reset to the start of the program. Relative would look like: RESTORE [line_num|label], line_offset or possibly: RESTORE RELATIVE line_offset where any of 'line_num', 'label' and 'line_offset' could be literals or variables. Which I think would also provide a way to deal with the issue, provided you were careful in the arrangement of your DATA statements and were prepared to live with it all going haywire if you decided to add some comments or additional lines within the DATA statements. I really was quite happy with CSUB and PEEK(CFUNADDR cfun). Best wishes, Tom Edited 2022-03-03 23:14 by thwill Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6792 |
Good point - RESTORE n only restores to the line, not the actual data. I messed up there. Fine if you have an equal number of items on every line and you read a full line though. Why would comments make a difference? Peter does have a point though, CSUB was never intended to be used for data storage and using them as such is a first class kludge - even if it does work. :) I suppose fixed-length data can be POKED into strings or even longsstrings. (Waiting for Tom to store data in BMP files... :) ) Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
William Leue Guru Joined: 03/07/2020 Location: United StatesPosts: 393 |
I get "illegal option" when I do OPTION USBKEYBOARD US -Bill |
||||
thwill Guru Joined: 16/09/2019 Location: United KingdomPosts: 4040 |
Because it would change the line numbers and mess up any carefully setup pointers from your original example. I agree, let's have: BINARY name 01234567 89ABCDE ... END BINARY Though due to MMBasic limitations BINARY and CSUB would probably end up having to share a command token so basically you're back to using the CSUB mechanism with some syntactic sugar. My real issue is that we had something with all the power required and so far we're looking at replacing it with something less capable. It's not where to store the data in memory that is the problem, it's where to read the data you want to store in memory from, or to put it another way how to initialise the memory storage. If I wanted to use files I could just use binary files, they wouldn't have to be bitmaps. Also locating files relative to a .INC file is problematic since once a program is running MMBasic doesn't know anything about the arrangement of the .INC files that made up any given program. But it does pose the question without the CSUB mechanism how do you put bitmaps into programs on 'mites that don't have an SD card reader ? Best wishes, Tom Edited 2022-03-04 02:26 by thwill Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
Page 2 of 30 |
Print this page |