// TimerTest for Nios II -- Bruce Land 3july2006

//I like these:
#define begin {
#define end }

//from http://www.cc65.org/faq.php#PeekPoke
#define POKE(addr,val)     (*(unsigned char*) (addr) = (val))
#define POKEW(addr,val)    (*(unsigned*) (addr) = (val))
#define PEEK(addr)         (*(unsigned char*) (addr))
#define PEEKW(addr)        (*(unsigned*) (addr))

//Addresses from ptf file or SOPC interface
#define switches 0x1820
#define LEDs 0x1830

//Timer description from http://altera.com/literature/hb/nios2/n2cpu_nii51008.pdf
//NOTE--these are 16 bit registers, BUT offsets in the manual are in 32-bit chunks
#define timer_status 0x1800	//bit1=RUN bit0=TO
#define timer_control 0x1804	//bit3=STOP bit2=START bit1=CONT bit0=ITO
#define timer_periodl 0x1808	//timer autoreloads these at count==zero
#define timer_periodh 0x180c
#define timer_snapl 0x1810	//a write causes timer copy here
#define timer_snaph 0x1814
#define exception_addr 0x20	//contains: branch to the interrupt handler
//Timer behavior:
//TO bit is set by hardware when count==0
//uses ISR bit 1 (see configuration data in SOPC builder)
//On interrupt, you must Clear the TO bit of the status register
//RUN bit is read-only and is one when running
//ITO bit enables the count==0 interrupt
//START bit starts the timer when written with a 1
//STOP bit stops the timer when written with a 1
//CONT bit causes a periodic interrupt when 1 
//for a 1 second period, 50e6 counts = 0x2FAF080

//For communication with ISR
register int flag asm ("r23");
register int temp asm ("r22");


//Timer ISR
static void TimerISR(void)
begin
    //next two lines are: POKE(timer_status, 0);
    asm volatile ("orhi r22, r0, %hi(0x1800) \n ori r22, r22, %lo(0x1800");		
    asm volatile ("stbio r0, 0(r22)");		
    asm volatile ("xori r23, r23, 0xff");		//flag = flag ^ 0xff 	
    asm volatile ("subi r29, r29, 4");		//fix exception return address	
    asm volatile ("eret");				//return from exception
end

//set up ISR, then
//just read some switches and flash some lights
main()
begin
    char sw; 			//holds switch values			
    void (*pISR) (void);	//pointer to ISR function
    int BR_exception;		//build a branch instruction as {(offset<<6) + 0x06}
    
    // set up timer ISR address 
    // Skip first 3 instructions of function to jump over 
    // the function init code generated by the compiler == 12 bytes
    pISR = &TimerISR + 0xc ;
    // Construct the instruction "br pISR" 
    // offset IMM16 is relative to instruction following exception_addr
    // The shift 6 sends the offset to the correct bits
    // The last 0x06 is the opcode for BR
    BR_exception = (((int)pISR - exception_addr - 4) << 6 ) + 0x06 ;
    POKEW(exception_addr, BR_exception);
    
    //set up timer period to one second
    POKEW(timer_periodl, 0xf080);
    POKEW(timer_periodh, 0x2fa);

    //set up timer control: START timer, set CONT=1, and enable timer ITO bit
    POKE(timer_control, 7);

    //set up timer IRQ bit in the enable control register
    asm("movi r22, 2");
    asm("wrctl ienable, r22");

    //set up master interrupt bit in the cpu status register
    asm("movi r22, 1");
    asm("wrctl status, r22");
    
    //initialize LED inverting bits
    flag = 0xff;

    while(1)
    begin
	sw = PEEK(switches); 	//read from switches
	POKE(LEDs,sw^flag);   	//write to LEDs
	sw = 1;			//defeats the optimizer
    end
end
