CSC231 Homework 9 Solution
--D. Thiebaut 19:53, 4 December 2008 (UTC)
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 a score file I simply used an assembly file in which I store the score in memory and write the contents of the memory buffer to a 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 file 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 )