openMSX
SymbolManager_test.cc
Go to the documentation of this file.
1#include "catch.hpp"
2#include "SymbolManager.hh"
3
4using namespace openmsx;
5
6TEST_CASE("SymbolManager: isHexDigit")
7{
17 CHECK(SymbolManager::isHexDigit('-') == std::nullopt);
18 CHECK(SymbolManager::isHexDigit('@') == std::nullopt);
19 CHECK(SymbolManager::isHexDigit('G') == std::nullopt);
20 CHECK(SymbolManager::isHexDigit('g') == std::nullopt);
21}
22
23TEST_CASE("SymbolManager: is4DigitHex")
24{
25 CHECK(SymbolManager::is4DigitHex("0000") == 0x0000);
26 CHECK(SymbolManager::is4DigitHex("12ab") == 0x12ab);
27 CHECK(SymbolManager::is4DigitHex("F000") == 0xf000);
28 CHECK(SymbolManager::is4DigitHex("fFfF") == 0xffff);
29 CHECK(SymbolManager::is4DigitHex("") == std::nullopt);
30 CHECK(SymbolManager::is4DigitHex("123") == std::nullopt);
31 CHECK(SymbolManager::is4DigitHex("12345") == std::nullopt);
32 CHECK(SymbolManager::is4DigitHex("abg1") == std::nullopt);
33}
34
35TEST_CASE("SymbolManager: parseValue")
36{
37 SECTION("ok") {
38 // hex
39 CHECK(SymbolManager::parseValue<uint16_t>("0xa1") == 0xa1);
40 CHECK(SymbolManager::parseValue<uint32_t>("0xa1") == 0xa1);
41 CHECK(SymbolManager::parseValue<uint16_t>("0x1234") == 0x1234);
42 CHECK(SymbolManager::parseValue<uint32_t>("0x1234") == 0x1234);
43 CHECK(SymbolManager::parseValue<uint16_t>("0XABCD") == 0xabcd);
44 CHECK(SymbolManager::parseValue<uint32_t>("0XABCD") == 0xabcd);
45 CHECK(SymbolManager::parseValue<uint16_t>("0x00002345") == 0x2345);
46 CHECK(SymbolManager::parseValue<uint32_t>("0x00002345") == 0x2345);
47 CHECK(SymbolManager::parseValue<uint16_t>("$fedc") == 0xfedc);
48 CHECK(SymbolManager::parseValue<uint32_t>("$fedc") == 0xfedc);
49 CHECK(SymbolManager::parseValue<uint16_t>("#11aA") == 0x11aa);
50 CHECK(SymbolManager::parseValue<uint32_t>("#11aA") == 0x11aa);
51 CHECK(SymbolManager::parseValue<uint16_t>("bbffh") == 0xbbff);
52 CHECK(SymbolManager::parseValue<uint32_t>("bbffh") == 0xbbff);
53 CHECK(SymbolManager::parseValue<uint16_t>("3210H") == 0x3210);
54 CHECK(SymbolManager::parseValue<uint32_t>("3210H") == 0x3210);
55 CHECK(SymbolManager::parseValue<uint16_t>("01234h") == 0x1234);
56 CHECK(SymbolManager::parseValue<uint32_t>("01234h") == 0x1234);
57 // dec
58 CHECK(SymbolManager::parseValue<uint16_t>("123") == 0x007b);
59 CHECK(SymbolManager::parseValue<uint32_t>("123") == 0x007b);
60 CHECK(SymbolManager::parseValue<uint16_t>("65535") == 0xffff);
61 CHECK(SymbolManager::parseValue<uint32_t>("65535") == 0xffff);
62 CHECK(SymbolManager::parseValue<uint16_t>("0020") == 0x0014); // NOT interpreted as octal
63 CHECK(SymbolManager::parseValue<uint32_t>("0020") == 0x0014); // NOT interpreted as octal
64 // bin
65 CHECK(SymbolManager::parseValue<uint16_t>("%1100") == 0x000c);
66 CHECK(SymbolManager::parseValue<uint32_t>("%1100") == 0x000c);
67 CHECK(SymbolManager::parseValue<uint16_t>("0b000100100011") == 0x0123);
68 CHECK(SymbolManager::parseValue<uint32_t>("0b000100100011") == 0x0123);
69 CHECK(SymbolManager::parseValue<uint16_t>("0B1111000001011000") == 0xf058);
70 CHECK(SymbolManager::parseValue<uint32_t>("0B1111000001011000") == 0xf058);
71 }
72 SECTION("error") {
73 // wrong format
74 CHECK(SymbolManager::parseValue<uint16_t>("0xFEDX") == std::nullopt);
75 CHECK(SymbolManager::parseValue<uint32_t>("0xFEDGFF") == std::nullopt);
76 CHECK(SymbolManager::parseValue<uint16_t>("1234a") == std::nullopt);
77 CHECK(SymbolManager::parseValue<uint32_t>("1234567a") == std::nullopt);
78 CHECK(SymbolManager::parseValue<uint16_t>("-3") == std::nullopt);
79 CHECK(SymbolManager::parseValue<uint32_t>("-100000") == std::nullopt);
80 CHECK(SymbolManager::parseValue<uint16_t>("0b00112110") == std::nullopt);
81 CHECK(SymbolManager::parseValue<uint32_t>("0b00110113") == std::nullopt);
82 // overflow
83 CHECK(SymbolManager::parseValue<uint16_t>("0x10000") == std::nullopt);
84 CHECK(SymbolManager::parseValue<uint16_t>("65536") == std::nullopt);
85 CHECK(SymbolManager::parseValue<uint16_t>("%11110000111100001") == std::nullopt);
86 CHECK(SymbolManager::parseValue<uint32_t>("0x100000000") == std::nullopt);
87 CHECK(SymbolManager::parseValue<uint32_t>("4294967296") == std::nullopt);
88 CHECK(SymbolManager::parseValue<uint32_t>("%111100001111000011110000111100001") == std::nullopt);
89 }
90}
91
92TEST_CASE("SymbolManager: checkLabel")
93{
94 CHECK(SymbolManager::checkLabel("foo", 123) == Symbol("foo", 123, {}, {}));
95 CHECK(SymbolManager::checkLabel("bar:", 234) == Symbol("bar", 234, {}, {}));
96 CHECK(SymbolManager::checkLabel("", 345) == std::nullopt);
97 CHECK(SymbolManager::checkLabel(":", 456) == std::nullopt);
98}
99
100TEST_CASE("SymbolManager: checkLabelAndValue")
101{
102 CHECK(SymbolManager::checkLabelAndValue("foo", "123") == Symbol("foo", 123, {}, {}));
103 CHECK(SymbolManager::checkLabelAndValue("", "123") == std::nullopt);
104 CHECK(SymbolManager::checkLabelAndValue("foo", "bla") == std::nullopt);
105}
106
107TEST_CASE("SymbolManager: checkLabelSegmentAndValue")
108{
109 CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "123") == Symbol("foo", 123, {}, {}));
110 CHECK(SymbolManager::checkLabelSegmentAndValue("", "123") == std::nullopt);
111 CHECK(SymbolManager::checkLabelSegmentAndValue("", "123") == std::nullopt);
112 CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "0x123456") == Symbol("foo", 0x3456, {}, 0x12));
113 CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "2311527") == Symbol("foo", 0x4567, {}, 0x23));
114 CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "0x123456") == Symbol("foo", 0x3456, {}, 0x12));
115}
116
117TEST_CASE("SymbolManager: detectType")
118{
119 SECTION("on extension") {
120 std::string_view buffer = "content doesn't matter";
121 CHECK(SymbolManager::detectType("symbols.noi", buffer) == SymbolFile::Type::NOICE);
122 CHECK(SymbolManager::detectType("symbols.NOI", buffer) == SymbolFile::Type::NOICE);
123 CHECK(SymbolManager::detectType("symbols.symbol", buffer) == SymbolFile::Type::GENERIC); // pasmo
124 CHECK(SymbolManager::detectType("symbols.publics", buffer) == SymbolFile::Type::GENERIC); // pasmo
125 CHECK(SymbolManager::detectType("symbols.sys", buffer) == SymbolFile::Type::GENERIC); // pasmo
126 CHECK(SymbolManager::detectType("symbols.unknown", buffer) == SymbolFile::Type::GENERIC); // unknown extension
127 }
128 SECTION(".map extension") {
129 CHECK(SymbolManager::detectType("symbols.Map", "HI-TECH Software ZC Compiler V7.80PL2") == SymbolFile::Type::LINKMAP);
130 CHECK(SymbolManager::detectType("symbols.Map", "CHGMOD = $005F ; const, local, , sample025, , sample025.asm:2") == SymbolFile::Type::GENERIC);
131 }
132 SECTION(".sym extension") {
133 CHECK(SymbolManager::detectType("myfile.sym", "; Symbol table from myfile.asm") == SymbolFile::Type::ASMSX);
134 CHECK(SymbolManager::detectType("myfile.sym", "bla: %equ 123") == SymbolFile::Type::GENERIC);
135 CHECK(SymbolManager::detectType("myfile.sym", "bla: equ 123") == SymbolFile::Type::GENERIC);
136 CHECK(SymbolManager::detectType("myfile.sym", "bla equ #123") == SymbolFile::Type::GENERIC);
137 CHECK(SymbolManager::detectType("myfile.sym", "Sections:") == SymbolFile::Type::VASM);
138 CHECK(SymbolManager::detectType("myfile.sym", "; this file was created with wlalink") == SymbolFile::Type::WLALINK_NOGMB);
139 CHECK(SymbolManager::detectType("myfile.sym", "anything else") == SymbolFile::Type::HTC);
140 }
141}
142
143TEST_CASE("SymbolManager: loadLines")
144{
145 auto dummyParser = [](std::span<std::string_view> tokens) -> std::optional<Symbol> {
146 if (tokens.size() == 1) return Symbol{std::string(tokens[0]), 123, {}, {}};
147 return {};
148 };
149
150 SECTION("empty file") {
151 std::string_view buffer;
152 auto file = SymbolManager::loadLines("file.sym", buffer, SymbolFile::Type::GENERIC, dummyParser);
153 CHECK(file.filename == "file.sym");
154 CHECK(file.type == SymbolFile::Type::GENERIC);
155 CHECK(file.symbols.empty());
156 }
157 SECTION("comment + symbol") {
158 std::string_view buffer =
159 "; This is a comment\n"
160 "bla\n" // ok for dummy parser
161 "foo bla\n" // ignored because 2 tokens
162 "bar ; comment on same line\n"; // only 1 token after removing comment
163 auto file = SymbolManager::loadLines("file2.sym", buffer, SymbolFile::Type::NOICE, dummyParser);
164 CHECK(file.filename == "file2.sym");
165 CHECK(file.type == SymbolFile::Type::NOICE);
166 REQUIRE(file.symbols.size() == 2);
167 CHECK(file.symbols[0].name == "bla");
168 CHECK(file.symbols[0].value == 123);
169 CHECK(file.symbols[1].name == "bar");
170 CHECK(file.symbols[1].value == 123);
171 }
172}
173
174TEST_CASE("SymbolManager: loadGeneric")
175{
176 std::string_view buffer =
177 "foo: equ 1\n"
178 "bar %equ 2\n"
179 "qux = 3 ; comment\n"
180 "; only comment\n"
181 "error equ\n"
182 "error equ 123 extra stuff\n";
183 auto file = SymbolManager::loadGeneric("myfile.sym", buffer);
184 CHECK(file.filename == "myfile.sym");
185 CHECK(file.type == SymbolFile::Type::GENERIC);
186 REQUIRE(file.symbols.size() == 3);
187 CHECK(file.symbols[0].name == "foo");
188 CHECK(file.symbols[0].value == 1);
189 CHECK(file.symbols[1].name == "bar");
190 CHECK(file.symbols[1].value == 2);
191 CHECK(file.symbols[2].name == "qux");
192 CHECK(file.symbols[2].value == 3);
193}
194
195TEST_CASE("SymbolManager: loadNoICE without segments")
196{
197 std::string_view buffer =
198 "def foo 1\n"
199 "def bar 234h ; comment\n"
200 "error def 123\n"
201 "def error 99 extra stuff\n";
202 auto file = SymbolManager::loadNoICE("noice.sym", buffer);
203 CHECK(file.filename == "noice.sym");
204 CHECK(file.type == SymbolFile::Type::NOICE);
205 REQUIRE(file.symbols.size() == 2);
206 CHECK(file.symbols[0].name == "foo");
207 CHECK(file.symbols[0].value == 1);
208 CHECK(file.symbols[0].segment == std::nullopt);
209 CHECK(file.symbols[1].name == "bar");
210 CHECK(file.symbols[1].value == 0x234);
211 CHECK(file.symbols[1].segment == std::nullopt);
212}
213
214TEST_CASE("SymbolManager: loadNoICE with segments")
215{
216 std::string_view buffer =
217 "def foo 1\n"
218 "def bar 234h ; comment\n"
219 "error def 123\n"
220 "def error 99 extra stuff\n"
221 "def baz 123456h ; comment\n";
222 auto file = SymbolManager::loadNoICE("noice.sym", buffer);
223 CHECK(file.filename == "noice.sym");
224 CHECK(file.type == SymbolFile::Type::NOICE);
225 REQUIRE(file.symbols.size() == 3);
226 CHECK(file.symbols[0].name == "foo");
227 CHECK(file.symbols[0].value == 1);
228 CHECK(file.symbols[0].segment == 0);
229 CHECK(file.symbols[1].name == "bar");
230 CHECK(file.symbols[1].value == 0x234);
231 CHECK(file.symbols[1].segment == 0);
232 CHECK(file.symbols[2].name == "baz");
233 CHECK(file.symbols[2].value == 0x3456);
234 CHECK(file.symbols[2].segment == 0x12);
235}
236
237TEST_CASE("SymbolManager: loadHTC")
238{
239 // TODO verify with an actual HTC file
240 std::string_view buffer =
241 "foo 1234 bla\n"
242 "error 1234\n";
243 auto file = SymbolManager::loadHTC("htc.sym", buffer);
244 CHECK(file.filename == "htc.sym");
245 CHECK(file.type == SymbolFile::Type::HTC);
246 REQUIRE(file.symbols.size() == 1);
247 CHECK(file.symbols[0].name == "foo");
248 CHECK(file.symbols[0].value == 0x1234);
249}
250
251TEST_CASE("SymbolManager: loadVASM")
252{
253 std::string_view buffer =
254 "12AB ignore\n"
255 "Symbols by value:\n"
256 "12AB label\n"
257 "bla foo\n";
258 auto file = SymbolManager::loadVASM("vasm.sym", buffer);
259 CHECK(file.filename == "vasm.sym");
260 CHECK(file.type == SymbolFile::Type::VASM);
261 REQUIRE(file.symbols.size() == 1);
262 CHECK(file.symbols[0].name == "label");
263 CHECK(file.symbols[0].value == 0x12ab);
264}
265
266TEST_CASE("SymbolManager: loadASMSX")
267{
268 std::string_view buffer =
269 "1234h ignore1\n"
270 "12h:3456h ignore2\n"
271 "; global and local\n"
272 "1234h l1\n"
273 "12h:abcdh l2\n";
274 auto file = SymbolManager::loadASMSX("asmsx.sym", buffer);
275 CHECK(file.filename == "asmsx.sym");
276 CHECK(file.type == SymbolFile::Type::ASMSX);
277 REQUIRE(file.symbols.size() == 2);
278 CHECK(file.symbols[0].name == "l1");
279 CHECK(file.symbols[0].value == 0x1234);
280 CHECK(file.symbols[1].name == "l2");
281 CHECK(file.symbols[1].value == 0xabcd);
282}
283
284TEST_CASE("SymbolManager: loadNoGmb")
285{
286 std::string_view buffer =
287 "; this file was created with wlalink\n"
288 "; no$gmb symbolic information for \"test.rom\".\n"
289 "00:4010 main\n"
290 "00:402d main@main_loop\n"
291 "00:404f Game_Initialize\n"
292 "00:404e Game_Update\n";
293 auto file = SymbolManager::loadNoGmb("myfile.sym", buffer);
294 CHECK(file.filename == "myfile.sym");
295 CHECK(file.type == SymbolFile::Type::WLALINK_NOGMB);
296 REQUIRE(file.symbols.size() == 4);
297 CHECK(file.symbols[0].name == "main");
298 CHECK(file.symbols[0].value == 0x4010);
299 CHECK(file.symbols[1].name == "main@main_loop");
300 CHECK(file.symbols[1].value == 0x402d);
301 CHECK(file.symbols[2].name == "Game_Initialize");
302 CHECK(file.symbols[2].value == 0x404f);
303 CHECK(file.symbols[3].name == "Game_Update");
304 CHECK(file.symbols[3].value == 0x404e);
305}
306
307TEST_CASE("SymbolManager: loadLinkMap")
308{
309 std::string_view buffer =
310 "ignore1 text 2CE7 ignore2 0AEE\n"
311 "ignore3 2E58 ignore3 text 2E4C\n"
312 " Symbol Table\n"
313 "asllmod text 2CE7 asllsub 0AEE\n"
314 "cret 2E58 csv text 2E4C\n"
315 "single text 9876\n"
316 "last 8765\n";
317 auto file = SymbolManager::loadLinkMap("link.map", buffer);
318 CHECK(file.filename == "link.map");
319 CHECK(file.type == SymbolFile::Type::LINKMAP);
320 REQUIRE(file.symbols.size() == 6);
321 CHECK(file.symbols[0].name == "asllmod");
322 CHECK(file.symbols[0].value == 0x2ce7);
323 CHECK(file.symbols[1].name == "asllsub");
324 CHECK(file.symbols[1].value == 0x0aee);
325 CHECK(file.symbols[2].name == "cret");
326 CHECK(file.symbols[2].value == 0x2e58);
327 CHECK(file.symbols[3].name == "csv");
328 CHECK(file.symbols[3].value == 0x2e4c);
329 CHECK(file.symbols[4].name == "single");
330 CHECK(file.symbols[4].value == 0x9876);
331 CHECK(file.symbols[5].name == "last");
332 CHECK(file.symbols[5].value == 0x8765);
333}
TEST_CASE("SymbolManager: isHexDigit")
static std::optional< unsigned > isHexDigit(char c)
static SymbolFile loadLines(std::string_view filename, std::string_view buffer, SymbolFile::Type type, function_ref< std::optional< Symbol >(std::span< std::string_view >)> lineParser)
static std::optional< Symbol > checkLabelSegmentAndValue(std::string_view label, std::string_view value)
static SymbolFile loadNoGmb(std::string_view filename, std::string_view buffer)
static SymbolFile::Type detectType(std::string_view filename, std::string_view buffer)
static SymbolFile loadNoICE(std::string_view filename, std::string_view buffer)
static SymbolFile loadHTC(std::string_view filename, std::string_view buffer)
static SymbolFile loadGeneric(std::string_view filename, std::string_view buffer)
static SymbolFile loadVASM(std::string_view filename, std::string_view buffer)
static std::optional< uint16_t > is4DigitHex(std::string_view s)
static std::optional< Symbol > checkLabel(std::string_view label, uint32_t value)
static SymbolFile loadASMSX(std::string_view filename, std::string_view buffer)
static SymbolFile loadLinkMap(std::string_view filename, std::string_view buffer)
static std::optional< Symbol > checkLabelAndValue(std::string_view label, std::string_view value)
CHECK(m3==m3)
This file implemented 3 utility functions:
Definition Autofire.cc:11