dolphin/Source/UnitTests/Core/DSP/DSPAssemblyTest.cpp
Pokechu22 6a2ec825a2 UnitTests: Fail DSPAssemblyTest if the assembled code doesn't match the expected result
This reveals that both HermesText and HermesBinary fail.  HermesBinary would have failed on master, too, if this had been implemented.
2022-06-14 12:22:06 -07:00

160 lines
3.9 KiB
C++

// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#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 RoundTrippableDisassemble(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 (!RoundTrippableDisassemble(code1, text))
{
fmt::print("RoundTrip: Disassembly failed.\n");
return false;
}
if (!DSP::Assemble(text, code2))
{
fmt::print("RoundTrip: Assembly failed.\n");
return false;
}
if (!DSP::Compare(code1, code2))
{
fmt::print("RoundTrip: Assembled code does not match input code\n");
return false;
}
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))
{
fmt::print("SuperTrip: First assembly failed\n");
return false;
}
fmt::print("First assembly: {} words\n", code1.size());
if (!RoundTrippableDisassemble(code1, text))
{
fmt::print("SuperTrip: Disassembly failed\n");
return false;
}
else
{
fmt::print("Disassembly:\n");
fmt::print("{}", text);
}
if (!DSP::Assemble(text, code2))
{
fmt::print("SuperTrip: Second assembly failed\n");
return false;
}
if (!DSP::Compare(code1, code2))
{
fmt::print("SuperTrip: Assembled code does not match between passes\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));
}