CMSI 284
Homework #4
Partial Answers
  1. Given

         A  B  C | X  Y
        ---------+------
         0  0  0 | 0  1
         0  0  1 | 0  0
         0  1  0 | 0  1
         0  1  1 | 1  0
         1  0  0 | 0  0
         1  0  1 | 1  1
         1  1  0 | 1  0
         1  1  1 | 0  1
    

    we see that

        x = a'bc + ab'c + abc'
    
        y = a'b'c' + a'bc' + ab'c + abc
          = a'c' + ac
    

    Also, using # to stand for NAND,

        x = ((a#a)#b#c) # (a#(b#b)#c) # (a#b#(c#c))
    
        y = ((a#a)#(c#c)) # (a # c)
    
  2. Here are some x86 logic instructions to perform the following operations on the ebx register:

  3. ; ----------------------------------------------------------------------------
    ; charsbyprintf.asm
    ;
    ; NASM implementation of a program that displays characters 32 through 126
    ; to standard output, 16 characters per line.
    ; ----------------------------------------------------------------------------	
    
    	global	_main
    	extern	_printf
    	
    	section .text
    _main:
    	mov	edx, S1			; edx will be next memory cell to fill
    	mov	al, 32			; al is next character to store
    L1:
    	mov	[edx], al		; store character in memory
    	inc	al			; next character
    	inc	edx			; next memory cell
    	
    	cmp	al, 127			; are we done?
    	je	L2			; yes
    	test	al, 15			; should we write a newline next?
    	jnz	L1			; nah, just do next character
            mov     byte [edx], 10          ; store a newline
    	inc	edx			; remember to "skip over" the newline
    	jmp	L1			; and go to next character
    L2:
            mov     byte [edx], 0           ; store string terminator for printf
    	push	dword S1
    	call	_printf
    	add	esp, 4
    	ret
    
    ; Store all the characters in memory.  There will be 5 rows of 17 characters
    ; (16 + newline) and one row of 15 characters, for a total of 100 characters.
    ; Since we have a C library we require a 101st byte for a terminating 0.
    
    	section	.bss
    S1:
    	resb	101
    
  4. ; ----------------------------------------------------------------------------
    ; reversedarguments.asm
    ;
    ; NASM implementation of a program that displays its commandline arguements,
    ; one per line, in reverse order.
    ; ----------------------------------------------------------------------------	
    
    	global	_main
    	extern	_printf
    	
    	section .text
    _main:
            push	esi			; save callee-save registers
            push	edi
            
    	mov	esi, [esp+12]	        ; argc
    L1:
    	dec	esi
    	test	esi, esi		; equal zero yet?
    	jz	L2			; if so, all done
    	
    	mov	edi, [esp+16]		; argv
    	push	dword [edi+4*esi]	; argv[i]
    	push	esi			; i
    	push	dword S1
    	call	_printf
    	add	esp, 12
    	
    	jmp	L1
    L2:	
    	pop	edi			; restore callee-save registers
    	pop	esi
    	ret
    
    S1:	db	'argv[%d] = %s', 10, 0
    
  5. ; ----------------------------------------------------------------------------
    ; gcd.asm
    ;
    ; NASM implementation of
    ;
    ;   unsigned gcd(unsigned x, unsigned y) {
    ;     return (y == 0) ? x : gcd(y, x mod y);
    ;   }
    ;
    ; Return value in eax, as per x86 convention.
    ; ----------------------------------------------------------------------------	
    
    	global	_gcd
    	
    	section .text
    _gcd:
    	cmp	dword [esp+8], 0        ; y == 0? 
    	jne	L1			; if not, go do work
    	mov	eax, [esp+4]		; otherwise, just return x
    	ret
    L1:
    	mov	eax, [esp+4]
    	xor	edx, edx		; edx:eax <- x
    	div	dword [esp+8]		; edx <- remainder
    	push	edx			; x % y
    	push	dword [esp+12]		; y
    	call	_gcd
    	add	esp, 8
    	ret
    
  6. ; ----------------------------------------------------------------------------
    ; callgcd.asm
    ;
    ; NASM implementation of a program that displays the GCD of its two
    ; command line arguments.  The GCD function is external.  It's not
    ; particularly efficient.
    ; ----------------------------------------------------------------------------	
    
    	global	_main
    	extern	_gcd
    	extern	_printf
    	extern	_atoi
    	
    	section .text
    _main:
    	push	esi			; callee save register
    	
    	cmp	dword [esp+8], 3        ; argc == 3? 
    	je	getx			; if so, get to it
    	push	dword S2		; otherwise print error message
    	call	_printf
    	add	esp, 4
    	ret				; and bail
    
    getx:	
    	mov	esi, [esp+12]		; esi <- argv
    	push	dword [esi+8]		; argv[2]
    	call	_atoi
    	add	esp, 4
    	push	eax			; push for subsequent gcd call
    	mov	[y], eax		; save for later
    gety:
    	mov	esi, [esp+16]
    	push	dword [esi+4]		; argv[1]
    	call	_atoi
    	add	esp, 4
    	push	eax			; argument to gcd
    	mov	[x], eax		; save for later
    callit:
    	call	_gcd
    	add	esp, 8			; gcd has two parameters
    L1:	
    	push	eax			; gcd(x, y)
    	push	dword [y]		; y
    	push	dword [x]		; x
    	push	dword S1		; format string for success
    	call	_printf
    	add	esp, 16
    	
    	pop	esi
    	ret
    
    S1:	db	'GCD(%d,%d) = %d', 10, 0
    S2:	db	'Exactly two arguments required', 10, 0
    
    	section	.bss
    x:	resd	1
    y:	resd	1
    
  7. /*
     * callgcd.c 
     *
     * Calls an external gcd program with the command line arguments.
     */
     
     #include <stdio.h>
     #include <stdlib.h>
     
     extern unsigned gcd(unsigned x, unsigned y);
     
     int main(int argc, char** argv) {
       unsigned x;
       unsigned y;
       
       if (argc != 3) {
         printf("Exactly two arguments required\n");
         return 1;
       }
       x = atoi(argv[1]);
       y = atoi(argv[2]);
       printf("GCD of %d and %d is %d\n", x, y, gcd(x, y));
       return 0;
     }
  8. Here is a solution using conditional moves and no conditional jumps. For fun, I used the technique of subtracting the minimum and the maximum from the sum. I haven't tested it.

    ; ----------------------------------------------------------------------------
    ; median3.asm
    ;
    ; Implements int median3(int threeIntegers[]); where threeIntegers is a
    ; pointer to three consecutive doublewords in memory.
    ; ----------------------------------------------------------------------------
    
    	global _median3
    
    	section .text
    _median3:
            mov     eax, [esp+4]	; sum will go in eax
    	mov	edx, eax	; min will go in edx
    	mov	ecx, eax	; max will go in ecx
    	add	eax, [x+4]
    	cmp	edx, [x+4]
    	cmovl	edx, [x+4]
    	cmp	ecx, [x+4]
    	cmovg	ecx, [x+4]
    	add	eax, [x+8]	; now the sum is complete
    	cmp	edx, [x+8]
    	cmovl	edx, [x+8]	; now the min is complete
    	cmp	ecx, [x+8]
    	cmovg	ecx, [x+8]	; now the max is complete
    	sub	eax, edx
    	sub	eax, ecx	; now eax has the median
    	ret
    

    Here is a solution that uses conditional jumps. For this one I imagined that (ebx,eax,ecx) was a three-word array and did an insertion sort. I haven't tested it.

    	mov	ebx, [x]
    	mov	eax, [x+4]
    	mov	ecx, [x+8]
    	cmp	ebx, eax
    	jl	L1
    	xchg	ebx, eax
    L1:	cmp	eax, ecx
    	jl	L2
    	xchg	eax, ecx
    L2:	cmp	ebx, eax
    	jl	L3
    	xchg	ebx, eax
    L3:	ret
    
  9. If you want to compute the log base 7 of x in the code, you can do this

    ; ----------------------------------------------------------------------------
    ; log7.asm
    ;
    ; NASM implementation of a function that returns the log base 7 of its sole
    ; argument.
    ;
    ;   double log7(double x)
    ; ----------------------------------------------------------------------------
    
            global  _log7
    
            section .text
    _log7:
            fld1                            ; 1
            fld     qword [esp+4]           ; 1  x
            fyl2x                           ; log2(x)
            fld1                            ; log2(x)  1
            fld     qword [seven]           ; log2(x)  1  7
            fyl2x                           ; log2(x)  log2(7)
            fdivp   st1                     ; log2(x)/log2(7)
            ret
    seven:
            dq      7.0
    
    

    But you can also define the reciprocal of the log base 2 of 7 directly in the code to make the function much, much shorter. I'm not giving the answers here; that's for you to try.