Putting things together (it will be a mess!)

Having all components for the factorial program (reading an integer, computing the factorial and printing an integer) we “just” have to put things together. However, you will see that this will be kind of a mess. That's because we haven't covered yet how two implement functions (this will be the topic of the next two sessions).

Why is it a mess?

Each of our program uses labels. When we combine them then some of them would be defined multiple times.

The program print_two_strings.s (you find the code below) prints two different strings:

theon$ ulmas -o print_two_strings print_two_strings.s
theon$ ulm print_two_strings
This is the first string ...
... and this another string.
theon$ 

It basically uses the code from the “hello, world!” program, i.e.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
        .equ       p,    1
        .equ       ch,   2

        .data
msg     .string    "hello, world!\n"

        .text
        ldzwq      msg,  %p
load    movzbq     (%p), %ch
        subq       0,    %ch,  %0
        jz         halt
        putc       %ch
        addq       1,    %p,   %p
        jmp        load
halt    halt       0

For printing two different strings the following modifications were applied:

  • two strings in the data segment with labels msg1 and msg2,

  • the instructions are basically duplicated (except for the halt instruction).

  • instead of jumping to the halt instruction when the string was printed we now jump to an label after the loop.

You can see that this pattern can be applied in a “mechanical style”:

 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
        .equ    P,      1
        .equ    CH,     2

        # strings for messages to the user
        .data
msg1:
        .string "This is the first string ...\n"
msg2
        .string "... and this another string.\n"


        .text
/*
        print msg1
*/
        ldzwq      msg1,        %P
.L0     movzbq     (%P),        %CH
        subq       0,           %CH,    %0
        jz         .L1
        putc       %CH
        addq       1,           %P,     %P
        jmp        .L0
.L1

/*
        print msg2
*/
        ldzwq      msg2,        %P
.L2     movzbq     (%P),        %CH
        subq       0,           %CH,    %0
        jz         .L3
        putc       %CH
        addq       1,           %P,     %P
        jmp        .L2
.L3
        
/*
        We are done
*/
halt    halt    0

What else might become a mess?

When we put things together for the factorial project each component uses (and modifies) some registers. In general it is very tedious to make sure that there are no conflicts. The complexity of this project is already at the limit where this is feasible. At least if we lower our demands. It would nice to have a program that can be used like that:

1
2
3
theon$ ulm factorial
n = 3
3! = 6

But we limit it to this

1
2
3
theon$ ulm factorial
n = 3
n! = 6

Do you see the difference? It just prints “n! = 6” instead of “3! = 6”. It is interesting how much this simplifies an implementation that does not use functions. And actually you should try to write a program that does actually print the result in the stye of “3! = 6” just to see how hard it is. But it is not required.

quiz05: Computing the factorial again

Write a program factorial.s that

  • prints “n = ”,

  • reads in an unsigned integer \(n\),

  • computes the factorial \(n!\),

  • prints “n = ” and

  • prints the computed factorial.

Note that if the factorial of \(n\) can not be represented as a 64-bit unsigned integer the program will print the factorial of \(n\) modulo \(2^{64}\), and that's ok. Submit you program on theon with the command submit quiz05 factorial.s.

Submit your program with

1
submit hpc quiz05 factorial.s

Skeleton for the quiz05

The code below is a skeleton for quiz05. In it's current for it just print two strings:

theon$ ulmas -o factorial_io_skeleton factorial_io_skeleton.s
theon$ ulm factorial_io_skeleton
n = n! = 
theon$ 

So obviously you have to fill in some gaps for reading in an integer, computing the factorial and printing the result. The skeleton already contains comments that suggest where to insert your code. Basically that will your code from the last three assignments (with some modifications as suggested in the skeleton). In order to avoid conflicts due to how the components make use of registers it also contains some .equ directives. These are supposed to map register names such that everything works seamlessly together. Depending on your code for the previous quizzes you might need to adjust these directives:

 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
        # used by code from factorial.s, printuint.s, readuint.s
        .equ    N,      1

        # needed for code from factorial.s
        .equ    RES,    N+4

        # needed for code from printuint.s
        .equ    NH,     N+1
        .equ    DIGIT,  N+2
        .equ    P,      N+3

        # needed for code from readuint.s
        .equ    CH,     N+5

        # needed for printuint.s 
        .bss
buf     .space  21

        # strings for messages to the user
        .data
n_equals:
        .string "n = "
result_is
        .string "n! = "


        .text
/*
        print "n = "
*/
        ldzwq      n_equals,    %P
.L0     movzbq     (%P),        %CH
        subq       0,           %CH,    %0
        jz         .L1
        putc       %CH
        addq       1,           %P,     %P
        jmp        .L0
.L1

/*
        TODO: read unsigned integer

        Modification: jump to done_readuint when done (instead to halt)
*/

        #
        # Your code here
        #
done_readuint

/*
        TODO: compute the factorial

        Modification: jump to done_factorial when done (instead to halt)
*/

        #
        # Your code here
        #
done_factorial  

/*
        print "n! = "
*/
        ldzwq      result_is,   %P
.L2     movzbq     (%P),        %CH
        subq       0,           %CH,    %0
        jz         .L3
        putc       %CH
        addq       1,           %P,     %P
        jmp        .L2
.L3
        
/*
        TODO: print unsigned integer in %RES

        Modification:
        1) Copy %RES into %N (instead of loading value from data segment)
        2) Jump to done_printuint when done (instead to halt)
*/

        #
        # Your code here
        #
done_printuint

/*
        We are done
*/
        putc    '\n'
halt    halt    0