;//--------------------------------------------------------------- ;// cpuspeed.s ;// ;// This program uses the 8254 Programmable Interval Timer to ;// produce a measure of the Pentium processor's clock speed, ;// based on increments to the Pentium's TimeStamp Counter. ;// ;// assemble with: $ as86 cpuspeed.s -b cpuspeed.b ;// install using: $ dd if=cpuspeed.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// written on: 03 MAY 2004 ;//--------------------------------------------------------------- ; this macro defines an unrecognized mnemonic opcode MACRO rdtsc .BYTE 0x0F, 0x31 ; opcode for 'rdtsc' MEND .SECT .TEXT ;----------------------------------------------------------------- .WORD 0xABCD ; programming signature ;----------------------------------------------------------------- main: mov ax, cs ; address this segment mov ds, ax ; with DS register pop dword return_address ; store return-address mov ss, ax ; adjust SS register lea esp, tos0 ; and set new stacktop call setup_the_8254_timer call measure_cycles_count call calculate_clockspeed call report_cpu_megahertz push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address ;----------------------------------------------------------------- clkhz: .LONG 1193182 ; CLK2 input-frequency outhz: .LONG 100 ; frequency-divisor cycles: .LONG 0, 0 ; elapsed cpu-cycles factor: .LONG 0 ; cycle-count divisor megahz: .LONG 0 ; cycles-per-millisecond million:.LONG 1000000 ; milliseconds-per-second radix: .LONG 10 ; base for decimal system ;----------------------------------------------------------------- report: .ASCII " processor\'s clock-speed = " outbuf: .ASCII " 0 Mhz " ; numeric output-buffer rptlen: .LONG * - report ; message string's length rptatt: .BYTE 0x0D ; video display attribute ;----------------------------------------------------------------- ;----------------------------------------------------------------- setup_the_8254_timer: ; turn off the 8254 Timer-Counter Channel 2 in al, #0x61 ; get PORT_B settings and al, #0xFC ; GATE2 and speaker off out #0x61, al ; output new settings ; initiate reprogramming of the Channel 2 count-value mov al, #0xB0 ; ch2, lsb-msb, one-shot, binary out #0x43, al ; output command to the 8254 PIT ; compute count-value for the prescribed duration mov ax, clkhz+0 ; input-frequency loword mov dx, clkhz+2 ; input-frequency hiword div word [outhz] ; OUT2 frequency divisor ; place the new count-value into the Channel 2 counter out #0x42, al ; write the count's LSB xchg al, ah ; swap the LSB with MSB out #0x42, al ; write the count's MSB ret ;----------------------------------------------------------------- measure_cycles_count: ; start the Channel 2 countdown in al, #0x61 ; read PORT_B settings or al, #0x01 ; enable GATE2 signal out #0x61, al ; begin timed duration ; read the Pentium's timestamp counter rdtsc ; save the initial timestamp in (ECX,EBX) mov ebx, eax ; EBX=timestamp[31..0] mov ecx, edx ; ECX=timestamp[63..32] ; spin until the Channel 2 count is exhausted .L1: in al, #0x61 ; read PORT_B settings test al, #0x20 ; is OUT2 signal active? jz .L1 ; no, continue spinning ; read the Pentium's timestamp counter again rdtsc ; compute the elapsed CPU cycle-count sub eax, ebx ; quadword subtraction sbb edx, ecx ; final minus initial ; save the count of cpu cycles mov cycles+0, eax ; cycle-count lodword mov cycles+4, edx ; cycle-count hidword ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- calculate_clockspeed: ; set factor equal to microseconds-per-delay-duration mov eax, million xor edx, edx div dword [outhz] mov factor, eax ; divide the cpu cycle-count by this delay-factor mov eax, cycles+0 ; cycle-count lodword mov edx, cycles+4 ; cycle-count hidword div dword [factor] ; divide by microseconds mov megahz, eax ; store cpuspeed in MHz ret ;----------------------------------------------------------------- report_cpu_megahertz: push es ; setup segment-addressing for the video display memory mov ax, #0xB800 ; address video memory mov es, ax ; with ES register ; fill the entire screen with blank characters cld ; do forward processing xor di, di ; point ES:DI to vram mov ax, #0x0720 ; space w/normal attribute mov cx, #2000 ; count of character-cells rep ; string repeat-prefix stosw ; store char w/attribute ; convert clock-speed (in MHz) to decimal digit-string mov eax, megahz ; get cpu speed (in MHz) lea di, outbuf+4 ; point to output buffer call eax2dec ; output decimal digits ; compute screen location for centering of message mov di, #12 ; number of middle row imul di, #160 ; times width of row mov ax, #80 ; characters per line sub ax, rptlen ; minus message-length and ax, #0xFFFE ; round to even number add di, ax ; indent for centering ; write report on cpu clock-speed to the video display lea si, report ; point DS:SI to message mov ah, rptatt ; setup character colors mov cx, rptlen ; setup message's length .W1: lodsb ; fetch next character stosw ; store char w/attribute loop .W1 ; again for other chars pop es ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- eax2dec: ; converts value in EAX to decimal digit-string at DS:DI pushad mov ecx, #4 ; maximum digits to show nxdgt: xor edx, edx ; extend EAX to quadword div dword [radix] ; perform 32bit division or dl, #0x30 ; remainder into ascii dec di ; decrement dest'n index mov [di], dl ; store the ascii numeral or eax, eax ; more significant digits? loopnz nxdgt ; yes, do another division popad ret ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END