/*
 * This file is part of libgreat
 *
 * SGPIO interrupt service routine template.
 */

// This is relatively simple code used in a fairly complex way, as speed in the SGPIO
// handler routines is absolutely of the essence. This code serves as a generic _template_
// for SGPIO-handling ISRs; but it needs to be customized to match the particular SGPIO
// slice toplogy we're using -- essentially, we need to copy data out of _some_ of the shadow
// regsiters, but which ones and in what order depend on the runtime configuration.
//
// Since we don't have enough cycles here to allow for code that's generic enough to handle all
// configurations, we instead provide this template and a set of functions that rewrite this
// template's -- effectively performing the relevant customization at runtime.


.global sgpio_dynamic_isr_args
.global sgpio_dynamic_isr_body
.global sgpio_dynamic_isr_end

// Constants that point to registers we'll need to modify in the SGPIO block.
.equ SGPIO_REGISTER_BLOCK_BASE,          0x40101000
.equ SGPIO_SHADOW_REGISTERS_BASE,        0x40101100
.equ SGPIO_EXCHANGE_INTERRUPT_CLEAR_REG, 0x40101F30

// Literal pool offsets. We'll create a small literal pool will store
// most of the addresses and values that are runtime dependent. The C
// runtime can then write values into this pool to configure some of the
// template code.
.equ SGPIO_CLEAR,                    0x00
.equ SGPIO_COPY_SIZE,                0x01
.equ SGPIO_BUFFER_ADDRESS,           0x02
.equ SGPIO_BUFFER_OFFSET_ADDRESS,    0x03
.equ SGPIO_BUFFER_OFFSET_MASK,       0x04
.equ SGPIO_FILL_COUNT_ADDRESS,       0x05
.equ SGPIO_ISR_ASM_ARGS_SIZE,        (4 * 6)

// Quick macro that loads a value from the literal pool -- allowing us touse
// an LDR-like syntax.
.macro load_arg reg entry_name
	ldr \reg, (sgpio_dynamic_isr_args + (4 * \entry_name))
.endm


// Comment this out if you're sure text is mutable and want the benefits of being in the text section. :)
//.section .data.generated_code
.section .text
.thumb

//
// SGPIO dynamically-generated ISR template
//
.thumb_func
.global sgpio_dynamic_isr
sgpio_dynamic_isr:

	// Clear the interrupt pending bits for the SGPIO slices we're working with.
	ldr r1, =SGPIO_EXCHANGE_INTERRUPT_CLEAR_REG
	load_arg r0, SGPIO_CLEAR
	str r0, [r1]

	// Grab the base address of the SGPIO shadow registers...
	ldr r1, =SGPIO_SHADOW_REGISTERS_BASE

	// ... and grab the address of the buffer segment we want to write to.
	load_arg r2, SGPIO_BUFFER_ADDRESS
	load_arg r0, SGPIO_BUFFER_OFFSET_ADDRESS // r0 = &position_in_buffer
	ldr r0, [r0]                             // r0 = position_in_buffer
	add r2, r2, r0                           // r2 = write_target = &buffer + position_in_buffer


// The body of our dynamically-generated ISR. This section serves as a placeholder for some
// machine code generated by our C -- see the `sgpio_isr.c` functions for the generated code.
// Essentially, each generated command does a buffer read followed by a buffer write. We generate
// these copies to match the individual SGPIO instruction's slice requirements.
sgpio_dynamic_isr_body:

	// The maximum number of slices a single function can use is 8; so we'll provide for up to
	// eight copy operations.
	.rept 8
	ldr r0, [r1, #60]
	str r0, [r2, #32]
	.endr


// The end of our dynamically generated ISR. This updates the pointers affected by the relevant routine.
sgpio_dynamic_isr_end:

	// total fill += size_copied
	load_arg r1, SGPIO_COPY_SIZE
	load_arg r2, SGPIO_FILL_COUNT_ADDRESS    // r2 = &fill_count
	ldr r0, [r2]                             // r0 = fill_count
	add r0, r1, r0                           // r0 = fill_count + size_copied
	str r0, [r2]                             // fill_count = fill_count + size_copied

	// position_in_buffer = (position_in_buffer + size_copied) % buffer_size
	load_arg r2, SGPIO_BUFFER_OFFSET_ADDRESS // r2 = &position_in_buffer
	ldr r0, [r2]                             // r0 = position_in_buffer
	add r0, r1, r0                           // r0 = position_in_buffer + size_copied
	load_arg r1, SGPIO_BUFFER_OFFSET_MASK
	and r0, r1                               // r0 = (position_in_buffer + size_copied) % buffer_size
	str r0, [r2]                             // position_in_buffer = (position_in_buffer + size_copied) % buffer_size

	// and return
	bx lr


//
// Storage for the aforementioned literal pool, and for any literals the compilers
// wants to stick into the literal-pool area.
//
.align 4
sgpio_dynamic_isr_args:
	.skip SGPIO_ISR_ASM_ARGS_SIZE

sgpio_dynamic_isr_literal_pool:
	.pool


