Mail Archives: djgpp/1995/03/23/11:50:53
Hi,
I'm still having trouble with interrupts (but the trouble's got a lot
more subtle now).
The following source file is supposed to implement a technique for
using the PIT and int 0x8 to refresh the screen. Basically the idea is to run
the PIT at about ~140Hz (twice the VGA refresh rate) and in odd-numbered
interrupts ScreenBuffer is blasted onto the video memory, whereas in even
numbered interrupts the PIT is resynchronised with the VGA retrace.
(This was "C", I'll now add "C++"-style comments to explain things to you:
***** timerint.c ***************************************************************
#include <pc.h>
#include <dpmi.h>
#include <go32.h>
#include <stdio.h>
#include <stdlib.h>
#define _TIMERINT_C_
#include "sb.h" // This just declares ScreenBuffer
#include "timerint.h" // This file is appended
static volatile unsigned int LastWait = 0;
static volatile unsigned int altern = 1;
static volatile unsigned int tics = 0;
static unsigned int Clock_Inc=0x10000;
static volatile unsigned int CCL = 0, CCH = 0, CV = 0x34;
static _go32_dpmi_seginfo old_rm_handler;
static _go32_dpmi_seginfo new_pm_handler, new_rm_handler;
static _go32_dpmi_registers dummy, old;
static _go32_dpmi_registers t_old;
// The following routine just programs the PIT, val specifies the mode and
// ch and cl are the high/low bytes of the counter (or rather their low-bytes
// are).
static inline void timer_ports(register unsigned int val,
register unsigned int ch,
register unsigned int cl) {
asm("movl %%ebx, %%eax;
outb %%al, $0x43;
movl %%ecx, %%eax;
outb %%al, $0x40;
movl %%edx, %%eax;
outb %%al, $0x40;"
: /* no outputs */
: "b" (val), "c" (cl), "d" (ch)
: "eax");
}
// This routine sets the PIT to do one interrupt after 1/freq seconds.
static void SetOneShotTimer(unsigned int freq) {
Clock_Count = CLOCK_RATE / freq;
timer_ports((CV = 0x30),
(CCL = Clock_Count & 0xff),
(CCH = Clock_Count >> 8));
Clock_Inc = CLOCK_RATE / 140; /* should be kept to 140 by VGA retrace ? */
}
// This tidies up when we exit.
static void TimerCleanUp() {
timer_ports((CV = 0x34),
(CCL = 0),
(CCH = 0));
_go32_dpmi_set_real_mode_interrupt_vector(8, &old_rm_handler);
puts("TimerCleanUp");
}
// This is the interrupt routine, if altern = 1, it resynchronises, if
// altern = 0, it draws the screen.
static void TimerHandler() {
altern ^= 1; // flip the flag for what we do
if (!altern) { // This asm resynch's; it seems to work.
asm("xor %%ebx,%%ebx;
mov $0x3da,%%dx;
l_resynch_%=_2:
incl %%ebx;
inb %%dx,%%al;
test $0x08,%%al;
jz l_resynch_%=_2;"
: "=b" (LastWait)
:
: "edx", "eax", "ebx");
// Previous routine also keeps a count of how many cycles it has to wait for
// this is then used for picking the best rate for the PIT, the problem seems
// to be associated with this.
} else {
if (ScreenChanged) { // if there is actually anything to draw
// This 'asm' blasts the screen (it works)
asm("rep;
movsl;"
:
: "c" (16000),
"S" (ScreenBuffer),
"D" (SCREEN_MEMORY)
: "ecx", "esi", "edi");
ScreenChanged = 0;
}
}
tics += Clock_Inc; // keep a track of time
if (tics >= 0x10000) { // if necessary, call the original interrupt
// routine
memcpy(&t_old, &old, sizeof(_go32_dpmi_registers));
_go32_dpmi_simulate_fcall_iret(&t_old);
tics -= 0x10000;
} else { // otherwise acknowledge the interrupt
asm("movb $0x20, %%al;
outb %%al, $0x20"
:
:
: "eax");
}
MyTime++; // just count calls
timer_ports(CV, CCL, CCH); // ask for another interrupt after the same
// delay
}
// This routine sets up the interrupt-vectors
static void SetTimerVectors() {
/* _go32_dpmi_get_protected_mode_interrupt_vector(8, &old_pm_handler); */
_go32_dpmi_get_real_mode_interrupt_vector(8, &old_rm_handler);
memset(&old, 0, sizeof(_go32_dpmi_registers));
old.x.cs = old_rm_handler.rm_segment;
old.x.ip = old_rm_handler.rm_offset;
atexit(TimerCleanUp);
new_pm_handler.pm_offset = (int)TimerHandler;
new_pm_handler.pm_selector = _go32_my_cs();
new_rm_handler.pm_offset = (int)TimerHandler;
new_rm_handler.pm_selector = _go32_my_cs();
_go32_dpmi_allocate_real_mode_callback_iret(&new_rm_handler, &dummy);
_go32_dpmi_allocate_iret_wrapper(&new_pm_handler);
_go32_dpmi_set_protected_mode_interrupt_vector(8, &new_pm_handler);
_go32_dpmi_set_real_mode_interrupt_vector(8, &new_rm_handler);
}
// This is the routine that is called from the main program.
int SetUpTimer() {
int x, old_tics = 0, speed;
unsigned int cumulative, best_speed, best;
SetTimerVectors(); // the previous routine
best = 0xffffffff;
best_speed = 140;
// The following loop gives the problems, it's supposed to try some PIT
// delays and pick the one that gives the closest match to the VGA rate
// (after allowing for the time we spend in the interrupt routine).
for (speed = 140; speed < 155; speed++) { // suitable range of Hz
SetOneShotTimer(speed); // set pit
old_tics = MyTime + 10; // 10 cycles in the future
cumulative = 0; // zero a counter
for (x = 0; x < 5; x++) { // repeat 5 times
while(MyTime <= old_tics) // wait for a new cycle
;
printf("%i ", LastWait); // without this it's more stable
cumulative += LastWait; // add the time wasted to total
// The previous was for timing without actually doing any drawing, with the
// following uncommented the routine gets less stable.
/* ScreenChanged = 1; // Call this section-Q
while(ScreenChanged)
;
old_tics = MyTime;
printf("%i ", LastWait);
cumulative += LastWait; */
}
printf(" <%i>\n", speed);
if (cumulative < best) { // just find the PIT rate that
best_speed = speed; // comes closest to hitting the
best = cumulative; // retrace.
}
}
SetOneShotTimer(best_speed); // use best rate
return best_speed; // let caller know.
}
****** end of file *************************************************************
****** this is timerint.h file *************************************************
#ifndef _TIMERINT_H_
#define _TIMERINT_H_
#ifdef _TIMERINT_C_
#define CLOCK_RATE 1193181
volatile int ScreenChanged = 0;
volatile unsigned int MyTime = 0;
unsigned int Clock_Count=0x10000;
#else /* _TIMERINT_C_ */
extern volatile int ScreenChanged;
extern volatile unsigned int MyTime;
extern unsigned int Clock_Count;
#endif /* _TIMERINT_C_ */
#ifdef __cplusplus
extern "C" {
#endif
int SetUpTimer();
#ifdef __cplusplus
}
#endif
#endif /* _TIMERINT_H_ */
****** end of file *************************************************************
That's it. The main routine (in this test-case) just calls SetUpTimer and
prints the return value.
The problem is that it crashes somewhere in the "for(speed = " loop. The crash
seems to be connected with interrupts that occur while the CPU is in RM. If I
take the printf's out of the loop it behaves, if I add a "getkey()" or similar
to the loop it crashes. However, in a simpler test-program (before I started
calculating LastWait) PM/RM interrupts seem to both be handled correctly.
Without the printf's but with "section-Q" uncommented, the program also crashes,
seemingly in the same place as before (n.b the actual place seems to be in the
memcpy in TimerHandler, which doesn't make a lot of sense).
Sorry if this sounds vague but I'm having difficulty thinking of ways of testing
for whats wrong. I've played around with a lot of details, shifting the order
of the parts of TimerHandler, etc etc and the problem hasn't yet presented
itself in a format I can understand. I suspect variuos things but can't prove
any of them.
Does anybody have suggestions for a sensible way of approaching debugging this ?
(or is the problem obvious to anybody ?).
Badders
- Raw text -