| 1 | # Overview # |
| 2 | |
| 3 | This example uses `get_user_string` and `atoi` combined with `printf` to print |
| 4 | a string containing two user inputs of type string and integer, both supplied |
| 5 | by the user at runtime. |
| 6 | |
| 7 | # Code Commentary # |
| 8 | |
| 9 | Since we're building up to calling `printf`, refer to its call stack in |
| 10 | `stdio.pvvs`, reproduced below. |
| 11 | |
| 12 | Call Stack: |
| 13 | ACSII '\0' |
| 14 | string word n |
| 15 | ... |
| 16 | string word 1 |
| 17 | substitution n |
| 18 | ... |
| 19 | substitution 1 |
| 20 | number of substitutions <-- TOS |
| 21 | |
| 22 | We will begin by putting the null terminated ASCII string on the stack, |
| 23 | followed by building up the substitutions. Put a string on the stack as |
| 24 | introduced in the "Hello, World" example. Note the two substitutions. |
| 25 | |
| 26 | A"Hello, %s! In a trusting world, %u is older than I am.\n" |
| 27 | |
| 28 | Prompt the user for a name. Since we call `printf`, we also `PUSH 0` since this |
| 29 | is a static string. |
| 30 | |
| 31 | A"What is your name?\n" |
| 32 | SSSSN | PUSH 0 |
| 33 | NSTTSSSN | JSR > 1000 (printf) |
| 34 | |
| 35 | Call `get_user_string` from the stdlib. From its call stack we see that it |
| 36 | wants the location of a buffer and the size of that buffer. Since we have the |
| 37 | whole heap available, let us start at address `0x1000` and define the buffer to |
| 38 | be `0x1000` words long. |
| 39 | |
| 40 | SSSTSSSSSSSSSSSSN | PUSH 0x1000 (buf_size) |
| 41 | SSSTSSSSSSSSSSSSN | PUSH 0x1000 (buf_addr) |
| 42 | NSTTSSSTSN | JSR > 100010 (get_user_string) |
| 43 | |
| 44 | Since users always cooperate, we now have a null terminated string containing |
| 45 | the user name stored at `0x1000`. For now, just remember that address. |
| 46 | |
| 47 | Prompt the user for an age, calling `printf` just like we did earlier. |
| 48 | |
| 49 | A"How old age you?\n" |
| 50 | SSSSN | PUSH 0 |
| 51 | NSTTSSSN | JSR > 1000 (printf) |
| 52 | |
| 53 | Call `get_user_string` again, same as before, but this time at address `0x2000` |
| 54 | so we don't overwrite the user's name. |
| 55 | |
| 56 | SSSTSSSSSSSSSSSSN | PUSH 0x1000 (buf_size) |
| 57 | SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (buf_addr) |
| 58 | NSTTSSSTSN | JSR > 100010 (get_user_string) |
| 59 | |
| 60 | Now call `atoi` to convert the ASCII number typed by the user into an integer. |
| 61 | Per the call stack documentation, first push the address of the start of the |
| 62 | buffer (`0x2000`). |
| 63 | |
| 64 | SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (buf_addr) |
| 65 | NSTTTSSSSN | JSR > 110000 (atoi) |
| 66 | |
| 67 | We do not care about anything else that might be in that string, so we `DROP` |
| 68 | the TOS which, per the return stack documentation, was a pointer to the last |
| 69 | parsed character. All we keep is the converted integer, which we leave on the |
| 70 | stack. |
| 71 | |
| 72 | SNN | DROP |
| 73 | |
| 74 | At this point the stack contains our string and one of our two substitutions |
| 75 | (age). Push the other substitution onto the stack. Since we are substituting a |
| 76 | string, per the `printf` documentation, we push the address of our user name |
| 77 | buffer (`0x1000`) back onto the stack. |
| 78 | |
| 79 | SSSTSSSSSSSSSSSSN | PUSH 0x1000 (buf_addr) |
| 80 | |
| 81 | With all the substitutions on the stack and in order, we `PUSH 2` which is the |
| 82 | number of substitutions. |
| 83 | |
| 84 | SSSTSN | PUSH 2 |
| 85 | |
| 86 | The call stack is fully prepared. At the top we have the number of |
| 87 | substitutions in the string followed by the two substitutions in order, first a |
| 88 | pointer to a string and then an integer. Finally, we have the string itself, |
| 89 | with a null terminator at the end. |
| 90 | |
| 91 | It is time to call `printf` and then exit. |
| 92 | |
| 93 | NSTTSSSN | JSR > 1000 (printf) |
| 94 | NNN | Terminate program |
| 95 | |
| 96 | Do not forget to include the library files for any subroutines called. These |
| 97 | must be placed outside the path of normal execution flow. The end of the file |
| 98 | is usually suitable. |
| 99 | |
| 100 | #include <stdio.pvvs> |
| 101 | #include <string.pvvs> |
| 102 | #include <convert.pvvs> |
| 103 | |
| 104 | That's the whole program. The file `hello.pvvs` contains an uncommented version |
| 105 | of this exact source code and may be executed with a `make run` in this |
| 106 | directory. |
| 107 | |