/*
**	Interrupts
**		Tomaso Paoletti, 1994-1995
*/

/* 18/9/96	implementing priorities */

#ifndef MAC
#include "foreign.h"
#endif

#ifdef USE_INTERRUPTS	/* otherwise, all file is skipped */

#ifdef DEBUG
#include <stdio.h>
#endif

#include "sim.h"
#include "sim_low.h"
#include "sim_irq.h"
#include "sim_io.h"

#include "options.h"	

#define InterruptEnabled_()	(1)  /* (Options.interrupts) */

#define MAX_PENDING_INT		21   /* max no. pending interrupts (abundant) */

#ifdef __cplusplus
#define ALLOCATE_(data)		(new data)
#else
#include "stdlib.h"
#define ALLOCATE_(data)		(malloc(sizeof(data)))
#endif

/* high-level (require memory as pointer to data) */
/*
#define bitTest(adr,mask)		(get_mem8(memory,adr) & mask)
#define bitSet(adr,mask)		set_mem8(memory,adr, get_mem8(memory,adr) | mask )
#define bitClr(adr,mask)		set_mem8(memory,adr, get_mem8(memory,adr) & ~mask)
*/

/* low-level (bypass exotic register access) */
#define bitTest(adr,mask)		(memory[adr] & mask)
#define bitSet(adr,mask)		memory[adr] |= mask
#define bitClr(adr,mask)		memory[adr] &= ~mask

#define kDifferent	0xFF

/* prototypes */
void bad_op(m6811 state);

/* Queue */
#define QUEUED

typedef word   OurData;  

void	QInsert (int prior, OurData inData);
short	QRemove (OurData * outData);
int		QInit   (int n_blocks);


extern long caught_signal;
extern m6811 _null_state, *NULL_STATE;

	/* prescaling : E-cycles >> pr */
short pr_table[] = { 0,2,3,4 };
short prescaler = 0;

	/* rate selection : irq after rs E-cycles */
long  rs_table[] = { 1L<<13,1L<<14,1L<<15,1L<<16 };
long	rateSelect = 0;

	/* output compare vectors and masks */
word	OC_timer [] = { 0L, TOC1, TOC2, TOC3, TOC4, TI4O5 };
word	OC_mask [] = { 0L, MASK_7, MASK_6, MASK_5, MASK_4, MASK_3 };
word	OC_vector [] = { 0L, TOC1INT, TOC2INT, TOC3INT, TOC4INT, TOC5INT };

char * descr_table[] = { "SCI irq", "SPI irq", "Pulse accumulator irq",
			 "Pulse acc. overflow","Timer overflow","Output compare 5","Output compare 4",
			 "Output compare 3","Output compare 2","Output compare 1", 
			 "Input capture 3","Input capture 2","Input capture 1",
			 "Real timer irq","IRQ","XIRQ","Software interrupt","Bad instruction",
			 "Cop failure","Clock failure","Reset" };

typedef struct m6811subsys_s {
  unsigned int	wait_state : 1;	/* entered wait state (after WAI) */
  unsigned int	beyond64 : 1;	/* flag on first 64 instructions */
  
  short	prescaler;
  
  long	TO_ref;		/* timer overflow reference time */
  
  long	rateSelect;
  long	RTI_ref;	/* real timer reference time */
  word	RTI_free_cnt;
  
  long	TD_expire,
    RD_expire,
    cCycles;
  short	TDRE_read,
    RDRF_read;
  byte	old_BAUD;
  
  long	AD_ref;
} m6811subsys;

short	beyond_64 = FALSE,
  wait_state = FALSE;

long	TO_ref = 0L;
long	RTI_ref = 0L;
word	RTI_free_cnt = 0L; /* TCNT : $100E-$100F */

long	AD_ref = 0L;

byte	old_BAUD = kDifferent,
  RDR_shadow,
  TDR_shadow;

long	cCycles  = 0L,		/* number of E-cycles per character */
  TD_expire = 0L,
  RD_expire = 0L;

short	TDRE_read = FALSE,
  RDRF_read = FALSE;


#ifndef QUEUED
word gVector = 0;
#endif

/*---------------- macros ---------------------*/
#define timer_cmp(t,lo,hi)	(lo<=hi ? lo<=tmp && tmp<hi : tmp<hi || tmp>lo ) 

#define isRegisterPage(adr) (adr>=0x1000 && adr<=0x103F)

/*--------------------------------------------------*/
void reset_interrupts(m6811 * state, byte *memory)
{	
  word i;
  
  if (InterruptEnabled_())
    {
      wait_state = FALSE;
      beyond_64 = 0;
      TO_ref = 0L;
      RTI_ref = 0L;
      RTI_free_cnt = 0L;
      AD_ref = 0L;
      old_BAUD = kDifferent;
      TD_expire = 0L;
      RD_expire = 0L;
      cCycles = 0;
      RDR_shadow = 0;
      TDR_shadow = 0;
      TDRE_read = FALSE;
      RDRF_read = FALSE;
      
      /* clear registers */
      for(i=0x1000; i<=0x103F; i++)
	memory[i] = 0;
      
      /* custom values */
      memory[ PIOC ] = 0x03;
      memory[ SPCR ] = 0x40;
      memory[ SCSR ] = 0xC0;
      memory[ OPTION ] = 0x10;
      
      
#ifdef QUEUED
      QInit ( MAX_PENDING_INT );
#else
      gVector = 0;
#endif
    }
}

/*---------------------------------------------------------*/

PRIVATE
void add_pending_irq( int priority, word vec_adr )
{
#ifdef QUEUED
  QInsert( priority, (OurData)vec_adr);
#else
  gVector = vec_adr;
#endif
  
#ifdef DEBUG
  /* NotifyIRQToUser(vec_adr, &state, TRUE); */
#endif
}

PRIVATE
short XIRQ_pin_low()
{
  return 0;
}

/*-------------------------------------------------------------*/
PRIVATE
void stack_registers(byte *memory, m6811 *state)
{
  set_s(state, inc16(get_s(state), -2, NULL_STATE));	/* push PC */
  set_mem16(memory, get_s(state)+1, get_pc(state));
  
  set_s(state, inc16(get_s(state), -2, NULL_STATE));	/* push Y */
  set_mem16(memory, get_s(state)+1, get_y(state));
  
  set_s(state, inc16(get_s(state), -2, NULL_STATE));	/* push X */
  set_mem16(memory, get_s(state)+1, get_x(state));
  
  set_s(state, inc16(get_s(state), -1, NULL_STATE));	/* push A */		
  set_mem16(memory, get_s(state), get_a(state));			
  
  set_s(state, inc16(get_s(state), -1, NULL_STATE));	/* push B */		
  set_mem16(memory, get_s(state), get_b(state));			
  
  set_s(state, inc16(get_s(state), -1, NULL_STATE));	/* push CCR */		
  set_mem16(memory, get_s(state), get_ccw(state));		
}

PRIVATE
void jump_to_ISR( word vec_adr, byte *memory, m6811 *state)
{	
  /**
    if ( get_idisable(state)) 
    return;
    **/
#ifdef DEBUG
  if (vec_adr<SCIINT && vec_adr>RESETINT )
    /* Error("Wrong interrupt vector :%04lX",vec_adr); */
  ;
#endif
  
  wait_state = FALSE;
  
  stack_registers(memory, state);
  
  set_idisable(state,1);			/* disable interrupts */
  set_pc(state,get_mem16(memory,vec_adr));	/* fetch vector */
  
  /* NotifyIRQToUser(vec_adr, state, FALSE); */
}

void return_from_ISR(byte *memory, m6811 *state)
{	
  set_s(state, inc16(get_s(state), 1, NULL_STATE));	/* pull CCR */
  set_ccw(state, get_mem8(memory, get_s(state)));
  
  set_s(state, inc16(get_s(state), 1, NULL_STATE));	/* pull B */
  set_b(state, get_mem8(memory, get_s(state)));
  
  set_s(state, inc16(get_s(state), 1, NULL_STATE));	/* pull A */
  set_a(state, get_mem8(memory, get_s(state)));
  
  set_s(state, inc16(get_s(state), 1, NULL_STATE));	/* pull X */
  set_x(state, get_mem16(memory, get_s(state)));
  set_s(state, inc16(get_s(state), 1, NULL_STATE));
  
  set_s(state, inc16(get_s(state), 1, NULL_STATE));	/* pull Y */
  set_y(state, get_mem16(memory, get_s(state)));
  set_s(state, inc16(get_s(state), 1, NULL_STATE));
  
  set_s(state, inc16(get_s(state), 1, NULL_STATE));	/* pull PC */
  set_pc(state, get_mem16(memory, get_s(state)));
  set_s(state, inc16(get_s(state), 1, NULL_STATE));
  
  if (get_x_interrupt(state) == 1) set_x_interrupt(state,0);	/* cannot set X ! */
}

/*---------------------------------------------------------*/
CAVEAT("is same-priority model correct?");

/* priorities : -1 is reserved for 'hard' interrupts */
#define kPri_hard	-1
#define kPri_TOINT	1
#define kPri_RTIINT	1
#define kPri_OC		1
#define kPri_SCIINT 1

/* core interrupt simulation */
short idle_subsystems(byte *memory, m6811 *state)
{	
  register word tmp;
  word	vec_adr = 0;
  byte i;
  word RTI_old_cnt;
  short priority;
  
  if (! InterruptEnabled_())
    return 0;
  
  do /* once, and for every wait state */
    { 
      /* check for limit of 64 E-cycles after reset */ 	
      if (! beyond_64) 
	{	if (state->t > 64) beyond_64 = 1;
	else 
	  {			/* ...still in time */	
	    tmp = get_mem8(memory,TMSK2);
	    prescaler = pr_table[ tmp & 0x3 ]; 	
	  }
	}
      
      /* update free-running clock */
      RTI_old_cnt = RTI_free_cnt;	
      RTI_free_cnt = (state->t - TO_ref ) >> prescaler;	
      set_mem16(memory, TCNT, RTI_free_cnt);
      
      
      
      /*%%%%%%%%%%%%%% PENDING INTERRUPTS %%%%%%%%%%%%%%%%%%%%%*/
      /*======== XIRQ external ===========*/
      
      if (!get_x_interrupt(state) && XIRQ_pin_low() )
	{
	  add_pending_irq( kPri_hard, XIRQINT);
	}
      
      /*====== highest priority ======*/
      /* is handled simply by dynamically changing priorities */
      
      /*======== IRQ external ============*/
      
      /*============== RTI ===============*/
      
      tmp = get_mem8(memory,PACTL);			/* get RT rate selector */
      rateSelect = rs_table[ tmp & 0x3 ];
      
      if ((state->t - RTI_ref) > rateSelect ) /* check for RealTime interrupt */
	{	
	  RTI_ref = state->t;					/* reset int. counter */
	  bitSet(TFLG2,MASK_6);				/* set RTIF flag */
	  if (bitTest(TMSK2,MASK_6))	/* check for RTII flag */
	    {	add_pending_irq( kPri_RTIINT, RTIINT); }	
	}
      
      /*======== input compare ===========*/
      
      /*======== output compare ==========*/
      /* one-cycle inhibit not implemented */
      
      for (i=1;i<=5;i++)
	{	tmp = get_mem16(memory, OC_timer[i] );
	
	/* if (RTI_free_cnt > tmp && RTI_old_cnt <= tmp) */
	if (timer_cmp(tmp,RTI_old_cnt,RTI_free_cnt))
	  {	bitSet(TFLG1, OC_mask[i] );
	  if (bitTest(TMSK1, OC_mask[i]))
	    {	add_pending_irq( kPri_OC, OC_vector[i]); }
	  }
	}
      
      /*======== timer overflow ==========*/
      
      if (RTI_free_cnt > 0xffff)			/* check for overflow */
	{	
	  TO_ref = state->t;				/* reset int. counter */
	  bitSet(TFLG2,MASK_7);			/* set TOF flag */
	  if (bitTest(TMSK2,MASK_7))		/* check for TOI flag */
	    {		add_pending_irq( kPri_TOINT, TOINT); }			
	}
      
      /*========= pulse accumulator =======*/
      
      /*========== sync. serial ===========*/
      
      /*========== async. serial ==========*/
      
      CAVEAT("does not recognize changes in SCCR1");
      tmp = get_mem8(memory, BAUD);
      if (tmp != old_BAUD)	
	{	/* set baud rate */
	  switch((tmp>>4) & 0x03)	/* SCP prescale select */
	    {
	    case 0:	cCycles = 1; break;
	    case 1: cCycles = 3; break;
	    case 2:	cCycles = 4; break;
	    case 3: cCycles = 13; break;			
	    }
	  cCycles = cCycles << (4 + (tmp & 0x07));	/* SCR rate select and /16 */
	  cCycles *= bitTest(SCCR1, MASK_4) ? 11 : 10;	/* number of bits */
	  
	  old_BAUD = tmp;
	}
      
      if (bitTest(SCCR2, MASK_2))
	{	/* Receiver enabled */
	  
	  if (! RD_expire)	
	    {	
	      if (bitTest(SCSR, MASK_5))
		{
		  /* Overrun error */
		  old_BAUD = old_BAUD;
		  CAVEAT("overrun not handled");
		}
	      else
		 /* Original line:
		    if (serial_input(memory, state, &RDR_shadow))
		  */
		    if (0)
		      {	/* receiver is free, and a character was received */
			RD_expire = state->t + cCycles;
		      }
	    }
	  
	  if (RD_expire && state->t > RD_expire)
	    {	/* completed last tx sequence */
	      RD_expire = 0;	
	      bitSet(SCSR, MASK_5); /* set RDRF */
	      if (bitTest(SCCR2, MASK_5))
		{
		  add_pending_irq( kPri_SCIINT, SCIINT);
		}		
	    }
	  
	}
      
      if (bitTest(SCCR2, MASK_3))
	{	/* Transmitter enabled */
	  
	  if (! TD_expire && ! bitTest(SCSR, MASK_7))	
	    {	/* just started transmitting */
	      TD_expire = state->t + cCycles;
	    }
	  
	  if (TD_expire && state->t > TD_expire) 
	    {	/* completed last tx sequence */
	      TD_expire = 0;
	      /* serial_output(memory, state, TDR_shadow); */ /* show char */
	      bitSet(SCSR, MASK_7);	/* set TDRE */
	      if (bitTest(SCCR2, MASK_7))
		{
		  add_pending_irq( kPri_SCIINT, SCIINT);
		}
	    }
	  
	}
      
      /**** other subsystems (no irqs) ****************/
      
      /* A/D conversion complete */
      if (bitTest(OPTION, MASK_7) && state->t - AD_ref >= 127)
	{	
	  AD_ref = state->t;
	  bitSet(ADCTL,MASK_7);
	}		
      
      /**** check for pending interrupts ****/
#ifdef QUEUED
      if (!get_idisable(state) && (priority = QRemove(&vec_adr)))
	{
	  if (priority == kPri_hard)
	    set_x_interrupt(state,1); /* set X flag */
	  
	  jump_to_ISR(vec_adr, memory,state);
	}
#else
      /* barbarian way, in this way we may loose pending IRQs */
      if (!get_idisable(state) && gVector)
	{	vec_adr = gVector;
	gVector = 0;
	jump_to_ISR(vec_adr, memory,state);
	}
#endif
      
      if (wait_state) 
	state->t ++;	/* time goes on, anyway */
    } 
  while (wait_state && !caught_signal);	
  return 0;
}

/*---- modified memory accessors (redefined from sim.c) -----*/

/* memory modifiers */
INLINE void set_mem8(byte *m, word address, byte value)
{	if (! isRegisterPage(address))
  {
    m[address] = 0xFF & value;
  }
else
  {
    switch(address) {	
    case TFLG1: case TFLG2:	/* exotic flag-clearing */
      m[address] &= ~value;
      break;
    case SCDR:
      if (TDRE_read)
	{	bitClr(SCSR, MASK_7);	/* clear TDRE */
	TDRE_read = FALSE;
	}
      TDR_shadow = value;
      break;
    case ADCTL:
      m[address] = 0x7F & value;	/* always clear CCF */
      break;	
    default:
      m[address] = 0xFF & value;
    }
    
  }
}

INLINE void set_mem16(byte *m, word address, word value)
{	if (! isRegisterPage(address))
  {
    m[address]=(value>>8) & 0xFF;
    m[address+1] = value & 0xFF;	
  }
else
  {
    switch(address) {	/* exotic flag-clearing */
    case TFLG1: case TFLG2:
      /* Error("unsupported 16-bit exotic flag clearing"); */
      return;
    case SCDR:
      /* Error("unsupported 16-bit shadow SCDR"); */
      return;
    }
    m[address]=(value>>8) & 0xFF;
    m[address+1] = value & 0xFF;			
  }	
}

/* memory accessors */
INLINE byte get_mem8(byte *m, word add)
{	
  byte val = 0xFF & m[add];
  if (isRegisterPage(add))
    switch(add) {	
    case SCDR:
      if (RDRF_read)
	{	
	  bitClr(SCSR, MASK_5);	/* clear RDRF */
	  RDRF_read = FALSE;
	}
      val = RDR_shadow;
      break;
    case SCSR:
      TDRE_read = val & MASK_7;
      RDRF_read = val & MASK_5;
      break;
    }	
  return val;		
}

INLINE word get_mem16(byte *m, word add)
{	return 0xFFFF & ((m[add]<<8) + m[add+1]);} /* ## */

/*------------ utilities --------------------*/
char * describe_irq(word vec_adr)
{	
  word delta =  vec_adr - SCIINT ;
  return descr_table[ delta/2 ];	
}

/*---------------------------------------------------------*/

void handle_swi(byte * memory, m6811 * state)
{
  add_pending_irq( kPri_hard, SWIINT);
}

void handle_wai(byte *memory, m6811 *state)
{
  stack_registers(memory, state);
  wait_state = TRUE;
}

void bad_op(m6811 state)
{
  add_pending_irq( kPri_hard, BADOPINT);
  /* Error("Bad op at pc=%04lx", state.pc&0xffff); */
}

/*=============== priority queue =================*/
/* to improve performance, we pre-allocate all needed data blocks,
   and insertion/removal functions will juggle with pointers 
   
   in this implementation, insertion is O(n),
   while removal is constant-time
*/

typedef struct Q
{	OurData data;
  int		priority;
  struct Q  * next;
  
} pQueue;

/* globals */
pQueue  * toBeServedQ = NULL, * freeQ = NULL;


int QInit(int n_blocks)
{
  pQueue * p, *q;
  
  int i, cnt = 0;
  
  if (freeQ == NULL && toBeServedQ == NULL)
    {
      /* queue is empty by default */
      toBeServedQ = NULL;
      
      /* create our free structs */
      for(i=0; i<n_blocks;i++)
	{
	  p = ALLOCATE_(pQueue);
	  if (p==NULL)
	    return i;
	  q = freeQ;
	  p->next = q;
	  freeQ = p;	
	}
      return n_blocks;
    }
  else
    {	/* re-use blocks */
      OurData dummy;
      
      /* simply passing them from busy to free */
      while (QRemove(&dummy))
	{}
      /* and counting. */
      p = freeQ;
      while (p)
	{
	  p = p->next;
	  cnt++;
	}
      
      return cnt;
    }
}

void QInsert( int prior, OurData inData)
{
  pQueue **p, *q, *s;
  
  if (freeQ)
    {		
      /* insert with priority 
	 (linear complexity, not very time efficient) */
      p = &toBeServedQ;
      q = toBeServedQ;
      while (q && q->priority <= prior)
	{
	  p = &(q->next);
	  q = q->next;
	}
      /* the struct is now occupied */
      s = freeQ;
      freeQ = freeQ->next;
      
      /* finish links */
      *p = s;
      s->priority = prior;
      s->data = inData;
      s->next = q;
      
    }
  else 
    /* Error("INTERNAL ERROR: out of data blocks"); */
  ;
}


short QRemove (OurData * outData)
{
  pQueue * p;
  if (p = toBeServedQ)
    {
      /* serve next request */
      *outData = toBeServedQ->data;
      toBeServedQ = toBeServedQ->next;
      
      /* put back free struct */
      p->next = freeQ;
      freeQ = p;	
      
      return p->priority;
    }
  else return 0;
}

#endif /* USE_INTERRUPTS */






















