Subroutines

From MZXWiki
Revision as of 13:53, 11 January 2008 by Terryn (talk | contribs) (Reverted edits by Sharpnova (Talk); changed back to last version by Afkhideki)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Subroutines are callable sections of Robotic code, somewhat like functions but not as fully featured. They are accessed the same way as labels, and as such can't explicitly accept parameters or return values, like true functions could. Even so, they are an extremely useful way of organizing and managing Robotic, and are a very real extension of MZX capabilities, not just a shortcut for cleaner code. Subroutines were added in MZX version 2.65

How To Use

Subroutines are basically labels with advanced functionality; any label beginning with the character '#' is treated as a subroutine. They can be called from any place in Robotic as a label might be, including goto, send, if statements, failure branches (e.g. take 4 GEMS "#toopoor"), and the '?' textbox command (see quirks below). Subroutines are different from normal labels in that each robot has a call stack that keeps track of which line was being executed when a subroutine is called. Using the special subroutine label "#return" sends a robot one level up its call stack, to the code it was previously executing. Since it is a stack, subroutine calls can be arbitrarily nested. One other special subroutine label, "#top", sends the robot to the very top of the stack, where the first subroutine was called.

This is best understood through an example.

set "$str" "I am a robot with subroutines."
goto "#write"
goto "#sub1"
goto "#sub2"
set "$str" "The end."
goto "#write"
end
: "#sub1"
set "$str" "This is subroutine 1.  It calls:"
goto "#write"
goto "#suba"
goto "#subb"
goto "#subc"
set "$str" "This line is never seen."
goto "#write"
goto "#return"
: "#sub2"
set "$str" "This is subroutine 2.  It calls:"
goto "#write"
goto "#subb"
goto "#suba"
goto "#subd"
set "$str" "This line IS seen."
goto "#write"
goto "#return"
: "#suba"
set "$str" "Subroutine A"
goto "#write"
goto "#return"
: "#subb"
set "$str" "Subroutine B"
goto "#write"
goto "#return"
: "#subc"
set "$str" "Subroutine C"
goto "#write"
goto "#top"
: "#subd"
set "$str" "Subroutine D"
goto "#write"
goto "#return"
: "#write"
write overlay c0f "&$str&" at 0 "local"
inc "local" by 1
goto "#return"

This produces the following output, written to the overlay.

I am a robot with subroutines.
This is subroutine 1.  It calls:
Subroutine A
Subroutine B
Subroutine C
This is subroutine 2.  It calls:
Subroutine B
Subroutine A
Subroutine D
This line IS seen.
The end.

Besides demonstrating the function of subroutine nesting and repeated calling, this example shows how to practically write a piece of code to perform a specific function. In this case, a short subroutine to write a string to the next available overlay row. It also demonstrates the use of a string (or a counter) as an implicit parameter, so that subroutines can be used somewhat like functions.

Quirks and Special Notes

  • When subroutines were first introduced, they required the robot's call stack to be explicitly initialized. For this reason, in games made between version 2.65 and the port, robots that use subroutines contain lines like this:
. "#*-1-2-3"
The MZX interpreter would use the first comment string in the robot beginning with #* as a static call stack, storing the jump locations in that string in two bytes each. The "-1-2-3..." portion of the string was a convention to remind the programmer how many levels deep the stack was, and that any subroutine nesting beyond that point would break the code. Since the port, each robot's stack has been dynamically handled internally by MZX, and can be arbitrarily large.
  • When a robot sends itself to a subroutine (including when it uses a send or a send "all" command to do so), the stored return point is the command after the one which triggered the send. Otherwise a goto would cause an infinite loop on return, which would not be very useful. When a robot sends ANOTHER robot to a subroutine, the stored return point in that robot is the command it is currently on and would have executed otherwise. Otherwise there would be errors with subroutines causing robots to skip commands.
  • Such an error does in fact exist, when subroutines are used as choice labels in textboxes. When the following code is executed:
[ "Select an option"
? "#choice1" "Choice 1"
? "#choice2" "Choice 2"
explode 15
end
: "#choice1"
[ "You selected choice 1."
goto "#return"
: "#choice2"
[ "You selected choice 2."
goto "#return"
The robot will not explode, because the explode command will be skipped. This is because the "current" command after a textbox is displayed is the command immediately following the last textbox command, and so the return point is set to the command after that. This is slated to be fixed, but for now the easiest way to fix the problem in a game without affecting a future MZX bugfix is to insert a no-op like a comment or a blank line after the offending textbox.
  • When robots are sent to several subroutines during the same cycle, they react to all of them, in the reverse order in which they were received. This is a natural side-effect of the way cross-robot label sending and subroutines are handled, and is actually a very powerful feature. Basically what happens is when a robot is sent to a label, its current command is changed to be that label. When it is sent to multiple labels at the same time, the last label it received will be the one it ultimately executes. But when a robot is sent to a subroutine, it stores its current command location on the stack before moving to the new label. So when it receives multiple subroutine calls at the same time, it stores all of them on the stack, and then starts executing from the last subroutine received. Each goto "#return" moves up the stack to the previous subroutine received, until all of them are executed. Try this example with two robots, to see this in action:
. "@robot1"
send "robot2" "#sub1"
send "robot2" "#sub2"
send "robot2" "#sub3"
send "robot2" "#sub2"
end
---------------------
. "@robot2"
end
: "#sub1"
set "$str" "Subroutine 1"
goto "#write"
goto "#return"
: "#sub2"
set "$str" "Subroutine 2"
goto "#write"
goto "#return"
: "#sub3"
set "$str" "Subroutine 3"
goto "#write"
goto "#return"
: "#write"
write overlay c0f "&$str&" at 0 "local"
inc "local" by 1
goto "#return"
This results in the following printed to the overlay:
Subroutine 2
Subroutine 3
Subroutine 2
Subroutine 1