/*
    SupportAsm.S, Copyright (c) by Silvio Dragone & David Reist,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.

    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.

    For further information, please visit http://www.tik.ee.ethz.ch/~topsy

    This software is provided under the terms of the GNU General Public Licence.

    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/

#include "asm.h"
#include "cpu.h"

.text
	
ENTRY(testAndSet)
	mov	ip, sp
	stmdb	sp!, {fp, ip, lr, pc} // stack working register
	sub	fp, ip, #4
	
	mrs	ip, cpsr	// get the CPSR ...
	str	ip, [fp]	// ... and stack that too

	orr	ip, ip, #0xC0
	msr	cpsr_cxsf, ip	// disable interrupts

	ldr	ip, [a0]	// load lockVariable	
	teq	ip, #0x0	// check if lockVariable is FALSE
	moveq	ip, #0x1	// if so, then load TRUE into IP
	streq	ip, [a0]	// if so, write it back	
	moveq	a0, #0x1	// if so, then return TRUE (successful locking)

	ldr	ip, [fp]	// recover the working register as CPSR
	nop
	msr	cpsr_cxsf, ip	// put the CPSR back
	ldmdb	fp, {fp, sp, pc} // restore last working register and ...
				 // ... return

	
ENTRY(getBADVM)
	/* read out CP15 register 6 */
	
	mrc	p15, 0, a0, c6, c0		/* read out CP15 reg 6 */
	
END(getBADVM)


ENTRY(getFSTATE)
	/* read out CP15 register 5 */
	
	mrc	p15, 0, a0, c5, c0		/* read out CP15 reg 5 */
	
END(getFSTATE)

	
ENTRY(getPRID)
	/* read out CP15 register 0 (ID_CHIP) */
	/* retarg 0 in R0 */
	
	mrc	p15, 0, a0, c0, c0		/* read out CP15 reg 0 */
	
END(getPRID)			// Return


/* This function stops TOPSY and returns to cygmon by a SWI */
ENTRY(cygmon_return)
	swi	#0x1


/* This function stops TOPSY and reboots cygmon by a RESET */
ENTRY(cygmon_reboot)
	mov	a0, #0x0
	mov	a0, a0, lsl #31
	mov	pc, a0


/* This function puts an ASCII-No to Minicom. It does NOT
 * stop TOPSY operation!!!!!
 *
 * Usage: void cygmon_char(unsigned long int value[0..9])
 */
ENTRY(cygmon_char)
	bic	a0, a0, #0xFF0
	add	a0, a0, #0x30

	ldr	a2, =UART_SR
	ldr	a1, =UART_TX_READY

LABEL(loop_uart)
	ldr	a3, [a2]
	and	a3, a3, a1
	teq	a3, a1
	bne	loop_uart

	ldr	a1, =UART_DR	
	str	a0, [a1]
END(cygmon_char)
	
/* This function DISABLES the MMU!! But it doesn't flush the
 * TLB!! Let the MMU in BIGENDIAN mode!!
 *
 * Usage: void cygmon_mmu()
 */ 
ENTRY(cygmon_mmu)
	mov	a0, #0x80
	mcr	p15, 0, a0, c1, c0
	nop
	nop
	nop
	nop
END(cygmon_mmu)
	

/* This function flushes the whole TLB. It is usefull if the
 * MMU is to set or reset!
 *
 * Usage: void flush_tlb()
 */ 
ENTRY(flush_tlb)
	mov	ip, #0
	mcr	p15, 0, ip, c7, c10, 4
	mcr	p15, 0, ip, c8, c7, 0	
END(flush_tlb)


/* This function gets the current stack pointer.
 *
 * Usage: int take_sp()
 */ 
ENTRY(take_sp)
	mov	a0, sp
END(take_sp)


/* This function sets and enables the instruction breakpoint.
 * If the breakpoint becomes active the processor takes a prefetch 
 * abort exception. The interrupt routine must examine R14 (the saved
 * program counter) to determine if the exception was caused by the 
 * breakpoint.
 *
 * Usage: void setInstrBrkPt((Address) breakpointaddress)
 */ 
ENTRY(setInstrBrkPt)
	orr	a0, a0, #0x1		// enable breakpoint bit 0
	mcr	p15, 0, a0, c14, c8, 0  // Write instruction breakpoint ...
					// ...address and control register (IBCR)
END(setInstrBrkPt)

/* This function disables the instruction breakpoint.
 *
 * Usage: void unsetInstrBrkPt();
 */
ENTRY(unsetInstrBrkPt)
	mov	a0, #0x0		// disable breakpoint bit 0
	mcr	p15, 0, a0, c14, c8, 0	// Write instruction breakpoint ...
					// ...control register (IBCR)
END(unsetInstrBrkPt)


