Difference between revisions of "SCI/Specifications/SCI virtual machine/The Sierra PMachine"

Jump to navigation Jump to search
m
Text replacement - "</source>" to "</syntaxhighlight>"
(Merging of the SCI documentation. Work in progress)
 
m (Text replacement - "</source>" to "</syntaxhighlight>")
(12 intermediate revisions by 5 users not shown)
Line 1: Line 1:
'''WORK IN PROGRESS. DOCUMENT INCOMPLETE'''
=The Sierra PMachine=
=The Sierra PMachine=
''Original document by Lars Skovlund, Dark Minister and Christoph Reichenbach''
''Original document by Lars Skovlund, Dark Minister and Christoph Reichenbach''
Line 33: Line 31:


;IP           
;IP           
:The instruction pointer.10 Points to the currently executing instruction
:The instruction pointer. Points to the currently executing instruction. In ScummVM this is called the "Program Counter" or PC, which is the more general term.
Vars an array of 4 values, pointing to the current variables of each mentioned type Object points to the currently executing object.
 
;Vars
:An array of four values, each pointing to the current variables of each mentioned type
 
;Object
:Points to the currently executing object.


;SP
;SP
:The current stack pointer. Note that the stack in the original SCI interpreter is used
:The current stack pointer. Note that the stack in the original SCI interpreter is used bottom-up instead of the more usual top-down.
bottom-up instead of the more usual top-down.


The PMachine, apart from the actual instruction pointer, keeps a record of which object is currently executing.
The PMachine, apart from the actual instruction pointer, keeps a record of which object is currently executing.


==The instruction set==
==The instruction set==
Line 58: Line 59:
Certain instructions (in particular, branching ones) take relative addresses as a parameter. The actual address is calculated based on the instruction after the branching instruction itself. In this example, the <tt>bnt</tt> instruction, if the branch is made, jumps over the <tt>ldi</tt> instruction.
Certain instructions (in particular, branching ones) take relative addresses as a parameter. The actual address is calculated based on the instruction after the branching instruction itself. In this example, the <tt>bnt</tt> instruction, if the branch is made, jumps over the <tt>ldi</tt> instruction.


<syntax type="assembler">
<syntaxhighlight lang="asm">
     eq?
     eq?
     bnt +2
     bnt +2
     ldi byte 2
     ldi byte 2
     push
     push
</syntax>
</syntaxhighlight>


Relative addresses are signed values.
Relative addresses are signed values.


===Dispatch addresses==
===Dispatch addresses===
The <tt>callb</tt> and <tt>calle</tt> instructions take a so-called dispatch index as a parameter. This index is used to look up an actual script address, using the so-called dispatch table. The dispatch table is located in script block type 7 in the script file. It is a series of words - the first one, as in so many other places in the script file, is the number of entries.
The <tt>callb</tt> and <tt>calle</tt> instructions take a so-called dispatch index as a parameter. This index is used to look up an actual script address, using the so-called dispatch table. The dispatch table is located in script block type 7 in the script file. It is a series of words - the first one, as in so many other places in the script file, is the number of entries.


Line 73: Line 74:
In every call instruction, a value is included which determines the size of the parameter list, as an offset into the stack. This value discounts the list size pushed by the SCI code. For instance, consider this example from real SCI code:
In every call instruction, a value is included which determines the size of the parameter list, as an offset into the stack. This value discounts the list size pushed by the SCI code. For instance, consider this example from real SCI code:


<syntax type="assembler">
<syntaxhighlight lang="asm">
     pushi 3 ; three parameters passed
     pushi 3 ; three parameters passed
     pushi 4 ; the screen flag
     pushi 4 ; the screen flag
Line 79: Line 80:
     pTos y ; push the y property
     pTos y ; push the y property
     callk OnControl, 6
     callk OnControl, 6
</syntax>
</syntaxhighlight>


Notice that, although the <tt>callk</tt> line specifies 6 bytes of parameters, the kernel routine has access to the list size (which is at offset 8)!
Notice that, although the <tt>callk</tt> line specifies 6 bytes of parameters, the kernel routine has access to the list size (which is at offset 8)!
Line 97: Line 98:




<syntax type="C">
<syntaxhighlight lang="C">
pop(): sp -= 2; return *sp;
pop(): sp -= 2; return *sp;
push(x): *sp = x; sp += 2; return x;
push(x): *sp = x; sp += 2; return x;
</syntax>
</syntaxhighlight>


The following rules apply to opcodes:
The following rules apply to opcodes:
Line 112: Line 113:




op 0x00: bnot (1 byte)<br>
;<nowiki>op 0x00: bnot (1 byte)</nowiki>
op 0x01: bnot (1 byte)
;<nowiki>op 0x01: bnot (1 byte)</nowiki>
:Binary not
:Binary not
<syntax type="C">
<syntaxhighlight lang="C">
acc ^= 0xffff;
acc ^= 0xffff;
</syntax>
</syntaxhighlight>




op 0x02: add (1 byte)<br>
;<nowiki>op 0x02: add (1 byte)</nowiki>
op 0x03: add (1 byte)
;<nowiki>op 0x03: add (1 byte)</nowiki>
:Addition:
:Addition:
<syntax type="C">
<syntaxhighlight lang="C">
acc += pop();
acc += pop();
</syntax>
</syntaxhighlight>




op 0x04: sub (1 byte)<br>
;<nowiki>op 0x04: sub (1 byte)</nowiki>
op 0x05: sub (1 byte)
;<nowiki>op 0x05: sub (1 byte)</nowiki>
:Subtraction:
:Subtraction:
<syntax type="C">
<syntaxhighlight lang="C">
acc = pop() - acc;
acc = pop() - acc;
</syntax>
</syntaxhighlight>




op 0x06: mul (1 byte)<br>
;<nowiki>op 0x06: mul (1 byte)</nowiki>
op 0x07: mul (1 byte)
;<nowiki>op 0x07: mul (1 byte)</nowiki>
:Multiplication:
:Multiplication:
<syntax type="C">
<syntaxhighlight lang="C">
acc *= pop();
acc *= pop();
</syntax>
</syntaxhighlight>




op 0x08: div (1 byte)<br>
;<nowiki>op 0x08: div (1 byte)</nowiki>
op 0x09: div (1 byte)
;<nowiki>op 0x09: div (1 byte)</nowiki>
:Division:
:Division:
<syntax type="C">
<syntaxhighlight lang="C">
acc = pop() / acc;
acc = pop() / acc;
</syntax>
</syntaxhighlight>
Division by zero is caught => acc = 0.
Division by zero is caught => acc = 0.




op 0x0a: mod (1 byte)<br>
;<nowiki>op 0x0a: mod (1 byte)</nowiki>
op 0x0b: mod (1 byte)
;<nowiki>op 0x0b: mod (1 byte)</nowiki>
:Modulo:
:Modulo:
<syntax type="C">
<syntaxhighlight lang="C">
acc = pop() % acc;
acc = pop() % acc;
</syntax>
</syntaxhighlight>
Modulo by zero is caught => acc = 0.
Modulo by zero is caught => acc = 0.




op 0x0c: shr (1 byte)<br>
;<nowiki>op 0x0c: shr (1 byte)</nowiki>
op 0x0d: shr (1 byte)
;<nowiki>op 0x0d: shr (1 byte)</nowiki>
:Shift Right logical:
:Shift Right logical:
<syntax type="C">
<syntaxhighlight lang="C">
acc = pop() >> acc;
acc = pop() >> acc;
</syntax>
</syntaxhighlight>




op 0x0e: shl (1 byte)<br>
;<nowiki>op 0x0e: shl (1 byte)</nowiki>
op 0x0f: shl (1 byte)
;<nowiki>op 0x0f: shl (1 byte)</nowiki>
:Shift Left logical:
:Shift Left logical:
<syntax type="C">
<syntaxhighlight lang="C">
acc = pop() << acc;
acc = pop() << acc;
</syntax>
</syntaxhighlight>




op 0x10: xor (1 byte)<br>
;<nowiki>op 0x10: xor (1 byte)</nowiki>
op 0x11: xor (1 byte)
;<nowiki>op 0x11: xor (1 byte)</nowiki>
:Exclusive or:
:Exclusive or:
<syntax type="C">
<syntaxhighlight lang="C">
acc ^= pop();
acc ^= pop();
</syntax>
</syntaxhighlight>




op 0x12: and (1 byte)<br>
;<nowiki>op 0x12: and (1 byte)</nowiki>
op 0x13: and (1 byte)
;<nowiki>op 0x13: and (1 byte)</nowiki>
:Logical and:
:Logical and:
<syntax type="C">
<syntaxhighlight lang="C">
acc &= pop();
acc &= pop();
</syntax>
</syntaxhighlight>




op 0x14: or (1 byte)<br>
;<nowiki>op 0x14: or (1 byte)</nowiki>
op 0x15: or (1 byte)
;<nowiki>op 0x15: or (1 byte)</nowiki>
:Logical or:
:Logical or:
<syntax type="C">
<syntaxhighlight lang="C">
acc |= pop();
acc |= pop();
</syntax>
</syntaxhighlight>




op 0x16: neg (1 byte)<br>
;<nowiki>op 0x16: neg (1 byte)</nowiki>
op 0x17: neg (1 byte)
;<nowiki>op 0x17: neg (1 byte)</nowiki>
:Sign negation:
:Sign negation:
<syntax type="C">
<syntaxhighlight lang="C">
acc = -acc;
acc = -acc;
</syntax>
</syntaxhighlight>




op 0x18: not (1 byte)<br>
;<nowiki>op 0x18: not (1 byte)</nowiki>
op 0x19: not (1 byte)
;<nowiki>op 0x19: not (1 byte)</nowiki>
:Boolean not:
:Boolean not:
<syntax type="C">
<syntaxhighlight lang="C">
acc = !acc;
acc = !acc;
</syntax>
</syntaxhighlight>




op 0x1a: eq? (1 byte)<br>
;<nowiki>op 0x1a: eq? (1 byte)</nowiki>
op 0x1b: eq? (1 byte)
;<nowiki>op 0x1b: eq? (1 byte)</nowiki>
:Equals?:
:Equals?:
<syntax type="C">
<syntaxhighlight lang="C">
prev = acc;
prev = acc;
acc = (acc == pop());
acc = (acc == pop());
</syntax>
</syntaxhighlight>




op 0x1c: ne? (1 byte)<br>
;<nowiki>op 0x1c: ne? (1 byte)</nowiki>
op 0x1d: ne? (1 byte)
;<nowiki>op 0x1d: ne? (1 byte)</nowiki>
:Is not equal to?
:Is not equal to?
<syntax type="C">
<syntaxhighlight lang="C">
prev = acc;
prev = acc;
acc = !(acc == pop());
acc = !(acc == pop());
</syntax>
</syntaxhighlight>




op 0x1e: gt? (1 byte)<br>
;<nowiki>op 0x1e: gt? (1 byte)</nowiki>
op 0x1f: gt? (1 byte)
;<nowiki>op 0x1f: gt? (1 byte)</nowiki>
:Greater than?
:Greater than?
<syntax type="C">
<syntaxhighlight lang="C">
prev = acc;
prev = acc;
acc = (pop() > acc);
acc = (pop() > acc);
</syntax>
</syntaxhighlight>




op 0x20: ge? (1 byte)<br>
;<nowiki>op 0x20: ge? (1 byte)</nowiki>
op 0x21: ge? (1 byte)
;<nowiki>op 0x21: ge? (1 byte)</nowiki>
:Greater than or equal to?
:Greater than or equal to?
<syntax type="C">
<syntaxhighlight lang="C">
prev = acc;
prev = acc;
acc = (pop() >= acc);
acc = (pop() >= acc);
</syntax>
</syntaxhighlight>




op 0x22: lt? (1 byte)<br>
;<nowiki>op 0x22: lt? (1 byte)</nowiki>
op 0x23: lt? (1 byte)
;<nowiki>op 0x23: lt? (1 byte)</nowiki>
:Less than?
:Less than?
<syntax type="C">
<syntaxhighlight lang="C">
prev = acc;
prev = acc;
acc = (pop() < acc);
acc = (pop() < acc);
</syntax>
</syntaxhighlight>




op 0x24: le? (1 byte)<br>
;<nowiki>op 0x24: le? (1 byte)</nowiki>
op 0x25: le? (1 byte)
;<nowiki>op 0x25: le? (1 byte)</nowiki>
:Less than or equal to?
:Less than or equal to?
<syntax type="C">
<syntaxhighlight lang="C">
prev = acc;
prev = acc;
acc = (pop() <= acc);
acc = (pop() <= acc);
</syntax>
</syntaxhighlight>




op 0x26: ugt? (1 byte)<br>
;<nowiki>op 0x26: ugt? (1 byte)</nowiki>
op 0x27: ugt? (1 byte)
;<nowiki>op 0x27: ugt? (1 byte)</nowiki>
:Unsigned: Greater than?
:Unsigned: Greater than?
<syntax type="C">
<syntaxhighlight lang="C">
acc = (pop() > acc);
acc = (pop() > acc);
</syntax>
</syntaxhighlight>




op 0x28: uge? (1 byte)<br>
;<nowiki>op 0x28: uge? (1 byte)</nowiki>
op 0x29: uge? (1 byte)
;<nowiki>op 0x29: uge? (1 byte)</nowiki>
:Unsigned: Greather than or equal to?
:Unsigned: Greather than or equal to?
<syntax type="C">
<syntaxhighlight lang="C">
acc = (pop() >= acc);
acc = (pop() >= acc);
</syntax>
</syntaxhighlight>




op 0x2a: ult? (1 byte)<br>
;<nowiki>op 0x2a: ult? (1 byte)</nowiki>
op 0x2b: ult? (1 byte)
;<nowiki>op 0x2b: ult? (1 byte)</nowiki>
:Unsigned: Less than?
:Unsigned: Less than?
<syntax type="C">
<syntaxhighlight lang="C">
acc = (pop() < acc);
acc = (pop() < acc);
</syntax>
</syntaxhighlight>




op 0x2c: ule? (1 byte)<br>
;<nowiki>op 0x2c: ule? (1 byte)</nowiki>
op 0x2d: ule? (1 byte)
;<nowiki>op 0x2d: ule? (1 byte)</nowiki>
:Unsigned: Less than or equal to?
:Unsigned: Less than or equal to?
<syntax type="C">
<syntaxhighlight lang="C">
acc = (pop() >= acc);
acc = (pop() >= acc);
</syntax>
</syntaxhighlight>




op 0x2e: bt W relpos (3 bytes)<br>
;<nowiki>op 0x2e: bt W relpos (3 bytes)</nowiki>
op 0x2f: bt B relpos (2 bytes)
;<nowiki>op 0x2f: bt B relpos (2 bytes)</nowiki>
:Branch relative if true
:Branch relative if true
<syntax type="C">
<syntaxhighlight lang="C">
if (acc) pc += relpos;
if (acc) pc += relpos;
</syntax>
</syntaxhighlight>
 
 
;<nowiki>op 0x30: bnt W relpos (3 bytes)</nowiki>
;<nowiki>op 0x31: bnt B relpos (2 bytes)</nowiki>
:Branch relative if not true
<syntaxhighlight lang="C">
if (!acc) pc += relpos;
</syntaxhighlight>
 
 
;<nowiki>op 0x32: jmp W relpos (3 bytes)</nowiki>
;<nowiki>op 0x33: jmp B relpos (2 bytes)</nowiki>
:Jump
<syntaxhighlight lang="C">
pc += relpos;
</syntaxhighlight>
 
 
;<nowiki>op 0x34: ldi W data (3 bytes)</nowiki>
;<nowiki>op 0x35: ldi B data (2 bytes)</nowiki>
:Load data immediate
<syntaxhighlight lang="C">
acc = data;
</syntaxhighlight>
:Sign extension is done for 0x35 if required.
 
 
;<nowiki>op 0x36: push (1 byte)</nowiki>
;<nowiki>op 0x37: push (1 byte)</nowiki>
:Push to stack
<syntaxhighlight lang="C">
push(acc)
</syntaxhighlight>
 
 
;<nowiki>op 0x38: pushi W data (3 bytes)</nowiki>
;<nowiki>op 0x39: pushi B data (2 bytes)</nowiki>
:Push immediate
<syntaxhighlight lang="C">
push(data)
</syntaxhighlight>
:Sign extension for 0x39 is performed where required.
 
 
;<nowiki>op 0x3a: toss (1 byte)</nowiki>
;<nowiki>op 0x3b: toss (1 byte)</nowiki>
:TOS subtract
<syntaxhighlight lang="C">
pop();
</syntaxhighlight>
:For confirmation: Yes, this simply tosses the TOS value away.
 
 
;<nowiki>op 0x3c: dup (1 byte)</nowiki>
;<nowiki>op 0x3d: dup (1 byte)</nowiki>
:Duplicate TOS element
<syntaxhighlight lang="C">
push(*TOS);
</syntaxhighlight>
 
 
;<nowiki>op 0x3e: link W size (3 bytes)</nowiki>
;<nowiki>op 0x3f: link B size (2 bytes)</nowiki>
<syntaxhighlight lang="C">
sp += (size * 2);
</syntaxhighlight>
 
 
;<nowiki>op 0x40: call W relpos, B framesize (4 bytes)</nowiki>
;<nowiki>op 0x41: call B relpos, B framesize (3 bytes)</nowiki>
:Call inside script.
:(See description below)
<syntaxhighlight lang="C">
sp -= (framesize + 2 + &rest_modifier);
&rest_modifier = 0;
</syntaxhighlight>
:This calls a script subroutine at the relative position relpos, setting up the <tt>ParmVar</tt> pointer first. <tt>ParmVar points</tt> to sp-<tt>''framesize''</tt> (but see also the <tt>&rest</tt> operation). The number of parameters is stored at word offset <tt>-1</tt> relative to <tt>ParmVar</tt>.
 
 
 
;<nowiki>op 0x42: callk W kfunct, B kparams (4 bytes)</nowiki>
;<nowiki>op 0x43: callk B kfunct, B kparams (3 bytes)</nowiki>
:Call kernel function (see the [[SCI/Specifications/SCI virtual machine/Kernel functions|Kernel functions]] section)
<syntaxhighlight lang="C">
sp -= (kparams + 2 + &rest_modifier);
&rest_modifier = 0;
(call kernel function kfunct)
</syntaxhighlight>
 
 
;<nowiki>op 0x44: callb W dispindex, B framesize (4 bytes)</nowiki>
;<nowiki>op 0x45: callb B dispindex, B framesize (3 bytes)</nowiki>
:Call base script
:(See description below)
<syntaxhighlight lang="C">
sp -= (framesize + 2 + &rest_modifier);
&rest_modifier = 0;
</syntaxhighlight>
:This operation starts a new execution loop at the beginning of script 0, public method <tt>dispindex</tt> (Each script comes with a dispatcher list (type 7) that identifies public methods). Parameters are handled as in the call operation.
 
 
;<nowiki>op 0x46: calle W script, W dispindex, B framesize (5 bytes)</nowiki>
;<nowiki>op 0x47: calle B script, B dispindex, B framesize (4 bytes)</nowiki>
:Call external script
:(See description below)
<syntaxhighlight lang="C">
sp -= (framesize + 2 + &rest_modifier);
&rest_modifier = 0;
</syntaxhighlight>
:This operation performs a function call (implicitly placing the current program counter on the execution stack) to an ``external'' procedure of a script. More precisely, exported procedure <tt>dispindex</tt> of script <tt>script</tt> is invoked, where <tt>dispindex</tt> is an offset into the script's Exports list (i.e., <tt>dispindex = ''n'' * 2</tt> references the ''n''th exported procedure).
:The ``Exports list'' is defined in the script's type 7 object (cf. section [[SCI/Specifications/SCI_virtual_machine/Introduction#Script_resources|Script resources]]). It is an error to invoke a script which does not exist or which does not provide an Exports list, or to use a dispatch index which does not point into an even address within the Exports list.
 
 
;<nowiki>op 0x48: ret (1 byte)</nowiki>
;<nowiki>op 0x49: ret (1 byte)</nowiki>
:Return: returns from an execution loop started by <tt>call</tt>, <tt>calle</tt>, <tt>callb</tt>, <tt>send</tt>, <tt>self</tt> or <tt>super</tt>.
 
 
;<nowiki>op 0x4a: send B framesize (2 bytes)</nowiki>
;<nowiki>op 0x4b: send B framesize (2 bytes)</nowiki>
:Send for one or more selectors. This is the most complex SCI operation (together with self and class).
:Send looks up the supplied selector(s) in the object pointed to by the accumulator. If the selector is a variable selector, it is read (to the accumulator) if it was sent for with zero parameters. If a parameter was supplied, this selector is set to that parameter. Method selectors are called with the specified parameters.
:The selector(s) and parameters are retreived from the stack frame. Send first looks up the selector ID at the bottom of the frame, then retreives the number of parameters, and, eventually, the parameters themselves. This algorithm is iterated until all of the stack frame has been "used up". Example:
<syntaxhighlight lang="asm">
; This is an example for usage of the SCI send operation
  pushi x      ; push the selector ID of x
  push1        ; 1 parameter: x is supposed to be set
  pushi 42    ; That's the value x will get set to
  pushi moveTo ; In this example, moveTo is a method selector.
  push2        ; It will get called with two parameters-
  push        ; The accumulator...
  lofss 17    ; ...and PC-relative address 17.
  pushi foo    ; Let's assume that foo is another variable selector.
  push0        ; This will read foo and return the value in acc.
  send 12      ; This operation does three quite different things.
</syntaxhighlight>
 
 
;<nowiki>op 0x4c</nowiki>
;<nowiki>op 0x4d</nowiki>
;<nowiki>op 0x4e</nowiki>
;<nowiki>op 0x4f</nowiki>
:These opcodes don't exist in SCI.
 
 
;<nowiki>op 0x50: class W function (3 bytes)</nowiki>
;<nowiki>op 0x51: class B function (2 bytes)</nowiki>
:Get class address. Sets the accumulator to the memory address of the specified <tt>function</tt> of the current object.
 
 
;<nowiki>op 0x52</nowiki>
;<nowiki>op 0x53</nowiki>
:These opcodes don't exist in SCI.
 
 
;<nowiki>op 0x54: self B stackframe (2 bytes)</nowiki>
;<nowiki>op 0x55: self B stackframe (2 bytes)</nowiki>
:Send to self. This operation is the same as the send operation, except that it sends to the current object instead of the object pointed to by the accumulator.
 
 
;<nowiki>op 0x56: super W class, B stackframe (4 bytes)</nowiki>
;<nowiki>op 0x57: super B class, B stackframe (3 bytes)</nowiki>
:Send to any class. This operation is the same as the send operation, except that it sends to an arbitrary <tt>class</tt>.
 
 
;<nowiki>op 0x58: &rest W paramindex (3 bytes)</nowiki>
;<nowiki>op 0x59: &rest B paramindex (2 bytes)</nowiki>
:Pushes all or part of the <tt>ParmVar</tt> list on the stack. The number specifies the first parameter variable to be pushed. I'll give a small example. Suppose we have two functions:
:<tt>function a(y,z) and function b(x,y,z)</tt>
:function b wants to call function a with its own y and z parameters. Easy job, using the the normal lsp instruction. Now suppose that both function a and b are designed to take a variable number of parameters:
:<tt>function a(y,z,...) and function b(x,y,z,...)</tt>
:Since lsp does not support register indirection, we can't just push the variables in a loop (as we would in C). Instead this function is used. In this case, the instruction would be &rest 2, since we want the copying to start from y (inclusive), the second parameter.
:Note that the values are copied to the stack '''immediately'''. The <tt><nowiki>&rest_modifier</nowiki></tt> is set to the number of variables pushed afterwards.
 
 
 
;<nowiki>op 0x5a: lea W type, W index ( bytes)</nowiki>
;<nowiki>op 0x5b: lea B type, B index ( bytes)</nowiki>
:Load Effective Address
:The variable type is a bit-field used as follows:
:;bit 0
::unused
;;bit 1-2
::the number of the variable list to use
::0 - globalVar
::2 - localVar
::4 - tempVar
::6 - parmVar
;;bit 3
::unused
;;bit 4
::set if the accumulator is to be used as additional index
:::Because it is so hard to explain, I have made a transcription of it here:
<syntaxhighlight lang="C">
short *vars[4];
 
int acc;
 
int lea(int vt, int vi)
{
  return &((vars[(vt >> 1) &amp; 3])[vt &amp; 0x10 ? vi+acc : vi]);
}
</syntaxhighlight>
 
 
;<nowiki>op 0x5c: selfID (1 bytes)</nowiki>
;<nowiki>op 0x5d: selfID (1 bytes)</nowiki>
:Get 'self' identity: SCI uses heap pointers to identify objects, so this operation sets the accumulator to the address of the current object.
<syntaxhighlight lang="C">acc = object</syntaxhighlight>
 
 
;<nowiki>op 0x5e</nowiki>
;<nowiki>op 0x5f</nowiki>
:These opcodes don't exist in SCI.
 
 
;<nowiki>op 0x60: pprev (1 bytes)</nowiki>
;<nowiki>op 0x61: pprev (1 bytes)</nowiki>
:Push prev: Pushes the value of the prev register, set by the last comparison bytecode (eq?, lt?, etc.), on the stack.
<syntaxhighlight lang="C">push(prev)</syntaxhighlight>
 
 
;<nowiki>op 0x62: pToa W offset (3 bytes)</nowiki>
;<nowiki>op 0x63: pToa B offset (2 bytes)</nowiki>
:Property To Accumulator: Copies the value of the specified property (in the current object) to the accumulator. The property is specified as an offset into the object structure.
 
 
;<nowiki>op 0x64: aTop W offset (3 bytes)</nowiki>
;<nowiki>op 0x65: aTop B offset (2 bytes)</nowiki>
:Accumulator To Property: Copies the value of the accumulator into the specified property (in the current object). The property number is specified as an offset into the object structure.
 
 
;<nowiki>op 0x66: pTos W offset (3 bytes)</nowiki>
;<nowiki>op 0x67: pTos B offset (2 bytes)</nowiki>
:Property To Stack: Same as pToa, but pushes the property value on the stack instead.
 
 
;<nowiki>op 0x68: sTop W offset (3 bytes)</nowiki>
;<nowiki>op 0x69: sTop B offset (2 bytes)</nowiki>
:Stack To Property: Same as aTop, but gets the new property value from the stack instead.
 
 
;<nowiki>op 0x6a: ipToa W offset (3 bytes)</nowiki>
;<nowiki>op 0x6b: ipToa B offset (2 bytes)</nowiki>
:Incement Property and copy To Accumulator: Increments the value of the specified property of the current object and copies it into the accumulator. The property number is specified as an offset into the object structure.
 
 
;<nowiki>op 0x6c: dpToa W offset (3 bytes)</nowiki>
;<nowiki>op 0x6d: dpToa B offset (2 bytes)</nowiki>
:Decrepent Property and copy to Accumulator: Decrements the value of the specified property of the current object and copies it into the accumulator. The property number is specified as an offset into the object structure.
 
 
;<nowiki>op 0x6e: ipTos W offset (3 bytes)</nowiki>
;<nowiki>op 0x6f: ipTos B offset (2 bytes)</nowiki>
:Increment Property and push to Stack Same as ipToa, but pushes the result on the stack instead.
 
 
;<nowiki>op 0x70: dpTos W offset (3 bytes)</nowiki>
;<nowiki>op 0x71: dpTos B offset (2 bytes)</nowiki>
:Decrement Property and push to stack: Same as dpToa, but pushes the result on the stack instead.
 
 
;<nowiki>op 0x72: lofsa W offset (3 bytes)</nowiki>
;<nowiki>op 0x73: lofsa B offset (2 bytes)</nowiki>
:Load Offset to Accumulator:
<syntaxhighlight lang="C">acc = pc + offset</syntaxhighlight>
:Adds a value to the post-operation pc and stores the result in the accumulator.
 
 
;<nowiki>op 0x74: lofss W offset (3 bytes)</nowiki>
;<nowiki>op 0x75: lofss B offset (2 bytes)</nowiki>
:Load Offset to Stack:
<syntaxhighlight lang="C">push(pc + offset)</syntaxhighlight>
:Adds a value to the post-operation pc and pushes the result on the stack.
 
 
;<nowiki>op 0x76: push0 (1 bytes)</nowiki>
;<nowiki>op 0x77: push0 (1 bytes)</nowiki>
:Push 0:
<syntaxhighlight lang="C">push(0)</syntaxhighlight>
 
 
;<nowiki>op 0x78: push1 (1 bytes)</nowiki>
;<nowiki>op 0x79: push1 (1 bytes)</nowiki>
:Push 1:
<syntaxhighlight lang="C">push(1)</syntaxhighlight>
 
 
;<nowiki>op 0x7a: push2 (1 bytes)</nowiki>
;<nowiki>op 0x7b: push2 (1 bytes)</nowiki>
:Push 2:
<syntaxhighlight lang="C">push(2)</syntaxhighlight>
 
 
;<nowiki>op 0x7c: pushSelf (1 bytes)</nowiki>
;<nowiki>op 0x7d: pushSelf (1 bytes)</nowiki>
:Push self:
<syntaxhighlight lang="C">push(object)</syntaxhighlight>
 
 
;<nowiki>op 0x7e</nowiki>
;<nowiki>op 0x7f</nowiki>
:These operations don't exist in SCI.
 
 
;<nowiki>op 0x80 - 0xfe: [ls+-][as][gltp]i? W index (3 bytes)</nowiki>
;<nowiki>op 0x81 - 0xff: [ls+-][as][gltp]i? B index (2 bytes)</nowiki>
:The remaining SCI operations work on one of the four variable types. The variable index is retreived by taking the heap pointer for the specified variable type, adding the <tt>index</tt> and possibly the accumulator, and executing the operation according to the following table:
:;Bit 0
::Used as with all other opcodes with variably-sized parameters:
:::0: 16 bit parameter
:::1: 8 bit parameter
:;Bits 1,2
::The type of variable to operate on:
:::0: Global
:::1: Local
:::2: Temporary
:::3: Parameter
:;Bit 3
::Whether to use the accumulator or the stack for operations:
:::0: Accumulator
:::1: Stack
:;Bit 4
::Whether to use the accumulator as a modifier to the supplied index:
:::0: Don't use accumulator as an additional index
:::1: Use the accumulator as an additional index
:;Bits 5,6
::The type of execution to perform:
:::0: Load the variable to the accumulator or stack
:::1: Store the accumulator or stack in the variable
:::2: Increment the variable, then load it into acc or on the stack
:::3: Decrement the variable, then load it into acc or on the stack
:;Bit 7
::Always 1 (identifier for these opcodes)
::Example: "sagi 2" would Store the Accumulator in the Global variable indexed with 2 plus the current accumulator value (this rarely makes sense, obviously). "+sp 6" would increment the parameter at offset 6 (the third parameter, not counting the argument counter), and push it on the stack.
TrustedUser
2,147

edits

Navigation menu