/*
Copyright (C) 1998-2003 Scott Dattalo
2003 Mike Durian
This file is part of gpsim.
gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <glib.h> // for guint64
#include <assert.h>
#include "intcon.h"
#include "gpsim_classes.h" // for RESET_TYPE
#include "trace.h"
#include "pir.h"
#include "16bit-registers.h"
#include "16bit-processors.h"
#include "breakpoints.h"
//#define DEBUG
#if defined(DEBUG)
#define Dprintf(arg) {printf("%s:%d",__FILE__,__LINE__); printf arg; }
#else
#define Dprintf(arg) {}
#endif
//--------------------------------------------------
// member functions for the INTCON base class
//--------------------------------------------------
INTCON::INTCON(void)
: interrupt_trace(0)
{
new_name("intcon");
}
void INTCON::set_T0IF(void)
{
Dprintf((" INTCON::%s\n",__FUNCTION__));
trace.raw(write_trace.get() | value.get());
value.put(value.get() | T0IF);
if (value.get() & (GIE | T0IE))
{
trace.interrupt();
}
}
void INTCON::set_cpu(Processor *new_cpu)
{
cpu = new_cpu;
pic_processor *pcpu = dynamic_cast<pic_processor *>(cpu);
assert(pcpu);
}
void INTCON::put(unsigned int new_value)
{
Dprintf((" INTCON::%s\n",__FUNCTION__));
trace.raw(write_trace.get() | value.get());
value.put(new_value);
// Now let's see if there's a pending interrupt
// The INTCON bits are:
// GIE | ---- | TOIE | INTE | RBIE | TOIF | INTF | RBIF
// There are 3 sources for interrupts, TMR0, RB0/INTF
// and RBIF (RB7:RB4 change). If the corresponding interrupt
// flag is set AND the corresponding interrupt enable bit
// is set AND global interrupts (GIE) are enabled, THEN
// there's an interrupt pending.
// note: bit6 is not handled here because it is processor
// dependent (e.g. EEIE for x84 and ADIE for x7x).
if(value.get() & GIE)
{
if( (((value.get()>>3)&value.get()) & (T0IF | INTF | RBIF)) )
{
trace.interrupt();
bp.set_interrupt();
}
else if(value.get() & XXIE)
{
if(check_peripheral_interrupt())
{
trace.interrupt();
bp.set_interrupt();
}
}
}
}
void INTCON::peripheral_interrupt(void)
{
Dprintf((" INTCON::%s\n",__FUNCTION__));
if( (value.get() & (GIE | XXIE)) == (GIE | XXIE) )
bp.set_interrupt();
}
bool INTCON_14_PIR::check_peripheral_interrupt()
{
assert(pir_set != 0);
Dprintf((" INTCON::%s\n",__FUNCTION__));
return (pir_set->interrupt_status());
}
//----------------------------------------------------------------------
// INTCON_16
//
// intcon for the 16-bit processor cores.
//
//----------------------------------------------------------------------
INTCON_16::INTCON_16()
: interrupt_vector(0), rcon(0), intcon2(0)
{
}
//----------------------------------------------------------------------
// void INTCON_16::clear_gies(void)
//
// This routine clears the global interrupt enable bit(s). If priority
// interrupts are used (IPEN in RCON is set) then the appropriate gie
// bit (either giel or gieh) is cleared.
//
// This routine is called from 16bit_processor::interrupt().
//
//----------------------------------------------------------------------
void INTCON_16::clear_gies(void)
{
assert(cpu != 0);
if(haveHighPriorityInterrupt())
put(get() & ~GIEH);
else
put(get() & ~GIEL);
}
//----------------------------------------------------------------------
// void INTCON_16::clear_gies(void)
//
//----------------------------------------------------------------------
void INTCON_16::set_gies(void)
{
assert(rcon != 0);
assert(intcon2 != 0);
assert(cpu != 0);
get(); // Update the current value of intcon
// (and emit 'register read' trace).
if(rcon->value.get() & RCON::IPEN)
{
// Interrupt priorities are being used.
if(0 == (value.get() & GIEH))
{
// GIEH is cleared, so we need to set it
put(value.get() | GIEH);
return;
}
else
{
// GIEH is set. This means high priority interrupts are enabled.
// So we most probably got here because of an RETFIE instruction
// after handling a low priority interrupt. We could check to see
// if GIEL is low before calling put(), but it's not necessary.
// So we'll just blindly re-enable giel, and continue with the
// simulation.
put(value.get() | GIEL);
return;
}
}
else
{
// Interrupt priorities are not used, so re-enable GIEH (which is in
// the same bit-position as GIE on the mid-range core).
put(value.get() | GIEH);
return;
}
}
//----------------------------------------------------------------------
// void INTCON_16::put(unsigned int new_value)
//
// Here's were the 18cxxx interrupt logic is primarily handled.
//
// inputs: new_value -
// outputs: none
//
//----------------------------------------------------------------------
void INTCON_16::put(unsigned int new_value)
{
trace.raw(write_trace.get() | value.get());
//trace.register_write(address,value.get());
value.put(new_value);
//cout << " INTCON_16::put\n";
// Now let's see if there's a pending interrupt
// if IPEN is set in RCON, then interrupt priorities
// are being used. (In other words, there are two
// interrupt priorities on the 18cxxx core. If a
// low priority interrupt is being serviced, it's
// possible for a high priority interrupt to interject.
if(rcon->value.get() & RCON::IPEN)
{
unsigned int i1;
// Use interrupt priorities
if( 0 == (value.get() & GIEH))
return; // Interrupts are disabled
// Now we just go through the interrupt logic of the 18cxxx
// First we check the high priorities and then we check the
// low ones. When ever we detect an interrupt, then the
// bp.interrupt flag is set (which will cause the interrupt
// to be handled at the high level) and additional checks
// are aborted.
// If TO, INT, or RB flags are set AND their correspond
// interrupts are enabled, then the lower three bits of
// i1 will reflect this. Note that INTF does NOT have an
// associated priority bit!
i1 = ( (value.get()>>3)&value.get()) & (T0IF | INTF | RBIF);
if(i1 & ( (intcon2->value.get() & (T0IF | RBIF)) | INTF))
{
//cout << " selecting high priority vector\n";
set_interrupt_vector(INTERRUPT_VECTOR_HI);
trace.interrupt();
bp.set_interrupt();
return;
}
// If we reach here, then there are no high priority
// interrupts pending. So let's check for the low priority
// ones.
if(i1 & ( (~intcon2->value.get() & (T0IF | RBIF)) | INTF))
{
//cout << " selecting low priority vector\n";
set_interrupt_vector(INTERRUPT_VECTOR_LO);
trace.interrupt();
bp.set_interrupt();
return;
}
}
else
{
// ignore interrupt priorities
set_interrupt_vector(INTERRUPT_VECTOR_HI);
if(value.get() & GIE)
{
if( ( (value.get()>>3)&value.get()) & (T0IF | INTF | RBIF) )
{
trace.interrupt();
bp.set_interrupt();
}
else if(value.get() & XXIE)
{
if(check_peripheral_interrupt())
{
trace.interrupt();
bp.set_interrupt();
}
}
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1