package arm_test

import (
	"testing"

	"git.urbach.dev/cli/q/src/arm"
	"git.urbach.dev/cli/q/src/cpu"
	"git.urbach.dev/go/assert"
)

func TestStoreRegister(t *testing.T) {
	usagePatterns := []struct {
		Source cpu.Register
		Base   cpu.Register
		Offset cpu.Register
		Scale  arm.Scale
		Length byte
		Code   uint32
	}{
		{arm.X0, arm.X1, arm.X2, arm.Scale1, 8, 0xF8226820},
		{arm.X0, arm.X1, arm.X2, arm.Scale1, 4, 0xB8226820},
		{arm.X0, arm.X1, arm.X2, arm.Scale1, 2, 0x78226820},
		{arm.X0, arm.X1, arm.X2, arm.Scale1, 1, 0x38226820},
		{arm.X0, arm.X1, arm.X2, arm.ScaleLength, 8, 0xF8227820},
	}

	for _, pattern := range usagePatterns {
		t.Logf("str %s, [%s, #%s] %db", pattern.Source, pattern.Base, pattern.Offset, pattern.Length)
		code := arm.StoreRegister(pattern.Source, pattern.Base, pattern.Offset, pattern.Scale, pattern.Length)
		assert.Equal(t, code, pattern.Code)
	}
}

func TestStoreFixedOffsetRegister(t *testing.T) {
	usagePatterns := []struct {
		Source cpu.Register
		Base   cpu.Register
		Mode   arm.AddressMode
		Offset int
		Length byte
		Code   uint32
	}{
		{arm.X1, arm.X0, arm.UnscaledImmediate, 0, 8, 0xF8000001},
		{arm.X0, arm.X1, arm.UnscaledImmediate, 0, 8, 0xF8000020},

		{arm.X0, arm.X1, arm.UnscaledImmediate, -8, 1, 0x381F8020},
		{arm.X1, arm.X0, arm.UnscaledImmediate, -8, 1, 0x381F8001},
		{arm.X0, arm.X1, arm.UnscaledImmediate, -8, 2, 0x781F8020},
		{arm.X1, arm.X0, arm.UnscaledImmediate, -8, 2, 0x781F8001},
		{arm.X0, arm.X1, arm.UnscaledImmediate, -8, 4, 0xB81F8020},
		{arm.X1, arm.X0, arm.UnscaledImmediate, -8, 4, 0xB81F8001},
		{arm.X0, arm.X1, arm.UnscaledImmediate, -8, 8, 0xF81F8020},
		{arm.X1, arm.X0, arm.UnscaledImmediate, -8, 8, 0xF81F8001},

		{arm.X0, arm.SP, arm.PreIndex, -16, 8, 0xF81F0FE0},
		{arm.X1, arm.SP, arm.PreIndex, -16, 8, 0xF81F0FE1},
		{arm.X2, arm.SP, arm.PreIndex, -16, 8, 0xF81F0FE2},
	}

	for _, pattern := range usagePatterns {
		t.Logf("stur %s, [%s, #%d] %db", pattern.Source, pattern.Base, pattern.Offset, pattern.Length)
		code := arm.StoreFixedOffsetRegister(pattern.Source, pattern.Base, pattern.Mode, pattern.Offset, pattern.Length)
		assert.Equal(t, code, pattern.Code)
	}
}

func TestStoreFixedOffsetRegisterScaled(t *testing.T) {
	usagePatterns := []struct {
		Source cpu.Register
		Base   cpu.Register
		Mode   arm.AddressMode
		Offset uint
		Length byte
		Code   uint32
	}{
		{arm.X1, arm.X0, arm.UnscaledImmediate, 0, 8, 0xF9000001},
		{arm.X1, arm.X0, arm.UnscaledImmediate, 1, 8, 0xF9000401},
		{arm.X1, arm.X0, arm.UnscaledImmediate, 4095, 8, 0xF93FFC01},
	}

	for _, pattern := range usagePatterns {
		t.Logf("str %s, [%s, #%d] %db", pattern.Source, pattern.Base, pattern.Offset*uint(pattern.Length), pattern.Length)
		code := arm.StoreFixedOffsetRegisterScaled(pattern.Source, pattern.Base, pattern.Mode, pattern.Offset, pattern.Length)
		assert.Equal(t, code, pattern.Code)
	}
}