//----------------------------------------------------------------- // cpuspeed.s // // This program's purpose is to measure the CPU's clock-speed // in cycles-per-second, based on the known speed of the PC's // Programmable Interval Timer chip. To avoid distortion due // to our Operating System's timesharing among several tasks, // this code is designed to be run outside the context of any // operating system, at 'boot-time' before any OS has loaded. // A special linker-script is used when linking this program, // and then the resulting 'binary-executable' file is written // into a designated area in an otherwise unused partition of // our hard-disk (where the 'GRUB' boot-loader can find it). // // to assemble: $ as cpuspeed.s -o cpuspeed.o // and to link: $ ld cpuspeed.o -T ldscript -o cpuspeed.b // and install: $ dd if=cpuspeed.b of=/dev/sda4 seek=1 // // NOTE: This code begins execution with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // written on: 18 APR 2007 //----------------------------------------------------------------- # manifest constants .equ CLK_HERTZ, 1193182 # timer-input frequency .equ PIT_LATCH, 65535 # latch-register value .section .text #------------------------------------------------------------------ .short 0xABCD #------------------------------------------------------------------ main: .code16 mov %sp, %cs:exit_pointer+0 mov %ss, %cs:exit_pointer+2 mov %cs, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss lea tos, %sp call take_timing_measurements call calculate_cpu_clockspeed call display_report_of_result lss %cs:exit_pointer, %sp lret #------------------------------------------------------------------ exit_pointer: .short 0, 0 #------------------------------------------------------------------ stamp0: .quad 0 stamp1: .quad 0 cycles: .quad 0 speed: .long 0 #------------------------------------------------------------------ #------------------------------------------------------------------ take_timing_measurements: # turn on input to timer channel 2 in $0x61, %al and $0xFC, %al or $0x01, %al out %al, $0x61 # program the interval-timer's channel 2 mov $0xB0, %al # ch2, one-shot, lsb/msb out %al, $0x43 # output command to PIT mov $PIT_LATCH, %dx # latch-register value mov %dl, %al # latch-register's LSB out %al, $0x42 # written to channel 2 mov %dh, %al # latch-register's MSB out %al, $0x42 # written to channel 2 # disable interrupts during timing-measurements cli # clear IF-bit in EFLAGS # save the TimeStamp Counter's value rdtsc # read TimeStamp Counter mov %eax, stamp0+0 # save bits 31..0 mov %edx, stamp0+4 # save bits 63..32 # use 'polling' to wait for the timer-counter to expire spin: in $0x61, %al # input PORT_B settings test $0x20, %al # test channel-2 status jz spin # again if not expired # save the TimeStamp Counter's value rdtsc # read TimeStamp Counter mov %eax, stamp1+0 # save bits 31..0 mov %edx, stamp1+4 # save bits 63..32 # reenable interrupts from peripheral devices sti # set IF-bit in EFLAGS ret #------------------------------------------------------------------ calculate_cpu_clockspeed: # compute elapsed CPU cycles (= stamp1 - stamp0) mov stamp1+0, %eax mov stamp1+4, %edx sub stamp0+0, %eax sbb stamp0+4, %edx mov %eax, cycles+0 mov %edx, cycles+4 # compute CPU cycles-per-second = cycles * CLK_HERTZ / PIT_LATCH mov cycles+0, %eax mov $CLK_HERTZ, %ecx mul %ecx mov $PIT_LATCH, %ecx div %ecx mov %eax, speed # convert CPU's speed to a decimal-format string mov speed, %eax mov $10, %ebx xor %cx, %cx nxdiv: xor %edx, %edx div %ebx push %dx inc %cx or %eax, %eax jnz nxdiv lea buf, %di nxdgt: pop %dx or $'0', %dl mov %dl, (%di) inc %di loop nxdgt ret #------------------------------------------------------------------ display_report_of_result: # here we call ROM-BIOS services to display our message # get current display-page in BH mov $0x0F, %ah # get_display_status int $0x10 # request BIOS service # get current cursor-location (row,col) in (DH,DL) mov $0x03, %ah # get_cursor_location int $0x10 # request BIOS service # write string to display-page at (row,column) lea msg, %bp # point ES:BP to string mov len, %cx # setup CX with length mov att, %bl # setup BL with colors mov $0x1301, %ax # write_string function int $0x10 # request BIOS service ret #------------------------------------------------------------------ msg: .ascii " CPU speed: " # legend for the report buf: .ascii " \r\n" # buffer for the number len: .int . - msg # length of the message att: .byte 0x30 # colors: black on cyan #------------------------------------------------------------------ .align 16 # assures stack-alignment .space 512 # area reserved for stack tos: # labels the top-of-stack #------------------------------------------------------------------ .end # no more to be assembled