//---------------------------------------------------------------- // 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. // // assemble using: $ as xmas.s -o xmas // and link using: $ ld xmas.o base10io.o -o xmas // // programmer: ALLAN CRUSE // written on: 07 FEB 2006 // revised on: 11 MAR 2009 -- for x86_64 Linux environment //---------------------------------------------------------------- # manifest constants .equ dev_STDOUT, 1 .equ sys_WRITE, 1 .equ sys_EXIT, 60 .section .data param: .quad 2009 # this year is a default radix: .quad 10 # base of decimal system seven: .quad 7 # number of days-per-week four: .quad 4 # frequency of leap-years years: .quad 0 # counts years since 1900 leaps: .quad 0 # count of the leap-years days: .quad 0 # elapsed days since 1900 theday: .quad 0 # weekday number (0=Tues) daylst: .ascii "Tue,Wed,Thu,Fri,Sat,Sun,Mon," # names for days report: .ascii "xxx, Dec 25 \n" # message text to display replen: .quad . - report # length of the message errmsg: .ascii "Year cannot be before 1900\n" # error-message errlen: .quad . - errmsg # length of the message .section .text _start: call obtain_input # get the designated year call process_data # calculate Christmas day call print_output # display results to user jmp exit_program # return control to Linux obtain_input: cmpq $1, 8(%rsp) # check: does argc == 1? je argx # yes, keep default value mov 24(%rsp), %rsi # get pointer to argv[1] call asc2rax # convert ascii to number mov %rax, param # save the argument-value cmpq $1900, param # check: param < 1900 ? jge argx # no, parameter accepted mov $sys_WRITE, %rax # system-call ID-number mov $dev_STDOUT, %rdi # device-file ID-number lea errmsg, %rsi # error-message address mov errlen, %rdx # error-message length syscall # invoke kernel service jmp exit_program # give control to Linux argx: ret # return to the caller exit_program: mov $sys_EXIT, %rax # system-call ID-number xor %rdi, %rdi # let zero be exit-code syscall # invoke kernel service process_data: # compute the number of years since 1900 mov param, %rax # get the parameter value sub $1900, %rax # subtract the value 1900 mov %rax, years # store as count of years # compute the number of leap-years since 1900 mov years, %rax # get the number of years xor %rdx, %rdx # extend dividend to long divq four # perform unsigned divide mov %rax, leaps # quotient counts leapyrs # compute the total number of days since 1900 mov $365, %rax # number of days-per-year mulq years # times number of years add leaps, %rax # plus the "extra" days mov %rax, days # gives days since 1900 # divide number of days by seven mov days, %rax # get the number of days xor %rdx, %rdx # extended as a dividend divq seven # divide by days-in-week mov %rdx, theday # remainder is day-of-week ret # return to caller print_output: # copy the day's name into our report-string mov theday, %rsi # get day-number in ESI mov daylst(,%rsi,4), %eax # lookup name in table mov %eax, report # put name into string # display our report on the day when Christmas occurs mov $sys_WRITE, %rax # system-call ID-number mov $dev_STDOUT, %rdi # device-file ID-number lea report, %rsi # message's address mov replen, %rdx # message's length syscall # invoke kernel service ret asc2rax: # converts decimal digit-string at RSI to integer in RAX push %rbx # save working registers push %rcx push %rdx push %rsi mov $10, %rbx # decimal radix into RBX xor %rax, %rax # initialize RAX to zero nxchr: mov (%rsi), %cl # next character into CL inc %rsi # advance source pointer # return RAX if character is not a decimal digit cmp $'0', %cl # does char preceed '0'? jb notdd # yes, not a decimal digit cmp $'9', %cl # does char follow '9'? ja notdd # yes, not a decimal digit # ok, it's a decimal digit, so add it to ten times total sub $'0', %cl # convert ascii to binary and $0xF, %rcx # zero-extend to 64-bits mulq %rbx # previous value times ten add %rcx, %rax # plus the new number jmp nxchr notdd: pop %rsi # restore saved registers pop %rdx pop %rcx pop %rbx ret .global _start # make entry-point visible .end # nothing more to assemble