#!/bin/sh

set -e

cd $AUTOPKGTEST_TMP

cat <<EOF > main.cpp
#include <array>
#include <cstdint>
#include <cstdio>
#include <exception>

#include <dynarmic/interface/A32/a32.h>
#include <dynarmic/interface/A32/config.h>

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

class MyEnvironment final : public Dynarmic::A32::UserCallbacks {
public:
	u64 ticks_left = 0;
	std::array<u8, 2048> memory{};

	u8 MemoryRead8(u32 vaddr) override {
		if (vaddr >= memory.size()) {
			return 0;
		}
		return memory[vaddr];
	}

	u16 MemoryRead16(u32 vaddr) override {
		return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
	}

	u32 MemoryRead32(u32 vaddr) override {
		return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
	}

	u64 MemoryRead64(u32 vaddr) override {
		return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
	}

	void MemoryWrite8(u32 vaddr, u8 value) override {
		if (vaddr >= memory.size()) {
			return;
		}
		memory[vaddr] = value;
	}

	void MemoryWrite16(u32 vaddr, u16 value) override {
		MemoryWrite8(vaddr, u8(value));
		MemoryWrite8(vaddr + 1, u8(value >> 8));
	}

	void MemoryWrite32(u32 vaddr, u32 value) override {
		MemoryWrite16(vaddr, u16(value));
		MemoryWrite16(vaddr + 2, u16(value >> 16));
	}

	void MemoryWrite64(u32 vaddr, u64 value) override {
		MemoryWrite32(vaddr, u32(value));
		MemoryWrite32(vaddr + 4, u32(value >> 32));
	}

	void InterpreterFallback(u32 /*pc*/, size_t /*num_instructions*/) override {
		// This is never called in practice.
		std::terminate();
	}

	void CallSVC(u32 /*swi*/) override {
		// Do something.
	}

	void ExceptionRaised(u32 /*pc*/, Dynarmic::A32::Exception /*exception*/) override {
		// Do something.
	}

	void AddTicks(u64 ticks) override {
		if (ticks > ticks_left) {
			ticks_left = 0;
			return;
		}
		ticks_left -= ticks;
	}

	u64 GetTicksRemaining() override {
		return ticks_left;
	}
};

int main() {
	MyEnvironment env;
	Dynarmic::A32::UserConfig user_config;
	user_config.callbacks = &env;
	Dynarmic::A32::Jit cpu{user_config};

	// Execute at least 1 instruction.
	// (Note: More than one instruction may be executed.)
	env.ticks_left = (int)1;

	// Write some code to memory.
	env.MemoryWrite16(0, 0x0088); // lsls r0, r1, #2
	env.MemoryWrite16(2, 0xE7FE); // b +#0 (infinite loop)

	// Setup registers.
	cpu.Regs()[0] = 1;
	cpu.Regs()[1] = 2;
	cpu.Regs()[15] = 0; // PC = 0
	cpu.SetCpsr(0x00000030); // Thumb mode

	// Execute!
	cpu.Run();

	// Here we would expect cpu.Regs()[0] == 8
	printf("R0: %u\n", cpu.Regs()[0]);
}
EOF

cat <<EOF > CMakeLists.txt
cmake_minimum_required(VERSION 3.18)
project("autopkgtest_cmake")

add_executable(main "main.cpp")

find_package("dynarmic")

target_link_libraries(main PRIVATE dynarmic::dynarmic)
EOF

export CXXFLAGS="-Wall -Wextra -Wpedantic -Werror"

cmake -S . -B build
cmake --build build

out=$(./build/main)

if [ "$out" != 'R0: 8' ]
	then >&2 echo Unexpected result: $out
	exit 1
fi
