rayzor

annotate src/timer.c @ 22:5380ff64e83f

minor changes from dos, and line endings cleanup
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 02 May 2014 14:32:58 +0300
parents a826bf0fb169
children
rev   line source
nuclear@1 1 /*
nuclear@22 2 pit8254 timer code for DOS programs.
nuclear@22 3 Copyright (C) 2011-2014 John Tsiombikas <nuclear@member.fsf.org>
nuclear@1 4
nuclear@1 5 This program is free software: you can redistribute it and/or modify
nuclear@1 6 it under the terms of the GNU General Public License as published by
nuclear@1 7 the Free Software Foundation, either version 3 of the License, or
nuclear@1 8 (at your option) any later version.
nuclear@1 9
nuclear@1 10 This program is distributed in the hope that it will be useful,
nuclear@1 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
nuclear@1 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
nuclear@1 13 GNU General Public License for more details.
nuclear@1 14
nuclear@1 15 You should have received a copy of the GNU General Public License
nuclear@1 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
nuclear@1 17 */
nuclear@1 18 #include <stdio.h>
nuclear@1 19 #include <stdlib.h>
nuclear@1 20 #include <conio.h>
nuclear@1 21 #include <dos.h>
nuclear@1 22 #include <i86.h>
nuclear@1 23 #include "pit8254.h"
nuclear@1 24
nuclear@22 25 #ifdef BORLANDC
nuclear@22 26 #error borland unsupported
nuclear@22 27 #endif
nuclear@22 28
nuclear@1 29 #define PIT_TIMER_INTR 8
nuclear@1 30 #define DOS_TIMER_INTR 0x1c
nuclear@1 31
nuclear@1 32 /* macro to divide and round to the nearest integer */
nuclear@1 33 #define DIV_ROUND(a, b) \
nuclear@1 34 ((a) / (b) + ((a) % (b)) / ((b) / 2))
nuclear@1 35
nuclear@1 36 static void set_timer_reload(int reload_val);
nuclear@1 37 static void cleanup(void);
nuclear@1 38 static void __interrupt __far timer_irq();
nuclear@1 39 static void __interrupt __far dos_timer_intr();
nuclear@1 40
nuclear@1 41 static void (__interrupt __far *prev_timer_intr)();
nuclear@1 42
nuclear@1 43 static unsigned long ticks;
nuclear@1 44 static unsigned long tick_interval, ticks_per_dos_intr;
nuclear@1 45 static int inum;
nuclear@1 46
nuclear@1 47 void init_timer(int res_hz)
nuclear@1 48 {
nuclear@1 49 _disable();
nuclear@1 50
nuclear@1 51 if(res_hz > 0) {
nuclear@1 52 int reload_val = DIV_ROUND(OSC_FREQ_HZ, res_hz);
nuclear@1 53 set_timer_reload(reload_val);
nuclear@1 54
nuclear@1 55 tick_interval = DIV_ROUND(1000, res_hz);
nuclear@1 56 ticks_per_dos_intr = DIV_ROUND(65535L, reload_val);
nuclear@1 57
nuclear@1 58 inum = PIT_TIMER_INTR;
nuclear@1 59 prev_timer_intr = _dos_getvect(inum);
nuclear@1 60 _dos_setvect(inum, timer_irq);
nuclear@1 61 } else {
nuclear@1 62 tick_interval = 55;
nuclear@1 63
nuclear@1 64 inum = DOS_TIMER_INTR;
nuclear@1 65 prev_timer_intr = _dos_getvect(inum);
nuclear@1 66 _dos_setvect(inum, dos_timer_intr);
nuclear@1 67 }
nuclear@1 68 _enable();
nuclear@1 69
nuclear@1 70 atexit(cleanup);
nuclear@1 71 }
nuclear@1 72
nuclear@1 73 static void cleanup(void)
nuclear@1 74 {
nuclear@1 75 if(!prev_timer_intr) {
nuclear@1 76 return; /* init hasn't ran, there's nothing to cleanup */
nuclear@1 77 }
nuclear@1 78
nuclear@1 79 _disable();
nuclear@1 80 if(inum == PIT_TIMER_INTR) {
nuclear@1 81 /* restore the original timer frequency */
nuclear@1 82 set_timer_reload(65535);
nuclear@1 83 }
nuclear@1 84
nuclear@1 85 /* restore the original interrupt handler */
nuclear@1 86 _dos_setvect(inum, prev_timer_intr);
nuclear@1 87 _enable();
nuclear@1 88 }
nuclear@1 89
nuclear@1 90 void reset_timer(void)
nuclear@1 91 {
nuclear@1 92 ticks = 0;
nuclear@1 93 }
nuclear@1 94
nuclear@1 95 unsigned long get_msec(void)
nuclear@1 96 {
nuclear@1 97 return ticks * tick_interval;
nuclear@1 98 }
nuclear@1 99
nuclear@1 100 static void set_timer_reload(int reload_val)
nuclear@1 101 {
nuclear@1 102 outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
nuclear@1 103 outp(PORT_DATA0, reload_val & 0xff);
nuclear@1 104 outp(PORT_DATA0, (reload_val >> 8) & 0xff);
nuclear@1 105 }
nuclear@1 106
nuclear@1 107 static void __interrupt __far dos_timer_intr()
nuclear@1 108 {
nuclear@1 109 ticks++;
nuclear@1 110 _chain_intr(prev_timer_intr); /* DOES NOT RETURN */
nuclear@1 111 }
nuclear@1 112
nuclear@1 113 /* first PIC command port */
nuclear@1 114 #define PIC1_CMD 0x20
nuclear@1 115 /* end of interrupt control word */
nuclear@1 116 #define OCW2_EOI (1 << 5)
nuclear@1 117
nuclear@1 118 static void __interrupt __far timer_irq()
nuclear@1 119 {
nuclear@1 120 static unsigned long dos_ticks;
nuclear@1 121
nuclear@1 122 ticks++;
nuclear@1 123
nuclear@1 124 if(++dos_ticks >= ticks_per_dos_intr) {
nuclear@1 125 /* I suppose the dos irq handler does the EOI so I shouldn't
nuclear@1 126 * do it if I am to call the previous function
nuclear@1 127 */
nuclear@1 128 dos_ticks = 0;
nuclear@1 129 _chain_intr(prev_timer_intr); /* XXX DOES NOT RETURN */
nuclear@1 130 return; /* just for clarity */
nuclear@1 131 }
nuclear@1 132
nuclear@1 133 /* send EOI to the PIC */
nuclear@1 134 outp(PIC1_CMD, OCW2_EOI);
nuclear@1 135 }