CSC231 Homework 9 Fall 2017

From dftwiki3
Revision as of 16:31, 13 December 2017 by Thiebaut (talk | contribs)
Jump to: navigation, search

--D. Thiebaut (talk) 20:19, 4 December 2017 (EST)






This homework is due 12/11/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.





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.

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) as inspiration and create a program called hw9b.asm that will use a recursive function to print all the moves required to displace disks in the Towers of Hanoi game. The program will also display the number of moves required to move N disks from the source peg to the destination peg.

Your program must be written in assembly, and you can use the hanoi.asm program we saw in class.

Requirements


  • Your program will call the recursive function moveDisks and pass it the characters 'A', 'B', 'C' as well as the number of disks that will be read from the command line. 'A' will be the source, and 'B' the destination for the N disks. Your program will then display an output consisting of two letters separated by a space, followed by a new-line. For example:
A B
A C
B C
A B
...
  • At the end, the program will output an integer equal to the number of moves printed. You are free to decide how to compute this quantity. It just have to be correct.
  • When calling the moveDisks function, you may pass the characters and the number of disk through registers, or through the stack. This is up to you.
  • 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 hw9b.asm
231b@aurora ~  $ ld -melf_i386 -o hw9b hw9b.o 231Lib.o
cs231a@aurora ~ $ ./hw9b 
cs231a@aurora ~ $ ./hw9b 1
A B
1
cs231a@aurora ~ $ ./hw9b 2
A C
A B
C B
3
cs231a@aurora ~ $ ./hw9b 3
A B
A C
B C
A B
C A
C B
A B
7
cs231a@aurora ~ $ ./hw9b 4
A C
A B
C B
A C
B A
B C
A C
A B
C B
C A
B A
C B
A C
A B
C B
15

Submission


Submit your program on Moodle.



Solution Programs


Program 1


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// getMax: returns the largest of 3 integers passed
// by value
int getMin( int a, int b, int c ) {
  if ( a <= b && a <= c ) 
    return a;
  if ( b <= a && b <= c )
    return b;
  return c;
}

// zap: given a string haystack, and a string needle,
// looks for the firlst location of needle in the haystack, and
// when it finds it, replaces it with dashes.  Example
// char s1[] = "Mississippi";
// char s2[] = "ss";
// char *p  = s1, *q = s2;
// zap( s1, s2 ) will replace s1 with "Mi--issippi".
// zap( s1, "mm" ) will leave s1 unchanged.
// Returns 0 if zap couldn't fine the needle in
// the haystack.  Returns 1 if some character replacement
// took place.
int zap( char *haystack, char *needle ) {
  char *p = strstr( haystack, needle );
  char *q;
  if ( p==NULL )
    return 0;
  for ( q=p; q != p+strlen( needle ); q++ )
    *q = '-';
  return 1;
}

// merge(): takes two sorted arrays of ints of length 5 
// and merges them into an array of length 10, so that
// the third array is sorted, in increasing order.
void merge( int A[], int B[], int C[] ) {
  int i = 0;
  int j = 0;
  int k = 0;
  while ( 1 ) {
    if ( A[i] < B[j] ) {
      C[k++] = A[i++];
      //printf( "A[%d] (%d) < B[%d] (%d) ==> C[%d] (%d)\n", i-1, A[i-1], j, B[j], k-1, C[k-1] );
    }
    else {
      C[k++] = B[j++];
      //printf( "A[%d] (%d) > B[%d] (%d) ==> C[%d] (%d)\n", i, A[i], j-1, B[j-1], k-1, C[k-1] );
    }
    if ( i>=5 || j>=5 )
      break;
  }  

  while ( i<5 && k <10 ) {
    C[k++] = A[i++];
    //printf( "C[%d] (%d) <--  A[%d] (%d)\n", k-1, C[k-1], i-1, A[i-1] );
  }
  while ( j<5 && k <10 ) {
    C[k++] = B[j++];  
    //printf( "C[%d] (%d) <--  B[%d] (%d)\n", k-1, C[k-1], j-1, B[j-1] );

  }
}


Problem 2


;;; hanoi.asm
;;; D. Thiebaut  
;;;
;;; This program solves the "Towers of Hanoi" program
;;; in assembly.  It prompts the user for an integer
;;; number of disks (must be larger than 0) and displays
;;; the name of the peg from which to move a disk, and
;;; the name of the peg to move it to.  The pegs are
;;; labeled 'A', 'B', and 'C', and the disks are always
;;; assumed to moved originally from 'A' to 'B'.
;;; To assemble, link, and run:
;;;     nasm -f elf  231Lib.o
;;;     nasm -f elf  hanoi.asm  
;;;     ld -melf_i386 -o hanoi hanoi.o 231Lib.o
;;;     ./hanoi        
;;;      

                section .data
N		dd	5
count   	dd  0
	
                section .text
extern		_getInput
extern		_println
extern      	_printInt
extern		_printString
extern		_atoi	

        	global  _start
_start:

;;; get N from command line
;;; 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

		cmp	eax, 1
		jle	exit

;;; integer was passed on command line
	
		mov	eax, dword[ebp+4+4] ; make eax points to arv[1]
		call	_atoi		    ; convert ascii string to int
		mov	dword[N], eax	    ; save N
	

;;; define the 3 pegs and pass them in bl, cl, and dl.

		mov	bl, 'A'
		mov	cl, 'B'
		mov	dl, 'C'
	
;;; moveDisks( N, 'A', 'B', 'C' )       ; eax <- N
		call	moveDisks 	; bl  <- 'A'
					; cl  <- 'B'
					; dl  <- 'C'
		mov     eax, dword[count]
		call    _printInt
		call    _println

;;; exit
exit:	        mov     ebx, 0
                mov     eax, 1
                int     0x80

	
;;; ------------------------------------------------------------------
;;; moveDisks( n, source, dest, extra )
;;;           eax   bl     cl     dl
;;; Moves the n disks from source to dest using extra if necessary.
;;; Uses recursion to move the N-1 disks above the last one.
;;; Does not modify any of the registers
;;; ------------------------------------------------------------------
moveDisks:	pushad
	
;;; if n==1:
;;;    print( source, dest )
		cmp	eax, 1
		jg	recurse
		mov	al, bl 		; print source
		call	printChar 	
		mov	al, ' '		; print space
		call	printChar
		mov	al, cl		; print dest 
		call	printChar
		call	_println 	; print \n
	
		popad			; done! return
		inc     dword[count]
		ret
	
recurse:
;;; moveDisks( n-1, source, temp, dest )
		dec	eax		; eax <- n-1
		xchg	cl, dl		; swap cl & dl
		call	moveDisks       ; move n-1
 		xchg	cl, dl		; swap them back


;;; print( source, dest )
                mov     al, bl		; print source
		call    printChar
		mov     al, ' '		; print space
	        call    printChar
	        mov     al, cl 		; print dest
	        call    printChar
	        call    _println 	; print \n
        inc     dword[count]
        
;;; moveDisks( n-1, temp, dest, source )
		popad
 		pushad		        ; makes sense, but not needed
		xchg	bl, dl
		dec	eax
		call	moveDisks

 		popad                   ; makes sense, by not needed
		ret
	
;;; ------------------------------------------------------------------
;;; printChar: prints the character in al
;;; does not modify any other register
;;; ------------------------------------------------------------------
		section	.data
char		db	'A'
		section	.text
printChar:	pushad
		mov	byte[char],al
		mov	ecx, char
		mov	edx, 1
		call	_printString
		popad
		ret