From mboxrd@z Thu Jan 1 00:00:00 1970 Message-ID: <6befec06592023a83a26e343d54335b2@plan9.bell-labs.com> To: 9fans@cse.psu.edu Subject: Re: [9fans] A quite amusing thing... From: "Russ Cox" In-Reply-To: <000601c2daa7$17972450$270363d9@makr4j0ty5i9an> MIME-Version: 1.0 Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit Date: Sat, 22 Feb 2003 15:30:42 -0500 Topicbox-Message-UUID: 6e296dd6-eacb-11e9-9e20-41e7f4b1d025 That _is_ fairly amusing. We've had a form of that code (including the big comment) in our source tree since July 15, 1994. But the 1994 code didn't have the for loop to run until loops got big enough (it assumed 10000 was enough) and it didn't have all the clock frequency stuff (which got added later). So either Plan 9 copied Amoeba back in 1994 (before the original project was terminated) and has been secretly tracking the code since then, or Amoeba copied Plan 9 some time later after the code was changed to look more like it does today. I know where my money is. Also, I downloaded the original Amoeba code and the pit.c looked nothing like the Plan 9 code as of 1996. Below you'll find Plan 9's clock.c from 1994, Amoeba's pit.c from 1996, and Amoeba's pit.c from today. Note the difference between the last two. Sure looks like * Stefan Bosse (12/1999-7/2000) * sbosse@physik.uni-bremen.de * * -> pit_hw_milli now default routine with 1ms resolution * -> cpuspeed measurement and delay loop reference copied the Plan 9 code. > Hmm... It is possible too that Amoeba developer's have copied this code > but I doubt it. Why? Russ /* --rw-rw-r-- M 3014 jmk sys 2766 Jul 15 1994 sys/src/brazil/pc/clock.c */ void clockinit(void) { ulong x, y; /* change in counter */ ulong cycles, loops; /* * set vector for clock interrupts */ setvec(Clockvec, clock, 0); /* * set clock for 1/HZ seconds */ outb(Tmode, Load0|Square); outb(T0cntr, (Freq/HZ)); /* low byte */ outb(T0cntr, (Freq/HZ)>>8); /* high byte */ /* * measure time for the loop * * MOVL loops,CX * aaml1: AAM * LOOP aaml1 * * the time for the loop should be independent from external * cache's and memory system since it fits in the execution * prefetch buffer. * */ loops = 10000; outb(Tmode, Latch0); x = inb(T0cntr); x |= inb(T0cntr)<<8; aamloop(loops); outb(Tmode, Latch0); y = inb(T0cntr); y |= inb(T0cntr)<<8; x -= y; /* * counter goes at twice the frequency, once per transition, * i.e., twice per the square wave */ x >>= 1; /* * figure out clock frequency and a loop multiplier for delay(). */ switch(cputype = x86()){ case 386: cycles = 30; break; case 486: cycles = 24; break; default: cycles = 23; break; } cpufreq = (cycles*loops) * (Freq/x); loopconst = (cpufreq/1000)/cycles; /* AAM+LOOP's for 1 ms */ } /* @(#)pit.c 1.4 94/04/06 09:23:12 */ /* * Copyright 1994 Vrije Universiteit, The Netherlands. * For full copyright and restrictions on use see the file COPYRIGHT in the * top level of the Amoeba distribution. */ /* * pit.c * * Driver for the 8254 timer (PIT). The i8254 timer has three timer channels * of which only one (channel 0) is available for timer interrupts. The other * channels are used for the speaker and memory refresh. * * Author: * Leendert van Doorn */ #include #include INIT_ASSERT #include #include #include "sys/proto.h" #include "i386_proto.h" #include "pit.h" #ifndef PIT_DEBUG #define PIT_DEBUG 0 #endif static int pit_debug; /* current debug level */ #ifdef notyet static uint32 pit_hw_milli(); #endif static void pit_intr(); /* * Initialize channel 0 of the i8254A timer */ void pit_init() { register uint32 counter = (PIT_FREQ * PIT_INTERVAL) / 1000L; #ifndef NDEBUG if ((pit_debug = kernel_option("pit")) == 0) pit_debug = PIT_DEBUG; if (pit_debug > 1) printf("pit_init(), pit_interval = 0x%x (%d), counter = %x\n", PIT_INTERVAL, PIT_INTERVAL, counter); #endif /* set timer to run continuously */ assert(counter <= 0xFFFF); out_byte(PIT_MODE, PIT_MODE3); out_byte(PIT_CH0, (int) (counter & 0xFF)); out_byte(PIT_CH0, (int) ((counter >> 8) & 0xFF)); #ifdef notyet /* I still have to thoroughly test this */ set_hw_milli(pit_hw_milli); #endif setirq(PIT_IRQ, pit_intr); pic_enable(PIT_IRQ); } /* * Read i8254's channel 0 counter. The counter decrements at twice the * timer frequency (one full cycle for each half of a square wave). */ uint16 pit_channel0() { register uint16 counter; out_byte(PIT_MODE, PIT_LC); counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8); return counter; } /* * Delay for at least ``msec'' milli seconds */ void pit_delay(msec) int msec; { register uint16 current, previous, diff; register uint32 total; /* * The counter decrements at twice the timer frequency * (one full cycle for each half of a square wave). */ diff = 100; /* just in case */ total = (uint32) msec * (2 * PIT_FREQ / 1000); previous = pit_channel0(); for (;;) { current = pit_channel0(); if (current < previous) diff = previous - current; if (diff >= total) break; total -= diff; previous = current; } } #ifdef notyet /* * Return number of actual milli-seconds that have passed */ static uint32 pit_hw_milli() { extern uint32 milli_uptime; register uint32 milli; register int flags; uint16 counter; int status; flags = get_flags(); disable(); out_byte(PIT_MODE, PIT_RB); out_byte(PIT_MODE, 0xC2); status = in_byte(PIT_CH0); counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8); milli = milli_uptime + (PIT_INTERVAL / 2) * (counter / (PIT_FREQ * PIT_INTERVAL) / 1000L); if ((status & 0x80) == 0) milli += PIT_INTERVAL/2; set_flags(flags); return milli; } #endif /* * The actual clock interrupt */ /* ARGSUSED */ static void pit_intr(reason, frame) int reason; struct fault *frame; { void sweeper_run(); void flp_motoroff(); extern int motortime; #ifdef MCA /* ps/2 clock needs to be told to stop interrupting */ out_byte(0x61, in_byte(0x61) | 0x80); #endif enqueue(sweeper_run, (long) PIT_INTERVAL); #if (defined(ISA) || defined(MCA)) && !defined(NOFLOPPY) /* stop running floppy motor */ if (motortime != 0 && --motortime == 0) flp_motoroff(); #endif } /* * This file is part of the FIREBALL AMOEBA System. * * * Last modified: * 18/02/01 * * Stefan Bosse (12/1999-7/2000) * sbosse@physik.uni-bremen.de * * -> pit_hw_milli now default routine with 1ms resolution * -> cpuspeed measurement and delay loop reference * * * * FIREBALL AMOEBA is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; version 2. * * The FIREBALL AMOEBA is distributed in the hope that it will be usefull, * but WITHOUT ANY WARRANTY; without even implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * Original Copyright: Vrije Universiteit, The Netherlands. */ /* * pit.c * * Driver for the 8254 timer (PIT). The i8254 timer has three timer channels * of which only one (channel 0) is available for timer interrupts. The other * channels are used for the speaker and memory refresh. * * Author: * Leendert van Doorn */ #include #include INIT_ASSERT #include #include #include "sys/proto.h" #include "i386_proto.h" #include #include "pit.h" #include #ifndef PIT_DEBUG #define PIT_DEBUG 0 #endif #ifdef STATISTICS static unsigned long pit_hw_milli_count=0; static unsigned long pit_hw_milli_wrap1=0; static unsigned long pit_hw_milli_wrap2=0; static unsigned long pit_delay_count=0; #endif static int pit_debug; /* current debug level */ static int initialized=0; #ifdef PIT_HW_MILLI static uint32 pit_hw_milli(); #endif static void pit_intr(); /* * milli_uptime will be incremented in sweeper_run, but it's delayed * because of enqueue() handling. * milli_uptime should be handled ***here***. * Hack: Local we use timer_ticks for pit_hw_milli(), incremented in low * level ISR below... */ unsigned long timer_ticks=0; /* for time monotony checking; we have a serious problem with * this faulty timer hardware */ static unsigned long last_time=0; /* * Initialize channel 0 of the i8254A timer */ void pit_init() { register uint32 counter = (PIT_FREQ * PIT_INTERVAL) / 1000L; if(!initialized) { initialized=1; #ifndef NDEBUG if ((pit_debug = kernel_option("pit")) == 0) pit_debug = PIT_DEBUG; if (pit_debug > 1) printf("pit_init(), pit_interval = 0x%x (%d), counter = %x\n", PIT_INTERVAL, PIT_INTERVAL, counter); #endif /* set timer to run continuously */ assert(counter <= 0xFFFF); if(check_region(PIT_CH0,4)<0) panic("Some fool allocated timer ports"); request_region(PIT_CH0,4,"PIT"); out_byte(PIT_MODE, PIT_MODE2); out_byte(PIT_CH0, (int) (counter & 0xFF)); out_byte(PIT_CH0, (int) ((counter >> 8) & 0xFF)); #ifdef PIT_HW_MILLI /* I still have to thoroughly test this */ /* SB: still timer wrap problems; but time monotonie is guranteed */ set_hw_milli(pit_hw_milli); #endif if(request_irq(PIT_IRQ, pit_intr, SA_NORMAL, "PIT", (void *)NULL)!=0) panic("Some fool allocated timer irq"); enable_irq(PIT_IRQ); /*pic_enable(PIT_IRQ);*/ } } /* * Read i8254's channel 0 counter. The counter decrements at twice the * timer frequency (one full cycle for each half of a square wave). */ uint16 pit_channel0() { register uint16 counter; unsigned long flags; save_flags(flags);cli(); out_byte(PIT_MODE, PIT_LC); counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8); restore_flags(flags); return counter; } /* * Delay for at least ``msec'' milli seconds */ void pit_delay(msec) int msec; { register uint16 current, previous, diff; register uint32 total; #ifdef STATISTICS pit_delay_count++; #endif /* * The counter decrements at twice the timer frequency * (one full cycle for each half of a square wave). */ diff = 100; /* just in case */ total = (uint32) msec * (PIT_FREQ / 1000); previous = pit_channel0(); for (;;) { current = pit_channel0(); if (current < previous) diff = previous - current; if (diff >= total) break; total -= diff; previous = current; } } #ifdef PIT_HW_MILLI /* * Return number of actual milli-seconds that have passed */ static uint32 pit_hw_milli() { extern uint32 milli_uptime; register uint32 milli; uint16 counter,counter2; int status,status2; unsigned long flags; unsigned long ticks=timer_ticks; #ifdef STATISTICS pit_hw_milli_count++; #endif save_flags(flags);cli(); /* Select timer0 and latch counter value. */ out_byte(PIT_MODE,PIT_LC); counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8); restore_flags(flags); milli = timer_ticks*PIT_INTERVAL + (1000*(((PIT_FREQ*PIT_INTERVAL)/1000) - counter )/(PIT_FREQ) ); /* check time monotony */ if(milli PIT_FREQ/(3*PIT_HZ)) break; } /* * figure out clock frequency and a loop multiplier for delay(). * n.b. counter goes up by 2*PIT_FREQ */ cpufreq = loops*((aalcycles*PIT_FREQ)/x); cpu.loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1ms */ /* check aalcycle value */ if(kernel_option("cud") == 1) printf("cpuspeed: aalcycle MHz=%d\n", (cpufreq + cpufreq/200)/1000000); if(havecycleclock){ if(dx2 == dx1) b = (ax2-ax1); else /* counter wrap */ b = (ax2+0xffffffff-ax1); b /= x; b *= PIT_FREQ; /* * round to the nearest megahz */ cpu.cpumhz = (b+500000)/1000000L; cpu.cpuhz = b; } else { /* * add in possible 0.5% error and convert to MHz */ cpu.cpumhz = (cpufreq + cpufreq/200)/1000000; cpu.cpuhz = cpufreq; } }