| e8555c89 Andy McCall |
1 |
#include "catch.hpp" |
| 2 |
#include "SymbolManager.hh" |
| 3 |
|
| 4 |
using namespace openmsx; |
| 5 |
|
| 6 |
TEST_CASE("SymbolManager: isHexDigit") |
| 7 |
{ |
| 8 |
CHECK(SymbolManager::isHexDigit('0') == 0); |
| 9 |
CHECK(SymbolManager::isHexDigit('4') == 4); |
| 10 |
CHECK(SymbolManager::isHexDigit('9') == 9); |
| 11 |
CHECK(SymbolManager::isHexDigit('a') == 10); |
| 12 |
CHECK(SymbolManager::isHexDigit('e') == 14); |
| 13 |
CHECK(SymbolManager::isHexDigit('f') == 15); |
| 14 |
CHECK(SymbolManager::isHexDigit('A') == 10); |
| 15 |
CHECK(SymbolManager::isHexDigit('C') == 12); |
| 16 |
CHECK(SymbolManager::isHexDigit('F') == 15); |
| 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 |
|
| 23 |
TEST_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 |
|
| 35 |
TEST_CASE("SymbolManager: parseValue") |
| 36 |
{ |
| 37 |
SECTION("ok") { |
| 38 |
|
| 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 |
|
| 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); |
| 63 |
CHECK(SymbolManager::parseValue<uint32_t>("0020") == 0x0014); |
| 64 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 92 |
TEST_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 |
|
| 100 |
TEST_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 |
|
| 107 |
TEST_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 |
|
| 117 |
TEST_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); |
| 124 |
CHECK(SymbolManager::detectType("symbols.publics", buffer) == SymbolFile::Type::GENERIC); |
| 125 |
CHECK(SymbolManager::detectType("symbols.sys", buffer) == SymbolFile::Type::GENERIC); |
| 126 |
CHECK(SymbolManager::detectType("symbols.unknown", buffer) == SymbolFile::Type::GENERIC); |
| 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 |
|
| 143 |
TEST_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" |
| 161 |
"foo bla\n" |
| 162 |
"bar ; comment on same line\n"; |
| 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 |
|
| 174 |
TEST_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 |
|
| 195 |
TEST_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 |
|
| 214 |
TEST_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 |
|
| 237 |
TEST_CASE("SymbolManager: loadHTC") |
| 238 |
{ |
| 239 |
|
| 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 |
|
| 251 |
TEST_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 |
|
| 266 |
TEST_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 |
|
| 284 |
TEST_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 |
|
| 307 |
TEST_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 |
} |