Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

So it wouldn't blow your COMPUTER up, but your fans might fly to pieces trying to cool it down.  :p

I'll include a documentation update with the next update - JMP and IFJMP should definitely have some more detailing, both on the running functions/explodey aspect, and just in general, considering they're both heavily influenced by Redcode and thusly likely to confuse folks.

I know it's not immediately related to the JMP discussion, but should I think about adding an auto-save whenever you compile a program?  E.G, to [ProgramName].autosave.casm?  That way if your game explodes, you're not gonna lose your code.  Same for the drive, maybe do an autosave of it at regular intervals?

An in-game flame-out animation might be kinda fun - if it detects the loop, the halt light comes on and steam comes outta the box.

--

public List<int> runAddresses; //assume it's initialized elsewhere
public int currentCounter; //self-evident
public int[] RAM; //self-evident
void TimedUpdate() //this runs at the computer's clockrate to execute code and such
{
    //code surrounding halts and such goes here
    runAddresses.Clear();
    DoProcess();
}
void DoProcess() //this actually executes op-codes & whatnot
{
    runAddresses.Add(currentCounter);
    int data,opCode,arguments; //data is the memory data at RAM[currentCounter], and arguments is the last 28 bits of said data
    //insert all the other functions and such
    if(opCode==4) //JMP
    {
        int destAddress; //assume this is set by reading the arguments & whatnot
        if(runAddresses.Contains(destAddress))
        {
            DoFlameout();
        }
    }
}

Rough pseudocode for how to integrate the recursive loop prevention as you suggested it.  Clear the list of run addresses before running op-codes, and add the current program counter to the list of run addresses at the beginning of the op-code-runner function.  If the list of run addresses contains whatever JMP is supposed to be jumping to, cause a flame-out animation & halt the processor, rather than running it.

This SHOULD catch all possible recursive JMP loops that'd cause a stack overflow, without breaking things like the Blinkenlights loop.  Only situation I can think of would be something with self-modifying code looping, but...that shouldn't even be an issue (since a non-JMP call will reset the list of addresses) in the current version of the game.  A multithreaded two-core version of the TC-06 could encounter problems with that, if your modification was happening on the other processor, though.  :/

I do appreciate the suggestions & bug reports - thank you for providing them!

(As for halting and catching fire - I think that should literally just be "run JMP 0 0 for a special surprise" or something :P)

(+1)

Some kind of auto-save would probably be a good idea, yes - much like for a text editor, since it includes one - and I do agree that it should not auto-save by overwriting the proper (manual) save file(s). I believe the approach you suggest should work - not sure if it's the best one, or whether it should be a game-wide auto-save file, I suppose it depends on how/when you want to detect and load it. (As in, on game start, or when a user tries to load a previous save, or when they re-enter a level with any auto-save file, or what. Also consider asking first instead of just loading, in case the auto-save itself got corrupted enough to crash the game when loaded.) Whether to auto-save the entire game state in one file, or several, probably depends on what features you want to have as well - e.g. if you want to allow save/load of disk state separately from program or level, keeping them separate (and separately restorable) is probably better, but keeping the entire auto-save in one file would probably be easier if you (maybe later) find you want to save other parts of the game state as well (e.g. which stage of a tutorial the player was at).

To be honest, I think that's the largest and in many ways most difficult part of creating something like this - deciding how exactly you want it to work, to be properly user-friendly. I don't have any real answers for that part, it's not really my field - so you should consider anything I say about it to be just suggestions for things to consider when you make your own decisions about it.

--

Your pseudocode is pretty much it, yes - or close enough anyway. (Assuming the recursion only happens in the else branch of the inner if.) Not exactly what I had in mind (ends up halting at a different instruction), but I believe it should work.

Actually, come to think of it, assuming that no instruction should be able to be repeated (via recursion) in the same clock cycle without a flameout, there's a slightly simpler and safer variant that would catch loops of any instruction, not just a JMP. The additional safety comes from the code being concentrated in fewer places, and not needing to be duplicated for each relevant instruction.

... (the rest is same as before...)
void DoProcess() //this actually executes op-codes & whatnot
{
    if(runAddresses.Contains(currentCounter))
    {
        DoFlameout();
        return;
    }
    runAddresses.Add(currentCounter);
    ... code for each instruction goes here, no further checks needed...
}

Of course, if you ever add any instructions that should be able to re-run the same address immediately (e.g. an "overwrite self and immediately re-execute" instruction), this won't work and you'd have to spread it out per instruction again. But I'd suggest against adding something like that, since it's rather dangerous (it could overwrite itself with itself, and then there's a loop again. Better to say that the overwrite requires a clock cycle, even if the implicit jump-to-self that follows doesn't).

And yes, this solution would somewhat limit a two-core version of the PC, but logically, if such a loop would break a single-core CPU, then it should also break the core that ran the loop in a two-core setup as well, even if the other core would still work afterwards. (Which it might not, since the first core breaking might also break something on the motherboard that the second core needs to function.) It's also easy to work around - just ensure that your busy loop includes a non-JMP instruction to give the second core a moment between instructions to modify the first core's memory. (It's kind of arguable whether it matters which of the two instructions it modifies...) Or use IFJMP instead, since that's not instant. (Unless you intend to make it be?)