1 // Copyright 2017 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Common/FileUtil.h"
6 #include "Core/DSP/DSPCodeUtil.h"
7 #include "Core/DSP/DSPDisassembler.h"
8 
9 #include "DSPTestBinary.h"
10 #include "DSPTestText.h"
11 #include "HermesBinary.h"
12 
13 #include <gtest/gtest.h>
14 
RoundTrippableDissassemble(const std::vector<u16> & code,std::string & text)15 static bool RoundTrippableDissassemble(const std::vector<u16>& code, std::string& text)
16 {
17   DSP::AssemblerSettings settings;
18   settings.ext_separator = '\'';
19   settings.decode_names = true;
20   settings.decode_registers = true;
21   // These two prevent roundtripping.
22   settings.show_hex = false;
23   settings.show_pc = false;
24   DSP::DSPDisassembler disasm(settings);
25 
26   return disasm.Disassemble(code, text);
27 }
28 
29 // This test goes from text ASM to binary to text ASM and once again back to binary.
30 // Then the two binaries are compared.
RoundTrip(const std::vector<u16> & code1)31 static bool RoundTrip(const std::vector<u16>& code1)
32 {
33   std::vector<u16> code2;
34   std::string text;
35   if (!RoundTrippableDissassemble(code1, text))
36   {
37     printf("RoundTrip: Disassembly failed.\n");
38     return false;
39   }
40   if (!DSP::Assemble(text, code2))
41   {
42     printf("RoundTrip: Assembly failed.\n");
43     return false;
44   }
45   if (!DSP::Compare(code1, code2))
46   {
47     DSP::Disassemble(code1, true, text);
48     printf("%s", text.c_str());
49   }
50   return true;
51 }
52 
53 // This test goes from text ASM to binary to text ASM and once again back to binary.
54 // Very convenient for testing. Then the two binaries are compared.
SuperTrip(const char * asm_code)55 static bool SuperTrip(const char* asm_code)
56 {
57   std::vector<u16> code1, code2;
58   std::string text;
59   if (!DSP::Assemble(asm_code, code1))
60   {
61     printf("SuperTrip: First assembly failed\n");
62     return false;
63   }
64   printf("First assembly: %i words\n", (int)code1.size());
65 
66   if (!RoundTrippableDissassemble(code1, text))
67   {
68     printf("SuperTrip: Disassembly failed\n");
69     return false;
70   }
71   else
72   {
73     printf("Disassembly:\n");
74     printf("%s", text.c_str());
75   }
76 
77   if (!DSP::Assemble(text, code2))
78   {
79     printf("SuperTrip: Second assembly failed\n");
80     return false;
81   }
82   return true;
83 }
84 
85 // Let's start out easy - a trivial instruction..
TEST(DSPAssembly,TrivialInstruction)86 TEST(DSPAssembly, TrivialInstruction)
87 {
88   ASSERT_TRUE(SuperTrip("	NOP\n"));
89 }
90 
91 // Now let's do several.
TEST(DSPAssembly,SeveralTrivialInstructions)92 TEST(DSPAssembly, SeveralTrivialInstructions)
93 {
94   ASSERT_TRUE(SuperTrip("	NOP\n"
95                         "	NOP\n"
96                         "	NOP\n"));
97 }
98 
99 // Turning it up a notch.
TEST(DSPAssembly,SeveralNoParameterInstructions)100 TEST(DSPAssembly, SeveralNoParameterInstructions)
101 {
102   ASSERT_TRUE(SuperTrip("	SET16\n"
103                         "	SET40\n"
104                         "	CLR15\n"
105                         "	M0\n"
106                         "	M2\n"));
107 }
108 
109 // Time to try labels and parameters, and comments.
TEST(DSPAssembly,LabelsParametersAndComments)110 TEST(DSPAssembly, LabelsParametersAndComments)
111 {
112   ASSERT_TRUE(SuperTrip("DIRQ_TEST:	equ	0xfffb	; DSP Irq Request\n"
113                         "	si		@0xfffc, #0x8888\n"
114                         "	si		@0xfffd, #0xbeef\n"
115                         "	si		@DIRQ_TEST, #0x0001\n"));
116 }
117 
118 // Let's see if registers roundtrip. Also try predefined labels.
TEST(DSPAssembly,RegistersAndPredefinedLabels)119 TEST(DSPAssembly, RegistersAndPredefinedLabels)
120 {
121   ASSERT_TRUE(SuperTrip("	si		@0xfffc, #0x8888\n"
122                         "	si		@0xfffd, #0xbeef\n"
123                         "	si		@DIRQ, #0x0001\n"));
124 }
125 
126 // Let's try some messy extended instructions.
TEST(DSPAssembly,ExtendedInstructions)127 TEST(DSPAssembly, ExtendedInstructions)
128 {
129   ASSERT_TRUE(SuperTrip("   MULMV'SN    $AX0.L, $AX0.H, $ACC0 : @$AR2, $AC1.M\n"
130                         "   ADDAXL'MV   $ACC1, $AX1.L : $AX1.H, $AC1.M\n"));
131 }
132 
TEST(DSPAssembly,HermesBinary)133 TEST(DSPAssembly, HermesBinary)
134 {
135   ASSERT_TRUE(RoundTrip(s_hermes_bin));
136 }
137 
TEST(DSPAssembly,DSPTestText)138 TEST(DSPAssembly, DSPTestText)
139 {
140   ASSERT_TRUE(SuperTrip(s_dsp_test_text));
141 }
142 
TEST(DSPAssembly,DSPTestBinary)143 TEST(DSPAssembly, DSPTestBinary)
144 {
145   ASSERT_TRUE(RoundTrip(s_dsp_test_bin));
146 }
147 
148 /*
149 
150 if (File::ReadFileToString("C:/devkitPro/examples/wii/asndlib/dsptest/dsp_test.ds", &dsp_test))
151   SuperTrip(dsp_test.c_str());
152 
153 //.File::ReadFileToString("C:/devkitPro/trunk/libogc/libasnd/dsp_mixer/dsp_mixer.s", &dsp_test);
154 // This is CLOSE to working. Sorry about the local path btw. This is preliminary code.
155 */
156