/*************************************************************************************
   File : isr.c
   Authors : Alain Greiner
   Date : april 2011

   These routines must be "intalled" by the boot code in the
   interrupt vector, depending on the system architecture.
*************************************************************************************/

#include "isr.h"
#include "drivers.h"

#define in_isr __attribute__((section (".isr")))

struct plaf;

extern struct plaf seg_dma_base;
extern struct plaf seg_ioc_base;
extern struct plaf seg_timer_base;
extern struct plaf seg_tty_base;

extern struct plaf NB_TIMERS;
extern struct plaf NB_TASKS;

//////////////////////////////////////////////////////////////////////////////////////
//  _isr_dma
//  Each processor controls its own private DMA.
//  It acknowledge the IRQ using the dma base address depending on the proc_id
//  as computed by the  _segment_increment() function, writes the transfer
//  status in the _dma_status[pid] variable, and resets the _dma_busy[pid]
//  synchrnisation variable to signal completion.
//  Both variables are defined in the drivers.c file.
/////////////////////////////////////////////////////////////////////////////////////
in_isr void _isr_dma()
{
    int*		dma_address;
    unsigned int	base		= (unsigned int)&seg_dma_base;
    unsigned int	increment 	= _segment_increment(DMA_SPAN*4);
    size_t		pid		= _procid();
   
    dma_address	= (int*)(base + increment);
    
    _dma_status[pid]		= dma_address[DMA_LEN];	// save status
    _dma_busy[pid]		= 0;			// release DMA
    dma_address[DMA_RESET]	= 0;			// reset IRQ
}
//////////////////////////////////////////////////////////////////////////////////////
//  _isr_ioc
//  There is only one IOC controler shared by all tasks.
//  It acknowledge the IRQ using the ioc base address, save the status,
//  and set the _ioc_done variable to signal completion.
//  This variable is defined in the drivers.c file.
//////////////////////////////////////////////////////////////////////////////////////
in_isr void _isr_ioc()
{
    int* ioc_address = (int*)&seg_ioc_base;
    
    _ioc_status = ioc_address[BLOCK_DEVICE_STATUS];	// save status & reset IRQ
    _ioc_done   = 1;					// signals completion
}
//////////////////////////////////////////////////////////////////////////////////////
//  _isr_timer* (* = 0,1,2,3)
//  A single processor can use up to 4 independant timers.
//  These 4 ISRs handle up to 4 IRQs generated by 4 independant timers,
//  connected to a single processor.
//  It acknowledge the IRQ using the timer base address depending
//  on both the proc_id and the timer_id (0,1,2,3).
//  It displays a message on TTY[proc_id,task_id]. 
//////////////////////////////////////////////////////////////////////////////////////
in_isr void _isr_timer_indexed(size_t timer_id)
{
    int*		timer_address;
    size_t		ntimers		= (size_t)&NB_TIMERS;
    unsigned int	base		= (unsigned int)&seg_timer_base;
    unsigned int	increment 	= _segment_increment(ntimers*TIMER_SPAN*4);
    int			date 		= (int)_proctime();
    char		buf[10];

    timer_address = (int*)(base + increment + timer_id*TIMER_SPAN*4);

    timer_address[TIMER_RESETIRQ] = 0;			// reset IRQ

    _itoa_dec(date, buf);				// print message
    _tty_write("\n!!! interrupt timer0 received at cycle ", 40);
    _tty_write(buf, 10); 
    _tty_write("\n\n", 2);
}
in_isr void _isr_timer()
{
    _isr_timer_indexed(0);
}
in_isr void _isr_timer0()
{
    _isr_timer_indexed(0);
}
in_isr void _isr_timer1()
{
    _isr_timer_indexed(1);
}
in_isr void _isr_timer2()
{
    _isr_timer_indexed(2);
}
in_isr void _isr_timer3()
{
    _isr_timer_indexed(3);
}
//////////////////////////////////////////////////////////////////////////////////////
//  _isr_tty_get_task* (* = 0,1,2,3)
//  A single processor can run up to 4 tasks in pseudo-parallelismr,
//  and each task has is own private terminal.
//  These 4 ISRs handle up to 4 IRQs associate to 4 independant terminals
//  connected to a single processor.
//  It acknowledge the IRQ using the terminal basee address depending
//  on both the proc_id  and the task_id (0,1,2,3).
//  There is one communication buffer _tty_get_buf[tty_id] per terminal.
//  protected by a set/reset variable _tty_get_full[tty_id].
//  The _tty_get_full[tty_id] synchronisation variable is set
//  by the ISR, and reset by the OS.
//  Both variables are defined in the drivers.c file.
//  To access these buffers, the terminal index is computed as
//      tty_id = proc_id*ntasks + task_id
//  A character is lost if the buffer is full when the ISR is executed.
//////////////////////////////////////////////////////////////////////////////////////
in_isr void _isr_tty_get_indexed(size_t task_id)
{
    char*		tty_address;
    size_t		ntasks		= (size_t)&NB_TASKS;
    size_t		tty_id		= _procid()*ntasks + task_id;
    unsigned int	base		= (unsigned int)&seg_tty_base;
    unsigned int	increment 	= _segment_increment(ntasks*TTY_SPAN*4);

    tty_address = (char*)(base + increment + task_id*TTY_SPAN*4);

    _tty_get_buf[tty_id] = tty_address[TTY_READ*4];	// save character and reset IRQ
    _tty_get_full[tty_id] = 1;				// signals character available
}
in_isr void _isr_tty_get()
{
    _isr_tty_get_indexed(0);
}
in_isr void _isr_tty_get_task0()
{
    _isr_tty_get_indexed(0);
}
in_isr void _isr_tty_get_task1()
{
    _isr_tty_get_indexed(1);
}
in_isr void _isr_tty_get_task2()
{
    _isr_tty_get_indexed(2);
}
in_isr void _isr_tty_get_task3()
{
    _isr_tty_get_indexed(3);
}
//////////////////////////////////////////////////////////////////////////////////////
//  _isr_switch
//  This ISR is in charge of context switch.
//  It handles up to 4 IRQs, corresponding to 4 different processors.
//  If the processor uses several timers, the context switch is driven
//  by the IRQ associated to timer0.
//  It acknowledges the IRQ on TIMER[proc_id] and calls the _ctx_switch() function.
//////////////////////////////////////////////////////////////////////////////////////
in_isr void _isr_switch()
{
    int*		timer_address;
    unsigned int	base		= (unsigned int)&seg_timer_base;
    unsigned int	increment	= _segment_increment(TIMER_SPAN*4);

    timer_address = (int*)(base + increment);

    timer_address[TIMER_RESETIRQ] = 0;			// reset IRQ
    _ctx_switch();
}

/* Local Variables:
   tab-width: 4;
   c-basic-offset: 4;
   c-file-offsets:((innamespace . 0)(inline-open . 0));
   indent-tabs-mode: nil;
   End: */

/* vim: set filetype=c expandtab shiftwidth=4 tabstop=4 softtabstop=4: */

