dolphin/Source/UnitTests/Core/DSP/DSPAssemblyTest.cpp
Pokechu22 3cb0976367 UnitTests: Use hermes.s as part of an actual test
Before, the file just existed as the source code for HermesBinary.cpp, but we can test that things assemble correctly too (compare DSPTestBinary.cpp and DSPTestText.cpp).

A bit of jank is needed due to MSVC limitations (see https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?view=msvc-170).
2022-06-14 12:22:05 -07:00

152 lines
3.7 KiB
C++

// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/FileUtil.h"
#include "Core/DSP/DSPCodeUtil.h"
#include "Core/DSP/DSPDisassembler.h"
#include "DSPTestBinary.h"
#include "DSPTestText.h"
#include "HermesBinary.h"
#include "HermesText.h"
#include <gtest/gtest.h>
static bool RoundTrippableDissassemble(const std::vector<u16>& code, std::string& text)
{
DSP::AssemblerSettings settings;
settings.ext_separator = '\'';
settings.decode_names = true;
settings.decode_registers = true;
// These two prevent roundtripping.
settings.show_hex = false;
settings.show_pc = false;
DSP::DSPDisassembler disasm(settings);
return disasm.Disassemble(code, text);
}
// This test goes from text ASM to binary to text ASM and once again back to binary.
// Then the two binaries are compared.
static bool RoundTrip(const std::vector<u16>& code1)
{
std::vector<u16> code2;
std::string text;
if (!RoundTrippableDissassemble(code1, text))
{
printf("RoundTrip: Disassembly failed.\n");
return false;
}
if (!DSP::Assemble(text, code2))
{
printf("RoundTrip: Assembly failed.\n");
return false;
}
if (!DSP::Compare(code1, code2))
{
DSP::Disassemble(code1, true, text);
printf("%s", text.c_str());
}
return true;
}
// This test goes from text ASM to binary to text ASM and once again back to binary.
// Very convenient for testing. Then the two binaries are compared.
static bool SuperTrip(const char* asm_code)
{
std::vector<u16> code1, code2;
std::string text;
if (!DSP::Assemble(asm_code, code1))
{
printf("SuperTrip: First assembly failed\n");
return false;
}
printf("First assembly: %i words\n", (int)code1.size());
if (!RoundTrippableDissassemble(code1, text))
{
printf("SuperTrip: Disassembly failed\n");
return false;
}
else
{
printf("Disassembly:\n");
printf("%s", text.c_str());
}
if (!DSP::Assemble(text, code2))
{
printf("SuperTrip: Second assembly failed\n");
return false;
}
return true;
}
// Let's start out easy - a trivial instruction..
TEST(DSPAssembly, TrivialInstruction)
{
ASSERT_TRUE(SuperTrip(" NOP\n"));
}
// Now let's do several.
TEST(DSPAssembly, SeveralTrivialInstructions)
{
ASSERT_TRUE(SuperTrip(" NOP\n"
" NOP\n"
" NOP\n"));
}
// Turning it up a notch.
TEST(DSPAssembly, SeveralNoParameterInstructions)
{
ASSERT_TRUE(SuperTrip(" SET16\n"
" SET40\n"
" CLR15\n"
" M0\n"
" M2\n"));
}
// Time to try labels and parameters, and comments.
TEST(DSPAssembly, LabelsParametersAndComments)
{
ASSERT_TRUE(SuperTrip("DIRQ_TEST: equ 0xfffb ; DSP Irq Request\n"
" si @0xfffc, #0x8888\n"
" si @0xfffd, #0xbeef\n"
" si @DIRQ_TEST, #0x0001\n"));
}
// Let's see if registers roundtrip. Also try predefined labels.
TEST(DSPAssembly, RegistersAndPredefinedLabels)
{
ASSERT_TRUE(SuperTrip(" si @0xfffc, #0x8888\n"
" si @0xfffd, #0xbeef\n"
" si @DIRQ, #0x0001\n"));
}
// Let's try some messy extended instructions.
TEST(DSPAssembly, ExtendedInstructions)
{
ASSERT_TRUE(SuperTrip(" MULMV'SN $AX0.L, $AX0.H, $ACC0 : @$AR2, $AC1.M\n"
" ADDAXL'MV $ACC1, $AX1.L : $AX1.H, $AC1.M\n"));
}
TEST(DSPAssembly, HermesText)
{
ASSERT_TRUE(SuperTrip(s_hermes_text));
}
TEST(DSPAssembly, HermesBinary)
{
ASSERT_TRUE(RoundTrip(s_hermes_bin));
}
TEST(DSPAssembly, DSPTestText)
{
ASSERT_TRUE(SuperTrip(s_dsp_test_text));
}
TEST(DSPAssembly, DSPTestBinary)
{
ASSERT_TRUE(RoundTrip(s_dsp_test_bin));
}