Open main menu

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

Merging of the SCI documentation. Done
(Merging of the SCI documentation. Work in progress)
 
(Merging of the SCI documentation. Done)
Line 112: Line 112:




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">
<syntax type="C">
Line 120: Line 120:




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">
<syntax type="C">
Line 128: Line 128:




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">
<syntax type="C">
Line 136: Line 136:




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">
<syntax type="C">
Line 144: Line 144:




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">
<syntax type="C">
Line 153: Line 153:




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">
<syntax type="C">
Line 162: Line 162:




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">
<syntax type="C">
Line 170: Line 170:




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">
<syntax type="C">
Line 178: Line 178:




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">
<syntax type="C">
Line 186: Line 186:




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">
<syntax type="C">
Line 194: Line 194:




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">
<syntax type="C">
Line 202: Line 202:




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">
<syntax type="C">
Line 210: Line 210:




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">
<syntax type="C">
Line 218: Line 218:




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">
<syntax type="C">
Line 227: Line 227:




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">
<syntax type="C">
Line 236: Line 236:




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">
<syntax type="C">
Line 245: Line 245:




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">
<syntax type="C">
Line 254: Line 254:




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">
<syntax type="C">
Line 263: Line 263:




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">
<syntax type="C">
Line 272: Line 272:




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">
<syntax type="C">
Line 280: Line 280:




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">
<syntax type="C">
Line 288: Line 288:




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">
<syntax type="C">
Line 296: Line 296:




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">
<syntax type="C">
Line 304: Line 304:




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">
<syntax type="C">
if (acc) pc += relpos;
if (acc) pc += relpos;
</syntax>
</syntax>
;<nowiki>op 0x30: bnt W relpos (3 bytes)</nowiki>
;<nowiki>op 0x31: bnt B relpos (2 bytes)</nowiki>
:Branch relative if not true
<syntax type="C">
if (!acc) pc += relpos;
</syntax>
;<nowiki>op 0x32: jmp W relpos (3 bytes)</nowiki>
;<nowiki>op 0x33: jmp B relpos (2 bytes)</nowiki>
:Jump
<syntax type="C">
pc += relpos;
</syntax>
;<nowiki>op 0x34: ldi W data (3 bytes)</nowiki>
;<nowiki>op 0x35: ldi B data (2 bytes)</nowiki>
:Load data immediate
<syntax type="C">
acc = data;
</syntax>
: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
<syntax type="C">
push(acc)
</syntax>
;<nowiki>op 0x38: pushi W data (3 bytes)</nowiki>
;<nowiki>op 0x39: pushi B data (2 bytes)</nowiki>
:Push immediate
<syntax type="C">
push(data)
</syntax>
:Sign extension for 0x39 is performed where required.
;<nowiki>op 0x3a: toss (1 byte)</nowiki>
;<nowiki>op 0x3b: toss (1 byte)</nowiki>
:TOS subtract
<syntax type="C">
pop();
</syntax>
: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
<syntax type="C">
push(*TOS);
</syntax>
;<nowiki>op 0x3e: link W size (3 bytes)</nowiki>
;<nowiki>op 0x3f: link B size (2 bytes)</nowiki>
<syntax type="C">
sp += (size * 2);
</syntax>
;<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)
<syntax type="C">
sp -= (framesize + 2 + &rest_modifier);
&rest_modifier = 0;
</syntax>
: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 Kernel functions section)
<syntax type="C">
sp -= (kparams + 2 + &rest_modifier);
&rest_modifier = 0;
(call kernel function kfunct)
</syntax>
;<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)
<syntax type="C">
sp -= (framesize + 2 + &rest_modifier);
&rest_modifier = 0;
</syntax>
: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)
<syntax type="C">
sp -= (framesize + 2 + &rest_modifier);
&rest_modifier = 0;
</syntax>
: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 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:
<syntax type="assembler">
; 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.
</syntax>
;<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:
<syntax type="C">
short *vars[4];
int acc;
int lea(int vt, int vi)
{
  return &((vars[(vt >> 1) &amp; 3])[vt &amp; 0x10 ? vi+acc : vi]);
}
</syntax>
;<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.
<syntax type="C">acc = object</syntax>
;<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.
<syntax type="C">push(prev)</syntax>
;<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>
;<nowikiop 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:
<syntax type="C">acc = pc + offset</syntax>
: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:
<syntax type="C">push(pc + offset)</syntax>
: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:
<syntax type="C">push(0)</syntax>
;<nowiki>op 0x78: push1 (1 bytes)</nowiki>
;<nowiki>op 0x79: push1 (1 bytes)</nowiki>
:Push 1:
<syntax type="C">push(1)</syntax>
;<nowiki>op 0x7a: push2 (1 bytes)</nowiki>
;<nowiki>op 0x7b: push2 (1 bytes)</nowiki>
:Push 2:
<syntax type="C">push(2)</syntax>
;<nowiki>op 0x7c: pushSelf (1 bytes)</nowiki>
;<nowiki>op 0x7d: pushSelf (1 bytes)</nowiki>
:Push self:
<syntax type="C">push(object)</syntax>
;<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.
236

edits