/*  lowlevel.h
 *    Low-level inline I/O routines
 *    by Frank Barrus
 */



#define TIMER_HZ 1193182


static inline byte in_portb(word p) {
register byte v;
	asm volatile("inb %%dx,%%al":"a="(v):"d"(p));
	return (byte)v;
}

static inline void out_portb(word p, byte v) {
	asm volatile("outb %%al,%%dx"::"a"(v),"d"(p));
}


#define out_portb8(_p,_v) \
	asm volatile("outb %%al,%1"::"a"(_v),"i"(_p));

#define in_portb8(_p,_v) \
	asm volatile("inb %1,%%al":"a="(_v):"i"(_p));


static inline void disable_interrupts(void)
{
	if(iopl(3) < 0) {
		perror("You must be root to run this program\niopl");
		exit(1);
	}	
	asm volatile("cli");
}


static inline void enable_interrupts(void)
{ asm volatile("sti"); iopl(0); }


static inline void init_timer(void)
{ out_portb8(0x61, 0x00); out_portb8(0x43, 0xb6); }

static inline void start_timer(void)
{ out_portb8(0x61, 0x01); }

static inline void latch_timer(void)
{ out_portb8(0x43, 0x80); }


static inline void set_timer(word v)
{
	init_timer();
	asm volatile("	outb %%al, $0x42;
			movb %%ah, %%al; 
			outb %%al, $0x42; jmp 1f;  1: jmp 1f; 1:":"=a"(v):"a"(v));	
}


static inline word read_timer(void)
{
register word v;
	latch_timer();
	asm volatile("	inb $0x42,%%al;
		     	movb %%al,%%ah;
			inb $0x42,%%al;
			xchg %%al,%%ah":"a="(v));
	return v;
}


static inline void timer_sync(void)
{
register byte v;
	do {
		in_portb8(0x61, v);
	} while(v&0x20);
	do {
		in_portb8(0x61, v);
	} while(!(v&0x20));
}


static inline void timer_sync_half(void)
{
register byte v;
	do {
		in_portb8(0x61, v);
	} while(!(v&0x20));
	do {
		in_portb8(0x61, v);
	} while(v&0x20);
}


static inline int check_timeout(int *cycles)
{
static byte old = 0x20;
register byte v;
	in_portb8(0x61, v);
	v &= 0x20;
	if(v && !old)
		if(--(*cycles) <= 0)
			return True;
	old = v;
	return False;
}



static inline void delay(unsigned long t)
{
	set_timer(0);
	start_timer();
	for(; t > 0xffff; t -= 0x10000)
		timer_sync();
	set_timer(t);
	start_timer();
	timer_sync();
}


static inline void delay_sec(int sec)
{
	delay(TIMER_HZ*sec);
}
 
static inline void delay_msec(int msec)
{
	delay(TIMER_HZ*msec/1000);
}

static inline void delay_usec(int usec)
{
	delay(TIMER_HZ*usec/1000000);
}


static inline word tval_from_freq(int freq)
{
	return TIMER_HZ/freq;
}

static inline int freq_from_tval(word tval)
{
	return TIMER_HZ/tval;
}

