CSC231 Homework 9 Solution

From dftwiki3
Revision as of 08:54, 11 December 2008 by Thiebaut (talk | contribs) (Dumping the contents of a score file)
Jump to: navigation, search

--D. Thiebaut 19:53, 4 December 2008 (UTC)


This is the solution page for Homework #9.

Assembly

The solution program is shown below. Check further down for details on how I created score files and checked their contents.

;;; ; hw9.asm
;;; ; Yang Li
;;; ;
;;; ; This program reads the a score file generated by the tetrix game
;;; ; and modify the file in the following ways:
;;; ; 1) if player '231' is not in the score list, and the list has
;;; ;    less than 10 entries, add a new entry for player 231 in the
;;; ;    score list.
;;; ; 2) if player '231' is not in the score list, and the list has 10
;;; ;    entries, replace the player with lowest score by player '231'
;;; ; 3) Always update the score of player '231' to 10 points higher than
;;; ;    the current highest score.
;;; ;
;;; ; to assembly and run:
;;; ;	nasm -f elf -F stabs hw9.asm
;;; ;	ld -o hw9 hw9.o
;;; ;	./hw9
;;; ;
	%assign SYS_EXIT	1
	%assign SYS_WRITE	4
	%assign SYS_READ	3
	%assign SYS_LSEEK	19
	%assign SEEKSET	0
	%assign STDOUT		1
	%assign SYS_OPEN   5
	%assign SYS_CLOSE	6
	%assign SYS_CREATE	8


	%assign O_RDONLY	000000q
	%assign O_WRONLY        000001q
	%assign O_RDWR          000002q
	%assign O_CREAT		000100q

	%assign S_IRUSR		00400q
	%assign S_IWUSR		00200q
	%assign S_IXUSR		00100q

;;; ; --- MACRO -----------------------------------------------
;;; ;     print           msg,length
	%macro  print           2 ; %1 = address %2 = # of chars
	        pushad		  ; save all registers
	        mov             edx,%2
	        lea             ecx,[%1]
	        mov             eax,SYS_WRITE
	        mov             ebx,STDOUT
	        int             0x80
	        popad		; restore all registers
	%endmacro

;;; ; --- MACRO -----------------------------------------------
;;; ;     print2        "quoted string"
	%macro  print2        1	; %1 = immediate string,
	        section .data
	%%str   db              %1
	%%strL  equ             $-%%str
	        section .text
	        print           %%str, %%strL
	%endmacro

;;; ; --- MACRO -----------------------------------------------
;;; ; 	createFile filename,  handle
	%macro	createFile 2
	mov	eax,SYS_CREATE
	mov	ebx,%1
	mov	ecx, S_IRUSR|S_IWUSR|S_IXUSR
	int	0x80

	test	eax,eax
	jns	%%createfile
	print2  "Could not open file"
	mov	eax,SYS_EXIT
	mov	ebx,0
	int	0x80		; final system call

%%createfile:
	mov	%2,eax		; save handle
	%endmacro



;;; ; --- MACRO -----------------------------------------------
;;; ; 	openFile filename, mode, handle
	%macro	openFile 3
	mov	eax,SYS_OPEN
	mov	ebx,%1
	mov	ecx,%2
	mov	edx, S_IRUSR|S_IWUSR|S_IXUSR
	int	0x80

	test	eax,eax
	jns	%%readFile
	print2  "Could not open file"
	mov	eax,SYS_EXIT
	mov	ebx,0
	int	0x80		; final system call

%%readFile:
	mov	%3,eax		; save handle
	%endmacro


;;; ; --- MACRO -----------------------------------------------
;;; ; 	readFile handle, buffer, buffer-length, number-bytes-read
	%macro  readFile 4
	mov	eax,SYS_READ
	mov	ebx,%1		; file descriptor in bx
	mov	ecx,%2		; buffer address
	mov	edx,%3		; max number of bytes to read
	int	0x80
	mov	%4,eax		; save number bytes read
	%endmacro


;;; ; --- MACRO -----------------------------------------------
;;; ; 	writeBuf handle, buffer, noBytesToWrite
	%macro  writeFile 3
	mov	eax,SYS_WRITE
	mov	ebx,%1
	mov	ecx,%2
	mov	edx,%3
	int	0x80
	%endmacro


;;; ; --- MACRO -----------------------------------------------
;;; ;	close   handle
	%macro	close   1
	mov	eax,SYS_CLOSE
	mov	ebx,%1
	int	0x80
	%endmacro



;;;  -------------------------
;;;  data segment
;;;  -------------------------

	section	.data
	fileName db     "scores.dat",0 ;name of score file to be modified
	handle   dd     0	       ;file handler
	noRead   dd     0	       ;number of bytes read from score file
	noScore  dd	0	       ;number of entries from score file
	myName   db	"231"	       ;my initial


	section .bss
	MAXBUF  equ     100000	; maxium number of bytes can be read
	buffer  resb    MAXBUF	; 100,000 bytes of storage

;;;  -------------------------
;;;  code area
;;;  -------------------------

	section	.text
	global	_start

_start:
;;; ; open the file and put its contents in Buffer.
;;; ; keep track of # of bytes read in noRead
        openFile        fileName, O_RDWR,  [handle]
        readFile        [handle], buffer, MAXBUF, [noRead]
        close           [handle]
;;; ; calculate # of entries and store in noScore
	mov	edx,0
	mov	eax,[noRead]
	mov	ecx,7
	idiv	ecx
	mov	[noScore],eax	
;;; ; start comparisons and modifications
	mov	eax,0
	push	eax		
	call	findMe		;find address of my entry
	pop	eax		

	cmp	eax,0
	jne	change_score
	;; if my initial is not in the list
	cmp	dword[noScore],10
	jge	find_lowest
		;; if the number of entries is less than 10
		mov	eax,buffer ;extend buffer by 1 entry
		add	eax,[noRead]
		add	dword[noRead],7
		jmp	add_my_name
	;; else (number of entries is 10)
	find_lowest:			
		push	eax
		call	low_score  ;find address of entry with lowest score
		pop	eax	       
	;; change initial of that entry to my initial
add_my_name:	
	mov     ecx,3          
	mov     esi,0
	.for:		      
	        mov     bl,[myName+esi]
	        mov     [eax+esi],bl
		inc	esi
	        loop    .for
	;; hack my score
change_score:	
	push	eax
        push    ebx
        call    top_score     ;find address of entry with highest score
	pop     ebx
	add     ebx,10	      ;add 10 to highest score
	pop	eax	      ;restore eax,contains address of my entry
	mov     [eax+3],ebx   ;copy score to my entry
	
;;; ; write contents of buffer (noRead bytes) to same file

	createFile      fileName, [handle]
	writeFile       [handle], buffer, [noRead]
	close           [handle]

;;;  exit()

	mov	eax,SYS_EXIT
	mov	ebx,0
	int	0x80		; final system call
;;; ; -----------------------------------------------------------
;;; ; findMe: return (from stack) the address of my entry
;;; ; modified registers: eax ecx edx esi
;;; ; ------Psuedocode-------------------------------------------
;;; ; found = false
;;; ; for the ith entry in buffer:
;;; ;	  for jth character in ith entry's name:
;;; ; 		if jth char of ith name != jth character of my name:
;;; ;			found = false
;;; ;			break
;;; ;		endif
;;; ;	  end for
;;; ;	  if found == true:
;;; ;		break
;;; ;	  end if
;;; ; end for
;;; ; -----------------------------------------------------------
findMe:
		push	ebp
	 	mov	ebp,esp		
	%define	myEntry	dword[ebp+8] ; point to return value
		mov	esi,buffer   ; pointer for .outer loop
		mov	ecx,[noScore] 
	.outer:
		push	ecx
		mov	ecx,3 
		mov	edx,0	; index pointer for .inner loop
	.inner:		      
		mov	al,[myName+edx]
		cmp	byte[esi+edx],al
		jne	.break
		inc	edx
		loop 	.inner
		jmp	.found
	.break:	
		pop	ecx
		add	esi,7
		loop	.outer
		jmp	.done
	.found:
		pop	ecx
		mov	myEntry,esi 
	.done:
		pop	ebp	
	ret
;;; ; -----------------------------------------------------------
;;; ; top_score: return (from stack) highest score
;;; ; modified registers: eax ecx esi
;;; ; --------Psuedocode-----------------------------------------
;;; ; highest=0
;;; ; for ith entry in the buffer:
;;; ;	if score of ith entry > highest:
;;; ;	    highest = score of ith entry
;;; ;	endif
;;; ; end for
;;; ; -----------------------------------------------------------
top_score:
	 	push	ebp
	 	mov	ebp,esp		
	
		mov	eax,0	;eax contains current top score
		mov	esi,0	;index pointer
		mov	ecx,[noScore] 
	.for:
		cmp	dword[buffer+esi+3],eax
		jle	.next			
		mov	eax,dword[buffer+esi+3] 
	.next:					
		add	esi,7			
		loop	.for
		mov	dword[ebp+8],eax

		pop	ebp
		ret
;;; ; -----------------------------------------------------------
;;; ; low_score: return (from stack) address of entry with lowest score
;;; ; modified registers: eax ecx esi
;;; ; --------Psuedocode-----------------------------------------
;;; ; lowest=-1 (unsigned) 
;;; ; for ith entry in the buffer:
;;; ;	if score of ith entry < lowest:
;;; ;	    lowest = score of ith entry
;;; ;	endif
;;; ; end for
;;; ; -----------------------------------------------------------
low_score:
	 	push	ebp
	 	mov	ebp,esp		
	%define	lowest	dword[ebp+8]	;point to return value
		mov	eax,-1		;eax contains current lowest score
		mov	esi,buffer	;pointer
		mov	ecx,[noScore] 
	.for:
		cmp	dword[esi+3],eax
		jae	.next			
		mov	eax,dword[esi+3] ;set eax to highest score
		mov	lowest,esi	 ;point return value to highest entry
	.next:					
		add	esi,7
		loop	.for
	
		pop	ebp
		ret

Creating Score Files

To create different score files to test the programs, I simply used an already written assembly program that stored a portion of memory to file, and in which I put the score array in memory, a the beginning address associated with the label 'buffer'. Then it's only a question of writing the contents of the memory buffer to the score file.

;;; writeScoreFile.asm
;;; D. Thiebaut
;;; Writes the contents of the buffer defined in the data
;;; segment to a file called scores.dat.
;;;
;;; Uses macros for printing and functions for file operations.
;;; ---------------------------------------------------------
;;; FILE-RELATED CONSTANTS
;;; ---------------------------------------------------------
%assign SYS_EXIT	1
%assign	SYS_WRITE	4
%assign SYS_READ	3
%assign SYS_LSEEK	19
%assign SEEKSET		0
%assign	STDOUT		1
%assign SYS_OPEN        5
%assign SYS_CLOSE	6
%assign SYS_CREATE	8


%assign O_RDONLY	000000q
%assign O_WRONLY        000001q	
%assign O_RDWR          000002q
%assign O_CREAT		000100q

%assign S_IRUSR		00400q
%assign S_IWUSR		00200q
%assign S_IXUSR		00100q

;;; --- MACRO -----------------------------------------------
;;;     print           msg,length
%macro  print           2               ; %1 = address %2 = # of chars
        pushad                          ; save all registers
        mov             edx,%2
        lea             ecx,[%1]
        mov             eax,SYS_WRITE
        mov             ebx,STDOUT
        int             0x80
        popad                           ; restore all registers
%endmacro

;;; --- MACRO -----------------------------------------------
;;;     print2        "quoted string"
%macro  print2        1               ; %1 = immediate string,
        section .data
%%str   db              %1
%%strL  equ             $-%%str
        section .text
        print           %%str, %%strL
%endmacro
	
	;; -------------------------
	;; data segment
	;; -------------------------

	section	.data
fileName db     "scores3.dat",0
handle   dd     0
noRead   dd     0               ; to store # of chars read from file

;;; ============= THE SCORES!!! ===============

buffer   equ    $
        db      'ABC'                  ; User ABC has Score 10
        dd      10
        db      '233'
        dd      100
        db      'GHI'
        dd      101
        db      'JKL'
        dd      102
        db      'MNO'
        dd      103
        db      'PQR'
        dd      104
        
bufferLen   equ    $-buffer

        section .bss
MAXBUF  equ     100000
bigbuff resb    MAXBUF          ; 100,000 bytes of storage        

	;; -------------------------
	;; code area
	;; -------------------------

	section	.text
	global	_start

_start:
;;; open the file and put its contents in Buffer.
;;; keep track of # of bytes read in noRead

;;;     createFile( fileName,  [handle] )
        mov     eax, fileName
        push    eax
        mov     eax, handle
        push    eax
        call    createFile

;;;     writeFile( [handle], message, msgLen )
        push    dword [handle]
        mov     eax, buffer
        push    eax
        mov     eax, bufferLen
        push    eax
        call    writeFile

;;;     close( [handle] )
        push    dword [handle]
        call    close

        print   buffer, [noRead]
         
;;; exit()

	mov	eax,SYS_EXIT
	mov	ebx,0
	int	0x80		; final system call

	
;;; --------------------------------------------------------------
;;; readFile( handle, buffer, buffer_length, number_bytes_read )
;;; reads bytes into buffer.  At most buffer_length bytes are
;;; read.  Number of bytes read are stored in dword number_bytes_read.

readFile:
        push    ebp
        mov     ebp, esp
        
%define rf_handle dword [ebp+20]
%define rf_buffer dword [ebp+16]
%define rf_len    dword [ebp+12]
%define rf_bytes  dword [ebp+8]
        
        pushad
        
	mov	eax,SYS_READ
        mov     ebx, rf_handle
        mov     ecx, rf_buffer
        mov     edx, rf_bytes
	int	0x80
        mov     ebx, rf_bytes   ; get the address of variable
        mov     [ebx], eax      ; put # bytes read in it
        
        popad
        pop     ebp
        ret     4*4

;;; --------------------------------------------------------------
;;; close( handle )
;;; closes the file
close:  push    ebp
        mov     ebp, esp
        pushad
        
%define cl_handle dword [ebp+8]
        
        mov	eax,SYS_CLOSE
	mov	ebx,cl_handle
	int	0x80

        popad
        pop     ebp
        ret     4

	
;;; --------------------------------------------------------------
;;; createFile( filename,  handle )
;;; creates a file whose name is pointed to by filename, and puts
;;; the handle in the dword variable handle.
;;; Does not modify registers.
createFile:     
        push    ebp
        mov     ebp, esp
        pushad
        
%define cf_fileName dword[ebp+12]
%define cf_handle   dword[ebp+8]
        
	mov	eax,SYS_CREATE
	mov	ebx,cf_fileName
	mov	ecx, S_IRUSR|S_IWUSR|S_IXUSR
	int	0x80

	test	eax,eax
	jns	.fileOk
	print2  "Could not create file"
	mov	eax,SYS_EXIT
	mov	ebx,0
	int	0x80		; final system call

.fileOk:
        mov     ebx, cf_handle
        mov     [ebx], eax

.done:  popad                   ; restore registers
        pop     ebp
        ret     8

;;; --------------------------------------------------------------
;;; openFile( filename, handle )
;;; opens a file for reading, puts handle in dword whose address
;;; is passed in stack.
;;; Does not modify the registers, but modifies flags.
openFile:
        push    ebp
        mov     ebp, esp
        pushad
        
%define of_fileName dword[ebp+12]
%define of_handle   dword[ebp+8]
        
	mov	eax,SYS_OPEN
	mov	ebx, of_fileName
	mov	ecx, O_RDONLY
	mov	edx, S_IRUSR|S_IWUSR|S_IXUSR
	int	0x80

	test	eax,eax
	jns	.fileOk
        print2  "Error opening file"
        mov     eax, SYS_EXIT
        mov     ebx, 1
        int     0x80

.fileOk:
        mov     ebx, of_handle
	mov	[ebx], eax	; save handle
        
.done:  popad                   ; restore registers
        pop     ebp
        ret     8
	
;;; --------------------------------------------------------------
;;; writeFile( handle, buffer, bufferLen )
;;; writes bufferLen bytes stored in buffer to file whose handle
;;; is in "handle"
writeFile:
        push    ebp
        mov     ebp, esp
        pushad

%define wf_handle [ebp+16]
%define wf_buffer [ebp+12]
%define wf_len    [ebp+8]
        
	mov	eax,SYS_WRITE
	mov	ebx,wf_handle
	mov	ecx,wf_buffer
	mov	edx,wf_len
	int	0x80

        popad
        pop     ebp
        ret     3*4


Dumping the contents of a score file

This python program was used to display the contents of the score file on the screen.

#! /usr/bin/python
# dumpScores.py
# D. Thiebaut
# read binary file of scores where a score is represented by
# 3 ascii chars followed by a 4-byte integer in little-endian
# format.
# Syntax: dumpScore.py score_file_name  message
#
import struct
import sys

def displayScoreFile( fileName="scores.dat", message="" ):
    """ reads the score file, unpacks it, and displays it """
    fmt = 'ccci'   # format of the binary records
    record_size = struct.calcsize( fmt ) # size of record

    if len( message ):
        print "-------------------------------------"
        print " "* (len( message)/2),message
        print "-------------------------------------"

    smallestInitials="???"         # id of smallest score
    smallest        = 99999999     # to be sure we catch the smallest
    largestInitials = "???"        # id of largest score
    largest         = -1           # to catch the largest

    try:
        file = open( fileName, "rb" )  # open file
    except:
        print "could not open file"
        return

    while True:                    # keep reading until the eof
        block = file.read( 3 )     # read 3 bytes
        if len( block )==0:        # can't? then break
            break
        (i1,i2,i3 ) = struct.unpack( 'ccc', block )
        block = file.read( 4 )     # unpack as 3 chars, read 4 bytes
        if block is None:          # can't? then break
            break
        score = struct.unpack( 'i', block )[0]
                                   # unpack as a 32-bit int
        if ( score > largest ):    # find largest...
            largest = score
            largestInitials = i1+i2+i3
        if ( score < smallest ):   # and smallest...
            smallest = score
            smallestInitials = i1+i2+i3
        print i1+i2+i3, score

    print "-------------------------------------"
    print "Smallest score: %s %d" % (smallestInitials, smallest)
    print "Largest score:  %s %d" % (largestInitials, largest)
    print "-------------------------------------"

#--------------------------------------------------------------
# main program: gets the arguments, which should be the
# name of the score file followed by a message to be printed
# in the header of the display.
#--------------------------------------------------------------
argv = sys.argv
if len( argv ) == 1:
    file = raw_input( "score file? " )
else:
    file = argv[1]

if len( argv ) > 2:
    msg = ' '.join( argv[2:] )
else:
    msg = file

displayScoreFile( file, msg )