I don't know if this is a bug or not, it really depends on what the developers had intended, but I do find it to be some pretty strange behaviour which I don't quite understand the purpose of.
Let's say we have code executing in a given slot, we'll call it slot A. We also will be working with another different slot, let's call it slot B.
Slot A:
PRGEDIT B PRGEDIT AThe first line of the code above will execute just fine. We're allowed to edit the code in other slots. But the second line of code, such that we want to edit the slot our code is running in, crashes with an "Illegal function call error". Let's look at some more code. Slot A:
USE B GOSUB STR$(B)+":@THIS"Slot B:
@THIS PRGEDIT A PRGEDIT BUsing "GOSUB" to another slot has the same effect as the first code. When we are in Slot B, "PRGEDIT A" works fine, since we're editing another slot, but "PRGEDIT B" crashes because again, we're trying to edit our own slot, the slot the code is running in, and this causes a crash. So, at first glance it seems to me the developers don't want code being able to read and edit itself. I have no clue why this wouldn't be allowed, but that's what it seems. However, we haven't tried one thing yet. What if we use "GOTO" instead of "GOSUB"? Slot A:
USE B GOTO STR$(B)+":@THIS"Slot B:
@THIS PRGEDIT A PRGEDIT BSurprisingly, this code throws no errors. If you "GOTO" another slot, modifying your own slot becomes perfectly valid. This also works from Direct Mode. I can type in the GOTO statement and execute code that way from Direct Mode, and that code now is allowed to use PRGEDIT on itself. However, you have to attempt to run the code first, let it crash, and then execute from Direct Mode, since it needs to load the symbol table into memory first before GOTOs will work from Direct Mode. Let's take a look at this. Put this code into Slot 0:
@MAIN PRGEDIT 0 VAR I FOR I=0 TO PRGSIZE()-1 PRINT PRGGET$(); NEXTTry to run the code normally. It will crash with an error when it hits "PRGEDIT 0" because that's not allowed. However, by running the code we've now loaded the symbol table into memory. So go to Direct Mode and type:
GOTO "0:@MAINThe code will now execute just fine. It will read its own code and then print out to the screen. But, there's more interesting behaviour than this. The code that SmileBASIC interprets is not the code that is within the slot. SmileBASIC first loads the code from the slot into another location in memory and then executes it from there. We can see this using this code:
@MAIN PRGEDIT 0 PRGDEL -1 VAR I=0 WHILE BUTTON()!=#X PRINT I INC I WENDSo the WHILE loop of this code is simple, it just continues to count upwards forever until you press X. But look what's above the WHILE loop. Those two lines of code will delete all of the contents in Slot 0. But our code is stored within Slot 0. So at first you'd think, the code should delete itself, and then stop executing immediately, right? It will never reach the WHILE loop. But try it. Again type "RUN" in Direct Mode to get the error, then type the same GOTO statement mentioned before. The code will start counting away. It successfully executes, meaning it reached the WHILE loop. Now press X to stop the program. So, it reached the WHILE loop. Does that mean the program failed to delete itself? Open slot 0 now. You will find that it is empty. The code deletes itself successfully, but since it's not actually being executed from the slot itself but from another buffer in the 3DS's memory, let's call this "execution memory", it can continue executing even though it's deleted itself. So code can delete itself while continue to run. Using this knowledge, we can completely lift the restrictions from a program entirely while running it normally from Direct Mode like so: Slot A
1. Inject into Slot B so that it: - a. Deletes itself. - b. Jumps to line 4. 2. Use Slot B. 3. Jump to Slot B. 4. Some label here to signify the start of your code.Here's an example:
'Detect which slot we're in. VAR I FOR I=0 TO 3 IF PRGNAME$()==PRGNAME$(I) BREAK ENDIF NEXT 'Define our slots. VAR SLOT_A=I VAR SLOT_B=(SLOT_A+1) MOD 4 'Inject code into Slot B. PRGEDIT SLOT_B PRGDEL -1 PRGSET "@FIX" PRGSET "PRGEDIT "+STR$(SLOT_B) PRGSET "PRGDEL -1" PRGSET "GOTO "+CHR$(34)+STR$(SLOT_A)+":@MAIN"+CHR$(34) 'Execute the injected code. USE SLOT_B GOTO STR$(SLOT_B)+":@FIX" 'The main body of our program. @MAIN 'Print the code in the slot of the currently running program. PRGEDIT SLOT_A VAR I FOR I=0 TO PRGSIZE()-1 PRINT PRGGET$(); NEXT 'Delete itself. PRGDEL -1What this will do, when loaded into any given slot, is first lift the restrictions of "PRGEDIT", then it will read its own source code and print it to the screen, and then it will delete itself so the program you just executed will disappear from the slot (it will also clear the slot it used to lift the restrictions). At first to me it seemed like SmileBASIC devs seemingly don't want code that can read and modify itself at runtime. Why else would you disallow code from reading and writing to its own slot? But then again you can easily lift these restrictions with a GOTO statement so I don't know why they're there in the first place. In fact on their website it states under PRGEDIT that "Specifying the SLOT currently running will give an error". But it doesn't, that is, if you entered that slot with a GOTO statement. I don't know if this is a bug in the sense they don't want you to be able to use PRGEDIT on a program's own slot and this is a way around it, or if there's some good technical reason to why it doesn't initially work but is acceptable after GOTO statements. Either way it's definitely some strange behaviour and I thought it was worth pointing out.