//------------------------------------------------------------------- // timeoday.s // // This program illustrates the use of the privileged Pentium // 'in' and 'out' instructions, used to transfer data between // the CPU's accumulator register and some peripheral device. // In this example the device is the system's Real-Time Clock // which operates continuously with battery power whether the // machine itself is turned on or off. This program displays // the current time-of-day, based on inputs from this device. // (NOTE: Executing this program requires 'root' privileges.) // // assemble using: root# as timeoday.s -o timeoday.o // and link using: root# ld timeoday.o -o timeoday // // Reference: // Frank van Gilluwe, "The Undocumented PC (Second Edition)," // Addison-Wesley (1997), page 893. // // programmer: ALLAN CRUSE // written on: 30 APR 2006 //------------------------------------------------------------------- # manifest constants .equ rtc_ss, 0 # RTC's 'seconds' register .equ rtc_mm, 2 # RTC's 'minutes' register .equ rtc_hh, 4 # RTC's 'hours' register .equ STDOUT, 1 # device-ID for usual output .equ STDERR, 2 # device-ID for error output .equ sys_exit, 1 # system-ID for 'exit' .equ sys_write, 4 # system-ID for 'write' .equ sys_iopl, 110 # system-ID for 'iopl' .equ RTC_ADDR, 0x70 # RTC's address-port .equ RTC_DATA, 0x71 # RTC's data-port .section .data msg: .ascii "\n\tcurrent time is " # message legend hh: .ascii "xx:" # hours-field mm: .ascii "xx:" # minutes-field ss: .ascii "xx \n\n" # seconds-field len: .int . - msg # message length err: .ascii "unable to adjust i/o privilege-level\n" esz: .int . - err # message length radix: .byte 16 # number system base .section .text #-------------------------------------------------------------------- _start: # execute the 'iopl' system-call mov $sys_iopl, %eax # service-ID for 'iopl' mov $3, %ebx # desired value for IOPL int $0x80 # enter Linux kernel cmp $-1, %eax # did function succeed? jne cont # yes, we can continue # display the failure message mov $sys_write, %eax # service-ID for 'write' mov $STDERR, %ebx # device-ID for errors lea err, %ecx # message's addrexx mov esz, %edx # message's length int $0x80 # enter Linux kernel jmp exit # jump to program exit cont: # setup the 'hours' field mov $rtc_hh, %al # specify register-number lea hh, %edi # point to message-field call rtc_format # input and format datum # setup the 'minutes' field mov $rtc_mm, %al # specify register-number lea mm, %edi # point to message-field call rtc_format # input and format datum # setup the 'seconds' field mov $rtc_ss, %al # specify register-number lea ss, %edi # point to message-field call rtc_format # input and format datum # display the time-of-day message mov $sys_write, %eax # service-ID for 'write' mov $STDERR, %ebx # device-ID for output lea msg, %ecx # message's address mov len, %edx # message's length int $0x80 # enter Linux kernel exit: # terminate this application mov $sys_exit, %eax # service-ID for 'exit' mov $0, %ebx # value for 'exit-code' int $0x80 # enter Linux kernel #-------------------------------------------------------------------- rtc_format: # # Expects: AL = Real-Time Clock register address # EDI = address of buffer for numeral-pair # push %eax # preserve register out %al, $RTC_ADDR # select RTC register in $RTC_DATA, %al # input register value xor %ah, %ah # extend byte to word divb radix # seperate the nybbles orw $0x3030, %ax # convert to numeral-pair mov %ax, (%edi) # store pair into buffer pop %eax # recover saved register ret # return to caller #-------------------------------------------------------------------- .global _start .end