www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1995/03/23/11:50:53

Date: Thu, 23 Mar 1995 14:18:16 EST
From: THE MASKED PROGRAMMER <badcoe AT bsa DOT bristol DOT ac DOT uk>
To: djgpp AT sun DOT soe DOT clarkson DOT edu
Cc: badcoe AT bsa DOT bristol DOT ac DOT uk
Subject: Int 0x8 problems

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 -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019