Logo Pending


Frame sizes

And I don’t mean graphical windows.

I’ll admit it, I’m only passingly familiar with the Sierra PMachine’s internal workings. I know much more about the SC script code than the PMachine bytecode it compiles to. Specifically, I know it’s a stack machine with a single accumulator and that literally everything is a 16-bit value but while researching that thing from last time I finally figured out how sends work.

Let’s go through some examples of various things you might do in SCI code.

Let’s say we have a local variable that we want to set to a particular value: (= aLocal 42). Simple enough, right? That translates to ldi 42, sal aLocal. Well, technically the disassembler has no notion of what the local variables are named so that’d actually come out as sal local0 or whatever our local’s place in the list is but the instructions are clear: set the accumulator to a value, then store the accumulator in a local variable. A global variable is much the same but would use sag instead.

Here’s a slightly more complicated one:

(if aLocal
	(Printf "lol")
)
_:
	lal aLocal
	bnt notTrue
	push1 
	lofss strLol
	calle 921 1 2 //Printf, script 921 export 1.
notTrue:
	//rest of the script

See what’s going on there? First we use lal to load a local var’s value to the accumulator. Branch to another spot if that value is not truthy (non-zero), skipping over the Printf. Then we first push the amount of arguments given (just the one), and load the offset of our string to the stack. Then we call an external function by number. With one 16-bit argument on the stack taking two bytes, we look back that far plus another two to reach the 1, which is our argument count, so that Printf can later tell how many arguments it was given. Likewise, (Printf "lol" 42) would become

_:
	push2 
	lofss strLol
	pushi 42
	calle 921 1 4

We pass a frame size, as they’re called, of four, so we look back that many bytes plus two in the stack, passing an argc of two, a pointer to our string, and the number 42 to Printf.

How about a little trickery? (Printf "lol" (+ aLocal 1)) perhaps?

_:
	push2 
	lofss strLol
	lsl aLocal
	ldi 1 
	add 
	push 
	calle 921 1 4

So first we load a local to the stack, not the accumulator, then load the immediate value 1 to the accumulator (there is no accumulator version of push1 after all). The add command takes the value on top of the stack and adds it to the accumulator, leaving the result in the accumulator. Then push takes the accumulator and puts it on the stack, thus giving a stack of two arguments, the offset to “lol”, and the value of aLocal plus one. We look back four bytes, plus another two, and we know what to tell Printf.

And that’s how Kawa learned what a frame size actually is.

Like
[ ]

Leave a Reply

Your email address will not be published.