Difference between revisions of "CSC231 Optional Homework 9 2017"
(→Functions) |
(→Preparation for Problem 2: Accessing argc and argv) |
||
(19 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
---- | ---- | ||
− | <center> | + | <!--center> |
<font size="+2">Page under construction!</font> | <font size="+2">Page under construction!</font> | ||
</center> | </center> | ||
Line 8: | Line 8: | ||
<center> | <center> | ||
[[File:UnderConstruction.jpg|300px]] | [[File:UnderConstruction.jpg|300px]] | ||
− | </center> | + | </center--> |
+ | <br /> | ||
+ | <br /> | ||
+ | <bluebox>This homework is due 05/01/17 at 11:55 p.m. It is optional. If you submit it, you will have the opportunity to replace a homework you may have missed, or up the lowest grade so far on a previous homework. | ||
+ | </bluebox> | ||
+ | <br /> | ||
+ | <br /> | ||
=Problem 1: C Functions= | =Problem 1: C Functions= | ||
<br /> | <br /> | ||
− | Write a '''C''' program that contains only C functions. The functions will be called by an external main program. This is very similar to the the last homework assignment, except then it was done in assembly. | + | Write a '''C''' program called '''funcs.c''' that contains only C functions. You will need to write an additional file called '''funcs.h''', and the information about what it contains is the section on testing. The functions will be called by an external main program. This is very similar to the the last homework assignment, except then it was done in assembly. |
<br /> | <br /> | ||
==Functions== | ==Functions== | ||
<br /> | <br /> | ||
− | + | Your '''funcs.c''' program should contain 3 functions: '''getMin()''', '''zap()''' and '''merge()'''. | |
<br /> | <br /> | ||
; getMin() | ; getMin() | ||
Line 52: | Line 58: | ||
// zap(s2) = Mississippi Burning | // zap(s2) = Mississippi Burning | ||
</source> | </source> | ||
+ | <br /> | ||
+ | :Note that zap() will return 1 if it has performed a substitution, and 0 otherwise. | ||
<br /> | <br /> | ||
; merge() | ; merge() | ||
Line 62: | Line 70: | ||
merge( A, B, C ); | merge( A, B, C ); | ||
− | for ( i=0; i<10 | + | for ( i=0; i<10; i++ ) |
printf( "%d, ", C[i] ); | printf( "%d, ", C[i] ); | ||
printf( "\n" ); | printf( "\n" ); | ||
Line 70: | Line 78: | ||
</source> | </source> | ||
<br /> | <br /> | ||
+ | |||
==Merging in Python== | ==Merging in Python== | ||
<br /> | <br /> | ||
Line 122: | Line 131: | ||
==Testing Your Program== | ==Testing Your Program== | ||
<br /> | <br /> | ||
+ | Here is a simple example showing how to write two C programs, one containing functions, and one containing a main program, and link them together. | ||
+ | <br /> | ||
+ | ===funcDemo.c=== | ||
+ | <br /> | ||
+ | ::<source lang="C"> | ||
+ | // funcDemo.c | ||
+ | // D. Thiebaut | ||
+ | // demonstration code for illustrating how to link | ||
+ | // several C programs. | ||
+ | // This program contains only 1 function. | ||
+ | |||
+ | // sum() | ||
+ | // returns the sum of the 2 ints it gets as parameters. | ||
+ | int sum( int a, int b ) { | ||
+ | return a + b; | ||
+ | } | ||
+ | </source> | ||
<br /> | <br /> | ||
+ | ===mainDemo.c=== | ||
<br /> | <br /> | ||
+ | ::<source lang="C"> | ||
+ | // mainDemo.c | ||
+ | // D. Thiebaut | ||
+ | // main program that calls the sum() | ||
+ | // function from funcDemo.c | ||
+ | #include <stdio.h> | ||
+ | #include "funcDemo.h" | ||
+ | |||
+ | void main() { | ||
+ | int a=3, b=10; | ||
+ | int c; | ||
+ | |||
+ | c = sum( -1, 2000000000 ); | ||
+ | printf( "sum( %d, %d ) = %d\n", -1, 2000000000, sum(-1, 2000000000 ) ); | ||
+ | printf( "sum( %d, %d ) = %d\n", a, b, sum(a, b) ); | ||
+ | printf( "sum( %d, sum( %d, %d ) ) = %d\n", a, 2, b, sum(a, sum( 2, b ) ) ); | ||
+ | printf( "sum( %d, %d ) = %d\n", -1, 1, sum(-1, 1) ); | ||
+ | } | ||
+ | </source> | ||
<br /> | <br /> | ||
+ | ===funcDemo.h=== | ||
<br /> | <br /> | ||
+ | In order to link together '''funcDemo.c''' and '''mainDemo.c''', we need a third file, called a ''header'' file, that will list all the functions in funcDemo.c, but just the name of the functions and what kind of parameters they take, along with the type of value they return. We'll ''include'' this file in '''mainDemo.c''', so that it knows what functions are available "on the outside." For funcDemo.c, the header file '''funcDemo.h''' looks like this: | ||
<br /> | <br /> | ||
+ | ::<source lang="C"> | ||
+ | // funcDemo.h | ||
+ | // D. Thiebaut | ||
+ | // header file for funcDemo.c | ||
+ | // | ||
+ | |||
+ | int sum( int a, int b ); | ||
+ | |||
+ | </source> | ||
<br /> | <br /> | ||
+ | ===Compiling Steps=== | ||
<br /> | <br /> | ||
+ | |||
+ | gcc -c funcDemo.c | ||
+ | gcc -o funcMain mainDemo.c funcDemo.o | ||
+ | |||
+ | * the first execution of the '''gcc''' command instructs it to generate an object file (with a .o extension) for '''funcDemo.c''' | ||
+ | * the second execution of '''gcc''' instructs it to compile '''funcMain.c''', link it with the object file '''funcDemo.o''' and output the resulting executable in '''funcMain.''' | ||
<br /> | <br /> | ||
+ | * to run the program, we simply type: | ||
+ | |||
+ | ./funcMain | ||
+ | |||
+ | : and we get the following output: | ||
+ | |||
+ | sum( -1, 2000000000 ) = 1999999999 | ||
+ | sum( 3, 10 ) = 13 | ||
+ | sum( 3, sum( 2, 10 ) ) = 15 | ||
+ | sum( -1, 1 ) = 0 | ||
+ | |||
<br /> | <br /> | ||
+ | ==A Test Program for '''funcs.c'''== | ||
<br /> | <br /> | ||
+ | In case this could be useful, here is a test main program to test your functions with, and its output when linked with the solution program containing the functions. | ||
<br /> | <br /> | ||
+ | ::<source lang="C"> | ||
+ | // main.c | ||
+ | // D. Thiebaut | ||
+ | // Test program for functions of Homework 9. | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <string.h> | ||
+ | #include "funcs.h" | ||
+ | |||
+ | void main(){ | ||
+ | char s1[] = "Mississippi Burning"; | ||
+ | char s2[] = "Mississippi Burning"; | ||
+ | int i; | ||
+ | |||
+ | //--- test getMin --- | ||
+ | printf( "Min of -10, 1, 10 = %d\n", getMin( -10, 1, 10 ) ); | ||
+ | printf( "Min of -10, 1, 10, -20, 2 = %d\n", getMin( getMin( -10, 1, 10 ), -20, 2 ) ); | ||
+ | |||
+ | //--- test zap --- | ||
+ | printf( "s1 = %s\n", s1 ); | ||
+ | zap( s1, "ss" ); | ||
+ | printf( "zap(s1) = %s\n", s1 ); | ||
+ | |||
+ | printf( "s2 = %s\n", s2 ); | ||
+ | zap( s2, "tt" ); | ||
+ | printf( "zap(s2) = %s\n", s2 ); | ||
+ | |||
+ | strcpy( s1, "Mississippi less" ); | ||
+ | printf( "s1 = %s\n", s1 ); | ||
+ | while ( zap( s1, "ss" ) ) | ||
+ | /* do nothing */; | ||
+ | printf( "zap(zap(...(s1))) = %s\n", s1 ); | ||
+ | |||
+ | //--- test merge --- | ||
+ | int A[] = { 1, 2, 3, 10, 11 }; | ||
+ | int B[] = { 4, 5, 12, 13, 15 }; | ||
+ | int C[10]; | ||
+ | |||
+ | merge( A, B, C ); | ||
+ | for ( i=0; i<10 && A[i]!=-1; i++ ) | ||
+ | printf( "%d, ", C[i] ); | ||
+ | printf( "\n" ); | ||
+ | |||
+ | } | ||
+ | </source> | ||
<br /> | <br /> | ||
+ | : output | ||
<br /> | <br /> | ||
+ | |||
+ | Min of -10, 1, 10 = -10 | ||
+ | Min of -10, 1, 10, -20, 2 = -20 | ||
+ | s1 = Mississippi Burning | ||
+ | zap(s1) = Mi--issippi Burning | ||
+ | s2 = Mississippi Burning | ||
+ | zap(s2) = Mississippi Burning | ||
+ | s1 = Mississippi less | ||
+ | zap(zap(...(s1))) = Mi--i--ippi le-- | ||
+ | 1, 2, 3, 4, 5, 10, 11, 12, 13, 15, | ||
+ | |||
+ | <br /> | ||
+ | |||
+ | ==Submission== | ||
+ | <br /> | ||
+ | Submit your program as two different files (funcs.c and funcs.h) in the Homework 9 section, on Moodle. | ||
<br /> | <br /> | ||
+ | =Preparation for Problem 2: Accessing argc and argv= | ||
<br /> | <br /> | ||
+ | Using argc and argv to get command line arguments is standard practice in most programming languages (at least the one that can be run from the command line). | ||
+ | We can also do it in assembly, as well. | ||
+ | <br /> | ||
+ | You will need a new version of 231Lib.asm to make this work. Get a fresh new copy of the library, please: | ||
+ | |||
+ | getcopy 231Lib.asm | ||
+ | nasm -f elf 231Lib.asm | ||
+ | |||
+ | Here is a demo program that gets '''argc''' (the number of arguments on the command line, including the name of the program itself), and '''argv[1]''' from the command line: | ||
+ | <br /> | ||
+ | ::<source lang="asm"> | ||
+ | ;;; getParams.asm | ||
+ | ;;; D. Thiebaut | ||
+ | ;;; | ||
+ | ;;; gets argc and argv[1] as an int from the command | ||
+ | ;;; line. | ||
+ | ;;; | ||
+ | ;;; To assemble, link, and run: | ||
+ | ;;; nasm -f elf getParams.asm | ||
+ | ;;; nasm -f elf 231Lib.asm | ||
+ | ;;; ld -melf_i386 -o getParams getParams.o 231Lib.o | ||
+ | ;;; ./getParams | ||
+ | ;;; | ||
+ | |||
+ | |||
+ | section .text | ||
+ | extern _atoi ; note the new function | ||
+ | extern _printDec | ||
+ | extern _println | ||
+ | |||
+ | global _start | ||
+ | _start: | ||
+ | |||
+ | ;;; When any assembly language program starts, the operating system | ||
+ | ;;; passes it argc and argv through the statck. The esp register | ||
+ | ;;; points to argc. at esp+4, is a pointer to the beginning of argv[0], | ||
+ | ;;; as a string. At esp+8 is a pointer to the beginning of argv[1], | ||
+ | ;;; as a string. | ||
+ | |||
+ | mov ebp, esp | ||
+ | mov eax, dword[ebp] ; put argc into eax | ||
+ | call _printDec ; print it | ||
+ | call _println | ||
+ | |||
+ | mov eax, dword[ebp+4+4] ; make eax points to arv[1] | ||
+ | call _atoi ; convert ascii string to int | ||
+ | call _printDec ; print it. | ||
+ | call _println ; new line. | ||
+ | |||
+ | ;;; exit | ||
+ | mov ebx, 0 | ||
+ | mov eax, 1 | ||
+ | int 0x80 | ||
+ | |||
+ | </source> | ||
+ | <br /> | ||
+ | * Compile and run it with a variety of input parameters | ||
+ | |||
+ | 231b@aurora ~ $ nasm -f elf getParams.asm | ||
+ | 231b@aurora ~ $ nasm -f elf getParams.asm | ||
+ | 231b@aurora ~ $ nasm -f elf 231Lib.asm | ||
+ | 231b@aurora ~ $ ld -melf_i386 getParams.o 231Lib.o -o getParams | ||
+ | 231b@aurora ~ $ ./getParams 123 | ||
+ | 2 | ||
+ | 123 | ||
+ | 231b@aurora ~ $ ./getParams 12 hello | ||
+ | 3 | ||
+ | 12 | ||
+ | |||
+ | |||
+ | * When you run the program with command line arguments, such as "123" or "123 hello", getParams prints the number of arguments first, follow by the integer that is entered right after the program name. | ||
+ | |||
+ | ./getParams 123 | ||
+ | |||
+ | : in this case we have 2 words on the command line, "./getParams" and "123", so the first number printed is 2, and "123" is translated from a string to an int by the function '''atoi''', and that's the second number printed. | ||
+ | |||
+ | ./getParams 123 hello | ||
+ | |||
+ | : in this second case, we have 3 words on the command line, so the first number printed is 3, and '''atoi''' transforms "12" into the integer 12, which is the 2nd number printed. | ||
+ | |||
+ | =Problem 2= | ||
+ | <br /> | ||
+ | |||
+ | Your assignment is to use the previous program (getParams.asm) and add to it a recursive function that will print the moves required to displace disks in the Towers of Hanoi game. All in assembly, of course. | ||
+ | |||
+ | You can use this Python version for inspiration: | ||
+ | <br /> | ||
+ | ::<source lang="python"> | ||
+ | # hanoi.py | ||
+ | # D. Thiebaut | ||
+ | # implements the game of hanoi in python. | ||
+ | # uses recursion. | ||
+ | # | ||
+ | # to run, type | ||
+ | # python hanoi.py | ||
+ | # at the command line, or run in Idle | ||
+ | from __future__ import print_function | ||
+ | |||
+ | def moveDisk( source, dest, extra, n ): | ||
+ | if n==1: | ||
+ | print( "move disk from %s to %s" % ( source, dest ) ) | ||
+ | return | ||
+ | |||
+ | # more than 1 disk... | ||
+ | moveDisk( source, extra, dest, n-1) | ||
+ | print( "move disk from %s to %s" % ( source, dest ) ) | ||
+ | moveDisk( extra, dest, source, n-1) | ||
+ | |||
+ | def main(): | ||
+ | moveDisk( "A", "B", "C", 5 ) | ||
+ | |||
+ | main() | ||
+ | </source> | ||
+ | <br /> | ||
+ | The output of the program above is: | ||
+ | <code><pre> | ||
+ | move disk from A to B | ||
+ | move disk from A to C | ||
+ | move disk from B to C | ||
+ | move disk from A to B | ||
+ | move disk from C to A | ||
+ | move disk from C to B | ||
+ | move disk from A to B | ||
+ | move disk from A to C | ||
+ | move disk from B to C | ||
+ | move disk from B to A | ||
+ | move disk from C to A | ||
+ | move disk from B to C | ||
+ | move disk from A to B | ||
+ | move disk from A to C | ||
+ | move disk from B to C | ||
+ | move disk from A to B | ||
+ | move disk from C to A | ||
+ | move disk from C to B | ||
+ | move disk from A to B | ||
+ | move disk from C to A | ||
+ | move disk from B to C | ||
+ | move disk from B to A | ||
+ | move disk from C to A | ||
+ | move disk from C to B | ||
+ | move disk from A to B | ||
+ | move disk from A to C | ||
+ | move disk from B to C | ||
+ | move disk from A to B | ||
+ | move disk from C to A | ||
+ | move disk from C to B | ||
+ | move disk from A to B | ||
+ | </pre></code> | ||
+ | |||
+ | ==Requirements== | ||
+ | * Your program will call the recursive function '''moveDisks''' and pass it the characters 'A', 'B', 'C' and the number passed on the command line, and will display an output similar to the one shown above, except that for every move, all you have to print is two letters separated by a space, followed by a new-line. For example: | ||
+ | |||
+ | <code><pre> | ||
+ | A B | ||
+ | A C | ||
+ | B C | ||
+ | A B | ||
+ | ... | ||
+ | </pre></code> | ||
+ | |||
+ | * When calling the '''moveDisks''' function, you may pass the characters and the number of disk through registers, or through the stack. | ||
+ | * You can assume that the user will always be well-behaved and will always pass an integer on the command line, and that this integer will always be larger than 0. | ||
+ | <br /> | ||
+ | |||
+ | ==Program Behavior== | ||
+ | <br /> | ||
+ | Below is an example of how your program should behave: | ||
+ | <br /> | ||
+ | 231b@aurora ~ $ '''nasm -f elf 231Lib.asm''' | ||
+ | 231b@aurora ~ $ '''nasm -f elf hanoi.asm''' | ||
+ | 231b@aurora ~ $ '''ld -melf_i386 -o hanoi hanoi.o 231Lib.o''' | ||
+ | 231b@aurora ~ $ '''./hanoi 1''' | ||
+ | A B | ||
+ | 231b@aurora ~ $ '''./hanoi 2''' | ||
+ | A C | ||
+ | A B | ||
+ | C B | ||
+ | 231b@aurora ~ $ '''./hanoi 3''' | ||
+ | A B | ||
+ | A C | ||
+ | B C | ||
+ | A B | ||
+ | C A | ||
+ | C B | ||
+ | A B | ||
+ | 231b@aurora ~ $ | ||
+ | |||
+ | ==Submission== | ||
<br /> | <br /> | ||
+ | Submit your program on Moodle. | ||
<br /> | <br /> | ||
<br /> | <br /> |
Latest revision as of 04:51, 4 May 2017
--D. Thiebaut (talk) 11:10, 22 April 2017 (EDT)
Problem 1: C Functions
Write a C program called funcs.c that contains only C functions. You will need to write an additional file called funcs.h, and the information about what it contains is the section on testing. The functions will be called by an external main program. This is very similar to the the last homework assignment, except then it was done in assembly.
Functions
Your funcs.c program should contain 3 functions: getMin(), zap() and merge().
- getMin()
- Receives 3 ints as parameters and returns the smallest.
- Example
printf( "Min of -10, 1, 10 = %d\n", getMin( -10, 1, 10 ) ); printf( "Min of -10, 1, 10, -20, 2 = %d\n", getMin( getMin( -10, 1, 10 ), -20, 2 ) ); // output: // Min of -10, 1, 10 = -10 // Min of -10, 1, 10, -20, 2 = -20
- zap()
- Receives two strings as parameters and modifies the first one by finding the first
- string in it, and replacing it with dashes.
- Example
char s1[] = "Mississippi Burning"; char s2[] = "Mississippi Burning"; //--- test zap --- printf( "s1 = %s\n", s1 ); zap( s1, "ss" ); printf( "zap(s1) = %s\n", s1 ); printf( "s2 = %s\n", s2 ); zap( s2, "tt" ); printf( "zap(s2) = %s\n", s2 ); // output: // s1 = Mississippi Burning // zap(s1) = Mi--issippi Burning // s2 = Mississippi Burning // zap(s2) = Mississippi Burning
- Note that zap() will return 1 if it has performed a substitution, and 0 otherwise.
- merge()
- receives 3 arrays of ints. The first two have dimension 5, each, and are sorted. The third one is of dimension 10, and contains random information, i.e. it is not initialized. Merge() takes the ints from both arrays of dimension 5 and merges them into the third array, keeping the ints sorted. You may assume that the first two arrays will always have dimension 5, and the third one will always have dimension 10.
- Example
int A[] = { 1, 2, 3, 10, 11 }; int B[] = { 4, 5, 12, 13, 15 }; int C[10]; merge( A, B, C ); for ( i=0; i<10; i++ ) printf( "%d, ", C[i] ); printf( "\n" ); // output: // 1, 2, 3, 4, 5, 10, 11, 12, 13, 15,
Merging in Python
For reference, here is how merge would work in Python:
from __future__ import print_function def merge( A, B, C ): """ merges 2 arrays into a 3rd one. The dimensions of the arrays can be any valid integer. C must be a mutable list. """ i = 0 j = 0 k = 0 while i<len(A) and j<len(B): if A[i] < B[j]: C.append( A[i] ) i += 1 else: C.append( B[j] ) j += 1 if i >= len( A) or j >= len( B ): break while i < len( A ): C.append( A[i] ) i += 1 while j < len( B ): C.append( B[j] ) j += 1 def main(): A = [1, 3, 10, 20, 30 ] B = [2, 3, 4, 5, 100 ] C = [] merge( A, B, C ) print( "A = ", A ) print( "B = ", B ) print( "C = ", C ) main() # output # A = [1, 3, 10, 20, 30, 31] # B = [2, 3, 4, 5, 100] # C = [1, 2, 3, 3, 4, 5, 10, 20, 30, 31, 100]
Testing Your Program
Here is a simple example showing how to write two C programs, one containing functions, and one containing a main program, and link them together.
funcDemo.c
// funcDemo.c // D. Thiebaut // demonstration code for illustrating how to link // several C programs. // This program contains only 1 function. // sum() // returns the sum of the 2 ints it gets as parameters. int sum( int a, int b ) { return a + b; }
mainDemo.c
// mainDemo.c // D. Thiebaut // main program that calls the sum() // function from funcDemo.c #include <stdio.h> #include "funcDemo.h" void main() { int a=3, b=10; int c; c = sum( -1, 2000000000 ); printf( "sum( %d, %d ) = %d\n", -1, 2000000000, sum(-1, 2000000000 ) ); printf( "sum( %d, %d ) = %d\n", a, b, sum(a, b) ); printf( "sum( %d, sum( %d, %d ) ) = %d\n", a, 2, b, sum(a, sum( 2, b ) ) ); printf( "sum( %d, %d ) = %d\n", -1, 1, sum(-1, 1) ); }
funcDemo.h
In order to link together funcDemo.c and mainDemo.c, we need a third file, called a header file, that will list all the functions in funcDemo.c, but just the name of the functions and what kind of parameters they take, along with the type of value they return. We'll include this file in mainDemo.c, so that it knows what functions are available "on the outside." For funcDemo.c, the header file funcDemo.h looks like this:
// funcDemo.h // D. Thiebaut // header file for funcDemo.c // int sum( int a, int b );
Compiling Steps
gcc -c funcDemo.c gcc -o funcMain mainDemo.c funcDemo.o
- the first execution of the gcc command instructs it to generate an object file (with a .o extension) for funcDemo.c
- the second execution of gcc instructs it to compile funcMain.c, link it with the object file funcDemo.o and output the resulting executable in funcMain.
- to run the program, we simply type:
./funcMain
- and we get the following output:
sum( -1, 2000000000 ) = 1999999999 sum( 3, 10 ) = 13 sum( 3, sum( 2, 10 ) ) = 15 sum( -1, 1 ) = 0
A Test Program for funcs.c
In case this could be useful, here is a test main program to test your functions with, and its output when linked with the solution program containing the functions.
// main.c // D. Thiebaut // Test program for functions of Homework 9. #include <stdio.h> #include <stdlib.h> #include <string.h> #include "funcs.h" void main(){ char s1[] = "Mississippi Burning"; char s2[] = "Mississippi Burning"; int i; //--- test getMin --- printf( "Min of -10, 1, 10 = %d\n", getMin( -10, 1, 10 ) ); printf( "Min of -10, 1, 10, -20, 2 = %d\n", getMin( getMin( -10, 1, 10 ), -20, 2 ) ); //--- test zap --- printf( "s1 = %s\n", s1 ); zap( s1, "ss" ); printf( "zap(s1) = %s\n", s1 ); printf( "s2 = %s\n", s2 ); zap( s2, "tt" ); printf( "zap(s2) = %s\n", s2 ); strcpy( s1, "Mississippi less" ); printf( "s1 = %s\n", s1 ); while ( zap( s1, "ss" ) ) /* do nothing */; printf( "zap(zap(...(s1))) = %s\n", s1 ); //--- test merge --- int A[] = { 1, 2, 3, 10, 11 }; int B[] = { 4, 5, 12, 13, 15 }; int C[10]; merge( A, B, C ); for ( i=0; i<10 && A[i]!=-1; i++ ) printf( "%d, ", C[i] ); printf( "\n" ); }
- output
Min of -10, 1, 10 = -10 Min of -10, 1, 10, -20, 2 = -20 s1 = Mississippi Burning zap(s1) = Mi--issippi Burning s2 = Mississippi Burning zap(s2) = Mississippi Burning s1 = Mississippi less zap(zap(...(s1))) = Mi--i--ippi le-- 1, 2, 3, 4, 5, 10, 11, 12, 13, 15,
Submission
Submit your program as two different files (funcs.c and funcs.h) in the Homework 9 section, on Moodle.
Preparation for Problem 2: Accessing argc and argv
Using argc and argv to get command line arguments is standard practice in most programming languages (at least the one that can be run from the command line).
We can also do it in assembly, as well.
You will need a new version of 231Lib.asm to make this work. Get a fresh new copy of the library, please:
getcopy 231Lib.asm nasm -f elf 231Lib.asm
Here is a demo program that gets argc (the number of arguments on the command line, including the name of the program itself), and argv[1] from the command line:
;;; getParams.asm ;;; D. Thiebaut ;;; ;;; gets argc and argv[1] as an int from the command ;;; line. ;;; ;;; To assemble, link, and run: ;;; nasm -f elf getParams.asm ;;; nasm -f elf 231Lib.asm ;;; ld -melf_i386 -o getParams getParams.o 231Lib.o ;;; ./getParams ;;; section .text extern _atoi ; note the new function extern _printDec extern _println global _start _start: ;;; When any assembly language program starts, the operating system ;;; passes it argc and argv through the statck. The esp register ;;; points to argc. at esp+4, is a pointer to the beginning of argv[0], ;;; as a string. At esp+8 is a pointer to the beginning of argv[1], ;;; as a string. mov ebp, esp mov eax, dword[ebp] ; put argc into eax call _printDec ; print it call _println mov eax, dword[ebp+4+4] ; make eax points to arv[1] call _atoi ; convert ascii string to int call _printDec ; print it. call _println ; new line. ;;; exit mov ebx, 0 mov eax, 1 int 0x80
- Compile and run it with a variety of input parameters
231b@aurora ~ $ nasm -f elf getParams.asm 231b@aurora ~ $ nasm -f elf getParams.asm 231b@aurora ~ $ nasm -f elf 231Lib.asm 231b@aurora ~ $ ld -melf_i386 getParams.o 231Lib.o -o getParams 231b@aurora ~ $ ./getParams 123 2 123 231b@aurora ~ $ ./getParams 12 hello 3 12
- When you run the program with command line arguments, such as "123" or "123 hello", getParams prints the number of arguments first, follow by the integer that is entered right after the program name.
./getParams 123
- in this case we have 2 words on the command line, "./getParams" and "123", so the first number printed is 2, and "123" is translated from a string to an int by the function atoi, and that's the second number printed.
./getParams 123 hello
- in this second case, we have 3 words on the command line, so the first number printed is 3, and atoi transforms "12" into the integer 12, which is the 2nd number printed.
Problem 2
Your assignment is to use the previous program (getParams.asm) and add to it a recursive function that will print the moves required to displace disks in the Towers of Hanoi game. All in assembly, of course.
You can use this Python version for inspiration:
# hanoi.py # D. Thiebaut # implements the game of hanoi in python. # uses recursion. # # to run, type # python hanoi.py # at the command line, or run in Idle from __future__ import print_function def moveDisk( source, dest, extra, n ): if n==1: print( "move disk from %s to %s" % ( source, dest ) ) return # more than 1 disk... moveDisk( source, extra, dest, n-1) print( "move disk from %s to %s" % ( source, dest ) ) moveDisk( extra, dest, source, n-1) def main(): moveDisk( "A", "B", "C", 5 ) main()
The output of the program above is:
move disk from A to B
move disk from A to C
move disk from B to C
move disk from A to B
move disk from C to A
move disk from C to B
move disk from A to B
move disk from A to C
move disk from B to C
move disk from B to A
move disk from C to A
move disk from B to C
move disk from A to B
move disk from A to C
move disk from B to C
move disk from A to B
move disk from C to A
move disk from C to B
move disk from A to B
move disk from C to A
move disk from B to C
move disk from B to A
move disk from C to A
move disk from C to B
move disk from A to B
move disk from A to C
move disk from B to C
move disk from A to B
move disk from C to A
move disk from C to B
move disk from A to B
Requirements
- Your program will call the recursive function moveDisks and pass it the characters 'A', 'B', 'C' and the number passed on the command line, and will display an output similar to the one shown above, except that for every move, all you have to print is two letters separated by a space, followed by a new-line. For example:
A B
A C
B C
A B
...
- When calling the moveDisks function, you may pass the characters and the number of disk through registers, or through the stack.
- You can assume that the user will always be well-behaved and will always pass an integer on the command line, and that this integer will always be larger than 0.
Program Behavior
Below is an example of how your program should behave:
231b@aurora ~ $ nasm -f elf 231Lib.asm 231b@aurora ~ $ nasm -f elf hanoi.asm 231b@aurora ~ $ ld -melf_i386 -o hanoi hanoi.o 231Lib.o 231b@aurora ~ $ ./hanoi 1 A B 231b@aurora ~ $ ./hanoi 2 A C A B C B 231b@aurora ~ $ ./hanoi 3 A B A C B C A B C A C B A B 231b@aurora ~ $
Submission
Submit your program on Moodle.