#include "tb_cxxrtl_io.h"
#include "hazard3_irq.h"
#include <stdlib.h>

// Test intent: check that IRQs interrupting PC-setting phase of cm.popret does
// not cause stack corruption. (This is not covered by zmp_irq_kill)

uint32_t __attribute__((naked)) foreground_task(uint32_t iters) {
	asm volatile (
		// Save sp and ra
		"addi sp, sp, -16\n"
		"sw s0, 0(sp)\n"
		"sw ra, 4(sp)\n"
		"mv s0, sp\n"
		"li a1, 0xf00b0000\n"
		// Short push + popret sequence
	"1:\n"
		"la ra, 2f\n"
		".hword 0xb852\n" // cm.push   {ra,s0},-16
		".hword 0xbe52\n" // cm.popret {ra,s0}, 16
	"2:\n"
		// Check we haven't moved the stack pointer or lost our s0 contents
		"bne s0, sp, 3f\n"
		"addi a0, a0, -1\n"
		"addi a1, a1, 1\n"
		"bgtz a0, 1b\n"
		// Normal return -- we've been counting up in a1 as we count down in
		// a0, so return a1 to confirm we iterated the expected number of times
		"mv a0, a1\n"
		"lw s0, 0(sp)\n"
		"lw ra, 4(sp)\n"
		"addi sp, sp, 16\n"
		"ret\n"
	"3:\n"
		// Uh oh, either sp or s0 changed. Panic!
		"mv sp, s0\n"
		"j panic_sp_changed\n"
	);
}

void __attribute__((noreturn)) panic_sp_changed(void) {
	tb_puts("Stack pointer changed, that wasn't supposed to happen!\n");
	tb_exit(-1);
	__builtin_unreachable();
}

void __attribute__((interrupt)) isr_machine_timer(void) {
	unsigned int dwell = rand() % 301;
	mm_timer->mtimecmp = mm_timer->mtime + dwell;
}

int main() {
	tb_puts("Starting\n");
	timer_irq_enable(true);
	global_irq_enable(true);
	const uint32_t expected_iterations = 1000;
	tb_printf("Running %u iterations\n", expected_iterations);
	uint32_t result = foreground_task(expected_iterations);
	tb_printf("Result: %08x\n", result);
	tb_assert(result == 0xf00b0000u + expected_iterations, "Wrong number of iterations");

	return 0;
}

/*EXPECTED-OUTPUT***************************************************************
Starting
Running 1000 iterations
Result: f00b03e8
*******************************************************************************/
