|
Forum Index : Microcontroller and PC projects : PicoMite V6.02.00 release candidates - Structured types
| Author | Message | ||||
| PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 1688 |
![]() Broken record alert: Android + OLIBasic for the HMI SQL is part of Android Just as easy as MMBasic Many remote control options (TeamViewer, etc.) Amazing graphics handling Battery backup WiFi, Bluetooth, etc. Capacitive touchscreen Keyboard/mouse support Cameras, all kinds of sensors Dirt cheap Ubiquitous |
||||
| damos Regular Member Joined: 15/04/2016 Location: AustraliaPosts: 81 |
My first thought was to use MAUI because I can use it on Windows or Android (or even iOS for those fruit people, or Linux for the people on this forum). I have found bluetooth serial to MAUI is quite simple through a HC06 and it works very well on Windows and Android. I can use Pico and all the other embedded options for the hardware (I have done the RFID sensors in Arduino because they have a library which just works). Peter has added this nice stuff with the structs which will make every project better and it is getting close to the people where everything can be done on the Pico itself. While I also use many databases >10GB, the SQLite stuff I have used for Android is really tiny and the files are tiny at around 50kB for 3 files with not much in the database. The data size will be largely the same even if I use text files but realistically you will probably only have dozens to hundreds of rows and it will fit on the build in flash drive. It is really all about the convenience of SQL for finding and sorting. The struct feature doesn't quite make a database yet. It is in-memory which really limits the size and the only collection type is array which means that every search needs to be implemented in specialized code. If we had an array find where you can search the array for a struct where the specified property matches a condition it would be close. |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10838 |
Volhout Done more research and this is crap code ctrl.poll.single is being called with driver$ set to "" and then it relies on ON ERROR SKIP to not error on the line "call driver$,ctrlOPEN" For some reason the on error skip is not being actioned on the 5th time the function is called with a blank value in driver$ but bluntly this is just rubbish |
||||
| lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3546 |
Suppose I have 500 records of the following struct in a file: Type JobRec LinkID As INTEGER LastName As STRING LENGTH 15 Age As INTEGER Occupation As STRING LENGTH 20 Salary As INTEGER End Type DIM job as JobRec I figure the length is 8+(15+1)+8+(20+1)+8 is 61. [Wrong, see below] How do I read, say, the 100th record in the file into job with random access? Note if I run a program listing the first 10 records using this struct and then do PRINT len(job), I get 10. What does that signify? Is there a way to retrieve the length of a structure without counting? Hmmm. I see that I disregarded alignment. Recalculating, maybe 64. Anyway, if I run this: Option EXPLICIT Option DEFAULT NONE Type JobRec LinkID As INTEGER LastName As STRING LENGTH 15 Age As INTEGER Occupation As STRING LENGTH 20 Salary As INTEGER End Type Dim job As JobRec Dim i As INTEGER Print "LinkID | Last Name | Age | Occupation | Salary " Print "-------+-----------------+-----+----------------------+---------" Open "jobs.dbt" For INPUT As #1 For i = 1 To 10 If Eof(#1) Then Exit For Struct LOAD #1, job ' Format: LinkID (4 digits right-aligned) Print Right$(" " + Str$(job.LinkID), 4) + " | "; ' Format: Name (15 chars left-aligned) Print Left$(job.LastName + Space$(15), 15) + " | "; ' Format: Age (3 chars right-aligned) Print Right$(" " + Str$(job.Age), 3) + " | "; ' Format: Occupation (20 chars left-aligned) Print Left$(job.Occupation + Space$(20), 20) + " | "; ' Format: Salary (8 chars right-aligned) Print Right$(" " + Str$(job.Salary), 8) Next i Close #1 > run I get this LinkID | Last Name | Age | Occupation | Salary -------+-----------------+-----+----------------------+--------- 1 | Butt | 63 | Analyst | 116000 2 | Darakjy | 61 | Analyst | 64000 3 | Venere | 63 | Analyst | 106000 4 | Paprocki | 37 | Engineer | 48000 5 | Foller | 31 | Analyst | 100000 6 | Morasca | 30 | Teacher | 36000 7 | Tollner | 65 | Engineer | 87000 8 | Dilliard | 18 | Sales | 63000 9 | Wieser | 31 | Teacher | 124000 10 | Marrier | 37 | Sales | 104000 But with this: Option EXPLICIT Option DEFAULT NONE Type JobRec LinkID As INTEGER LastName As STRING LENGTH 15 Age As INTEGER Occupation As STRING LENGTH 20 Salary As INTEGER End Type Dim job As JobRec Dim i As INTEGER Print "LinkID | Last Name | Age | Occupation | Salary " Print "-------+-----------------+-----+----------------------+---------" Open "jobs.dbt" For random As #1 Struct LOAD #1, job PrintRec Seek #1,64*2 Struct LOAD #1, job PrintRec Close #1 End Sub printrec ' Format: LinkID (4 digits right-aligned) Print Right$(" " + Str$(job.LinkID), 4) + " | "; ' Format: Name (15 chars left-aligned) Print Left$(job.LastName + Space$(15), 15) + " | "; ' Format: Age (3 chars right-aligned) Print Right$(" " + Str$(job.Age), 3) + " | "; ' Format: Occupation (20 chars left-aligned) Print Left$(job.Occupation + Space$(20), 20) + " | "; ' Format: Salary (8 chars right-aligned) Print Right$(" " + Str$(job.Salary), 8) End Sub I get this (even the first record doesn't print correctly): LinkID | Last Name | Age | Occupation | Salary -------+-----------------+-----+----------------------+--------- 0 | | 0 | | 0 768 | | 160 | | 27136000 Here's the file zipped. jobs.zip ~ Edited 2026-01-07 14:30 by lizby PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
TassyJim![]() Guru Joined: 07/08/2011 Location: AustraliaPosts: 6426 |
Seek #1,64*2+1 first byte is 1 so add 1 to the calculated offset Jim VK7JH MMedit |
||||
| toml_12953 Guru Joined: 13/02/2015 Location: United StatesPosts: 540 |
You don't have to calculate manually, you can find the length of your structure by: Struct(SIZEOF "JobRec") In your case, this is 64 bytes. |
||||
| toml_12953 Guru Joined: 13/02/2015 Location: United StatesPosts: 540 |
No need for the leader bytes. This will do the same thing: Print Str$(job.LinkID, 4) + " | "; The STR$ function automatically pads numbers with spaces on the left to create the size you specify and right-justify your numbers. |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5602 |
Interesting. Thanks for diving so deep into it. I might not have been able to grasp to complexity of the code. Good decision I did not decide to start altering it. The intersting question however is: why does ON ERROR SKIP work 4 times, but not the 5'th time (in the same situation) ? Is this a nesting issue ? Maybe it points to an issue in 6.02.00 (regardless how good the code is). Volhout P.S.This code has not been changed since 5.08.00 (release of Game*Mite). Although there may be better ways to do this today, MMBasic was not that mature in 5.07.xx when the code was written. This may have been the way to do things back then. PicomiteVGA PETSCII ROBOTS |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10838 |
That is what I need to solve (it isn't always the fifth), but I also want to test a very simple workaround for the program for you. Are there any other programs that don't run or just this one? UPDATE This change fixes the bug in terms of the program running. I don't know what it is supposed to do so can't test further Function ctrl.poll_single%(driver$, mask%) On Error Ignore If driver$<>"" Then Call driver$, ctrl.OPEN If MM.Errno = 0 Then Local key%, t% = Timer + 5 Do If driver$<>"" Then Call driver$, key% ctrl.poll_single% = key% And mask% If ctrl.poll_single% Then Do While key% : Pause 5 : Call driver$, key% : Loop Exit Do EndIf Loop While Timer < t% If driver$<>"" Then Call driver$, ctrl.SOFT_CLOSE EndIf On Error Abort End Function UPDATE to the UPDATE found and fixed. Both the DIM bug and this one will be fixed in RC2 Edited 2026-01-07 18:50 by matherp |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5602 |
There is still a long list of programs to test, but the (historically) most critical ones have been tested, and run. Appart from Lazer-Cycle I have not seen problems (except for Kong, but Kong was not in release state yet. It is in a development phase. And it challenged heap (I recall). I could focus on Kong first (maybe it points to something ?), and then follow up on the other programs. One that I need to re-create a test setup for is the thermal camera (the parts are scattered around my lab). I know that one pushed the boundaries of heap also (so many arrays with floating point variables, and MATH) but was final, on both Game*Mite as well as VGA. Volhout Edited 2026-01-07 18:46 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10838 |
See previous updated post. Note: my fix is still recommended as erroring and recovering is very inefficient Edited 2026-01-07 19:25 by matherp |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10838 |
V6.02.00RC2 PicoMiteV6.02.00RC2.zip Fixes bug introduced the cause an error when the DIM command used a function in the dimension calculatation Fixes bug that can occur when ON ERROR IGNORE is used in a program also running MMbasic interrupts For info, this was the bug fix process for ON ERROR IGNORE. The DIM bug was much harder. ME: “The implementation of structures has somehow affected the operation of ON ERROR IGNORE. This subroutine should never give an error when driver$ is invalid but it does at undefined intervals which suggests some something in structure parsing is changing the status of OptionErrorSkip from -1. Investigate this and fix it. Note the names with . characters are not structures just normal variables but this doesn't seem to be the issue. Function ctrl.poll_single%(driver$, mask%) “On Error Ignore Call driver$, ctrl.OPEN If MM.Errno = 0 Then Local key%, t% = Timer + 5 Do Call driver$, key% ctrl.poll_single% = key% And mask% If ctrl.poll_single% Then Do While key% : Pause 5 : Call driver$, key% : Loop Exit Do EndIf Loop While Timer < t% Call driver$, ctrl.SOFT_CLOSE EndIf On Error Abort End Function CLAUDE OPUS 4.5: Now let me look at structure-related code that might be affecting OptionErrorSkip. Let me search for structure parsing: Let me look at the findvar function to understand how variables with dots are handled: Now let me look at how struct member access is resolved, particularly in the FindStructBase and ResolveStructMember functions, and see if they may be modifying OptionErrorSkip: Now, let me search for ResolveStructMember: These functions don't seem to modify OptionErrorSkip. Let me look at the call flow when a variable with a dot is accessed, and check specifically where evaluate() is called: Now, let me look specifically for the code where errors are thrown or handled in evaluate(): MMBasic.c I see! At line 433-434, after each command, OptionErrorSkip is decremented if it's in the range 0 < OptionErrorSkip < 100000. When ON ERROR IGNORE is set, it sets OptionErrorSkip = -1 which should NOT be decremented. Now let me search for places where OptionErrorSkip could be being modified in the structure-related code or elsewhere: Now let me check MM_Misc.c at the lines that save and restore OptionErrorSkip: This is interesting! At lines 8953-8957, when an interrupt occurs, the code saves OptionErrorSkip if it's > 0, otherwise sets SaveOptionErrorSkip = 0, and then sets OptionErrorSkip = 0. The bug is here: When OptionErrorSkip = -1 (ON ERROR IGNORE), the condition if (OptionErrorSkip > 0) is false, so SaveOptionErrorSkip is set to 0. Then OptionErrorSkip is set to 0, losing the IGNORE state. When returning from the interrupt (line 1896-1897), because SaveOptionErrorSkip was saved as 0, the restore code doesn't do anything, and OptionErrorSkip remains 0. Let me verify this is the issue by looking at the full context: MM_Misc.c Now I understand the bug: 1. When ON ERROR IGNORE is set, OptionErrorSkip = -1 2. When an interrupt occurs, the save code checks if (OptionErrorSkip > 0) which is false for -1 3. So SaveOptionErrorSkip is set to 0 4. Then OptionErrorSkip is set to 0 5. When returning from interrupt, if (SaveOptionErrorSkip > 0) is false (it's 0), so OptionErrorSkip is not restored 6. Result: OptionErrorSkip is now 0 instead of -1, meaning ON ERROR IGNORE is lost The fix should handle the case where OptionErrorSkip is negative (IGNORE mode). I need to change both the save and restore logic: Edited MM_Misc.c+3-1 Let me verify the changes look correct: The fix is complete. Here's a summary of the bug and fix: Bug Analysis The issue was in the interrupt handling code in MM_Misc.c. When ON ERROR IGNORE is active, OptionErrorSkip is set to -1. The problem: 1. When an interrupt fires (line 8953), the code saved OptionErrorSkip only if > 0, missing the -1 (IGNORE) case 2. For IGNORE mode, SaveOptionErrorSkip was set to 0 3. OptionErrorSkip was then reset to 0 during interrupt processing 4. On IRETURN (line 1896), the restore code only handled > 0, so IGNORE mode was never restored Result: Any interrupt occurring while ON ERROR IGNORE was active would silently disable error suppression, causing sporadic unexpected errors. Fix Applied 1. Save logic (line 8955): Changed if (OptionErrorSkip > 0) to if (OptionErrorSkip != 0) to also save negative values like -1 2. Restore logic (line 1898-1899): Added else if (SaveOptionErrorSkip < 0) branch to restore ON ERROR IGNORE state Edited 2026-01-07 20:05 by matherp |
||||
| bfwolf Senior Member Joined: 03/01/2025 Location: GermanyPosts: 131 |
I would recommend always using the `Struct(SIZEOF "typeName")` function, as this also takes any necessary padding bytes into account. Peter has written quite a bit about this in the Struct manual: The reason is alignment. Calculating the lengths manually is very tedious and prone to errors! If you really need to know them, add a `Print Struct(SIZEOF "typeName")` statement to your program, which you can then comment out when no longer needed. |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5602 |
@Peter, is this real ? Claude fixed the bug ? Nice !!! Volhout PicomiteVGA PETSCII ROBOTS |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10838 |
Absolutely. and this is the dialog for the DIM bug. Note how I stopped it processing and steered it to find the solution. ME: User: The parsing of structure in cmd_dim has broken something. Analyse and fix numpoll%=22 > Dim CTRLS_TO_POLL$(Max(1, num_poll% - 1)) Error : Unexpected text: ) CLAUDE: GitHub Copilot: Searched for text `void cmd_dim`, 1 result Searched for regex `cmd_dim\(void\)\s*\{`, no results Searched for text `cmd_dim(void)`, 2 results Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4721-4721), lines 4721 to 4900 Let me continue reading the function to understand the structure parsing: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4900-4900), lines 4900 to 5100 Now I need to look at the `getcsargs` function as it seems to be the one parsing the arguments: Searched for text `getcsargs`, 20 results Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.h#225-225), lines 225 to 260 Now let me look at the `MakeCommaSeparatedArgs` function which is used for RP2350: Searched for text `MakeCommaSeparatedArgs`, 20 results Searched for text `void MakeCommaSeparatedArgs`, 1 result Searched for regex `MakeCommaSeparatedArgs\(unsigned char`, 1 result Let me search for the implementation: Searched for text `MakeCommaSeparatedArgs(unsigned char **tp`, 1 result Searched for files matching `**/MMBasic.c`, 1 match Searched for text `MakeCommaSeparatedArgs` (`**/MMBasic.c`), 1 result Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4211-4211), lines 4211 to 4400 Now I see the issue. The `MakeCommaSeparatedArgs` function should be properly handling nested parentheses. Let me check the `getclosebracket` function to understand how brackets are matched: Searched for text `getclosebracket`, 20 results Searched for regex `getclosebracket\(` (`**/MMBasic.c`), 3 results Let me find the getclosebracket function implementation: Searched for regex `unsigned char \*getclosebracket`, 1 result User: This was working functionality before the implementation of structures. The bug must be in the structure parsing in the DIM command GitHub Copilot: Let me look more closely at the `cmd_dim` function to find the structure-related parsing that was added: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4721-4721), lines 4721 to 4820 Now let me look at the `skipvar` function and `CheckIfTypeSpecified` to understand the flow: Searched for text `CheckIfTypeSpecified`, 20 results Searched for text `unsigned char *CheckIfTypeSpecified` (`**/MMBasic.c`), no results Searched for regex `CheckIfTypeSpecified\(unsigned`, 2 results Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4634-4634), lines 4634 to 4720 Now I see the issue! The `CheckIfTypeSpecified` function is checking for structure types before falling back to the default type. When parsing `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`, it's checking if `Max` is a structure type name. Since `Max` is not a structure type, it should work... but let me trace through more carefully. The issue is in the `CheckIfTypeSpecified` function. It's being called on the cmdline which includes the variable name and dimensions. Let me look at what's happening: 1. `getcsargs` is called to split up the arguments 2. `argv[i]` is `CTRLS_TO_POLL$(Max(1, num_poll% - 1))` The problem might be in how `MakeCommaSeparatedArgs` handles this - it might be splitting on the comma inside the `Max()` function! Let me check `MakeCommaSeparatedArgs` more closely: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4211-4211), lines 4211 to 4310 I see the issue now! In `MakeCommaSeparatedArgs`, when checking for opening bracket or function token: ```c if (*tp == '(' || (tokentype(*tp) & T_FUN)) ``` It calls `getclosebracket(tp)` to find the matching closing bracket. But if `tp` points to a function token (like `Max`), `getclosebracket` expects to start at `(`, not at the function token itself. Let me find the `getclosebracket` implementation: Searched for regex `unsigned char \*.*getclosebracket` (`**/*.c`), no results Searched for text `getclosebracket(unsigned char *p)`, 1 result Searched for regex `not_in_flash.*getclosebracket`, 1 result Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#5747-5747), lines 5747 to 5810 Now I see the problem! In `getclosebracket`, it starts counting from `i = 0` and counts `(` or function tokens as `i++`. But if you call it when `*p` is a function token (like `Max`), it will: 1. Start with `i = 0` 2. See a function token, increment `i` to 1 3. Move to the next character (the `(`) 4. See `(`, increment `i` to 2 5. Continue until it finds TWO `)` characters This means when `getclosebracket` is called on a function token, it incorrectly counts the function token itself as an opening bracket and then ALSO counts the actual `(` - so it ends up expecting two closing brackets instead of one! The issue is that the `getclosebracket` function was modified to handle function tokens, but this breaks when called from `MakeCommaSeparatedArgs` which detects function tokens AND opening brackets. Let me check the old behavior - let's look at the `makeargs` function (non-rp2350) to see how it handles this: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4400-4400), lines 4400 to 4550 Both `makeargs` and `MakeCommaSeparatedArgs` use the same pattern and call `getclosebracket`. The bug is in `getclosebracket` - when it's called on a pointer that points to a function token (like `Max`), it counts that token as an opening bracket. But then the actual `(` that follows is ALSO counted as an opening bracket. The fix should be in `getclosebracket`. When it encounters a function token, it shouldn't count it as an opening bracket because the `(` that follows will be counted separately. Let me look at what version of `getclosebracket` was there before. The problem is likely that someone added `|| (tokentype(*p) & T_FUN)` to getclosebracket to handle function tokens, but this creates double-counting. The fix should be: when `getclosebracket` sees a function token, it should just advance past it without incrementing the counter, since the `(` that follows will be counted. Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#5745-5745), lines 5745 to 5790 Now I understand the bug completely. When `getclosebracket` is called on a function token like `Max`: 1. `i = 0` 2. It sees `Max` token (T_FUN), increments `i` to 1, advances `p` 3. It sees `(`, increments `i` to 2, advances `p` 4. It sees `1`, just advances 5. It sees `,`, just advances 6. It sees `num_poll%`, just advances 7. It sees `-`, just advances 8. It sees `1`, just advances 9. It sees `)`, decrements `i` to 1, advances `p` 10. It keeps going looking for ANOTHER `)` since `i` is still 1! The fix is that function tokens should NOT be counted - only the `(` should be counted. Function tokens are followed by `(` which will be counted. Using "Replace String in File" ``` ``` The fix separates the handling of function tokens from bracket counting. Now: - Function tokens (`T_FUN`) are simply skipped (advance past without counting) - Only actual `(` characters increment the bracket counter - Only actual `)` characters decrement the bracket counter This prevents the double-counting issue where both the function token AND its following `(` were being counted as opening brackets. Made changes. ME: User: This is the implementation of getclosebracket before structures were implemented. Check that your change will operate exactly the same as the original code in the error example just posted // scans text looking for the matching closing bracket // it will handle nested strings, brackets and functions // it expects to be called pointing at the opening bracket or a function token unsigned char __not_in_flash_func (*getclosebracket)(unsigned char *p) { int i = 0; do { if (*p == 0) error("Expected closing bracket"); // Handle quoted strings - skip them entirely if (*p == '\"') { p++; while (*p != '\"') { if (*p == 0) error("Expected closing bracket"); p++; } p++; continue; } // Check for closing bracket if (*p == ')') i--; // Check for opening bracket or function token else if (*p == '(' || (tokentype(*p) & T_FUN)) i++; p++; } while (i); return p - 1; } // check that there is no excess text following an element // will skip spaces and abort if a zero char is not found CLAUDE: GitHub Copilot: Let me trace through the original code for `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: In MMBasic, function tokens (T_FUN) include their opening parenthesis as part of the token. So `Max(` is a single token. This means: **Original getclosebracket starting at the first `(` (array dimension):** - `i=0`, sees `(`, `i=1`, advance - sees `Max(` T_FUN token (includes the `(`), `i=2`, advance - sees `1`, advance - sees `,`, advance - sees `num_poll%`, advance - sees `-`, advance - sees `1`, advance - sees `)` (closes Max), `i=1`, advance - sees `)` (closes array dim), `i=0`, advance - Loop exits correctly! **My modified code:** - `i=0`, sees `(`, `i=1`, advance - sees `Max(` T_FUN token, **skips without counting**, advance - sees `1`, advance - ... - sees `)` (closes Max), `i=0`, advance ← **exits too early!** My fix was wrong! Since T_FUN tokens include the opening parenthesis, they SHOULD be counted. Let me revert to the original: Using "Replace String in File" ``` ``` Good, I've reverted `getclosebracket` to the original. The bug must be elsewhere in the structure-related changes to `cmd_dim`. Let me look more carefully at `CheckIfTypeSpecified` since it's called early in `cmd_dim`: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4634-4634), lines 4634 to 4685 Now let me look at `FindStructType`: Searched for text `int FindStructType`, 2 results Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#6330-6330), lines 6330 to 6370 Now I see the issue! In `CheckIfTypeSpecified`, when `STRUCTENABLED` is defined, it calls `FindStructType(p)` for ANY input that doesn't match INTEGER/STRING/FLOAT. For the DIM command parsing `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: 1. `CheckIfTypeSpecified` is called with `p` pointing to `CTRLS_TO_POLL$...` 2. It checks for INTEGER, STRING, FLOAT - no match 3. It calls `FindStructType(p)` which tries to match `CTRLS_TO_POLL` against structure names 4. It returns -1 (not found) 5. Since `AllowDefaultType` is true, it sets `tp = p` and `*type = DefaultType` That seems fine. Let me trace through `cmd_dim` more carefully: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4721-4721), lines 4721 to 4760 Wait - after `getcsargs`, the code loops through `argv[]` with `i += 2`, expecting odd `argc`. This is correct for comma-separated args where each variable and comma are separate entries. Let me check what `skipvar` does after `argv[i]`: Searched for text `unsigned char *skipvar`, 1 result Searched for regex `not_in_flash.*skipvar`, 1 result Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#5533-5533), lines 5533 to 5630 I see `skipvar` now handles struct member access with the `STRUCTENABLED` code. Let me continue reading: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#5620-5620), lines 5620 to 5700 Wait, I see an important detail. In `skipvar`, when it handles array indices, it uses: ```c if (*p == '(' || (tokentype(*p) & T_FUN)) i++; ``` This is the same bracket-counting logic. The problem might be in the struct member parsing section. Let me check the flow again for `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: Actually wait - the error message is "Unexpected text: )". This error comes from `checkend`. Let me trace where this is called in cmd_dim: Searched for text `checkend` (`**/Commands.c`), 15 results There's no `checkend` in cmd_dim. Let me search in other files: Searched for text `Unexpected text`, 4 results The "Unexpected text: $" message (which shows the actual text) is from `checkend` or another similar function. Let me look at the findvar code path since that's where the variable is actually created: Searched for text `void *findvar`, 1 result Searched for regex `void \*.*findvar\(unsigned char`, 1 result Searched for regex `not_in_flash.*\*findvar`, 1 result Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#3438-3438), lines 3438 to 3600 Let me see if there's code that might be calling `checkend` after `findvar`: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4000-4000), lines 4000 to 4100 I found it! Look at line 4051-4052: ```c else if (!(*p == ',' || *p == 0 || tokenfunction(*p) == op_equal || tokenfunction(*p) == op_invalid)) error("Unexpected text: $", p); ``` This code is looking after the LENGTH keyword parsing (or lack thereof) for string arrays. It's checking if there's unexpected text. The issue is that after processing the array dimensions, the pointer `p` should be pointing at the end, but something is going wrong. Let me look at the code just above this: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4025-4025), lines 4025 to 4060 Found it! This is the bug. Look at lines 4038-4046: ```c do { if (*p == '(') i++; if (tokentype(*p) & T_FUN) i++; if (*p == ')') i--; p++; } while (i); ``` This code is trying to skip over the array dimensions for string arrays. It uses the same pattern of counting `(` and T_FUN as openers and `)` as closers. But here's the problem: For `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: 1. `*p == '('` → true, `i = 1` 2. advance to `Max` token, `tokentype(*p) & T_FUN` → true, `i = 2` 3. This `Max` token IS the `Max(` combined, so advance puts us at `1` 4. `1` → neither condition, advance 5. `,` → neither, advance 6. ... etc ... 7. First `)` → `i = 1` 8. Second `)` → `i = 0` 9. Loop exits Wait, but this seems correct? Actually wait - step 3 is wrong. After processing the T_FUN token `Max(`, we advance and the next char is `(` from the tokenized `Max(`... no wait, T_FUN tokens include the `(`. Let me think again. The issue is that when we see the `(` from the array dimension, we increment `i`. Then we see the `Max(` function token - but this token ALREADY includes the opening paren. So we're double-counting the function's bracket. Actually no - this code first checks `*p == '('`, then checks `tokentype(*p) & T_FUN`. So: 1. `(` - array dimension open, `i=1` 2. `Max(` token - NOT `(` (it's a function token), but IS T_FUN, so `i=2` The function token `Max(` already contains the `(`, so the counter is now at 2 when it should only be at 2 (1 for array, 1 for Max's paren). Wait, that's actually correct then... Let me trace through more carefully what the stream looks like after tokenization. Actually the problem is that when the loop finishes, `p` should point AFTER the closing `)`. But wait - there are TWO closing parens: one for `Max()` and one for the array dimension. Let me trace: For `(Max(1, num_poll% - 1))` (the array dimension part including both parens): If T_FUN includes the `(`: 1. See `(` → i=1, advance 2. See `Max(` token (T_FUN) → i=2, advance 3. See `1` → neither, advance 4. See `,` → neither, advance 5. See stuff → advance 6. See `)` (closes Max) → i=1, advance 7. See `)` (closes array) → i=0, advance 8. Loop exits This seems correct! But wait - step 2 advances past the ENTIRE `Max(` token. After advancing from a T_FUN token, where are we? At `1`. So we never see a second `(` at all. Hmm, let me check what happens in `getcsargs`/`MakeCommaSeparatedArgs`. Perhaps the problem is there. When `MakeCommaSeparatedArgs` processes `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: 1. Copies `CTRLS_TO_POLL$` 2. Sees `(`, calls `getclosebracket(tp)` starting at `(` 3. In getclosebracket: - See `(` → i=1 - See `Max(` T_FUN → i=2 - See `1` → nothing - See `,` → nothing ← WAIT! This comma is inside the parens! - ... - See `)` → i=1 - See `)` → i=0 - Return 4. Back in MakeCommaSeparatedArgs, copy everything from `(` to last `)` inclusive Actually I wonder if the issue is that after `getcsargs` splits the line, the `argv[i]` string is being re-parsed incorrectly. Let me check what `getcsargs` gives for `Dim CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: After tokenization and `getcsargs`, we should have: - `argc = 1` - `argv[0] = "CTRLS_TO_POLL$(Max(1, num_poll% - 1))"` Since there's only one variable (no comma outside parens), argc should be 1. Then in cmd_dim: ```c p = skipvar(argv[i], false); // point to after the variable ``` After `skipvar`, `p` should point to the null terminator (since the entire string is the variable). Actually wait - when `argv[i]` is processed by `getcsargs`, is the closing `)` included in the copied argument? Let me re-read `MakeCommaSeparatedArgs`: ```c if (*tp == '(' || (tokentype(*tp) & T_FUN)) { int x; x = (getclosebracket(tp) - tp) + 1; memcpy(op, tp, x); op += x; tp += x; continue; } ``` So when it sees `(`, it finds the close bracket and copies EVERYTHING including the close bracket. Then it continues to the next character. After the `)`, the next character should be null (end of line). So the entire expression should be in argv[0]. Let me check if the problem is in how `findvar` handles the expression. In `findvar`, there's a call to `getargs`: ```c getargs(&p, MAXDIM * 2, (unsigned char *)"(,"); ``` This parses the dimensions. For `(Max(1, num_poll% - 1))`: - The `(,` delimiters mean: expect opening `(`, split on `,` - It should find `Max(1, num_poll% - 1)` as the dimension expression Wait, but `getargs` is different from `getcsargs`. Let me check `getargs`: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4430-4430), lines 4430 to 4500 OK so `getargs` (makeargs) also uses `getclosebracket`. Let me focus on the actual error location. The error "Unexpected text: )" comes from findvar at line 4051-4052. Let me look at that code path again. In findvar, after parsing the array dimensions with `getargs`, the code then checks for the LENGTH keyword for string arrays. The loop at lines 4038-4046 is trying to skip the parentheses: ```c if (*p == '(') { do { if (*p == '(') i++; if (tokentype(*p) & T_FUN) i++; if (*p == ')') i--; p++; } while (i); } ``` But wait - at this point, `getargs` has ALREADY consumed the `(...)` and `p` should be pointing AFTER the closing paren. Let me check... Actually no - `getargs` doesn't modify the original pointer in the same way. Let me re-read how `getargs` is called in findvar: ```c getargs(&p, MAXDIM * 2, (unsigned char *)"(,"); ``` The `&p` means `p` will be updated by getargs. Looking at the makeargs function... it uses a local `tp` and doesn't modify `*p` directly at the end. Actually let me check the macro: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.h#210-210), lines 210 to 230 The `getargs` macro calls `makeargs(x, ...)` where `x` is a pointer to pointer. Looking at the makeargs signature: ```c void MIPS16 __not_in_flash_func(makeargs)(unsigned char **p, int maxargs, unsigned char *argbuf, unsigned char *argv[], int *argc, unsigned char *delim) ``` The first param is `unsigned char **p` - a pointer to a pointer. So yes, `makeargs` could potentially update `*p`. But looking at the function body, it uses `tp = *p` and doesn't seem to update `*p` at the end. Let me search for where `*p` is updated in makeargs: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4310-4310), lines 4310 to 4400 I need to find where `*p` is updated at the end of makeargs: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4455-4455), lines 4455 to 4480 I notice that `makeargs` does NOT update `*p` at the end! It uses a local `tp` but never writes back to `*p`. So after `getargs`, `p` in `findvar` still points to the opening `(` of the array dimensions. Now I understand the issue better. After the `getargs` call, `p` still points at `(`. Then the code at line 4037-4046 tries to skip over the parentheses by doing its own counting loop. For `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`, after `skipvar` has parsed up to the `$`, and after `getargs` has parsed the dimensions, `p` points to `(Max(1, num_poll% - 1))`. Now the loop: ```c do { if (*p == '(') // First: yes, i=1 i++; if (tokentype(*p) & T_FUN) // First: no (it's '(') i++; if (*p == ')') // First: no i--; p++; // Now p points to Max token } while (i); // i=1, continue // Second iteration: // p points to Max token if (*p == '(') // No, it's a token not '(' i++; if (tokentype(*p) & T_FUN) // YES! Max is T_FUN, i=2 i++; if (*p == ')') // No i--; p++; // p advances ONE byte (past the token byte), NOT to after the '(' that's part of the token // This is the bug! After a T_FUN token, we only advance 1 byte but the token includes '(' ``` The problem is that when we see a T_FUN token, we increment `i` (correctly treating it as containing an opening paren), but then we only advance `p` by 1. But function tokens are multi-byte tokens! After a function token comes its opening `(` which should NOT be counted again (since T_FUN already counts it). Wait, let me reconsider. In the tokenized form, is `Max(` a single byte token, or is it the token byte followed by the `(` character? Looking at how `getclosebracket` works and the original code working - it seems like the tokenizer converts `Max(` into a single token byte, and that's why counting T_FUN as one opener works. But in the loop at line 4037-4046, after we process that token byte and increment i, we do `p++` which moves to the NEXT byte. If the next byte happens to be `(` (stored after the token), we'd count it again! Let me check what happens. If `Max(` is tokenized as: - Option A: Single byte token that represents `Max(` - Option B: Token byte for `Max` followed by literal `(` character I think Option A is correct based on how `getclosebracket` works. But then in the loop, after seeing the token byte and doing `p++`, we'd be at the first argument `1`, not at `(`. So the bug might not be here. Actually, wait - the problem might be simpler. Let me check if this loop was even modified for structures: Searched for regex `if \*p == '\(' copy everything|skip over the dimension values`, 1 result Let me check git history or any recent changes. Actually, since the user said this was working before structures were implemented, let me look for any code in findvar related to structures that might affect `p`: Searched for text `STRUCTENABLED` (`**/MMBasic.c`), 20 results Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#3515-3515), lines 3515 to 3580 I see there's struct member checking code in findvar at line 3515-3558. This code looks for a `.` in the variable name. Let me see if this might be affecting the flow incorrectly. But for `CTRLS_TO_POLL$`, there's no `.` in the name, so this shouldn't trigger. The name extracted would be `CTRLS_TO_POLL` (without the `$` which is handled separately). Let me think about this differently. The user said "The parsing of structure in cmd_dim has broken something". Let me look more closely at the structure parsing in cmd_dim - specifically at `CheckIfTypeSpecified`: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4634-4634), lines 4634 to 4685 Wait! I see the issue now! In `CheckIfTypeSpecified`, when STRUCTENABLED is defined: 1. It first checks for INTEGER, STRING, FLOAT - these use `checkstring` which returns a pointer PAST the keyword 2. If none match, it calls `FindStructType(p)` 3. If that returns -1 (not a struct), it sets `tp = p` and returns `tp` But in the non-STRUCTENABLED version: ```c else { if (!AllowDefaultType) error("Variable type"); tp = p; *type = DefaultType; } ``` It just sets `tp = p`. Same thing. So that's not the problem. Let me trace through cmd_dim again: ```c p = CheckIfTypeSpecified(cmdline, &type, true); // check for DIM FLOAT A, B, ... ImpliedType = type; { getcsargs(&p, (MAX_ARG_COUNT * 2) - 1); ``` So `p` is set to the return value of `CheckIfTypeSpecified`. For `Dim CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: - `cmdline` points to `CTRLS_TO_POLL$(Max(1, num_poll% - 1))` - `CheckIfTypeSpecified` checks if this starts with INTEGER/STRING/FLOAT - NO - With STRUCTENABLED, it calls `FindStructType(p)` which looks at `CTRLS_TO_POLL` and returns -1 (not a struct) - It sets `tp = p`, returns `p` (unchanged) So `p` still points to `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`. Then `getcsargs(&p, ...)` is called. Let me check what happens: Continue to iterate? [object Object] Made changes. User: @agent Continue: "Continue to iterate?" GitHub Copilot: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.h#566-566), lines 566 to 566 Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.h#225-225), lines 225 to 250 I see! For RP2350, `getcsargs` calls `MakeCommaSeparatedArgs`. For non-RP2350, it calls `makeargs` with `","` as delimiter. Let me re-examine `MakeCommaSeparatedArgs` more carefully: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4211-4211), lines 4211 to 4315 Now I see the issue clearly. Look at line 4279-4287: ```c // if an opening bracket '(' copy everything until we hit the matching closing bracket // this includes special characters such as , and keeps track of any nested brackets if (*tp == '(' || (tokentype(*tp) & T_FUN)) { int x; x = (getclosebracket(tp) - tp) + 1; memcpy(op, tp, x); op += x; tp += x; continue; } ``` When processing `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: 1. First, it copies `CTRLS_TO_POLL$` character by character via the "anything else" case 2. Then it encounters `(` and calls `getclosebracket(tp)` where tp points to `(` Now in `getclosebracket`, starting at `(`: - `i=0`, see `(` → `i=1` - see `Max(` token (T_FUN) → `i=2` - see `1` → nothing - see `,` → nothing - see stuff → nothing - see `)` (Max's close) → `i=1` - see `)` (array's close) → `i=0` - return pointer to last `)` This should be correct! getclosebracket returns pointer to the SECOND `)`, which is the array's close paren. Then: - `x = (pointer to second ')') - (pointer to '(') + 1` = length including both parens - Copy all that - Advance both op and tp past the whole thing So `MakeCommaSeparatedArgs` should produce `argv[0] = "CTRLS_TO_POLL$(Max(1, num_poll% - 1))"`. Now, back in `cmd_dim`, after `getcsargs`: ```c p = skipvar(argv[i], false); // point to after the variable ``` `skipvar` should parse `CTRLS_TO_POLL$(Max(1, num_poll% - 1))` and return a pointer to the null terminator (end of the string). But wait - let me look at skipvar again. The struct-related code might be the problem: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#5595-5595), lines 5595 to 5665 In the STRUCTENABLED section of `skipvar`, after processing the base variable and its array indices, it looks for `.` to handle member access. For `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`, there's no `.`, so this loop should just break immediately and return `p`. Let me check the earlier part of skipvar that handles the array indices: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#5560-5560), lines 5560 to 5600 OK so `skipvar` handles array indices correctly using the same pattern. After this, `p` should point past the closing `)`. For `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`: 1. Parse name: `CTRLS_TO_POLL` 2. Parse suffix: `$` 3. See `(`, enter array handling 4. Start with `i=1`, advance past `(` 5. See `Max(` token (T_FUN), `i=2` 6. See `1`, nothing 7. See `,`, nothing 8. See stuff, nothing 9. See `)`, `i=1` 10. See `)`, `i=0`, break 11. `p++` to step over closing bracket 12. Return `p` pointing past last `)` So skipvar returns pointing at the null terminator (end of argv[0]). Let me continue tracing cmd_dim: ```c p = skipvar(argv[i], false); // point to after the variable while (!(*p == 0 || *p == tokenAS || *p == (unsigned char)'\'' || *p == tokenEQUAL)) p++; // skip over a LENGTH keyword if there and see if we can find "AS" ``` After skipvar, `p` points to `\0` (end of string). The while loop condition checks if `*p == 0` - YES, so the loop doesn't execute. ```c chSave = *p; // chSave = 0 chPosit = p; *p = 0; // no change, already 0 ``` Then it checks `if (chSave == tokenAS)` - NO (chSave is 0). ```c strcpy((char *)VarName, (char *)argv[i]); // VarName = "CTRLS_TO_POLL$(Max(1, num_poll% - 1))" v = findvar(VarName, type | V_NOFIND_NULL); // check if the variable exists ``` Now `findvar` is called with `VarName = "CTRLS_TO_POLL$(Max(1, num_poll% - 1))"`. In findvar, it parses the name, sees `$` suffix (string type), then sees `(` and calls `getargs` to parse dimensions. `getargs` is called with delim `"(,"`. Let me trace through what happens: The string to parse is `CTRLS_TO_POLL$(Max(1, num_poll% - 1))`. But wait - in findvar, `p` has already advanced past the name and suffix. Let me check: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#3460-3460), lines 3460 to 3510 So after parsing the name `CTRLS_TO_POLL` and the `$` suffix, `p` points to `(Max(1, num_poll% - 1))`. Then the struct member check happens (but no `.` in name, so it falls through). Then at line ~3560: ```c if (*p == '(') { ... getargs(&p, MAXDIM * 2, (unsigned char *)"(,"); ``` `getargs` with delim `"(,"` expects the list to start with `(` (because first char of delim is `(`). It will: 1. See `(`, expect closing bracket, advance past it 2. Split on `,` So for `(Max(1, num_poll% - 1))`: - Skip opening `(` - Now at `Max(1, num_poll% - 1))` - Process until we hit `)` or `,` - See `Max(` token (T_FUN), copy until closing bracket via getclosebracket Wait, let me check makeargs again for how it handles this: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4360-4360), lines 4360 to 4445 Now I see it. At line 4432-4439: ```c if (*tp == '(' || ((tokentype(*tp) & T_FUN) && !expect_cmd)) { int x; x = (getclosebracket(tp) - tp) + 1; memcpy(op, tp, x); op += x; tp += x; continue; } ``` When processing `Max(1, num_poll% - 1))` (after the outer `(` was skipped): - `*tp` is the `Max(` token (T_FUN) - The condition `*tp == '(' || ((tokentype(*tp) & T_FUN) && !expect_cmd)` is TRUE - `getclosebracket(tp)` is called starting at the `Max(` token Now in `getclosebracket` starting at `Max(` token: - `i=0`, see `Max(` T_FUN → `i=1` - see `1` → nothing - see `,` → nothing - see stuff → nothing - see `)` → `i=0`, return pointing at this `)` So `getclosebracket` returns pointing at the FIRST `)` (Max's close). Then: - `x = (pointer to first ')') - (pointer to Max) + 1` - Copy `Max(1, num_poll% - 1)` - Advance tp to point AFTER the first `)` Now `tp` points to `)` (the SECOND one, the array's close)! The main loop continues: - `expect_bracket == true` and `*tp == ')'` → break! So `makeargs` exits with just `Max(1, num_poll% - 1)` copied as the dimension. But then `tp` points to `)`. The outer loop has broken. Looking at line 4464-4467: ```c if (expect_bracket && *tp != ')') SyntaxError(); ``` Wait, `*tp == ')'` so this doesn't error. After makeargs returns, where does this leave things? The argv would contain the dimension expression `Max(1, num_poll% - 1)` properly. But `p` in findvar wasn't updated (makeargs doesn't update `*p`). Actually wait - let me re-read. After the dimension is parsed, the code continues. Let me find where the error actually occurs: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4025-4025), lines 4025 to 4060 This is in `findvar` when `action & V_DIM_VAR` (which it is, from cmd_dim). For string arrays, it tries to skip the parentheses to look for LENGTH. At this point, `p` still points to `(Max(1, num_poll% - 1))` (because getargs doesn't update `*p`). The loop: ```c do { if (*p == '(') // First: YES, i=1 i++; if (tokentype(*p) & T_FUN) // First: NO ('(' is not T_FUN) i++; if (*p == ')') // First: NO i--; p++; // Now p points to Max( token } while (i); // i=1, continue // Second iteration: if (*p == '(') // NO (it's a token, not '(') i++; if (tokentype(*p) & T_FUN) // YES! Max( is T_FUN, i=2 i++; if (*p == ')') // NO i--; p++; // p moves 1 byte past the token byte // i=2, continue // Third iteration: // p now points to '1' (first arg to Max) if (*p == '(') // NO i++; if (tokentype(*p) & T_FUN) // NO i++; if (*p == ')') // NO i--; p++; // p moves to ',' // i=2, continue // And so on until we hit ')': // First ')' -> i=1 // Second ')' -> i=0, loop exits ``` After this loop, `p` points one past the second `)`, which should be `\0`. Then: ```c skipspace(p); if ((s = checkstring(p, (unsigned char *)"LENGTH")) != NULL) size = getint(s, 1, MAXSTRLEN); else if (!(*p == ',' || *p == 0 || tokenfunction(*p) == op_equal || tokenfunction(*p) == op_invalid)) error("Unexpected text: $", p); ``` If `p` points to `\0`, then `*p == 0` is true, so the condition `!(*p == ',' || *p == 0 || ...)` is false, and no error is thrown. So this path seems OK... Let me think about what else could be wrong. The error message is "Unexpected text: )". This means `p` points to `)` when that check happens. What if the issue is in how the T_FUN token is handled in the skip loop? Let me think about what a tokenized function looks like. Actually, I wonder if the issue is that T_FUN tokens are multi-byte. When we do `p++` after seeing a T_FUN token, we only advance ONE byte, but the token might be longer! Let me check how tokens are structured. Function tokens might be: token_byte followed by function index, then `(`. Or they might be single-byte tokens where the `(` is implicit. Searched for text `T_FUN`, 30 results Let me look at the tokenizer to understand how function tokens are structured: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#2260-2260), lines 2260 to 2300 I see! Look at lines 2271-2280. When processing a T_FUN token: 1. `p1 = p + 1` - p1 points after the token byte 2. `p = getclosebracket(p)` - this finds the closing bracket starting from the token 3. I stop the analysis at this point ME: User: You are going down a rabbit hole. This code worked before structures were implmented. The RP2040 uses getargs and the rp2350 uses getcsargs. Both show exactly the same issue. The problem may be in findvar where the possibility of a structure is being checked and it is consuming characters that it shouldn't if one isn't found CLAUDE: GitHub Copilot: Let me look at the structure checking code in findvar: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#3515-3515), lines 3515 to 3565 This code looks at `name` for a `.`, not `p`. Let me look for where `p` might be incorrectly modified. Let me search for other STRUCTENABLED sections in findvar: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#3450-3450), lines 3450 to 3520 I see - after extracting the name and type suffix, `p` points to whatever comes after. For `CTRLS_TO_POLL$(Max(...))`, after the name parsing loop and the `$` check, `p` points to `(Max(...)`. Wait - let me look more carefully at the name parsing. The loop is: ```c do { u = mytoupper(*p++); ... } while (isnamechar(*p)); ``` So it copies chars while they're name chars. For `CTRLS_TO_POLL$`: - Copies `C`, `T`, `R`, `L`, `S`, `_`, `T`, `O`, `_`, `P`, `O`, `L`, `L` - Stops when `*p` is `$` (not a namechar) Then `$` is checked and `p++` advances past it. So `p` points to `(`. Now let me check if there's any STRUCTENABLED code that might be consuming the `(`: Searched for text `g_StructArg` (`**/MMBasic.c`), 15 results Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4100-4100), lines 4100 to 4145 Let me look at the CheckIfTypeSpecified function in Commands.c again - this is called in cmd_dim before getcsargs: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4721-4721), lines 4721 to 4740 So `p = CheckIfTypeSpecified(cmdline, &type, true)` is called first. For `Dim CTRLS_TO_POLL$(Max(1, num_poll% - 1))`, let me trace: - `cmdline` points to `CTRLS_TO_POLL$(Max(1, num_poll% - 1))` - `CheckIfTypeSpecified` checks for INTEGER/STRING/FLOAT - no match - With STRUCTENABLED, it calls `FindStructType(p)` where p = `CTRLS_TO_POLL$(Max...)` Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#6325-6325), lines 6325 to 6360 `FindStructType` just reads the name, it doesn't modify the input pointer. Let me look at `CheckIfTypeSpecified` again: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/Commands.c#4634-4634), lines 4634 to 4685 I see it now! In `CheckIfTypeSpecified`, when STRUCTENABLED: 1. It calls `FindStructType(p)` - this returns -1 (no struct named CTRLS_TO_POLL) 2. Since `structidx < 0`, it goes to the else branch: ```c else { if (!AllowDefaultType) error("Variable type"); tp = p; *type = DefaultType; } ``` 3. It sets `tp = p` and returns `tp` This looks correct - it returns `p` unchanged. But wait - look at line 4652: ```c skipspace(p); // Skip any leading whitespace before type name ``` This modifies `p`! If there's a space after DIM, this would skip it. But for `DIM CTRLS_TO_POLL$...`, after the space is skipped, p points to `CTRLS_TO_POLL$...` which is correct. Hmm, let me think about this differently. The user said the problem is in findvar where structure checking consumes characters it shouldn't. Let me look at the struct member access code more carefully in findvar - specifically where it modifies `p`: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#3515-3515), lines 3515 to 3560 This code looks for `.` in the `name` variable, not in `p`. For `CTRLS_TO_POLL`, there's no `.`, so `dot` is NULL and this block is skipped. Wait - but what if the issue is elsewhere? Let me search for all places in findvar where `p` is modified in STRUCTENABLED blocks: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#4160-4160), lines 4160 to 4200 This code doesn't modify `p`. Let me look for any code path that might accidentally consume from `p` when it shouldn't. Actually, let me search for where `p` is modified anywhere in the area of the findvar function after structure code was added: Searched for regex `p\+\+|p\s*=\s*|p\s*\+=` (`**/MMBasic.c`), 100 results This is too noisy. Let me focus on findvar specifically and look for where the structure parsing might consume the wrong data: Read [](file:///d%3A/Dropbox/PicoMite/PicoMite/MMBasic.c#3560-3560), lines 3560 to 3750 Found it! Lines 3600-3614: ```c // After getargs, advance p past the (indices) - makeargs doesn't update *p // This is needed for struct array member access like points(4).x if (*p == '(') { unsigned char *ptemp = p + 1; int depth = 1; while (depth > 0 && *ptemp) { if (*ptemp == '(') depth++; else if (*ptemp == ')') depth--; ptemp++; } p = ptemp; // Now points past the closing ) } ``` This code was added for struct array member access, but it has a bug - it only counts literal `(` and `)` characters, not function tokens (T_FUN). So for `(Max(1, num_poll% - 1))`: 1. Starts at `(` 2. `ptemp` starts at `M` (the Max token) 3. Loop: `Max` token - not `(` or `)`, advance 4. Continue until it hits the first `)` (closing Max's paren) 5. `depth--` → `depth = 0` 6. Loop exits 7. `p` now points AFTER the first `)`, but before the second `)`! Now `p` points to `)` (the array's closing paren), which later causes the "Unexpected text: )" error! The fix is to account for T_FUN tokens in this loop, just like the other bracket-counting code does: Using "Replace String in File" ``` ``` The fix adds `|| (tokentype(*ptemp) & T_FUN)` to the condition that increments `depth`. This ensures that function tokens (which include their opening parenthesis as part of the token) are counted as opening brackets, matching the behavior of `getclosebracket` and other bracket-counting code throughout the codebase. Made changes. ME: User: That seems to have fixed that problem . Edited 2026-01-07 22:15 by matherp |
||||
| mozzie Senior Member Joined: 15/06/2020 Location: AustraliaPosts: 193 |
G'day Peter, Might have found another minor bug to squash: > option list PicoMite MMBasic RP2350B V6.02.00RC2 OPTION FLASH SIZE 16777216 OPTION COLOURCODE ON OPTION PICO OFF OPTION CPUSPEED (KHz) 200000 OPTION PSRAM PIN GP0 > option reset list Palm PicoGame*Mite Pico-ResTouch-LCD-3.5 Pico-ResTouch-LCD-2.8 PICO BACKPACK RP2040-LCD-1.28 RP2040LCD-0.96 RP2040-GEEK > This is the same with RP2350A, RP2040 seems ok but I haven't tested them all. The onward march of "AI" is incredible, not sure if it is exilarating or terrifying.... Regards, Lyle. |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10838 |
What is the bug? UPDATE OK I see it now Edited 2026-01-08 00:01 by matherp |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5602 |
@Peter, Kong runs fine with rc2, maybe it's issue was related to the fix you did. Volhout PicomiteVGA PETSCII ROBOTS |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10838 |
|
||||
| mozzie Senior Member Joined: 15/06/2020 Location: AustraliaPosts: 193 |
G'day Peter, Apologies, should have made that clearer. Also, would you consider the following for a future version of PicoMite: The ability to change the USB port on the Picomite from Serial device to Keyboard / Mouse protocol device? (or possibly gamepad) This is in respone to a question from Bill (Turbo46) in another thread but has always been something I think would greatly enhance the usability, the ability to create custom keyboards etc with the power and ease of MMbasic to do the encoding / macros etc. The idea of a 7" touchscreen keyboard that can be customised endlessly springs to mind, would also be great for people with disabilities etc. The ability to then plug this into a PicoMite USB variant to control it as well opens many possibilities. I think this was discussed early in the PicoMite story and has reappeared from time to time. A further enhancement would be a serial port on the same channel, but I fear I'm getting ahead of myself.... As usual I have no real idea how difficult (or otherwise) this would be. Regards, Lyle. |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2026 |