rayzor

diff src/timer.c @ 0:2a5340a6eee4

rayzor first commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 05 Apr 2014 08:46:27 +0300
parents
children a826bf0fb169
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/timer.c	Sat Apr 05 08:46:27 2014 +0300
     1.3 @@ -0,0 +1,131 @@
     1.4 +/*
     1.5 +256-color 3D graphics hack for real-mode DOS.
     1.6 +Copyright (C) 2011  John Tsiombikas <nuclear@member.fsf.org>
     1.7 +
     1.8 +This program is free software: you can redistribute it and/or modify
     1.9 +it under the terms of the GNU General Public License as published by
    1.10 +the Free Software Foundation, either version 3 of the License, or
    1.11 +(at your option) any later version.
    1.12 +
    1.13 +This program is distributed in the hope that it will be useful,
    1.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.16 +GNU General Public License for more details.
    1.17 +
    1.18 +You should have received a copy of the GNU General Public License
    1.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
    1.20 +*/
    1.21 +#include <stdio.h>
    1.22 +#include <stdlib.h>
    1.23 +#include <conio.h>
    1.24 +#include <dos.h>
    1.25 +#include <i86.h>
    1.26 +#include "pit8254.h"
    1.27 +
    1.28 +#define PIT_TIMER_INTR	8
    1.29 +#define DOS_TIMER_INTR	0x1c
    1.30 +
    1.31 +/* macro to divide and round to the nearest integer */
    1.32 +#define DIV_ROUND(a, b) \
    1.33 +	((a) / (b) + ((a) % (b)) / ((b) / 2))
    1.34 +
    1.35 +static void set_timer_reload(int reload_val);
    1.36 +static void cleanup(void);
    1.37 +static void __interrupt __far timer_irq();
    1.38 +static void __interrupt __far dos_timer_intr();
    1.39 +
    1.40 +static void (__interrupt __far *prev_timer_intr)();
    1.41 +
    1.42 +static unsigned long ticks;
    1.43 +static unsigned long tick_interval, ticks_per_dos_intr;
    1.44 +static int inum;
    1.45 +
    1.46 +void init_timer(int res_hz)
    1.47 +{
    1.48 +	_disable();
    1.49 +
    1.50 +	if(res_hz > 0) {
    1.51 +		int reload_val = DIV_ROUND(OSC_FREQ_HZ, res_hz);
    1.52 +		set_timer_reload(reload_val);
    1.53 +
    1.54 +		tick_interval = DIV_ROUND(1000, res_hz);
    1.55 +		ticks_per_dos_intr = DIV_ROUND(65535L, reload_val);
    1.56 +
    1.57 +		inum = PIT_TIMER_INTR;
    1.58 +		prev_timer_intr = _dos_getvect(inum);
    1.59 +		_dos_setvect(inum, timer_irq);
    1.60 +	} else {
    1.61 +		tick_interval = 55;
    1.62 +
    1.63 +		inum = DOS_TIMER_INTR;
    1.64 +		prev_timer_intr = _dos_getvect(inum);
    1.65 +		_dos_setvect(inum, dos_timer_intr);
    1.66 +	}
    1.67 +	_enable();
    1.68 +
    1.69 +	atexit(cleanup);
    1.70 +}
    1.71 +
    1.72 +static void cleanup(void)
    1.73 +{
    1.74 +	if(!prev_timer_intr) {
    1.75 +		return; /* init hasn't ran, there's nothing to cleanup */
    1.76 +	}
    1.77 +
    1.78 +	_disable();
    1.79 +	if(inum == PIT_TIMER_INTR) {
    1.80 +		/* restore the original timer frequency */
    1.81 +		set_timer_reload(65535);
    1.82 +	}
    1.83 +
    1.84 +	/* restore the original interrupt handler */
    1.85 +	_dos_setvect(inum, prev_timer_intr);
    1.86 +	_enable();
    1.87 +}
    1.88 +
    1.89 +void reset_timer(void)
    1.90 +{
    1.91 +	ticks = 0;
    1.92 +}
    1.93 +
    1.94 +unsigned long get_msec(void)
    1.95 +{
    1.96 +	return ticks * tick_interval;
    1.97 +}
    1.98 +
    1.99 +static void set_timer_reload(int reload_val)
   1.100 +{
   1.101 +	outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
   1.102 +	outp(PORT_DATA0, reload_val & 0xff);
   1.103 +	outp(PORT_DATA0, (reload_val >> 8) & 0xff);
   1.104 +}
   1.105 +
   1.106 +static void __interrupt __far dos_timer_intr()
   1.107 +{
   1.108 +	ticks++;
   1.109 +	_chain_intr(prev_timer_intr);	/* DOES NOT RETURN */
   1.110 +}
   1.111 +
   1.112 +/* first PIC command port */
   1.113 +#define PIC1_CMD	0x20
   1.114 +/* end of interrupt control word */
   1.115 +#define OCW2_EOI	(1 << 5)
   1.116 +
   1.117 +static void __interrupt __far timer_irq()
   1.118 +{
   1.119 +	static unsigned long dos_ticks;
   1.120 +
   1.121 +	ticks++;
   1.122 +
   1.123 +	if(++dos_ticks >= ticks_per_dos_intr) {
   1.124 +		/* I suppose the dos irq handler does the EOI so I shouldn't
   1.125 +		 * do it if I am to call the previous function
   1.126 +		 */
   1.127 +		dos_ticks = 0;
   1.128 +		_chain_intr(prev_timer_intr);	/* XXX DOES NOT RETURN */
   1.129 +		return;	/* just for clarity */
   1.130 +	}
   1.131 +
   1.132 +	/* send EOI to the PIC */
   1.133 +	outp(PIC1_CMD, OCW2_EOI);
   1.134 +}