Tokumaru's PC saving trick

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Tokumaru's PC saving trick

Post by psycopathicteen »

A couple of times, Tokumaru has mentioned a trick he uses for doing object routines, where after a game object is finished with one move, it can save the program counter, and continue where it left off.

It sounds pretty useful, but I don't know exactly how to make use of it. Can you give an example of how a character like Mario would work using this trick. I'm guessing you wouldn't be able to do directional jumps by walking and jumping at the same time, like I typically do.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Tokumaru's PC saving trick

Post by tokumaru »

I would probably use this for changing states: don't save the PC and the object will do the same thing it did last frame, jump somewhere new and save the PC and the object will do something new.

It's particularly useful for scripted state changes, like an object that does certain things in a certain order, because you can code the whole behavior as a series of loops without having to officially create new states, with names and entries in a table of states.

It basically makes state management easier, which is done implicitly rather than explicitly.

EDIT: I can't think of a good example for Mario, or any other character directly controlled by the player, since their states are mostly controlled by player input, but let's consider a simple enemy that just patrols an area, walking left and right and stopping for a few frames after bumping on walls before turning around. This enemy's A.I. could go something like this:

Code: Select all

WalkRight:
	;-save the PC (will resume from the next command);
	;-increment X coordinate;
	;-call physics subroutine;
	;-if no wall was hit, RTS;

WaitRight:
	;-set delay to 30 (half a second);
	;-save the PC (will resume from the next command);
	;-decrement delay;
	;-if delay is not 0, RTS;

WalkLeft:
	;-save the PC (will resume from the next command);
	;-decrement X coordinate;
	;-call physics subroutine;
	;-if no wall was hit, RTS;

WaitLeft:
	;-set delay to 30 (half a second);
	;-save the PC (will resume from the next command);
	;-decrement delay;
	;-if delay is not 0, RTS;
	JMP WalkRight
This example is a bit silly, because this is a very dumb enemy and I completely ignored collisions with the player, which obviously have to be taken care of (I would probably use subroutines for this, since this would happen in all states), but you get the picture. You get to program sequences of behaviors that seamlessly spill into the next, without having to bother creating formal states or a system for jumping between them with pointer tables and the like. If you want to switch states just jump to (or naturally flow into) it, that the new state will initialize itself (e.g. setting the delay) and mark the point at which it repeats (by saving the PC).
Last edited by tokumaru on Sun Apr 26, 2015 6:23 pm, edited 2 times in total.
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Tokumaru's PC saving trick

Post by thefox »

Sounds a lot like coroutines. (http://en.wikipedia.org/wiki/Coroutine)

And I agree that this concept can be very useful. State machines that implement an equivalent behavior can get very wild otherwise.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Tokumaru's PC saving trick

Post by tokumaru »

thefox wrote:Sounds a lot like coroutines. (http://en.wikipedia.org/wiki/Coroutine)
Indeed!
State machines that implement an equivalent behavior can get very wild otherwise.
Yes, the whole point is to avoid a traditional state machine, and code everything as sequentially as possible.

psycopathicteen, now I understand what you said about Mario... Just because "jumping" and "running" are different states you're saying that he wouldn't be able to do both at the same time, right? Well, that will depend on how you code your states. For me, the difference between "running" and "jumping" is whether the object has to stick to the ground or not, whether vertical physics is applied or not, how much influence player input has over the horizontal speed, but in both cases the horizontal physics will keep working. So yes, if you code the states properly, objects can do more than one thing in the same state.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: Tokumaru's PC saving trick

Post by psycopathicteen »

What's the benefeit for doing it this instead of just storing an immediate label? Is it to avoid making 2 8-bit writes to memory?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Tokumaru's PC saving trick

Post by tokumaru »

psycopathicteen wrote:What's the benefeit for doing it this instead of just storing an immediate label? Is it to avoid making 2 8-bit writes to memory?
Implicit state management. You don't have to do any of these, for example:

Code: Select all

StatePointers:
	.dw WalkingRight
	.dw WaitingRight
	.dw WalkingLeft
	.dw WaitingLeft

Code: Select all

	lda #$02
	sta ObjectState, x

Code: Select all

	lda ObjectState, x
	asl
	tay
	lda StatePointers+0, y
	sta Temp+0
	lda StatePointers+1, y
	sta Temp+1
	jmp (Temp)
Might not look like much, but once you have dozens of objects, each with their own set of possible states, things start getting harder to manage as you add/move/remove states. You have to think of a way to index the pointers by object AND by state, you have to change the codes for the states as you add/remove them (or create constants for all of them), things like that.

Besides getting rid of these annoyances, you get the ellegant "roll onto the next state" effect for the more linear behaviors, which is almost like a script system. You could make cutscenes with "scripted" actions like walking around, making gestures and talking without having to create full-fledged states for each little action that would be used only once but would still clutter the list of state pointers.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: Tokumaru's PC saving trick

Post by psycopathicteen »

So it's more useful for the 6502, than it is for the 65816? On the 65816, I can do "lda #special_move, sta AI_routine."
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Tokumaru's PC saving trick

Post by tokumaru »

psycopathicteen wrote:So it's more useful for the 6502, than it is for the 65816?
I don't know much about the 65816, but yeah, I imagine that being able to manipulate 16-bit pointers directly makes the whole process less clumsy, but I don't think that steals all the merit from the PC saving method.

I'm out of arguments to defend this technique though, so If you still don't see any advantage this method has over others, that's fine, I never claimed it was superior to other methods, just that I'm comfortable using it (it isn't even MY trick, like the thread title might suggest). :?

I'm not sure I got the point of this thread though... did you really want to know more about the technique or did you just want to point out a flaw you thought you detected (since in the very first post you guessed it wouldn't work for certain behaviors)? Then in your other posts you basically just dissed all the advantages I pointed out. Twice already I pointed out the script-like behavior that this technique allows, which would certainly be helpful on the SNES as well, but both times you only focused on 8-bit vs. 16-bit data transfers, which have nothing to do with actual algorithms, they're just small implementation details.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: Tokumaru's PC saving trick

Post by psycopathicteen »

No, I didn't make this thread to point out flaws, or criticize it. I just wanted to know more information about it, because I'm looking for ways on making my code more manageable, and I thought it was a neat idea.

I'll reread your posts to see if I missed anything important. I'm not really that familiar with scripts. I probably misunderstood what you said.
Last edited by psycopathicteen on Tue Apr 28, 2015 8:40 pm, edited 1 time in total.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Tokumaru's PC saving trick

Post by tepples »

Is it anything like continuation-passing style?
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Tokumaru's PC saving trick

Post by rainwarrior »

I've used a similar way of handling state in the past. This was in C++, so saving and resuming the PC wasn't really an option, but it has a similar way of treating the state as a pointer to code.

Code: Select all

struct Object
{
	int x,y,etc; // common object state data, position, velocity, etc.
	int tick_time; // if 0 do tick this frame, otherwise decrement once per frame
	void (*tick)(Object*); // function pointer to be called when tick_time is 0
	void (*damage)(Object*); // function pointer to be called when object is damaged
	void (*draw)(Object*); // function pointer for drawing object
};

// bee object

void bee_init(Object* o)
{
	o->tick = bee_tick_wait;
	o->damage = bee_damage;
	o->draw = bee_draw_normal;
	o->tick_time = 0;
}

void bee_damage(Object* o)
{
	--o->hp;
	if (o->hp <= 0)
	{
		// change state to animate death
		o->tick = bee_tick_die;
		o->draw = bee_draw_die;
		o->tick_time = 0;
		return;
	}
	else
	{
		// change visual state to indicate damage
		o->draw = bee_draw_damaged;
	}
}

void bee_tick_wait(Object* o)
{
	if (player_near(o))
	{
		// change to attack state
		o->tick = bee_tick_attack;
		o->tick_time = 0;
		return;
	}
	else
	{
		// check again in 30 frames
		o->tick_time = 30;
	}
}

void bee_tick_attack(Object* o)
{
	move_toward_player(o);
	if (player_touching(o))
	{
		// attack successful, go back to wait state
		damage_player();
		o->tick = bee_tick_wait;
		o->tick_time = 60; // don't update for 60 frames
		return;
	}
	o->tick_time = 0; // update every frame
}
I think it's not that different, conceptually, from what tokumaru is doing. Basically he would have the ability to put a YIELD macro or something similar in the middle of a function to defer execution for a frame, whereas I would have to break it into multiple functions, more or less, but the basic idea is the same: when you're done for the frame, store a pointer to the next code you want to run on this object.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: Tokumaru's PC saving trick

Post by psycopathicteen »

Okay I understand it better now. If your doing a cutscene, you don't need to explicitly make a routine for every little thing your character does.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Tokumaru's PC saving trick

Post by tokumaru »

psycopathicteen wrote:I'm not really that familiar with scripts. I probably misunderstood what you said.
Since Gunstar Heroes is being discussed in that other thread, and I just checked it out to comment on that thread, let me use it as an example. At the very beginning, there's an intro sequence, where the characters run, look up, talk, point at things... that's essentially a scripted animation. How would you code that in your game? Have you though of a way yet?

There are many ways you can do it, but one I can think of that wouldn't be a pain in the ass would be to write all the actions of each object in sequence, and advance (roll over) to the next state based on whatever is more convenient (end of action, other object's actions, frame count, etc.). This allows you to use the same objects you use in-game (with all their animations, physics, actions) for these animations, all you have to do is code the whole script as if it were a state and force that state when the cutscene starts.

The alternative would probably be coding an actual scripting engine (which is really not worth it unless you're coding a game that heavily relies on scripts, such an RPG), faking all the action with animations, tediously recreating it frame by frame.

This is sort of a "bonus" that comes with the technique though. The main point is still to simplify the state management. If you ask me, simply not having to name every little intermediary action is already a hell of an advantage!
tepples wrote:Is it anything like continuation-passing style?
I'm sleepy and I didn't understand a word of that article! :lol:
rainwarrior wrote:This was in C++
I always wondered how this would work in a high-level language. Interesting solution!
Basically he would have the ability to put a YIELD macro or something similar in the middle of a function to defer execution for a frame
Or simply RTS if I wanted the object to keep doing what it's currently doing.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Tokumaru's PC saving trick

Post by rainwarrior »

tokumaru wrote:
tepples wrote:Is it anything like continuation-passing style?
I'm sleepy and I didn't understand a word of that article! :lol:
That's because it's poorly written.

The idea is the current "state" of a function is both an argument and its return value. Returning from the function is like a "yield" or a "sleep", expecting it to be called again later with the state it returned, allowing it to resume.

It mostly comes up in functional programming languages, because they often lack access to a global context outside the function, so it's like doing multithreading "by hand", passing the current context as an argument and return value.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Re: Tokumaru's PC saving trick

Post by Bregalad »

it isn't even MY trick, like the thread title might suggest
I also use a PC saving trick, and I don't know how I got the idea, but I remember I was using traditional state machines originally. Not only it is annoying and a bit counterintuitive to program, but also it wastes a lot of ROM to have all this state dispatching and state saving instructions that you don't need anymore with a clever manipulation of the stack.

I also be sure to call object code directly from the main program when the stack is $ff. That way, for boss enemies, since they are more complicated they often need sub-states, so they can leave whathever they want on the stack and it'll still work. It just "offset" the stack of the remaining of the game by a couple of bytes. Whenever the game loop exists I make sure to set the stack pointer to $ff again, to avoid it become permanent (for example if the player dies during a boss battle, the boss will not terminate and leave bytes on the stack). This only work for one boss at a time of course, but I am pretty sure it's not a huge limitation.
Post Reply