3. Assembly Language

3.1. Overview of the CoCo VM

_images/coco.png

Fig. 1: The CoCo Virtual Machine

3.2. Getting Started

3.2.1. Running the Disassembler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from disassembler import *

def main():
        x = 5
        y = 6
        z = x + y
        print(z)

#main()
disassembler.disassemble(main)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
MyComputer> python3.2 addtwo.py
Function: main/0
Constants: None, 5, 6
Locals: x, y, z
Globals: print
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          BINARY_ADD
          STORE_FAST                     2
          LOAD_GLOBAL                    0
          LOAD_FAST                      2
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END
MyComputer> python3.2 addtwo.py > addtwo.casm

3.2.2. Running CoCo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
MyComputer> coco addtwo.casm
Function: main/0
Constants: None, 5, 6
Locals: x, y, z
Globals: print
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          BINARY_ADD
          STORE_FAST                     2
          LOAD_GLOBAL                    0
          LOAD_FAST                      2
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

11
MyComputer> coco addtwo.casm 2> /dev/null
11
MyComputer>
MyComputer> python3.2 addtwo.py > addtwo.casm
MyComputer> coco addtwo.casm

3.3. Input/Output

3.3.1. Python I/O

1
2
3
4
5
6
7
8
9
import disassembler

def main():
    name = input("Enter your name: ")
    age = int(input("Enter your age: "))
    print(name + ", a year from now you will be", age+1, "years old.")

#main()
disassembler.disassemble(main)

3.3.2. CoCo I/O

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Function: main/0
Constants: None,
  "Enter your name: ", "Enter your age: ",
  ", a year from now you will be",
  1, "years old."
Locals: name, age
Globals: input, int, print
BEGIN
          LOAD_GLOBAL                    0
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          STORE_FAST                     0
          LOAD_GLOBAL                    1
          LOAD_GLOBAL                    0
          LOAD_CONST                     2
          CALL_FUNCTION                  1
          CALL_FUNCTION                  1
          STORE_FAST                     1
          LOAD_GLOBAL                    2
          LOAD_FAST                      0
          LOAD_CONST                     3
          BINARY_ADD
          LOAD_FAST                      1
          LOAD_CONST                     4
          BINARY_ADD
          LOAD_CONST                     5
          CALL_FUNCTION                  3
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

Practice 3.1

The code in section 3.3.2 is a bit wasteful which often happens when compiling a program written in a higher level language. Optimize the code in section 3.3.2 so it contains fewer instructions.

You can check your answer(s) at the end of the chapter.

3.4. If-Then-Else Statements

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import disassembler

def main():
    x = 5
    y = 6
    if x > y:
        z = x
    else:
        z = y

    print(z)

disassembler.disassemble(main)

3.4.1. CoCo If-Then-Else Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Function: main/0
Constants: None, 5, 6
Locals: x, y, z
Globals: print
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          COMPARE_OP                     4
          POP_JUMP_IF_FALSE        label00
          LOAD_FAST                      0
          STORE_FAST                     2
          JUMP_FORWARD             label01
label00:  LOAD_FAST                      1
          STORE_FAST                     2
label01:  LOAD_GLOBAL                    0
          LOAD_FAST                      2
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

3.4.2. CoCo Assembled Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Function: main/0
Constants: None, 5, 6
Locals: x, y, z
Globals: print
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          COMPARE_OP                     4
          POP_JUMP_IF_FALSE             11
          LOAD_FAST                      0
          STORE_FAST                     2
          JUMP_FORWARD                  13
          LOAD_FAST                      1
          STORE_FAST                     2
          LOAD_GLOBAL                    0
          LOAD_FAST                      2
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END
1
2
3
4
  onelabel:  LOAD_FAST                     1
             STORE_FAST                    2
  twolabel:
threelabel: LOAD_GLOBAL                    0

Practice 3.2

Without touching the code that compares the two values, the assembly in section 3.4.2 can be optimized to remove at least three instructions. Rewrite the code to remove at least three instructions from this code. With a little more work, five instructions could be removed.

You can check your answer(s) at the end of the chapter.

3.4.3. If-Then Statements

import disassembler

def main():
    x = 5
    y = 6
    if x > y:
        print(x)

    print(y)

disassembler.disassemble(main)

3.4.4. If-Then Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Function: main/0
Constants: None, 5, 6
Locals: x, y
Globals: print
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          COMPARE_OP                     4
          POP_JUMP_IF_FALSE        label00
          LOAD_GLOBAL                    0
          LOAD_FAST                      0
          CALL_FUNCTION                  1
          POP_TOP
          JUMP_FORWARD             label00
label00:  LOAD_GLOBAL                    0
          LOAD_FAST                      1
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

Practice 3.3

Rewrite the code in section 3.4.4 so it executes with the same result using POP_JUMP_IF_TRUE instead of the jump if false instruction. Be sure to optimize your code when you write it so there are no unnecessary instructions.

You can check your answer(s) at the end of the chapter.

3.5. While Loops

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import disassembler

def main():
    f = 8
    i = 1
    j = 1
    n = 1
    while n < f:
        n = n + 1
        tmp = j
        j = j + i
        i = tmp

    print("Fibonacci("+str(n)+") is",i)

disassembler.disassemble(main)

3.5.1. While Loop Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Function: main/0
Constants: None, 8, 1, "Fibonacci(", ") is"
Locals: f, i, j, n, tmp
Globals: print, str
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_CONST                     2
          STORE_FAST                     2
          LOAD_CONST                     2
          STORE_FAST                     3
          SETUP_LOOP               label02
label00:  LOAD_FAST                      3
          LOAD_FAST                      0
          COMPARE_OP                     0
          POP_JUMP_IF_FALSE        label01
          LOAD_FAST                      3
          LOAD_CONST                     2
          BINARY_ADD
          STORE_FAST                     3
          LOAD_FAST                      2
          STORE_FAST                     4
          LOAD_FAST                      2
          LOAD_FAST                      1
          BINARY_ADD
          STORE_FAST                     2
          LOAD_FAST                      4
          STORE_FAST                     1
          JUMP_ABSOLUTE            label00
label01:  POP_BLOCK
label02:  LOAD_GLOBAL                    0
          LOAD_CONST                     3
          LOAD_GLOBAL                    1
          LOAD_FAST                      3
          CALL_FUNCTION                  1
          BINARY_ADD
          LOAD_CONST                     4
          BINARY_ADD
          LOAD_FAST                      1
          CALL_FUNCTION                  2
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

Practice 3.4

Write a short program that tests the use of the BREAK_LOOP instruction. You don’t have to write a while loop to test this. Simply write some code that uses a BREAK_LOOP and prints something to the screen to verify that it worked.

You can check your answer(s) at the end of the chapter.

3.6. Exception Handling

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import disassembler

def main():
    try:
        x = float(input("Enter a number: "))
        y = float(input("Enter a number: "))
        z = x / y
        print(x,"/",y,"=",z)
    except Exception as ex:
        print(ex)

disassembler.disassemble(main)

3.6.1. Exception Handling Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Function: main/0
Constants: None,
    "Enter a number: ", "/", "="
Locals: x, y, z, ex
Globals: float, input, print, Exception
BEGIN
          SETUP_EXCEPT             label00
          LOAD_GLOBAL                    0
          LOAD_GLOBAL                    1
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          CALL_FUNCTION                  1
          STORE_FAST                     0
          LOAD_GLOBAL                    0
          LOAD_GLOBAL                    1
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          CALL_FUNCTION                  1
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          BINARY_TRUE_DIVIDE
          STORE_FAST                     2
          LOAD_GLOBAL                    2
          LOAD_FAST                      0
          LOAD_CONST                     2
          LOAD_FAST                      1
          LOAD_CONST                     3
          LOAD_FAST                      2
          CALL_FUNCTION                  5
          POP_TOP
          POP_BLOCK
          JUMP_FORWARD             label03
label00:  DUP_TOP
          LOAD_GLOBAL                    3
          COMPARE_OP                    10
          POP_JUMP_IF_FALSE        label02
          POP_TOP
          STORE_FAST                     3
          POP_TOP
          SETUP_FINALLY            label01
          LOAD_GLOBAL                    2
          LOAD_FAST                      3
          CALL_FUNCTION                  1
          POP_TOP
          POP_BLOCK
          POP_EXCEPT
          LOAD_CONST                     0
label01:  LOAD_CONST                     0
          STORE_FAST                     3
          DELETE_FAST                    3
          END_FINALLY
          JUMP_FORWARD             label03
label02:  END_FINALLY
label03:  LOAD_CONST                     0
          RETURN_VALUE
END

Practice 3.5

Write a short program that tests creating an exception, raising it, and printing the handled exception. Write this as a CoCo program without using the disassembler.

You can check your answer(s) at the end of the chapter.

3.7. List Constants

1
2
3
4
5
6
7
import disassembler

def main():
    lst = ["hello","world"]
    print(lst)

disassembler.disassemble(main)

3.7.1. Build List Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Function: main/0
Constants: None, "hello", "world"
Locals: lst
Globals: print
BEGIN
          LOAD_CONST                     1
          LOAD_CONST                     2
          BUILD_LIST                     2
          STORE_FAST                     0
          LOAD_GLOBAL                    0
          LOAD_FAST                      0
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

3.8. Calling a Method

1
2
3
4
5
6
7
8
9
import disassembler

def main():
    s = input("Enter a list of integers:")
    lst = s.split()

    print(lst)

disassembler.disassemble(main)

3.8.1. Method Call Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Function: main/0
Constants: None, "Enter a list of integers:"
Locals: s, lst
Globals: input, split, print
BEGIN
          LOAD_GLOBAL                    0
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          STORE_FAST                     0
          LOAD_FAST                      0
          LOAD_ATTR                      1
          CALL_FUNCTION                  0
          STORE_FAST                     1
          LOAD_GLOBAL                    2
          LOAD_FAST                      1
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

Practice 3.6

Normally, if you want to add to numbers together in Python, like 5 and 6, you write 5+6. This corresponds to using the BINARY_ADD instruction in CoCo which in turn calls the magic method __add__ with the method call 5.__add__(6). Write a short CoCo program where you add two integers together without using the BINARY_ADD instruction. Print the result to the screen.

You can check your answer(s) at the end of the chapter.

3.9. Iterating Over a List

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from disassembler import *

def main():
    x = input("Enter a list: ")
    lst = x.split()

    for b in lst:
        print(b)

disassemble(main)

3.9.1. List Iteration Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Function: main/0
Constants: None, "Enter a list: "
Locals: x, lst, b
Globals: input, split, print
BEGIN
          LOAD_GLOBAL                    0
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          STORE_FAST                     0
          LOAD_FAST                      0
          LOAD_ATTR                      1
          CALL_FUNCTION                  0
          STORE_FAST                     1
          SETUP_LOOP               label02
          LOAD_FAST                      1
          GET_ITER
label00:  FOR_ITER                 label01
          STORE_FAST                     2
          LOAD_GLOBAL                    2
          LOAD_FAST                      2
          CALL_FUNCTION                  1
          POP_TOP
          JUMP_ABSOLUTE            label00
label01:  POP_BLOCK
label02:  LOAD_CONST                     0
          RETURN_VALUE
END

Practice 3.7

Write a CoCo program that gets a string from the user and iterates over the characters of the string, printing them to the screen.

You can check your answer(s) at the end of the chapter.

3.10. Range Objects and Lazy Evaluation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from disassembler import *

def main():
    x = input("Enter a list: ")
    lst = x.split()

    for i in range(len(lst)-1,-1,-1):
        print(lst[i])

disassemble(main)

3.10.1. Range Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Function: main/0
Constants: None, "Enter a list: ", 1, -1, -1
Locals: x, lst, i
Globals: input, split, range, len, print
BEGIN
          LOAD_GLOBAL                    0
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          STORE_FAST                     0
          LOAD_FAST                      0
          LOAD_ATTR                      1
          CALL_FUNCTION                  0
          STORE_FAST                     1
          SETUP_LOOP               label02
          LOAD_GLOBAL                    2
          LOAD_GLOBAL                    3
          LOAD_FAST                      1
          CALL_FUNCTION                  1
          LOAD_CONST                     2
          BINARY_SUBTRACT
          LOAD_CONST                     3
          LOAD_CONST                     4
          CALL_FUNCTION                  3
          GET_ITER
label00:  FOR_ITER                 label01
          STORE_FAST                     2
          LOAD_GLOBAL                    4
          LOAD_FAST                      1
          LOAD_FAST                      2
          BINARY_SUBSCR
          CALL_FUNCTION                  1
          POP_TOP
          JUMP_ABSOLUTE            label00
label01:  POP_BLOCK
label02:  LOAD_CONST                     0
          RETURN_VALUE
END

3.11. Functions and Closures

1
2
3
4
5
6
7
8
9
def main():
  x = 10
  def f(x):
    def g():
      return x
    return g
  print(f(3)())
#main()
disassembler.disassemble(main)

3.11.1. Nested Functions Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Function: main/0
    Function: f/1
        Function: g/0
        Constants: None
        FreeVars: x
        BEGIN
                  LOAD_DEREF    0
                  RETURN_VALUE
        END
    Constants: None, code(g)
    Locals: x, g
    CellVars: x
    BEGIN
              LOAD_CLOSURE      0
              BUILD_TUPLE       1
              LOAD_CONST        1
              MAKE_CLOSURE      0
              STORE_FAST        1
              LOAD_FAST         1
              RETURN_VALUE
    END
Constants: None, 10, code(f), 3
Locals: x, f
Globals: print
BEGIN
          LOAD_CONST            1
          STORE_FAST            0
          LOAD_CONST            2
          MAKE_FUNCTION         0
          STORE_FAST            1
          LOAD_GLOBAL           0
          LOAD_FAST             1
          LOAD_CONST            3
          CALL_FUNCTION         1
          CALL_FUNCTION         0
          CALL_FUNCTION         1
          POP_TOP
          LOAD_CONST            0
          RETURN_VALUE
END
_images/nested.png

Fig. 2: Execution of nested.casm

Practice 3.8

The program in section 3.11.1 would work just fine without the cell. The variable x could refer directly to the 3 in both the f and g functions without any ramifications. Yet, a cell variable is needed in some circumstances. Can you come up with an example where a cell variable is absolutely needed?

You can check your answer(s) at the end of the chapter.

3.12. Recursion

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import disassembler

def factorial(n):
    if n==0:
        return 1

    return n*factorial(n-1)

def main():
    print(factorial(5))

disassembler.disassemble(factorial)
disassembler.disassemble(main)

3.12.1. Recursion Assembly

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Function: factorial/1
Constants: None, 0, 1
Locals: n
Globals: factorial
BEGIN
          LOAD_FAST                      0
          LOAD_CONST                     1
          COMPARE_OP                     2
          POP_JUMP_IF_FALSE        label00
          LOAD_CONST                     2
          RETURN_VALUE
label00:  LOAD_FAST                      0
          LOAD_GLOBAL                    0
          LOAD_FAST                      0
          LOAD_CONST                     2
          BINARY_SUBTRACT
          CALL_FUNCTION                  1
          BINARY_MULTIPLY
          RETURN_VALUE
END
Function: main/0
Constants: None, 5
Globals: print, factorial
BEGIN
          LOAD_GLOBAL                    0
          LOAD_GLOBAL                    1
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

Practice 3.9

Draw a picture of the run-time stack just before the instruction on line 11 of section 3.12.1 is executed. Use figure 2 as a guide to how you draw this picture. Be sure to include the code, the values of n, and the PC values.

You can check your answer(s) at the end of the chapter.

3.13. Chapter Summary

3.14. Review Questions

  1. How do the Python virtual machine and CoCo differ? Name three differences between the two implementations.
  2. What is a disassembler?
  3. What is an assembler?
  4. What is a stack frame? Where are they stored? What goes inside a stack frame?
  5. What is the purpose of the block stack and where is it stored?
  6. What is the purpose of the Program Counter?
  7. Name an instruction that is responsible for creating a list object and describe how it works.
  8. Describe the execution of the STORE_FAST and LOAD_FAST instructions.
  9. How can CoCo read a line of input from the keyboard?
  10. What is the difference between a disassembled Python program and an assembled CoCo program? Provide a short example and point out the differences.
  11. When a Python while loop is implemented in CoCo, what is the last instruction of the loop and what is its purpose?
  12. What do exception handling and loops have in common in the CoCo implementation?
  13. What is lazy evaluation and why is it important to Python and CoCo?
  14. What is a closure and why are closures needed?

3.15. Exercises

  1. Consulting the CoCo assembly language program in the solution to exercise 3.2, provide the contents of the operand stack after each instruction is executed.
  2. Write a CoCo program which reads an integer from the user and then creates a list of all the even numbers from 0 up to and including that integer. The program should conclude printing the list to the screen. Test your program with CoCo to be sure it works.
  3. Add some exception handling to the previous exercise to print “You didn’t enter an integer!” if the user fails to enter an integer in their program.
  4. Using a range object, write a CoCo program that computes the sum of the first n integers where the non-negative n is read from the user.
  5. Write a recursive CoCo program that adds up the first n numbers where n is read from the user. Remember, there must be a base case that comes first in this function and the recursive case must be called on something smaller which is used in computing the solution to the whole problem.

3.16. Solutions to Practice Problems

These are solutions to the practice problems. You should only consult these answers after you have tried each of them for yourself first. Practice problems are meant to help reinforce the material you have just read so make use of them.

3.16.1. Solution to Practice Problem 3.1

The assembly code in section 3.3.2 blindly pops the None at the end and then pushes None again before returning from main. This can be eliminated resulting in two fewer instructions. This would also mean that None is not needed in the constants, but this was not eliminated below.

Function: main/0
Constants: None,
    "Enter your name: ", "Enter your age: ",
    ", a year from now you will be",
    1, "years old."
Locals: name, age
Globals: input, int, print
BEGIN
          LOAD_GLOBAL                    0
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          STORE_FAST                     0
          LOAD_GLOBAL                    1
          LOAD_GLOBAL                    0
          LOAD_CONST                     2
          CALL_FUNCTION                  1
          CALL_FUNCTION                  1
          STORE_FAST                     1
          LOAD_GLOBAL                    2
          LOAD_FAST                      0
          LOAD_CONST                     3
          BINARY_ADD
          LOAD_FAST                      1
          LOAD_CONST                     4
          BINARY_ADD
          LOAD_CONST                     5
          CALL_FUNCTION                  3
          RETURN_VALUE
END

3.16.2. Solution to Practice Problem 3.2

As in practice 3.1 the POP_TOP and LOAD_CONST from the end can be eliminated. In the if-then-else code both the then part and the else part execute exactly the same STORE_FAST instruction. That can be moved after the if-then-else code and written just once, resulting in one less instruction and three less overall. Furthermore, if we move the LOAD_GLOBAL for the call to print before the if-then-else statement, we can avoid storing the maximum value in z at all and just leave the result on the top of the operand stack: either x or y. By leaving the bigger of x or y on the top of the stack, the call to print will print the correct value. This eliminates five instructions from the original code.

Function: main/0
Constants: None, 5, 6
Locals: x, y
Globals: print
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_GLOBAL                    0
          LOAD_FAST                      0
          LOAD_FAST                      1
          COMPARE_OP                     4
          POP_JUMP_IF_FALSE        label00
          LOAD_FAST                      0
          JUMP_FORWARD             label01
label00:  LOAD_FAST                      1
label01:  CALL_FUNCTION                  1
          RETURN_VALUE
END

It is worth noting that the code above is exactly the disassembled code from this Python program.

import disassembler

def main():
    x = 5
    y = 6
    print(x if x > y else y)

disassembler.disassemble(main)

When main is called, this code prints the result of a conditional expression. The if-then-else expression inside the print statement is different than an if-then-else statement. An if-then-else statement updates a variable or has some other side-effect. An if-then-else expression, or conditional expression as it is called in Python documentation, yields a value: either the then value or the else value. In the assembly language code we see that the yielded value is passed to the print function as its argument.

3.16.3. Solution to Practice Problem 3.3

Function: main/0
Constants: None, 5, 6
Locals: x, y
Globals: print
BEGIN
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          COMPARE_OP                     1
          POP_JUMP_IF_TRUE         label00
          LOAD_GLOBAL                    0
          LOAD_FAST                      0
          CALL_FUNCTION                  1
          POP_TOP
label00:  LOAD_GLOBAL                    0
          LOAD_FAST                      1
          CALL_FUNCTION                  1
          RETURN_VALUE
END

3.16.4. Solution to Practice Problem 3.4

The following code behaves differently if the BREAK_LOOP instruction is removed from the program.

Function: main/0
Constants: None, 7, 6
Locals: x, y
Globals: print
BEGIN
          SETUP_LOOP               label01
          LOAD_CONST                     1
          STORE_FAST                     0
          LOAD_CONST                     2
          STORE_FAST                     1
          LOAD_FAST                      0
          LOAD_FAST                      1
          COMPARE_OP                     1
          POP_JUMP_IF_TRUE         label00
          BREAK_LOOP
          LOAD_GLOBAL                    0
          LOAD_FAST                      0
          CALL_FUNCTION                  1
          POP_TOP
label00:  POP_BLOCK
label01:  LOAD_GLOBAL                    0
          LOAD_FAST                      1
          CALL_FUNCTION                  1
          RETURN_VALUE
END

3.16.5. Solution to Practice Problem 3.5

This is the hello world program with exception handling used to raise and catch an exception. This solution does not include code for finally handling in case an exception happened while handling the exception. It also assumes the exception will match when thrown since CoCo only supports one type of exception.

Function: main/0
Constants: None, "Hello World!"
Locals: ex
Globals: Exception, print
BEGIN
          SETUP_EXCEPT             label00
          LOAD_GLOBAL                    0
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          RAISE_VARARGS                  1
          POP_BLOCK
          JUMP_FORWARD             label01
label00:  LOAD_GLOBAL                    1
          ROT_TWO
          CALL_FUNCTION                  1
          POP_TOP
          POP_EXCEPT
label01:  LOAD_CONST                     0
          RETURN_VALUE
END

3.16.6. Solution to Practice Problem 3.6

This program adds 5 and 6 together using the __add__ magic method associated with integer objects. First 5 is loaded onto the operand stack. Then LOAD_ATTR is used to load the __add__ of the 5 object onto the stack. This is the function. The argument to __add__ is loaded next which is the 6. The 6 is loaded by the LOAD_CONST instruction. Then __add__ is called with one argument. The 11 is left on the operand stack after the function call. It is stored in x, the print is loaded, x is loaded onto the operand stack, and print is called to print the value. Since print leaves None on the stack, that value is returned from the main function.

Function: main/0
Constants: None, 5, 6
Locals: x
Globals: __add__, print
BEGIN

          LOAD_CONST                     1
          LOAD_ATTR                      0
          LOAD_CONST                     2
          CALL_FUNCTION                  1
          STORE_FAST                     0
          LOAD_GLOBAL                    1
          LOAD_FAST                      0
          CALL_FUNCTION                  1
          RETURN_VALUE
END

3.16.7. Solution to Practice Problem 3.7

Function: main/0
Constants: None, "Enter a string: "
Locals: x, a
Globals: input, print
BEGIN
          LOAD_GLOBAL                    0
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          STORE_FAST                     0
          SETUP_LOOP               label02
          LOAD_FAST                      0
          GET_ITER
label00:  FOR_ITER                 label01
          STORE_FAST                     1
          LOAD_GLOBAL                    1
          LOAD_FAST                      1
          CALL_FUNCTION                  1
          POP_TOP
          JUMP_ABSOLUTE            label00
label01:  POP_BLOCK
label02:  LOAD_CONST                     0
          RETURN_VALUE
END

3.16.8. Solution to Practice Problem 3.8

A cell variable is needed if an inner function makes a modification to a variable that is located in the outer function. Consider the CoCo program below. Without the cell the program below would print 10 to the screen and with the cell it prints 11. Why is that? Draw the run-time stack both ways to see what happens with and without the cell variable.

Function: f/1
    Function: g/1
    Constants: None, 1
    Locals: y
    FreeVars: x
    BEGIN
              LOAD_DEREF                 0
              LOAD_CONST                 1
              BINARY_ADD
              STORE_DEREF                0
              LOAD_DEREF                 0
              LOAD_FAST                  0
              BINARY_ADD
              RETURN_VALUE
    END
Constants: None, code(g)
Locals: x, g
CellVars: x
BEGIN
          LOAD_CLOSURE                   0
          BUILD_TUPLE                    1
          LOAD_CONST                     1
          MAKE_CLOSURE                   0
          STORE_FAST                     1
          LOAD_FAST                      1
          LOAD_DEREF                     0
          CALL_FUNCTION                  1
          LOAD_DEREF                     0
          BINARY_ADD
          RETURN_VALUE
END
Function: main/0
Constants: None, 3
Globals: print, f
BEGIN
          LOAD_GLOBAL                    0
          LOAD_GLOBAL                    1
          LOAD_CONST                     1
          CALL_FUNCTION                  1
          CALL_FUNCTION                  1
          POP_TOP
          LOAD_CONST                     0
          RETURN_VALUE
END

Interestingly, this program cannot be written in Python. The closest Python equivalent of this program is given below. However, it is not the equivalent of the program written above. In fact, the program below won’t even execute. There is an error on the line x = x + 1. The problem is that as soon as Python sees x = in the function g, it decides there is another x that is a local variable in g. But, then x = x + 1 results in an error because x in g has not yet been assigned a value.

def f(x):
    def g(y):
        x = x + 1
        return x + y

    return g(x) + x

def main():
    print(f(3))

main()

3.16.9. Solution to Practice Problem 3.9

A couple things to notice in figure 3. The run-time stack contains one stack frame for every function call to factorial. Each of the stack frames, except the one for the main function, point at the factorial code. While there is only one copy of each function’s code, there may be multiple stack frames executing the code. This happens when a function is recursive. There also multiple n values, one for each stack frame. Again this is expected in a recursive function.

_images/factorial.png

Fig. 3: Execution of fact.casm