Lingo bytecode

From Just Solve the File Format Problem
Revision as of 05:07, 7 March 2016 by Duncanc (Talk | contribs)

Jump to: navigation, search
File Format
Name Lingo bytecode
Ontology

This is a partial, work-in-progress examination of the bytecode created when Lingo code is compiled in Macromedia Director 4.0. It describes instructions for a stack-based virtual machine. This virtual machine is sometimes known as IML, or Idealized Machine Layer.

Each instruction is one, two or three bytes.

  • If the first byte is in the range 0x00-0x3F, then the full instruction is one byte.
  • If the first byte is in the range 0x40-0x7F, then the full instruction is two bytes.
  • If the first byte is in the range 0x80-0xFF, then the full instruction is three bytes.

Constant blobs like string literals are stored after the bytecode, and referred to by records that are six bytes long regardless of the actual length of the data. This means the first constant will be referred to as 0x00, the second constant as 0x06, the third as 0x0C, and so on. Integer literals over 32767 and floating-point number literals are also stored as constants.

There is also a namelist for referring to external identifiers, stored separately from the bytecode. This is a simple array of strings.

Contents

One-Byte Instructions

01 exit Leave the current function immediately and return to its caller. Automatically added as the final step of a function.
02
03

0

FALSE

+1

Push zero onto the stack.

04 (a * b) -2 +1 Pop two values from the stack, multiply them together and push the result.
05 (a + b) -2 +1 Pop two values from the stack, add them together and push the result.
06 (a - b) -2 +1 Pop two values from the stack, subtract the second from the first and push the result.
07 (a / b) -2 +1 Pop two values from the stack, divide the first by the second and push the result.
08 (a mod b) -2 +1 Pop two values from the stack, perform a modulo operation and push the result.
09 (-a) -1 +1 Pop one value from the stack, negate it and push the result.
0A (a & b) -2 +1 Pop two values from the stack, concatenate them and push the resulting string.
0B (a && b) -2 +1 Pop two values from the stack, concatenate them with one space character added in between, and push the resulting string.
0C (a < b) -2 +1 Pop two values from the stack, push 1 if the first is less than the second and 0 if not.
0D (a <= b) -2 +1 Pop two values from the stack, push 1 if the first is less than or equal to the second and 0 if not.
0E (a <> b) -2 +1 Pop two values from the stack, push 0 if the two values are the same and 1 if they are not.
0F (a = b) -2 +1 Pop two values from the stack, push 1 if the two values are the same and 0 if they are not.
10 (a > b) -2 +1 Pop two values from the stack, push 1 if the first is greater than the second and 0 if not.
11 (a >= b) -2 +1 Pop two values from the stack, push 1 if the first is greater than or equal to the sceond and 0 if not.
12 (a and b) -2 +1 Pop two values from the stack, push 1 if both are logically true and 0 if not.
13 (a or b) -2 +1 Pop two values from the stack, push 1 if either are logically true and 0 if not.
14 (!a) -1 +1 Pop one value from the stack, push 0 if it is logically true and 1 if not.
15 (a contains b) -2 +1 Pop two values from the stack, push 1 if the first is a string that contains the second and 0 if not.
16
17 (char a of c)

(char a to b of c)

(item 1 to 3 of someItems)

-9 +1 String slice/split operation. It takes nine arguments from the stack:
-9 First char position
-8 Last char position
-7 First word position
-6 Last word position
-5 First item position (items separated by the itemDelimiter, which is a comma by default)
-4 Last item position
-3 First line position
-2 Last line position
-1 The string to slice

The positions used here are one-based, so zero is invalid as a position and is instead used to indicate unused parameters. Only one "first X position" can be set, the rest must be zero. The corresponding "last X position" may either be set too, or it can be zero, in which case the first position will also be used as the last.

18 hilite word 1 of field 10 -9 Highlight (select) some text. The nine arguments taken from the stack are:
-9 First char position
-8 Last char position
-7 First word position
-6 Last word position
-5 First item position
-4 Last item position
-3 First line position
-2 Last line position
-1 Field number (cast ID)

The positions used here are one-based, so zero is invalid as a position and is instead used to indicate unused parameters. Only one "first X position" can be set, the rest must be zero. The corresponding "last X position" may either be set too, or it can be zero, in which case the first position will also be used as the last.

19 (sprite 1 intersects 2) -2 +1 Pop two sprite IDs and push 1 if the bounding rectangles of the two sprites touch at all, or 0 if they do not.
1A (sprite 1 within 2) -2 +1 Pop two sprite IDs and push 1 if the bounding rectangle of the first is entirely inside the bounding rectangle of the second, or 0 if not.
1B (field 1) -1 +1 Pop a cast ID (name or number), push the value of that cast member's text property.
1C
1D
1E -1 +1 Some kind of list transformation or check, seen used just before setting the actorList to []. More research is needed to know exactly what is happening there.
1F [#key: value] -1 +1 Pops a list that must be in the form [#symbol1, val1, #symbol2, val2 ...] to transform into [#symbol1: val1, #symbol2: val2 ...]

Two-Byte Instructions

41 XX 1 .. 127 +1 Push integer of value XX, which must be between 1 and 127, inclusive. To push zero, use 03. To push larger integers, use 81 XX YY.
42 XX a, b, c -XX +1 Pop the specified number of values off the top of the stack, create an unparenthesized argument list containing them (i.e. for a call statement like myFunction 1, 2, 3), and push that to the stack.
43 XX [a, b, c] -XX +1 Pop the specified number of values off the top of the stack, create a list for them (which can also be used for a parenthesized call expression like set result = myFunction(1, 2, 3)), and push that to the stack.
44 XX

"literal"

0.5

32768

+1 Push a constant from local constant records onto the stack. These records seem to be six bytes long (regardless of the actual size of the constant value), so pushing the first one is 44 00, the second is 44 06, the third is 44 0C, etc.
45 XX #symbol +1 Push a symbol with a name from namelist[XX]. Note that the name will be stored as "name", not "#name".
46 XX
47 XX
48 XX
49 XX

(someGlobal)

where previously declared:

global someGlobal

+1 Push the value of a global variable with a name from namelist[XX].
4C XX (someLocal) +1 Push the value of a local variable. The local variable records seem to be 6 bytes long, so the first is pushed with 4C 00, the second with 4C 06, etc.
4D XX
4E XX
4F XX set someGlobal = 0 -1 Pop one value and use it to set the global variable with name from namelist[XX].
50 XX
51 XX
52 XX set someLocal = 0 -1 Pop one value and use it to set a local variable. See code 4C 00 for a note about local variable records.
53 XX
54 XX end repeat Unconditional backwards jump by XX bytes, relative to the first byte of this instruction.
55 XX
56 XX
57 XX someFunction 1,2,3

(someFunction(1,2,3))

-1 +1 OR +0 Call the function with name from namelist[XX]. The top value on the stack must be an argument list. If the argument list was created with code 43 XX, one return value will be pushed to the stack. If the argument list was created with code 42 XX, no return value will be pushed.
58 XX
59 16 put "extra" into textVar -1 (Not sure how the target value is specified, needs more research)
59 25 put "extra" after textVar -1 (See above)
59 35 put "extra" before textVar -1 (See above)
5A XX
5B 05 delete word 3 of textVar -1 (See above)
5C 00

(the abbr date)

(the last word in someText)

-1 OR -2 +1

If the top value is a date-formatting ID, pop it from the stack, and push the current date formatted using it:

09 the short date
0A the abbreviated date, the abbrev date, the abbr date
0B the long date

If the top value is a slice type ID, pop both it and the previous value from the stack. The previous value will be a string, slice the last "bit" of it according to the slice type, and push the sliced value:

0C the last char
0D the last word
0E the last item
0F the last line
5C 01 (the number of chars in someText) -2 +1

Pop [text, stat ID] and push the stat value for the given text, using these stat IDs:

01 chars
02 words
03 items (separated by the itemDelimiter, which is a comma by default)
04 lines
5C 02 (the name of menu 1) -2 +1

Pop [menu ID, property ID] and push the value of the specified menu property, using these property IDs:

01 name
02 number of menuItems
5C 03 (the name of menuItem 3 of menu 1) -3 +1

Pop [item ID, menu ID, property ID] and push the value of the specified menu item property, using these property IDs:

01 name
02 checkMark
03 enabled
04 script
5C 06 (the cursor of sprite 3) -2 +1

Pop [sprite ID, property ID] and push the value of the specified sprite property, using these property IDs:

02 backColor
03 bottom
04 castNum
05 constraint
06 cursor
07 foreColor
0A ink
0B left
0C lineSize
0D locH
0E locV
0F movieRate
10 movieTime
12 puppet
13 right
17 top
1D scriptNum
1E moveableSprite
20 scoreColor
5C 08 (the number of castMembers) -1 +1

Pop a stat ID from the stack and push the value of the stat, using these stat IDs:

02 (the number of castMembers)
03 (the number of menus)
5C 09

(the picture of cast "bob")

(the name of cast 3)

-2 +1

Pop [cast ID, property ID] from the stack and push the value of the cast property, using these property IDs:

01 name
02 text
08 picture
0A number
0B size
11 foreColor
12 backColor
5C 0D (the sound of cast 5) -2 +1

Pop [cast ID, property ID] and set the property for the given cast according to these property IDs:

10 sound
5D 03 set the enabled of menuItem 3 of menu 5 = FALSE -3 Pop [menuItem ID, menu ID, new value, property ID] and set the menu item property. See the table for code 5C 03 for menu item property IDs.
5D 06 set the constraint of sprite 3 = 0 -3 Pop [sprite ID, new value, property ID] and set the sprite property. See the table for code 5C 06 for sprite property IDs.
5D 09 set the backColor of cast "bob" = 0 -3 Pop [cast ID, new value, property ID] and set the cast property. See the table for code 5C 09 for cast property IDs.
5D 0D set the sound of cast 3 to TRUE -3 Pop [cast ID, new value, property ID] and set the cast property. See the table for code 5C 0D for cast property IDs.
5E XX
5F XX (the someProperty) +1 Push the value of the contextual property with the name at namelist[XX].
60 XX set the someProperty = 0 -1 Pop a value and use it to set the contextual property with the name at namelist[XX].
61 XX (the someProperty of someVariable) -1 +1 Pop a property-owning object from the stack, and push the value of this object's property with the name at namelist[XX].
62 XX set the someProperty of someVariable = 1 -2 Pop [property-owning object, new value] from the stack and set the property of the object with the name at namelist[XX].
64 XX +1

Push a copy of a value already on the stack, relative to the top where 00 is the top slot, 01 is one slot beneath the top, etc.

This is used by the compiler when generating bytecode for the repeat with i in list block, to keep state in the loop without allocating new local variables.

65 XX -XX

Pop and discard XX values from the top of the stack.

This is used in the bytecode generated for a repeat with i in list block, to clean up at the end.

66 2E (the date) -1 +1 Pop an empty list off the stack and push the current date with default formatting (same as "the short date").

Three Byte Instructions

81 XX YY 128 .. 32767   +1 Push the integer ((XX * 0x100) + YY). Larger integers and floats are pushed using constants.
93 XX YY

else

exit repeat

next repeat

Unconditional jump: Advance by ((XX * 0x100) + YY) bytes, relative to the first byte of this instruction (i.e. it may be 3 more than you are expecting)

(next repeat jumps forward to end repeat instead of jumping back itself)

95 XX YY

if somethingIsTrue then

repeat while somethingIsTrue

-1 Conditional jump: Pop a value, and if it is logically FALSE, advance by ((XX * 0x100) + YY) bytes, relative to the first byte of this instruction

Syntactic Sugar

Some functions get special syntax when written out in source code, but under the hood, the compiler just transforms it into more regular syntax. Here is a mapping that shows the equivalent in plain, generalized Lingo that gets used for the bytecode.

Specialized Syntax Generalized Syntax
play frame 10 of movie "theMovie" play 10, "theMovie"
play frame 10 play 10
play movie "theMovie" play 1, "theMovie"
play done play
repeat with i = 15 to 20
  ...

end repeat
set i = 15
repeat while i <= 20
  ...
  set i = i + 1
end repeat
repeat with i = 15 down to 10
  ...

end repeat
set i = 15
repeat while i >= 10
  ...
  set i = i - 1
end repeat
sound fadeIn 5 sound #fadeIn, 5
sound fadeIn 5, 10 sound #fadeIn, 5, 10
sound fadeOut 5 sound #fadeOut, 5
sound fadeOut 5, 10 sound #fadeOut, 5, 10
sound playFile 1, "Thunder" sound #playFile, 1, "Thunder"
sound close 1 sound #close, 1
go to frame 10

go frame 10

go to 10

go 10
go to movie "theMovie"

go movie "theMovie"

go 1, "theMovie"
go to frame 10 of movie "theMovie"

go frame 10 of movie "theMovie"

go to 10 of movie "theMovie"

go 10 of movie "theMovie"

go 10, "theMovie"
go loop go #loop
go next go #next
go previous go #previous
open "document" with "application" open "document", "application"
Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox