Skip to content

Commit

Permalink
m68000: add instruction test harness (#1767)
Browse files Browse the repository at this point in the history
Add test harness for single step instruction tests located here:
https://github.com/SingleStepTests/m68000.git

See included scripts for cloning and running the tests.
  • Loading branch information
invertego authored Jan 15, 2025
1 parent 324a192 commit 2197df8
Show file tree
Hide file tree
Showing 7 changed files with 519 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
!/nall
!/ruby
!/scripts
!/tests/i8080
!/tests
!/thirdparty
!/tools
!/GNUMakefile
Expand All @@ -38,3 +38,4 @@ hiro/resource/resource.cpp
hiro/resource/resource.hpp
desktop-ui/resource/resource.cpp
desktop-ui/resource/resource.hpp
tests/m68000/tests
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ option(ARES_BUILD_OPTIONAL_TARGETS "Include supplemental tools and tests" OFF)

if(ARES_BUILD_OPTIONAL_TARGETS)
add_subdirectory(tests/i8080)
add_subdirectory(tests/m68000)
if(NOT OS_WINDOWS AND NOT OS_MACOS)
add_subdirectory(tools/genius)
else()
Expand All @@ -41,6 +42,7 @@ if(ARES_BUILD_OPTIONAL_TARGETS)
add_subdirectory(tools/mame2bml)
else()
target_disable_subproject(i8080 "i8080 processor test harness")
target_disable_subproject(m68000 "m68000 processor test harness")
target_disable_subproject(mame2bml "mame2bml (MAME manifest converter)")
target_disable_subproject(genius "genius (database editor)")
endif()
Expand Down
203 changes: 203 additions & 0 deletions nall/string/markup/json.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#pragma once

namespace nall::JSON {

struct ManagedNode;
using SharedNode = shared_pointer<ManagedNode>;

struct ManagedNode : Markup::ManagedNode {
protected:
auto isDigit(char c) const -> bool {
return c - '0' < 10u;
}

auto isHex(char c) const -> bool {
return c - '0' < 10u || c - 'A' < 6u || c - 'a' < 6u;
}

auto isWhitespace(char c) const -> bool {
if(c == ' ' || c == '\t') return true;
if(c == '\r' || c == '\n') return true;
return false;
}

auto skipWhitespace(const char*& p) const -> void {
while(isWhitespace(*p)) p++;
}

auto parseMember(const char*& p) -> void {
skipWhitespace(p);
parseString(_name, p);
skipWhitespace(p);
if(*p++ != ':') throw "Expected ':'";
parseElement(p);
}

auto parseObject(const char*& p) -> void {
if(*p++ != '{') throw "Expected '{'";
skipWhitespace(p);
if(*p == '}') {
p++;
return;
}
while(true) {
SharedNode node(new ManagedNode);
node->parseMember(p);
_children.append(node);
if(*p != ',') break;
p++;
}
if(*p++ != '}') throw "Expected '}'";
}

auto parseArray(const char*& p) -> void {
if(*p++ != '[') throw "Expected '['";
skipWhitespace(p);
if(*p == ']') {
p++;
return;
}
for(u32 index = 0;; index++) {
SharedNode node(new ManagedNode);
node->_name = index;
node->parseElement(p);
_children.append(node);
if(*p != ',') break;
p++;
}
if(*p++ != ']') throw "Expected ']'";
}

auto utf8Encode(vector<char>& output, u32 c) -> void {
if(c <= 0x00007f) {
output.append(c);
return;
}
if(c <= 0x0007ff) {
output.append(0b110'00000 | c >> 6 & 0b000'11111);
output.append(0b10'000000 | c >> 0 & 0b00'111111);
return;
}
if(c <= 0x00ffff) {
output.append(0b1110'0000 | c >> 12 & 0b0000'1111);
output.append(0b10'000000 | c >> 6 & 0b00'111111);
output.append(0b10'000000 | c >> 0 & 0b00'111111);
return;
}
if(c <= 0x10ffff) {
output.append(0b11110'000 | c >> 18 & 0b00000'111);
output.append(0b10'000000 | c >> 12 & 0b00'111111);
output.append(0b10'000000 | c >> 6 & 0b00'111111);
output.append(0b10'000000 | c >> 0 & 0b00'111111);
return;
}
throw "Illegal code point";
}

auto parseString(string& target, const char*& p) -> void {
vector<char> output;

if(*p++ != '"') throw "Expected opening '\"'";
while(*p && u8(*p) >= ' ' && *p != '"') {
if(*p == '\\') {
p++;
switch(*p) {
case '"':
case '\\':
case '/': output.append(*p++); break;
case 'b': output.append('\b'); p++; break;
case 'f': output.append('\f'); p++; break;
case 'n': output.append('\n'); p++; break;
case 'r': output.append('\r'); p++; break;
case 't': output.append('\t'); p++; break;
case 'u': {
p++;
char codepoint[5];
for(auto n : range(4)) {
if(!isHex(*p)) throw "Expected hex digit";
codepoint[n] = *p++;
}
codepoint[4] = 0;
//not implemented: combining surrogate pairs
utf8Encode(output, toHex(codepoint));
} break;
default: throw "Expected escape sequence";
}
} else {
output.append(*p++);
}
}
if(*p++ != '\"') throw "Expected closing '\"'";

target.resize(output.size());
memory::copy(target.get(), output.data(), output.size());
}

auto parseNumber(const char*& p) -> void {
const char* b = p;

//integer
if(*p == '-') p++;
if(!isDigit(*p)) throw "Expected digit";
if(*p++ != '0') {
while(isDigit(*p)) p++;
}

//fraction
if(*p == '.') {
p++;
if(!isDigit(*p++)) throw "Expected digit";
while(isDigit(*p)) p++;
}

//exponent
if(*p == 'E' || *p == 'e') {
p++;
if(*p == '+' || *p == '-') p++;
if(!isDigit(*p++)) throw "Expected digit";
while(isDigit(*p)) p++;
}

u32 length = p - b;
_value.resize(length);
memory::copy(_value.get(), b, length);
}

auto parseLiteral(const char*& p) -> void {
if(!memory::compare(p, "null", 4)) { _value = "null"; p += 4; return; }
if(!memory::compare(p, "true", 4)) { _value = "true"; p += 4; return; }
if(!memory::compare(p, "false", 5)) { _value = "false"; p += 5; return; }
throw "Expected literal";
}

auto parseValue(const char*& p) -> void {
if(*p == '{') return parseObject(p);
if(*p == '[') return parseArray(p);
if(*p == '"') return parseString(_value, p);
if(*p == '-' || isDigit(*p)) return parseNumber(p);
if(*p == 't' || *p == 'f' || *p == 'n') return parseLiteral(p);
throw "Unexpected character";
}

auto parseElement(const char*& p) -> void {
skipWhitespace(p);
parseValue(p);
skipWhitespace(p);
}

friend auto unserialize(const string&) -> Markup::Node;
};

inline auto unserialize(const string& markup) -> Markup::Node {
SharedNode node(new ManagedNode);
try {
const char* p = markup;
node->parseElement(p);
if(*p) throw "Unexpected trailing data";
} catch(const char* error) {
node.reset();
}
return Markup::SharedNode(node);
}

}
18 changes: 18 additions & 0 deletions tests/m68000/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
add_executable(m68000 m68000.cpp ../../ares/component/processor/m68000/m68000.cpp)

target_include_directories(m68000 PRIVATE ${CMAKE_SOURCE_DIR})

target_link_libraries(m68000 PRIVATE ares::ares)

if(OS_WINDOWS)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_link_libraries(
m68000
PRIVATE nall
)
endif()
endif()

set_target_properties(m68000 PROPERTIES FOLDER tests PREFIX "")
target_enable_subproject(m68000 "m68000 processor test harness")
ares_configure_executable(m68000)
8 changes: 8 additions & 0 deletions tests/m68000/clone-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env sh
set -euo pipefail

cd "$(dirname "$0")"
git clone --depth 1 https://github.com/SingleStepTests/m68000.git tests

cd tests
python3 decode.py
Loading

0 comments on commit 2197df8

Please sign in to comment.