⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i8253.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"/* *  8253 timer */enum{	T0cntr=	0x40,		/* counter ports */	T1cntr=	0x41,		/* ... */	T2cntr=	0x42,		/* ... */	Tmode=	0x43,		/* mode port (control word register) */	T2ctl=	0x61,		/* counter 2 control port */	/* commands */	Latch0=	0x00,		/* latch counter 0's value */	Load0l=	0x10,		/* load counter 0's lsb */	Load0m=	0x20,		/* load counter 0's msb */	Load0=	0x30,		/* load counter 0 with 2 bytes */	Latch1=	0x40,		/* latch counter 1's value */	Load1l=	0x50,		/* load counter 1's lsb */	Load1m=	0x60,		/* load counter 1's msb */	Load1=	0x70,		/* load counter 1 with 2 bytes */	Latch2=	0x80,		/* latch counter 2's value */	Load2l=	0x90,		/* load counter 2's lsb */	Load2m=	0xa0,		/* load counter 2's msb */	Load2=	0xb0,		/* load counter 2 with 2 bytes */	/* 8254 read-back command: everything > pc-at has an 8254 */	Rdback=	0xc0,		/* readback counters & status */	Rdnstat=0x10,		/* don't read status */	Rdncnt=	0x20,		/* don't read counter value */	Rd0cntr=0x02,		/* read back for which counter */	Rd1cntr=0x04,	Rd2cntr=0x08,	/* modes */	ModeMsk=0xe,	Square=	0x6,		/* periodic square wave */	Trigger=0x0,		/* interrupt on terminal count */	Sstrobe=0x8,		/* software triggered strobe */	/* T2ctl bits */	T2gate=	(1<<0),		/* enable T2 counting */	T2spkr=	(1<<1),		/* connect T2 out to speaker */	T2out=	(1<<5),		/* output of T2 */	Freq=	1193182,	/* Real clock frequency */	Tickshift=8,		/* extra accuracy */	MaxPeriod=Freq/HZ,	MinPeriod=Freq/(100*HZ),};typedef struct I8253 I8253;struct I8253{	Lock;	ulong	period;		/* current clock period */	int	enabled;	uvlong	hz;	ushort	last;		/* last value of clock 1 */	uvlong	ticks;		/* cumulative ticks of counter 1 */	ulong	periodset;};I8253 i8253;voidi8253init(void){	int loops, x;	ioalloc(T0cntr, 4, 0, "i8253");	ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");	i8253.period = Freq/HZ;	/*	 *  enable a 1/HZ interrupt for providing scheduling interrupts	 */	outb(Tmode, Load0|Square);	outb(T0cntr, (Freq/HZ));	/* low byte */	outb(T0cntr, (Freq/HZ)>>8);	/* high byte */	/*	 *  enable a longer period counter to use as a clock	 */	outb(Tmode, Load2|Square);	outb(T2cntr, 0);		/* low byte */	outb(T2cntr, 0);		/* high byte */	x = inb(T2ctl);	x |= T2gate;	outb(T2ctl, x);		/*	 * Introduce a little delay to make sure the count is	 * latched and the timer is counting down; with a fast	 * enough processor this may not be the case.	 * The i8254 (which this probably is) has a read-back	 * command which can be used to make sure the counting	 * register has been written into the counting element.	 */	x = (Freq/HZ);	for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){		outb(Tmode, Latch0);		x = inb(T0cntr);		x |= inb(T0cntr)<<8;	}}voidguesscpuhz(int aalcycles){	int loops, incr, x, y;	uvlong a, b, cpufreq;	/* find biggest loop that doesn't wrap */	incr = 16000000/(aalcycles*HZ*2);	x = 2000;	for(loops = incr; loops < 64*1024; loops += incr) {			/*		 *  measure time for the loop		 *		 *			MOVL	loops,CX		 *	aaml1:	 	AAM		 *			LOOP	aaml1		 *		 *  the time for the loop should be independent of external		 *  cache and memory system since it fits in the execution		 *  prefetch buffer.		 *		 */		outb(Tmode, Latch0);		cycles(&a);		x = inb(T0cntr);		x |= inb(T0cntr)<<8;		aamloop(loops);		outb(Tmode, Latch0);		cycles(&b);		y = inb(T0cntr);		y |= inb(T0cntr)<<8;		x -= y;			if(x < 0)			x += Freq/HZ;		if(x > Freq/(3*HZ))			break;	}	/* 	 *  figure out clock frequency and a loop multiplier for delay().	 *  n.b. counter goes up by 2*Freq	 */	cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);	m->loopconst = (cpufreq/1000)/aalcycles;	/* AAM+LOOP's for 1 ms */	if(m->havetsc){		/* counter goes up by 2*Freq */		b = (b-a)<<1;		b *= Freq;		b /= x;		/*		 *  round to the nearest megahz		 */		m->cpumhz = (b+500000)/1000000L;		m->cpuhz = b;		m->cyclefreq = b;	} else {		/*		 *  add in possible 0.5% error and convert to MHz		 */		m->cpumhz = (cpufreq + cpufreq/200)/1000000;		m->cpuhz = cpufreq;	}	i8253.hz = Freq<<Tickshift;}voidi8253timerset(uvlong next){	long period;	ulong want;	ulong now;	period = MaxPeriod;	if(next != 0){		want = next>>Tickshift;		now = i8253.ticks;	/* assuming whomever called us just did fastticks() */		period = want - now;		if(period < MinPeriod)			period = MinPeriod;		else if(period > MaxPeriod)			period = MaxPeriod;	}	/* hysteresis */	if(i8253.period != period){		ilock(&i8253);		/* load new value */		outb(Tmode, Load0|Square);		outb(T0cntr, period);		/* low byte */		outb(T0cntr, period >> 8);		/* high byte */		/* remember period */		i8253.period = period;		i8253.periodset++;		iunlock(&i8253);	}}static voidi8253clock(Ureg* ureg, void*){	timerintr(ureg, 0);}voidi8253enable(void){	i8253.enabled = 1;	i8253.period = Freq/HZ;	intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");}voidi8253link(void){}/* *  return the total ticks of counter 2.  We shift by *  8 to give timesync more wriggle room for interpretation *  of the frequency */uvlongi8253read(uvlong *hz){	ushort y, x;	uvlong ticks;	if(hz)		*hz = i8253.hz;	ilock(&i8253);	outb(Tmode, Latch2);	y = inb(T2cntr);	y |= inb(T2cntr)<<8;	if(y < i8253.last)		x = i8253.last - y;	else {		x = i8253.last + (0x10000 - y);		if (x > 3*MaxPeriod) {			outb(Tmode, Load2|Square);			outb(T2cntr, 0);		/* low byte */			outb(T2cntr, 0);		/* high byte */			y = 0xFFFF;			x = i8253.period;		}	}	i8253.last = y;	i8253.ticks += x>>1;	ticks = i8253.ticks;	iunlock(&i8253);	return ticks<<Tickshift;}voiddelay(int millisecs){	millisecs *= m->loopconst;	if(millisecs <= 0)		millisecs = 1;	aamloop(millisecs);}voidmicrodelay(int microsecs){	microsecs *= m->loopconst;	microsecs /= 1000;	if(microsecs <= 0)		microsecs = 1;	aamloop(microsecs);}/*   *  performance measurement ticks.  must be low overhead. *  doesn't have to count over a second. */ulongperfticks(void){	uvlong x;	if(m->havetsc)		cycles(&x);	else		x = 0;	return x;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -