//---------------------------------------------------------------- // xmas.s // // This program shows which day-of-the-week is Christmas in // a year that is provided by a command-line argument. The // algorithm is based upon finding the number of days which // will have elapsed since 1900 (when Christmas occurred on // a Tuesday), and then performing a division by seven, the // number of days which occurs in any week. In calculating // the elapsed days it is necessary to take account of leap // years, which normally occur in years evenly divisible by // four, although years divisible by 100 are an exception. // // programmer: ALLAN CRUSE // written on: 17 MAR 2005 //---------------------------------------------------------------- .section .data param: .int 2005 # command-line parameter radix: .int 10 # base of decimal system seven: .int 7 # number of days-per-week four: .int 4 # frequency of leap-years years: .int 0 # counts years since 1900 leaps: .int 0 # count of the leap-years days: .int 0 # elapsed days since 1900 theday: .int 0 # weekday number (0=Tues) daylst: .ascii "TueWedThuFriSatSunMon" # names of days in a week report: .asciz "xxx, Dec 25 \n" # message text to display errmsg: .asciz "Year cannot be before 1900\n" # error-message .section .text main: call obtain_input # get the designated year call process_data # calculate Christmas day call print_output # display results to user ret # return control to Linux print_output: # copy day's name into report-string xorl %edi, %edi # array-index for dst imul $3, theday, %esi # array-index for src movl $3, %ecx # chars to be copied nxmv: movb daylst(%esi), %al # fetch source char movb %al, report(%edi) # store dest'n char incl %esi # advance src index incl %edi # advance dst index loop nxmv # copy rest of chars # print report on day when Christmas occurs pushl $report # push message address call printf # call runtime library addl $4, %esp # discard the argument ret process_data: # compute the number of years since 1900 movl param, %eax # get the parameter value subl $1900, %eax # subtract the value 1900 movl %eax, years # store as count of years # compute the number of leap-years since 1900 movl years, %eax # get the number of years xorl %edx, %edx # extend dividend to long divl four # perform unsigned divide movl %eax, leaps # quotient counts leapyrs # compute the total number of days since 1900 imul $365, years, %eax # years * days-per-year addl leaps, %eax # plus the "extra" days movl %eax, days # gives days since 1900 # divide number of days by seven movl days, %eax # get the number of days xorl %edx, %edx # extended as a dividend divl seven # divide by days-in-week movl %edx, theday # remainder is day-of-week ret # return to caller obtain_input: movl 12(%ebp), %eax # get number of arguments cmpl $1, %eax # check: does argc == 1? je argx # yes, use a default year movl 16(%ebp), %ebx # get argv[] base-address movl $1, %edx # setup 1 as array-index movl (%ebx,%edx,4), %esi # get pointer to argument call ascii_to_int # convert ascii to number movl %eax, param # save the argument-value cmpl $1900, param # check: 1900 > param ? jge argx # no, parameter accepted pushl $errmsg # error-message address call printf # call runtime library addl $4, %esp # discard the argument pushl $1 # exit-function argument call exit # call runtime library argx: ret # return to the caller ascii_to_int: # # This procedure converts a string of decimal digits, located at # the memory-address found in register %esi, into its numerical # value, and returns that value in register %eax. Upon return, # register %esi points to the first non-digit that occurs in the # string. The contents of other general registers is unchanged. # pushl %ecx # preserve work registers pushl %edx xorl %eax, %eax # initialize accumulator nxdgt: movb (%esi), %cl # fetch next ascii-code cmpb $'0', %cl # is it below '0'? jb cvtxx # yes, not a digit cmpb $'9', %cl # is it above '9'? ja cvtxx # yes, not a digit andl $0xF, %ecx # convert digit to integer mull radix # ten times previous total addl %ecx, %eax # plus the newest integer incl %esi # advance source pointer jmp nxdgt # check for more digits cvtxx: popl %edx # restore saved registers popl %ecx ret .globl main # make entry-point public .end