diff --git a/.gitignore b/.gitignore index d93251f..bca203e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps +build/ # ---> C++ # Prerequisites diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..22cc8ff --- /dev/null +++ b/.gitmodules @@ -0,0 +1,10 @@ +[submodule "deps/check"] + path = deps/check + url = https://github.com/libcheck/check.git +[submodule "deps/SDL"] + path = deps/SDL + url = https://github.com/libsdl-org/SDL.git + branch = SDL2 +[submodule "deps/SDL_ttf"] + path = deps/SDL_ttf + url = https://github.com/libsdl-org/SDL_ttf.git diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6a9f67b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/build/gbemu/Debug/gbemu.exe", + "args": [ "${workspaceFolder}/roms/cpu_instrs.gb" ], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f95680a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +set(BUILD_SHARED_LIBS OFF) +add_subdirectory(deps/SDL) +add_subdirectory(deps/SDL_ttf) +add_subdirectory(lib/) +add_subdirectory(gbemu/) +add_subdirectory(deps/check/) +enable_testing() +add_subdirectory(tests/) +project(top) \ No newline at end of file diff --git a/cmake/FindCheck.cmake b/cmake/FindCheck.cmake new file mode 100644 index 0000000..47ee8f8 --- /dev/null +++ b/cmake/FindCheck.cmake @@ -0,0 +1,66 @@ +# - Try to find the CHECK libraries +# Once done this will define +# +# CHECK_FOUND - system has check +# CHECK_INCLUDE_DIR - the check include directory +# CHECK_LIBRARIES - check library +# +# This configuration file for finding libcheck is originally from +# the opensync project. The originally was downloaded from here: +# opensync.org/browser/branches/3rd-party-cmake-modules/modules/FindCheck.cmake +# +# Copyright (c) 2007 Daniel Gollub +# Copyright (c) 2007 Bjoern Ricks +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +# + +# Take care about check.pc settings +# + +if(WIN32) +SET (CHECK_FOUND 1) +SET (CHECK_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/deps/windows) +SET (CHECK_LIBRARIES ${PROJECT_SOURCE_DIR}/deps/windows/check.lib) +else() +INCLUDE( FindPkgConfig ) +PKG_SEARCH_MODULE( CHECK Check ) +endif() + +# Look for CHECK include dir and libraries +IF( NOT CHECK_FOUND ) + IF ( CHECK_INSTALL_DIR ) + MESSAGE ( STATUS "Using override CHECK_INSTALL_DIR to find Check" ) + SET ( CHECK_INCLUDE_DIR "${CHECK_INSTALL_DIR}/include" ) + SET ( CHECK_INCLUDE_DIRS "${CHECK_INCLUDE_DIR}" ) + FIND_LIBRARY( CHECK_LIBRARY NAMES check PATHS "${CHECK_INSTALL_DIR}/lib" ) + FIND_LIBRARY( COMPAT_LIBRARY NAMES compat PATHS "${CHECK_INSTALL_DIR}/lib" ) + SET ( CHECK_LIBRARIES "${CHECK_LIBRARY}" "${COMPAT_LIBRARY}" ) + ELSE ( CHECK_INSTALL_DIR ) + FIND_PATH( CHECK_INCLUDE_DIR check.h ) + FIND_LIBRARY( CHECK_LIBRARIES NAMES check ) + ENDIF ( CHECK_INSTALL_DIR ) + + IF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) + SET( CHECK_FOUND 1 ) + IF ( NOT Check_FIND_QUIETLY ) + MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" ) + ENDIF ( NOT Check_FIND_QUIETLY ) + ELSE ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) + IF ( Check_FIND_REQUIRED ) + MESSAGE( FATAL_ERROR "Could NOT find CHECK" ) + ELSE ( Check_FIND_REQUIRED ) + IF ( NOT Check_FIND_QUIETLY ) + MESSAGE( STATUS "Could NOT find CHECK" ) + ENDIF ( NOT Check_FIND_QUIETLY ) + ENDIF ( Check_FIND_REQUIRED ) + ENDIF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) +ENDIF( NOT CHECK_FOUND ) + +# Hide advanced variables from CMake GUIs +MARK_AS_ADVANCED( CHECK_INCLUDE_DIR CHECK_LIBRARIES ) + diff --git a/deps/SDL b/deps/SDL new file mode 160000 index 0000000..934d695 --- /dev/null +++ b/deps/SDL @@ -0,0 +1 @@ +Subproject commit 934d6954e21a816c1b28f8499e5b7d976cecd2b8 diff --git a/deps/SDL_ttf b/deps/SDL_ttf new file mode 160000 index 0000000..1dc1623 --- /dev/null +++ b/deps/SDL_ttf @@ -0,0 +1 @@ +Subproject commit 1dc162336f680c459aadef299b02c099a5533b34 diff --git a/deps/check b/deps/check new file mode 160000 index 0000000..455005d --- /dev/null +++ b/deps/check @@ -0,0 +1 @@ +Subproject commit 455005dc29dc6727de7ee36fee4b49a13b39f73f diff --git a/deps/windows/check.h b/deps/windows/check.h new file mode 100644 index 0000000..497f0ea --- /dev/null +++ b/deps/windows/check.h @@ -0,0 +1,2366 @@ +/*-*- mode:C; -*- */ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_H +#define CHECK_H + +#include +#include +#include +#include + +#include + +/* + Macros and functions starting with _ (underscore) are internal and + may change without notice. You have been warned!. +*/ + + +#ifdef __cplusplus +#define CK_CPPSTART extern "C" { +#define CK_CPPEND } +CK_CPPSTART +#endif + +/** + * __GNUC_PATCHLEVEL__ is new to GCC 3.0; + * it is also present in the widely-used development snapshots leading up to 3.0 + * (which identify themselves as GCC 2.96 or 2.97, depending on which snapshot you have). + * + * https://stackoverflow.com/questions/1936719/what-are-the-gcc-predefined-macros-for-the-compilers-version-number/1936745#1936745 + */ + +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) \ +((__GNUC__ > (major)) || \ + (__GNUC__ == (major) && __GNUC_MINOR__ > (minor)) || \ + (__GNUC__ == (major) && __GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patch)) ) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) \ +((__GNUC__ > (major)) || \ + (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) +#else +#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 +#endif + +#if GCC_VERSION_AT_LEAST(2,95,3) +#define CK_ATTRIBUTE_UNUSED __attribute__ ((unused)) +#define CK_ATTRIBUTE_FORMAT(a, b, c) __attribute__ ((format (a, b, c))) +#else +#define CK_ATTRIBUTE_UNUSED +#define CK_ATTRIBUTE_FORMAT(a, b, c) +#endif /* GCC 2.95 */ + +#if GCC_VERSION_AT_LEAST(2,5,0) +#define CK_ATTRIBUTE_NORETURN __attribute__ ((noreturn)) +#else +#define CK_ATTRIBUTE_NORETURN +#endif /* GCC 2.5 */ + +#if GCC_VERSION_AT_LEAST(4,7,4) && (__STDC_VERSION__ >= 199901L) +/* Operator _Pragma introduced in C99 */ +#define CK_DIAGNOSTIC_STRINGIFY(x) #x +#define CK_DIAGNOSTIC_HELPER1(y) CK_DIAGNOSTIC_STRINGIFY(GCC diagnostic ignored y) +#define CK_DIAGNOSTIC_HELPER2(z) CK_DIAGNOSTIC_HELPER1(#z) +#define CK_DIAGNOSTIC_PUSH_IGNORE(w) \ + _Pragma("GCC diagnostic push") \ + _Pragma(CK_DIAGNOSTIC_HELPER2(w)) +#define CK_DIAGNOSTIC_POP(w) _Pragma ("GCC diagnostic pop") +#else +#define CK_DIAGNOSTIC_PUSH_IGNORE(w) +#define CK_DIAGNOSTIC_POP(w) +#endif /* GCC 4.7.4 */ + +#undef GCC_VERSION_AT_LEAST + +#include + +#if defined(_MSC_VER) +/* define pid_t for Windows, as it is needed later */ +#define pid_t int +#endif /* _MSC_VER */ + +/* + * Used to create the linker script for hiding lib-local symbols. Shall + * be put directly in front of the exported symbol. + */ +#define CK_EXPORT + +/* + * Used for MSVC to create the export attribute + * CK_DLL_EXP is defined during the compilation of the library + * on the command line. + * + * This definition is only used when building or linking to + * the shared library, i.e. libcheck.so. When building the library + * the value must be "_declspec(dllexport)". + * When linking with the library, the value must be "_declspec(dllimport)" + * + * This is only used with Microsoft Visual C. In other systems + * the value is empty. In MSVC the value is empty when linking with + * a static library. + */ +#ifndef CK_DLL_EXP +#define CK_DLL_EXP +#endif + +/* check version numbers */ + +#define CHECK_MAJOR_VERSION (0) +#define CHECK_MINOR_VERSION (15) +#define CHECK_MICRO_VERSION (2) + +CK_DLL_EXP extern int CK_EXPORT check_major_version; +CK_DLL_EXP extern int CK_EXPORT check_minor_version; +CK_DLL_EXP extern int CK_EXPORT check_micro_version; + +#ifndef NULL +#define NULL ((void*)0) +#endif + +/** + * Type for a test case + * + * A TCase represents a test case. Create with tcase_create, free + * with tcase_free. For the moment, test cases can only be run + * through a suite +*/ +typedef struct TCase TCase; + +/** + * Type for a test function + */ +typedef void (*TFun) (int); + +/** + * Type for a setup/teardown function + */ +typedef void (*SFun) (void); + +/** + * Type for a test suite + */ +typedef struct Suite Suite; + +/** + * Type for a test, which wraps a test function + */ +typedef struct TTest { + const char *name; + TFun fn; + const char *file; + int line; +} TTest; + +/** + * Creates a test suite with the given name. + * + * Create a suite, which will contain test cases. Once + * created, use suite_add_tcase() to add test cases. + * When finished, create a suite runner from the + * suite using srunner_create() + * + * @param name name of the suite + * + * @return suite + * + * @since 0.6.0 + */ +CK_DLL_EXP Suite *CK_EXPORT suite_create(const char *name); + +/** + * Determines whether a given test suite contains a case named after a + * given string. + * + * @param s suite to check + * @param tcname test case to look for + * + * @return 1 iff the given test case is within the given suite; + * 0 otherwise + * + * @since 0.9.9 + */ +CK_DLL_EXP int CK_EXPORT suite_tcase(Suite * s, const char *tcname); + +/** + * Add a test case to a suite. + * + * Note that if the TCase has already been added attempting + * to add it again will be ignored. + * + * @param s suite to add test case to + * @param tc test case to add to suite + * + * @since 0.6.0 + */ +CK_DLL_EXP void CK_EXPORT suite_add_tcase(Suite * s, TCase * tc); + +/** + * Create a test case. + * + * Once created, tests can be added with the tcase_add_test() + * function, and the test case assigned to a suite with the + * suite_add_tcase() function. + * + * @param name name of the test case + * + * @return test case containing no tests + * + * @since 0.6.0 + * */ +CK_DLL_EXP TCase *CK_EXPORT tcase_create(const char *name); + +/** + * Associate a test case with certain tags. + * Replaces any existing tags with the new set. + * + * @param tc the test case + * + * @param tags string containing arbitrary tags separated by spaces. + * This will be copied. Passing NULL clears all tags. + * + * @since 0.11.0 + * */ +CK_DLL_EXP void CK_EXPORT tcase_set_tags(TCase * tc, + const char *tags); +/** + * Add a test function to a test case + * + * @param tc test case to add test to + * @param tf test function to add to test case + * + * @since 0.6.0 + * */ +#define tcase_add_test(tc,tf) tcase_add_test_raise_signal(tc,tf,0) + +/** + * Add a test function with signal handling to a test case + * + * The added test is expected to terminate by throwing the given signal + * + * @param tc test case to add test to + * @param tf test function to add to test case + * @param signal expected signal for test function to throw in order for + * the test to be considered passing + * + * @since 0.9.2 + * */ +#define tcase_add_test_raise_signal(tc,ttest,signal) \ + _tcase_add_test((tc),(ttest),(signal), 0, 0, 1) + +/** + * Add a test function with an expected exit value to a test case + * + * The added test is expected to terminate by exiting with the given value + * + * @param tc test case to add test to + * @param tf test function to add to test case + * @param expected_exit_value exit value for test function to return in + * order for the test to be considered passing + * + * @since 0.9.7 + */ +#define tcase_add_exit_test(tc, ttest, expected_exit_value) \ + _tcase_add_test((tc),(ttest),0,(expected_exit_value),0,1) + +/** + * Add a looping test function to a test case + * + * The test will be called in a for(i = s; i < e; i++) loop with each + * iteration being executed in a new context. The loop variable 'i' is + * available in the test. + * + * @param tc test case to add test to + * @param tf function to add to test case + * @param s starting index for value "i" in test + * @param e ending index for value "i" in test + * + * @since 0.9.4 + */ +#define tcase_add_loop_test(tc,ttest,s,e) \ + _tcase_add_test((tc),(ttest),0,0,(s),(e)) + +/** + * Add a looping test function with signal handling to a test case + * + * The test will be called in a for(i = s; i < e; i++) loop with each + * iteration being executed in a new context. The loop variable 'i' is + * available in the test. + * + * The added test is expected to terminate by throwing the given signal + * + * @param tc test case to add test to + * @param tf function to add to test case + * @param signal expected signal for test function to throw in order for + * the test to be considered passing + * @param s starting index for value "i" in test + * @param e ending index for value "i" in test + * + * @since 0.9.5 + */ +#define tcase_add_loop_test_raise_signal(tc,ttest,signal,s,e) \ + _tcase_add_test((tc),(ttest),(signal),0,(s),(e)) + +/** + * Add a looping test function with an expected exit value to a test case + * + * The test will be called in a for(i = s; i < e; i++) loop with each + * iteration being executed in a new context. The loop variable 'i' is + * available in the test. + * + * The added test is expected to terminate by exiting with the given value + * + * @param tc test case to add test to + * @param tf function to add to test case + * @param expected_exit_value exit value for test function to return in + * order for the test to be considered passing + * @param s starting index for value "i" in test + * @param e ending index for value "i" in test + * + * @since 0.9.7 + */ +#define tcase_add_loop_exit_test(tc,ttest,expected_exit_value,s,e) \ + _tcase_add_test((tc),(ttest),0,(expected_exit_value),(s),(e)) + +/* Add a test function to a test case + (function version -- use this when the macro won't work +*/ +CK_DLL_EXP void CK_EXPORT _tcase_add_test(TCase * tc, const TTest * ttest, + int _signal, int allowed_exit_value, + int start, int end); + +/** + * Add unchecked fixture setup/teardown functions to a test case + * + * Unchecked fixture functions are run at the start and end of the + * test case, and not before and after unit tests. Further, + * unchecked fixture functions are not run in a separate address space, + * like test functions, and so must not exit or signal (e.g., + * segfault). + * + * Also, when run in CK_NOFORK mode, unchecked fixture functions may + * lead to different unit test behavior if unit tests change data + * setup by the fixture functions. + * + * Note that if a setup function fails, the remaining setup functions + * will be omitted, as will the test case and the teardown functions. + * If a teardown function fails the remaining teardown functins will be + * omitted. + * + * @param tc test case to add unchecked fixture setup/teardown to + * @param setup function to add to be executed before the test case; + * if NULL no setup function is added + * @param teardown function to add to be executed after the test case; + * if NULL no teardown function is added + * @since 0.8.0 + */ +CK_DLL_EXP void CK_EXPORT tcase_add_unchecked_fixture(TCase * tc, SFun setup, + SFun teardown); + +/** + * Add checked fixture setup/teardown functions to a test case + * + * Checked fixture functions are run before and after each unit test inside + * of the address space of the test. Thus, if using CK_FORK + * mode the separate process running the unit test will survive signals + * or unexpected exits in the fixture function. Also, if the setup + * function is idempotent, unit test behavior will be the same in + * CK_FORK and CK_NOFORK modes. + * + * However, since fixture functions are run before and after each unit + * test, they should not be expensive code. + * + * Note that if a setup function fails, the remaining setup functions + * will be omitted, as will the test and the teardown functions. If a + * teardown function fails the remaining teardown functins will be + * omitted. + * + * @param tc test case to add checked fixture setup/teardown to + * @param setup function to add to be executed before each unit test in + * the test case; if NULL no setup function is added + * @param teardown function to add to be executed after each unit test in + * the test case; if NULL no teardown function is added + * + * @since 0.8.0 +*/ +CK_DLL_EXP void CK_EXPORT tcase_add_checked_fixture(TCase * tc, SFun setup, + SFun teardown); + +/** + * Set the timeout for all tests in a test case. + * + * A test that lasts longer than the timeout (in seconds) will be killed + * and thus fail with an error. + * + * If not set, the default timeout is one assigned at compile time. If + * the environment variable CK_DEFAULT_TIMEOUT is defined and no timeout + * is set, the value in the environment variable is used. + * + * If Check is compile without fork() support this call is ignored, + * as timeouts are not possible. + * + * @param tc test case to assign timeout to + * @param timeout to use, in seconds. If the value contains a decimal + * portion, but no high resolution timer is available, + * the value is rounded up to the nearest second. + * + * @since 0.9.2 + */ +CK_DLL_EXP void CK_EXPORT tcase_set_timeout(TCase * tc, double timeout); + +/* Internal function to mark the start of a test function */ +CK_DLL_EXP void CK_EXPORT tcase_fn_start(const char *fname, const char *file, + int line); + +/** + * Retreive the name of the current running test. This is the name + * of the test passed to START_TEST. This is only valid when called + * from a running test. The value return outside of a running test is + * undefined. + * + * @since 0.11.0 + */ +CK_DLL_EXP const char* CK_EXPORT tcase_name(void); + +/** + * Start a unit test with START_TEST(unit_name), end with END_TEST. + * + * One must use braces within a START_/END_ pair to declare new variables + * + * @since 0.6.0 + */ +#define START_TEST(__testname)\ +static void __testname ## _fn (int _i CK_ATTRIBUTE_UNUSED);\ +static const TTest __testname ## _ttest = {""# __testname, __testname ## _fn, __FILE__, __LINE__};\ +static const TTest * __testname = & __testname ## _ttest;\ +static void __testname ## _fn (int _i CK_ATTRIBUTE_UNUSED) + +/** + * End a unit test + * + * @since 0.6.0 + */ +#define END_TEST + +/* + * Fail the test case unless expr is false + * + * This call is deprecated. + */ +#define fail_unless(expr, ...) \ + (expr) ? \ + _mark_point(__FILE__, __LINE__) : \ + _ck_assert_failed(__FILE__, __LINE__, "Assertion '"#expr"' failed" , ## __VA_ARGS__, NULL) + +/* + * Fail the test case if expr is false + * + * This call is deprecated. + * + * NOTE: The space before the comma sign before ## is essential to be compatible + * with gcc 2.95.3 and earlier. + * FIXME: these macros may conflict with C89 if expr is + * FIXME: strcmp (str1, str2) due to excessive string length. + */ +#define fail_if(expr, ...)\ + (expr) ? \ + _ck_assert_failed(__FILE__, __LINE__, "Failure '"#expr"' occurred" , ## __VA_ARGS__, NULL) \ + : _mark_point(__FILE__, __LINE__) + +/* + * Fail the test + * + * This call is deprecated. + */ +#define fail(...) _ck_assert_failed(__FILE__, __LINE__, "Failed" , ## __VA_ARGS__, NULL) + +/* + * This is called whenever an assertion fails. + * Note that it only has the noreturn modifier when + * using fork. If fork is unavailable, the function + * calls longjmp() when a test assertion fails. Marking + * the function as noreturn causes gcc to make assumptions + * which are not valid, as longjmp() is like a return. + */ +#if 0 +CK_DLL_EXP void CK_EXPORT _ck_assert_failed(const char *file, int line, + const char *expr, const char *msg, + ...) CK_ATTRIBUTE_NORETURN CK_ATTRIBUTE_FORMAT(printf, 4, 5); +#else +CK_DLL_EXP void CK_EXPORT _ck_assert_failed(const char *file, int line, + const char *expr, const char *msg, + ...) CK_ATTRIBUTE_FORMAT(printf, 4, 5); +#endif + +/** + * Fail the test if expression is false + * + * @param expr expression to evaluate + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_assert(expr) ck_assert_msg(expr, NULL) + +/* The space before the comma sign before ## is essential to be compatible + with gcc 2.95.3 and earlier. +*/ +/** + * Fail the test if the expression is false; print message on failure + * + * @param expr expression to evaluate + * @param ... message to print (in printf format) if expression is false + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_assert_msg(expr, ...) \ + (expr) ? \ + _mark_point(__FILE__, __LINE__) : \ + _ck_assert_failed(__FILE__, __LINE__, "Assertion '"#expr"' failed" , ## __VA_ARGS__) + +/** + * Unconditionally fail the test + * + * @note Once called, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_abort() ck_abort_msg(NULL) +/** + * Unconditionally fail the test; print a message + * + * @param ... message to print (in printf format) + * + * @note Once called, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_abort_msg(...) _ck_assert_failed(__FILE__, __LINE__, "Failed" , ## __VA_ARGS__) + +/* Signed and unsigned integer comparison macros with improved output compared to ck_assert(). */ +/* OP may be any comparison operator. */ +#define _ck_assert_int(X, OP, Y) do { \ + intmax_t _ck_x = (X); \ + intmax_t _ck_y = (Y); \ + ck_assert_msg(_ck_x OP _ck_y, "Assertion '%s' failed: %s == %jd, %s == %jd", #X" "#OP" "#Y, #X, _ck_x, #Y, _ck_y); \ +} while (0) + +/** + * Check two signed integers to determine if X==Y + * + * If not X==Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_assert_int_eq(X, Y) _ck_assert_int(X, ==, Y) +/** + * Check two signed integers to determine if X!=Y + * + * If not X!=Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_assert_int_ne(X, Y) _ck_assert_int(X, !=, Y) +/** + * Check two signed integers to determine if XY + * + * If not X>Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_int_gt(X, Y) _ck_assert_int(X, >, Y) +/** + * Check two signed integers to determine if X>=Y + * + * If not X>=Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_int_ge(X, Y) _ck_assert_int(X, >=, Y) + +#define _ck_assert_uint(X, OP, Y) do { \ + uintmax_t _ck_x = (X); \ + uintmax_t _ck_y = (Y); \ + ck_assert_msg(_ck_x OP _ck_y, "Assertion '%s' failed: %s == %ju, %s == %ju", #X" "#OP" "#Y, #X, _ck_x, #Y, _ck_y); \ +} while (0) +/** + * Check two unsigned integers to determine if X==Y + * + * If not X==Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_uint_eq(X, Y) _ck_assert_uint(X, ==, Y) +/** + * Check two unsigned integers to determine if X!=Y + * + * If not X!=Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_uint_ne(X, Y) _ck_assert_uint(X, !=, Y) +/** + * Check two unsigned integers to determine if XY + * + * If not X>Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_uint_gt(X, Y) _ck_assert_uint(X, >, Y) +/** + * Check two unsigned integers to determine if X>=Y + * + * If not X>=Y, the test fails. + * + * @param X signed integer + * @param Y signed integer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_uint_ge(X, Y) _ck_assert_uint(X, >=, Y) + +/* Number of digits after the decimal point to output via printf */ +#ifndef CK_FLOATING_DIG +# define CK_FLOATING_DIG 6 +#endif /* CK_FLOATING_DIG */ + +/* Floating point number comparison macros with improved output + * compared to ck_assert(). */ +/* OP may be any comparison operator, TP is type, TM is type modifier. */ +#define _ck_assert_floating(X, OP, Y, TP, TM) do { \ + TP _ck_x = (X); \ + TP _ck_y = (Y); \ + ck_assert_msg(_ck_x OP _ck_y, \ + "Assertion '%s' failed: %s == %.*" TM "g, %s == %.*" TM "g", \ + #X" "#OP" "#Y, \ + #X, (int)CK_FLOATING_DIG, _ck_x, \ + #Y, (int)CK_FLOATING_DIG, _ck_y); \ +} while (0) + +/* Check floating point number is finise. */ +/* TP is type, TM is type modifier. */ +#define _ck_assert_floating_finite(X, TP, TM) \ +do { \ + TP _ck_x = (X); \ + ck_assert_msg(isfinite(_ck_x), \ + "Assertion '%s' failed: %s == %.*" TM "g", \ + #X" is finite", \ + #X, (int)CK_FLOATING_DIG, _ck_x); \ +} while (0) + +/* Check floating point number is infinise. */ +/* TP is type, TM is type modifier. */ +#define _ck_assert_floating_infinite(X, TP, TM) \ +do { \ + TP _ck_x = (X); \ + ck_assert_msg(isinf(_ck_x), \ + "Assertion '%s' failed: %s == %.*" TM "g", \ + #X" is infinite", \ + #X, (int)CK_FLOATING_DIG, _ck_x); \ +} while (0) + +/* Check floating point number is "Not a Number". */ +/* TP is type, TM is type modifier. */ +#define _ck_assert_floating_nan(X, TP, TM) \ +do { \ + TP _ck_x = (X); \ + ck_assert_msg(isnan(_ck_x), \ + "Assertion '%s' failed: %s == %.*" TM "g", \ + #X" is NaN", \ + #X, (int)CK_FLOATING_DIG, _ck_x); \ +} while (0) + +/* Check floating point number is not "Not a Number". */ +/* TP is type, TM is type modifier. */ +#define _ck_assert_floating_nonnan(X, TP, TM) \ +do { \ + TP _ck_x = (X); \ + ck_assert_msg(!isnan(_ck_x), \ + "Assertion '%s' failed: %s == %.*" TM "g", \ + #X" is not NaN", \ + #X, (int)CK_FLOATING_DIG, _ck_x); \ +} while (0) + +/* Floating point tolerance comparison macros with improved output + * compared to ck_assert(). */ +/* OP, D can have values: >, -1; <, 1. */ +#define _ck_assert_floating_op_tol(X, OP, Y, T, D, TP, TM) do { \ + TP _ck_x = (X); \ + TP _ck_y = (Y); \ + TP _ck_t = (T); \ + ck_assert_msg((_ck_x - _ck_y) OP _ck_t * (D), \ + "Assertion '%s' failed: %s == %.*" TM "g, %s == %.*" TM "g, %s == %.*" TM "g", \ + #X" "#OP"= "#Y", error < "#T, \ + #X, (int)CK_FLOATING_DIG, _ck_x, \ + #Y, (int)CK_FLOATING_DIG, _ck_y, \ + #T, (int)CK_FLOATING_DIG, _ck_t); \ +} while (0) + +/* Floating point tolerance comparison macros with improved output + * compared to ck_assert(). */ +/* OP can have values: <; >=. */ +#define _ck_assert_floating_absdiff_op_tol(X, Y, OP, T, TP, TM) \ +do { \ + TP _ck_x = (X); \ + TP _ck_y = (Y); \ + TP _ck_t = (T); \ + ck_assert_msg(fabsl(_ck_y - _ck_x) OP _ck_t, \ + "Assertion '%s' failed: %s == %.*" TM "g, %s == %.*" TM "g, %s == %.*" TM "g", \ + "fabsl("#Y" - "#X") "#OP" "#T, \ + #X, (int)CK_FLOATING_DIG, _ck_x, \ + #Y, (int)CK_FLOATING_DIG, _ck_y, \ + #T, (int)CK_FLOATING_DIG, _ck_t); \ +} while (0) + +/** + * Check two single precision floating point numbers to determine if X == Y. + * + * Note that the usefulness of this assertion is very limited. If you + * want to compare two floating point numbers for equality, you probably + * want to use ck_assert_float_eq_tol instead. + * + * If not X == Y, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_eq(X, Y) _ck_assert_floating(X, ==, Y, float, "") +/** + * Check two single precision floating point numbers to determine if X != Y. + * + * Note that the usefulness of this assertion is very limited. If you + * want to compare two floating point numbers for equality, you probably + * want to use ck_assert_float_ne_tol instead. + * + * If not X != Y, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_ne(X, Y) _ck_assert_floating(X, !=, Y, float, "") +/** + * Check two single precision floating point numbers to determine if X < Y + * + * If not X < Y, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_lt(X, Y) _ck_assert_floating(X, <, Y, float, "") +/** + * Check two single precision floating point numbers to determine if X <= Y + * + * If not X <= Y, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_le(X, Y) _ck_assert_floating(X, <=, Y, float, "") +/** + * Check two single precision floating point numbers to determine if X > Y + * + * If not X > Y, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_gt(X, Y) _ck_assert_floating(X, >, Y, float, "") +/** + * Check two single precision floating point numbers to determine if X >= Y + * + * If not X >= Y, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_ge(X, Y) _ck_assert_floating(X, >=, Y, float, "") + +/** + * Check two single precision floating point numbers to determine if X≈Y + * with specified tolerance + * + * If not X ≈ Y with error < T, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * @param T tolerance (float) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_eq_tol(X, Y, T) _ck_assert_floating_absdiff_op_tol(X, Y, <, T, float, "") + +/** + * Check two single precision floating point numbers to determine if not X≈Y + * with specified tolerance + * + * If X ≈ Y with error < T, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * @param T tolerance (float) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_ne_tol(X, Y, T) _ck_assert_floating_absdiff_op_tol(X, Y, >=, T, float, "") + +/** + * Check two single precision floating point numbers to determine if X>≈Y + * with specified tolerance + * + * If not X >≈ Y with error < T, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * @param T tolerance (float) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_ge_tol(X, Y, T) _ck_assert_floating_op_tol(X, >, Y, T, -1, float, "") + +/** + * Check two single precision floating point numbers to determine if X<≈Y + * with specified tolerance + * + * If not X <≈ Y with error < T, the test fails. + * + * @param X floating point number (float) + * @param Y floating point number (float) to compare against X + * @param T tolerance (float) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_le_tol(X, Y, T) _ck_assert_floating_op_tol(X, <, Y, T, 1, float, "") + +/** + * Check that a single precision floating point number is finite; i.e. is + * not +infinity, -infinity, or "Not a Number" (NaN) + * + * If X is +INFINITY or X is -INFINITY, or X is NaN, the test fails. + * + * @param X floating point number (float) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_finite(X) _ck_assert_floating_finite(X, float, "") + +/** + * Check that a single precision floating point number is infinite, + * either +infinity or -infinity + * + * If X is not +INFINITY and X is not -INFINITY, the test fails. + * + * @param X floating point number (float) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_infinite(X) _ck_assert_floating_infinite(X, float, "") + +/** + * Check that a single precision floating point number + * is "Not a Number" (NaN) + * + * If X is not NaN, the test fails. + * + * @param X floating point number (float) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_nan(X) _ck_assert_floating_nan(X, float, "") + +/** + * Check that a single precision floating point number is + * not "Not a Number" (NaN) + * + * If X is NaN, the test fails. + * + * @param X floating point number (float) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_float_nonnan(X) _ck_assert_floating_nonnan(X, float, "") + +/** + * Check two double precision floating point numbers to determine if X == Y. + * + * Note that the usefulness of this assertion is very limited. If you + * want to compare two floating point numbers for equality, you probably + * want to use ck_assert_double_eq_tol instead. + * + * If not X == Y, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_eq(X, Y) _ck_assert_floating(X, ==, Y, double, "") +/** + * Check two double precision floating point numbers to determine if X != Y. + * + * Note that the usefulness of this assertion is very limited. If you + * want to compare two floating point numbers for equality, you probably + * want to use ck_assert_double_ne_tol instead. + * + * If not X != Y, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_ne(X, Y) _ck_assert_floating(X, !=, Y, double, "") +/** + * Check two double precision floating point numbers to determine if X < Y + * + * If not X < Y, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_lt(X, Y) _ck_assert_floating(X, <, Y, double, "") +/** + * Check two double precision floating point numbers to determine if X <= Y + * + * If not X <= Y, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_le(X, Y) _ck_assert_floating(X, <=, Y, double, "") +/** + * Check two double precision floating point numbers to determine if X > Y + * + * If not X > Y, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_gt(X, Y) _ck_assert_floating(X, >, Y, double, "") +/** + * Check two double precision floating point numbers to determine if X >= Y + * + * If not X >= Y, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_ge(X, Y) _ck_assert_floating(X, >=, Y, double, "") + +/** + * Check two double precision floating point numbers to determine if X≈Y + * with specified tolerance + * + * If not X ≈ Y with error < T, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * @param T tolerance (double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_eq_tol(X, Y, T) _ck_assert_floating_absdiff_op_tol(X, Y, <, T, double, "") + +/** + * Check two double precision floating point numbers to determine if not X≈Y + * with specified tolerance + * + * If X ≈ Y with error < T, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * @param T tolerance (double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_ne_tol(X, Y, T) _ck_assert_floating_absdiff_op_tol(X, Y, >=, T, double, "") + +/** + * Check two double precision floating point numbers to determine if X>≈Y + * with specified tolerance + * + * If not X >≈ Y with error < T, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * @param T tolerance (double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_ge_tol(X, Y, T) _ck_assert_floating_op_tol(X, >, Y, T, -1, double, "") + +/** + * Check two double precision floating point numbers to determine if X<≈Y + * with specified tolerance + * + * If not X <≈ Y with error < T, the test fails. + * + * @param X floating point number (double) + * @param Y floating point number (double) to compare against X + * @param T tolerance (double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_le_tol(X, Y, T) _ck_assert_floating_op_tol(X, <, Y, T, 1, double, "") + +/** + * Check that a double precision floating point number is finite; i.e. is + * not +infinity, -infinity, or "Not a Number" (NaN) + * + * If X is +INFINITY or X is -INFINITY, or X is NaN, the test fails. + * + * @param X floating point number (double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_finite(X) _ck_assert_floating_finite(X, double, "") + +/** + * Check that a double precision floating point number is infinite, + * either +infinity or -infinity + * + * If X is not +INFINITY and X is not -INFINITY, the test fails. + * + * @param X floating point number (double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_infinite(X) _ck_assert_floating_infinite(X, double, "") + +/** + * Check that a double precision floating point number + * is "Not a Number" (NaN) + * + * If X is not NaN, the test fails. + * + * @param X floating point number (double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_nan(X) _ck_assert_floating_nan(X, double, "") + +/** + * Check that a double precision floating point number is + * not "Not a Number" (NaN) + * + * If X is NaN, the test fails. + * + * @param X floating point number (double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_double_nonnan(X) _ck_assert_floating_nonnan(X, double, "") + +/** + * Check two double precision floating point numbers to determine if X == Y. + * + * Note that the usefulness of this assertion is very limited. If you + * want to compare two floating point numbers for equality, you probably + * want to use ck_assert_ldouble_eq_tol instead. + * + * If not X == Y, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_eq(X, Y) _ck_assert_floating(X, ==, Y, long double, "L") +/** + * Check two double precision floating point numbers to determine if X != Y. + * + * Note that the usefulness of this assertion is very limited. If you + * want to compare two floating point numbers for equality, you probably + * want to use ck_assert_ldouble_ne_tol instead. + * + * If not X != Y, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_ne(X, Y) _ck_assert_floating(X, !=, Y, long double, "L") +/** + * Check two double precision floating point numbers to determine if X < Y + * + * If not X < Y, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_lt(X, Y) _ck_assert_floating(X, <, Y, long double, "L") +/** + * Check two double precision floating point numbers to determine if X <= Y + * + * If not X <= Y, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_le(X, Y) _ck_assert_floating(X, <=, Y, long double, "L") +/** + * Check two double precision floating point numbers to determine if X > Y + * + * If not X > Y, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_gt(X, Y) _ck_assert_floating(X, >, Y, long double, "L") +/** + * Check two double precision floating point numbers to determine if X >= Y + * + * If not X >= Y, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_ge(X, Y) _ck_assert_floating(X, >=, Y, long double, "L") + +/** + * Check two double precision floating point numbers to determine if X≈Y + * with specified tolerance + * + * If not X ≈ Y with error < T, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * @param T tolerance (long double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_eq_tol(X, Y, T) _ck_assert_floating_absdiff_op_tol(X, Y, <, T, long double, "L") + +/** + * Check two double precision floating point numbers to determine if not X≈Y + * with specified tolerance + * + * If X ≈ Y with error < T, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * @param T tolerance (long double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_ne_tol(X, Y, T) _ck_assert_floating_absdiff_op_tol(X, Y, >=, T, long double, "L") + +/** + * Check two double precision floating point numbers to determine if X>≈Y + * with specified tolerance + * + * If not X >≈ Y with error < T, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * @param T tolerance (long double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_ge_tol(X, Y, T) _ck_assert_floating_op_tol(X, >, Y, T, -1, long double, "L") + +/** + * Check two double precision floating point numbers to determine if X<≈Y + * with specified tolerance + * + * If not X <≈ Y with error < T, the test fails. + * + * @param X floating point number (long double) + * @param Y floating point number (long double) to compare against X + * @param T tolerance (long double) + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_le_tol(X, Y, T) _ck_assert_floating_op_tol(X, <, Y, T, 1, long double, "L") + +/** + * Check that a double precision floating point number is finite; i.e. is + * not +infinity, -infinity, or "Not a Number" (NaN) + * + * If X is +INFINITY or X is -INFINITY, or X is NaN, the test fails. + * + * @param X floating point number (long double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_finite(X) _ck_assert_floating_finite(X, long double, "L") + +/** + * Check that a double precision floating point number is infinite, + * either +infinity or -infinity + * + * If X is not +INFINITY and X is not -INFINITY, the test fails. + * + * @param X floating point number (long double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_infinite(X) _ck_assert_floating_infinite(X, long double, "L") + +/** + * Check that a double precision floating point number + * is "Not a Number" (NaN) + * + * If X is not NaN, the test fails. + * + * @param X floating point number (long double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_nan(X) _ck_assert_floating_nan(X, long double, "L") + +/** + * Check that a double precision floating point number is + * not "Not a Number" (NaN) + * + * If X is NaN, the test fails. + * + * @param X floating point number (long double) to be checked + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ldouble_nonnan(X) _ck_assert_floating_nonnan(X, long double, "L") + +/* String comparison macros with improved output compared to ck_assert() */ +/* OP might be any operator that can be used in '0 OP strcmp(X,Y)' comparison. */ +/* String pointer could be compared againts NULL with == (NULLEQ = 1) and != (NULLNE = 1) operators. */ +/* The x and y parameter swap in strcmp() is needed to handle >, >=, <, <= operators. */ +/* If the x or y parameter is NULL its value will be printed without quotes. */ +#define _ck_assert_str(X, OP, Y, NULLEQ, NULLNE) do { \ + const char* _ck_x = (X); \ + const char* _ck_y = (Y); \ + const char* _ck_x_s; \ + const char* _ck_y_s; \ + const char* _ck_x_q; \ + const char* _ck_y_q; \ + if (_ck_x != NULL) { \ + _ck_x_q = "\""; \ + _ck_x_s = _ck_x; \ + } else { \ + _ck_x_q = ""; \ + _ck_x_s = "(null)"; \ + } \ + if (_ck_y != NULL) { \ + _ck_y_q = "\""; \ + _ck_y_s = _ck_y; \ + } else { \ + _ck_y_q = ""; \ + _ck_y_s = "(null)"; \ + } \ + ck_assert_msg( \ + (NULLEQ && (_ck_x == NULL) && (_ck_y == NULL)) || \ + (NULLNE && ((_ck_x == NULL) || (_ck_y == NULL)) && (_ck_x != _ck_y)) || \ + ((_ck_x != NULL) && (_ck_y != NULL) && (0 OP strcmp(_ck_y, _ck_x))), \ + "Assertion '%s' failed: %s == %s%s%s, %s == %s%s%s", \ + #X" "#OP" "#Y, \ + #X, _ck_x_q, _ck_x_s, _ck_x_q, \ + #Y, _ck_y_q, _ck_y_s, _ck_y_q); \ +} while (0) + +/** + * Check two strings to determine if 0==strcmp(X,Y) + * + * If X or Y is NULL the test fails. + * If not 0==strcmp(X,Y), the test fails. + * + * @param X string + * @param Y string to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_assert_str_eq(X, Y) _ck_assert_str(X, ==, Y, 0, 0) + +/** + * Check two strings to determine if 0!=strcmp(X,Y) + * + * If X or Y is NULL the test fails. + * If not 0!=strcmp(X,Y), the test fails. + * + * @param X string + * @param Y string to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.6 + */ +#define ck_assert_str_ne(X, Y) _ck_assert_str(X, !=, Y, 0, 0) + +/** + * Check two strings to determine if 00) + * + * If X or Y is NULL the test fails. + * If not 0=0) + * + * If X or Y is NULL the test fails. + * If not 0<=strcmp(X,Y), the test fails. + * + * @param X string + * @param Y string to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_str_le(X, Y) _ck_assert_str(X, <=, Y, 0, 0) + +/** + * Check two strings to determine if 00) + * + * If X or Y is NULL the test fails. + * If not 0, Y, 0, 0) + +/** + * Check two strings to determine if 0>=strcmp(X,Y) (e.g. strcmp(X,Y)<=0) + * + * If X or Y is NULL the test fails. + * If not 0>=strcmp(X,Y), the test fails. + * + * @param X string + * @param Y string to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_str_ge(X, Y) _ck_assert_str(X, >=, Y, 0, 0) + +/** + * Check two strings to determine if 0==strcmp(X,Y) or if both are undefined + * + * If both X and Y are NULL the test passes. However, if only one is NULL + * the test fails. + * If not ((X==NULL)&&(Y==NULL)) || (0==strcmp(X,Y)), the test fails. + * + * @param X string + * @param Y string to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_pstr_eq(X, Y) _ck_assert_str(X, ==, Y, 1, 0) + +/** + * Check two strings to determine if 0!=strcmp(X,Y) or one of them is undefined + * + * If either X or Y is NULL the test passes, however if both are NULL + * the test fails. + * If not (X!=NULL)&&(Y!=NULL)&&(0!=strcmp(X,Y)), the test fails. + * + * @param X string + * @param Y string to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_pstr_ne(X, Y) _ck_assert_str(X, !=, Y, 0, 1) + +/* Memory location comparison macros with improved output compared to ck_assert() */ +/* OP might be any operator that can be used in '0 OP memcmp(X,Y,L)' comparison */ +/* The x and y parameter swap in memcmp() is needed to handle >, >=, <, <= operators */ +/* Output is limited to CK_MAX_ASSERT_MEM_PRINT_SIZE bytes */ +#ifndef CK_MAX_ASSERT_MEM_PRINT_SIZE +#define CK_MAX_ASSERT_MEM_PRINT_SIZE 64 +#endif + +/* Memory location comparison macros with improved output compared to ck_assert() */ +/* OP might be any operator that can be used in '0 OP memcmp(X,Y,L)' comparison */ +/* The x and y parameter swap in memcmp() is needed to handle >, >=, <, <= operators */ +/* Output is limited to CK_MAX_ASSERT_MEM_PRINT_SIZE bytes */ +#ifndef CK_MAX_ASSERT_MEM_PRINT_SIZE +#define CK_MAX_ASSERT_MEM_PRINT_SIZE 64 +#endif + +#define _ck_assert_mem(X, OP, Y, L) do { \ + const uint8_t* _ck_x = (const uint8_t*)(X); \ + const uint8_t* _ck_y = (const uint8_t*)(Y); \ + size_t _ck_l = (L); \ + char _ck_x_str[CK_MAX_ASSERT_MEM_PRINT_SIZE * 2 + 1]; \ + char _ck_y_str[CK_MAX_ASSERT_MEM_PRINT_SIZE * 2 + 1]; \ + static const char _ck_hexdigits[] = "0123456789abcdef"; \ + size_t _ck_i; \ + size_t _ck_maxl = (_ck_l > CK_MAX_ASSERT_MEM_PRINT_SIZE) ? CK_MAX_ASSERT_MEM_PRINT_SIZE : _ck_l; \ + for (_ck_i = 0; _ck_i < _ck_maxl; _ck_i++) { \ + _ck_x_str[_ck_i * 2 ] = _ck_hexdigits[(_ck_x[_ck_i] >> 4) & 0xF]; \ + _ck_y_str[_ck_i * 2 ] = _ck_hexdigits[(_ck_y[_ck_i] >> 4) & 0xF]; \ + _ck_x_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_x[_ck_i] & 0xF]; \ + _ck_y_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_y[_ck_i] & 0xF]; \ + } \ + _ck_x_str[_ck_i * 2] = 0; \ + _ck_y_str[_ck_i * 2] = 0; \ + if (_ck_maxl != _ck_l) { \ + _ck_x_str[_ck_i * 2 - 2] = '.'; \ + _ck_y_str[_ck_i * 2 - 2] = '.'; \ + _ck_x_str[_ck_i * 2 - 1] = '.'; \ + _ck_y_str[_ck_i * 2 - 1] = '.'; \ + } \ + ck_assert_msg(0 OP memcmp(_ck_y, _ck_x, _ck_l), \ + "Assertion '%s' failed: %s == \"%s\", %s == \"%s\"", #X" "#OP" "#Y, #X, _ck_x_str, #Y, _ck_y_str); \ +} while (0) +/** + * Check two memory locations to determine if 0==memcmp(X,Y,L) + * + * If not 0==memcmp(X,Y,L), the test fails. + * + * @param X memory location + * @param Y memory location to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_mem_eq(X, Y, L) _ck_assert_mem(X, ==, Y, L) +/** + * Check two memory locations to determine if 0!=memcmp(X,Y,L) + * + * If not 0!=memcmp(X,Y,L), the test fails. + * + * @param X memory location + * @param Y memory location to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_mem_ne(X, Y, L) _ck_assert_mem(X, !=, Y, L) +/** + * Check two memory locations to determine if 00) + * + * If not 0=0) + * + * If not 0<=memcmp(X,Y,L), the test fails. + * + * @param X memory location + * @param Y memory location to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_mem_le(X, Y, L) _ck_assert_mem(X, <=, Y, L) +/** + * Check two memory locations to determine if 00) + * + * If not 0, Y, L) +/** + * Check two memory locations to determine if 0>=memcmp(X,Y,L) (e.g. memcmp(X,Y,L)<=0) + * + * If not 0>=memcmp(X,Y,L), the test fails. + * + * @param X memory location + * @param Y memory location to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_mem_ge(X, Y, L) _ck_assert_mem(X, >=, Y, L) + +/* Pointer comparison macros with improved output compared to ck_assert(). */ +/* OP may only be == or != */ +#define _ck_assert_ptr(X, OP, Y) do { \ + const void* _ck_x = (X); \ + const void* _ck_y = (Y); \ + ck_assert_msg(_ck_x OP _ck_y, "Assertion '%s' failed: %s == %#lx, %s == %#lx", #X" "#OP" "#Y, #X, (unsigned long)(uintptr_t)_ck_x, #Y, (unsigned long)(uintptr_t)_ck_y); \ +} while (0) + +/* Pointer against NULL comparison macros with improved output + * compared to ck_assert(). */ +/* OP may only be == or != */ +#define _ck_assert_ptr_null(X, OP) do { \ + const void* _ck_x = (X); \ + ck_assert_msg(_ck_x OP NULL, \ + "Assertion '%s' failed: %s == %#lx", \ + #X" "#OP" NULL", \ + #X, (unsigned long)(uintptr_t)_ck_x); \ +} while (0) + +/** + * Check if two pointers are equal. + * + * If the two passed pointers are not equal, the test + * fails. + * + * @param X pointer + * @param Y pointer to compare against X + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.9.10 + */ +#define ck_assert_ptr_eq(X, Y) _ck_assert_ptr(X, ==, Y) + +/** + * Check if two pointers are not. + * + * If the two passed pointers are equal, the test fails. + * + * @param X pointer + * @param Y pointer to compare against X + * + * @since 0.9.10 + */ +#define ck_assert_ptr_ne(X, Y) _ck_assert_ptr(X, !=, Y) + +/** + * Check if a pointer is equal to NULL. + * + * If X != NULL, the test fails. + * + * @param X pointer to compare against NULL + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ptr_null(X) _ck_assert_ptr_null(X, ==) + +/** + * Check if a pointer is not equal to NULL. + * + * If X == NULL, the test fails. + * + * @param X pointer to compare against NULL + * + * @note If the check fails, the remaining of the test is aborted + * + * @since 0.11.0 + */ +#define ck_assert_ptr_nonnull(X) _ck_assert_ptr_null(X, !=) + +/** + * Mark the last point reached in a unit test. + * + * If the test throws a signal or exits, the location noted with the + * failure is the last location of a ck_assert*() or ck_abort() call. + * Use mark_point() to record intermediate locations (useful for tracking down + * crashes or exits). + * + * @since 0.6.0 +*/ +#define mark_point() _mark_point(__FILE__,__LINE__) + +/* Non macro version of #mark_point */ +CK_DLL_EXP void CK_EXPORT _mark_point(const char *file, int line); + +/** + * Enum describing the possible results of a test + */ +enum test_result +{ + CK_TEST_RESULT_INVALID, /**< Default value; should not encounter this */ + CK_PASS, /**< Test passed */ + CK_FAILURE, /**< Test completed but failed */ + CK_ERROR /**< Test failed to complete + (unexpected signal or non-zero early exit) */ +}; + +/** + * Enum specifying the verbosity of output a SRunner should produce + */ +enum print_output +{ + CK_SILENT, /**< No output */ + CK_MINIMAL, /**< Only summary output */ + CK_NORMAL, /**< All failed tests */ + CK_VERBOSE, /**< All tests */ + CK_ENV, /**< Look at environment var CK_VERBOSITY + for what verbosity to use, which can be + either "silent", "minimal", "normal", + or "verbose". If the environment variable + is not set, then CK_NORMAL will be used.*/ +#if 0 + CK_SUBUNIT, /**< Run as a subunit child process */ +#endif + CK_LAST /**< Not a valid option */ +}; + +/** + * Holds state for a running of a test suite + */ +typedef struct SRunner SRunner; + +/** + * Opaque type for a test failure + */ +typedef struct TestResult TestResult; + +/** + * Enum representing the types of contexts for a test + */ +enum ck_result_ctx +{ + CK_CTX_INVALID, /**< Default value; should not encounter this */ + CK_CTX_SETUP, /**< Setup before a test */ + CK_CTX_TEST, /**< Body of test itself */ + CK_CTX_TEARDOWN /**< Teardown after a test */ +}; + +/** + * Retrieve type of result that the given test result represents. + * + * This is a member of test_result, and can represent a + * pass, failure, or error. + * + * @param tr test result to retrieve result from + * + * @return result of given test + * + * @since 0.6.0 + */ +CK_DLL_EXP int CK_EXPORT tr_rtype(TestResult * tr); + +/** + * Retrieve context in which the result occurred for the given test result. + * + * The types of contents include the test setup, teardown, or the + * body of the test itself. + * + * @param tr test result to retrieve context from + * + * @return context to which the given test result applies + * + * @since 0.8.0 + */ +CK_DLL_EXP enum ck_result_ctx CK_EXPORT tr_ctx(TestResult * tr); + +/** + * Retrieve failure message from test result, if applicable. + * + * @return pointer to a message, if one exists. NULL otherwise. + * + * @since 0.6.0 + */ +CK_DLL_EXP const char *CK_EXPORT tr_msg(TestResult * tr); + +/** + * Retrieve line number at which a failure occurred, if applicable. + * + * @return If the test resulted in a failure, returns the line number + * that the failure occurred on; otherwise returns -1. + * + * @since 0.6.0 + */ +CK_DLL_EXP int CK_EXPORT tr_lno(TestResult * tr); + +/** + * Retrieve file name at which a failure occurred, if applicable. + * + * @return If the test resulted in a failure, returns a string + * containing the name of the file where the failure + * occurred; otherwise returns NULL. + * + * @since 0.6.0 + */ +CK_DLL_EXP const char *CK_EXPORT tr_lfile(TestResult * tr); + +/** + * Retrieve test case name in which a failure occurred, if applicable. + * + * @return If the test resulted in a failure, returns a string + * containing the name of the test suite where the failure + * occurred; otherwise returns NULL. + * + * @since 0.6.0 + */ +CK_DLL_EXP const char *CK_EXPORT tr_tcname(TestResult * tr); + +/** + * Creates a suite runner for the given suite. + * + * Once created, additional suites can be added to the + * suite runner using srunner_add_suite(), and the suite runner can be + * run with srunner_run_all(). Once finished, the suite runner + * must be freed with srunner_free(). + * + * @param s suite to generate a suite runner for + * + * @return suite runner for the given suite + * + * @since 0.6.0 + */ +CK_DLL_EXP SRunner *CK_EXPORT srunner_create(Suite * s); + +/** + * Add an additional suite to a suite runner. + * + * The first suite in a suite runner is always added in srunner_create(). + * This call adds additional suites to a suite runner. + * + * @param sr suite runner to add the given suite + * @param s suite to add to the given suite runner + * + * @since 0.7.0 + */ +CK_DLL_EXP void CK_EXPORT srunner_add_suite(SRunner * sr, Suite * s); + +/** + * Frees a suite runner, including all contained suite and test cases. + * + * This call is responsible for freeing all resources related to a + * suite runner and all contained suites and test cases. Suite and + * test cases need not be freed individually, as this call handles that. + * + * @param sr suite runner to free + * + * @since 0.6.0 + */ +CK_DLL_EXP void CK_EXPORT srunner_free(SRunner * sr); + +/** + * Runs a suite runner and all contained suite, printing results to + * stdout as specified by the print_mode. + * + * In addition to running all suites, if the suite runner has been + * configured to output to a log, that is also performed. + * + * Note that if the CK_RUN_CASE, CK_RUN_SUITE, CK_INCLUDE_TAGS and/or + * CK_EXCLUDE_TAGS environment variables are defined, then only the + * named suites or test cases will run. + * + * @param sr suite runner to run all suites from + * @param print_mode the verbosity in which to report results to stdout + * + * @since 0.6.0 + */ +CK_DLL_EXP void CK_EXPORT srunner_run_all(SRunner * sr, + enum print_output print_mode); + +/** + * Run a specific suite or test case from a suite runner, printing results + * to stdout as specified by the print_mode. + * + * In addition to running any applicable suites or test cases, if the + * suite runner has been configured to output to a log, that is also + * performed. + * + * Note that if the sname and tcname parameters are passed as null + * then the function will fallback to using the environment variables + * CK_RUN_SUITE and CK_RUN_CASE respectively in order to select the + * suite/cases. + * + * Similarly if the CK_INCLUDE_TAGS and/or CK_EXCLUDE_TAGS environment + * variables are defined then these will further filter the test cases + * (see srunner_run_tagged, below). + * + * @param sr suite runner where the given suite or test case must be + * @param sname suite name to run. A NULL means use the value of the + * environment variable CK_RUN_SUITE if set, otherwise run "any/every + * suite". + * @param tcname test case name to run. A NULL means use the value of + * the environment variable CK_RUN_CASE if set, otherwise run + * "any/every case". + * @param print_mode the verbosity in which to report results to stdout + * + * @since 0.9.9 + */ +CK_DLL_EXP void CK_EXPORT srunner_run(SRunner * sr, const char *sname, + const char *tcname, + enum print_output print_mode); + + +/** + * Run a specific suite or test case or testcases with specific tags + * from a suite runner, printing results to stdout as specified by the + * print_mode. + * + * In addition to running any applicable suites or test cases, if the + * suite runner has been configured to output to a log, that is also + * performed. + * + * Note that if sname, tcname, include_tags, exclude_tags parameters + * are passed as NULL then if the environment variables CK_RUN_SUITE, + * CK_RUN_CASE, CK_INCLUDE_TAGS, CK_EXCLUDE_TAGS are defined then these + * values will be used instead. + * + * @param sr suite runner where the given suite or test case must be + * @param sname suite name to run. A NULL means use the value of the + * environment variable CK_RUN_SUITE if set, otherwise run "any/every + * suite". + * @param tcname test case name to run. A NULL means use the value of + * the environment variable CK_RUN_CASE if set, otherwise run + * "any/every case". + * @param include_tags space separate list of tags. Only run test + * cases that share one of these tags. A NULL means use the value of + * the environment variable CK_INCLUDE_TAGS if set, otherwise run + * "any/every test case". + * @param exclude_tags space separate list of tags. Only run test + * cases that do not share one of these tags even if they are selected + * by an included tag. A NULL means use the value of the environment + * variable CK_EXCLUDE_TAGS if set, otherwise run "any/every test + * case". + * @param print_mode the verbosity in which to report results to stdout + * + * @since 0.11.0 + */ +CK_DLL_EXP void CK_EXPORT srunner_run_tagged(SRunner * sr, const char *sname, + const char *tcname, + const char *include_tags, + const char *exclude_tags, + enum print_output print_mode); + +/** + * Retrieve the number of failed tests executed by a suite runner. + * + * This value represents both test failures and errors. + * + * @param sr suite runner to query for all failed tests + * + * @return number of test failures and errors found by the suite runner + * + * @since 0.6.1 + */ +CK_DLL_EXP int CK_EXPORT srunner_ntests_failed(SRunner * sr); + +/** + * Retrieve the total number of tests run by a suite runner. + * + * @param sr suite runner to query for all tests run + * + * @return number of all tests run by the suite runner + * + * @since 0.6.1 + */ +CK_DLL_EXP int CK_EXPORT srunner_ntests_run(SRunner * sr); + +/** + * Return an array of results for all failures found by a suite runner. + * + * Number of results is equal to srunner_nfailed_tests(). + * + * Information about individual results can be queried using: + * tr_rtype(), tr_ctx(), tr_msg(), tr_lno(), tr_lfile(), and tr_tcname(). + * + * Memory is malloc'ed and must be freed; however free the entire structure + * instead of individual test cases. + * + * @param sr suite runner to retrieve results from + * + * @return array of TestResult objects + * + * @since 0.6.0 + */ +CK_DLL_EXP TestResult **CK_EXPORT srunner_failures(SRunner * sr); + +/** + * Return an array of results for all tests run by a suite runner. + * + * Number of results is equal to srunner_ntests_run(), and excludes + * failures due to setup function failure. + * + * Information about individual results can be queried using: + * tr_rtype(), tr_ctx(), tr_msg(), tr_lno(), tr_lfile(), and tr_tcname(). + * + * Memory is malloc'ed and must be freed; however free the entire structure + * instead of individual test cases. + * + * @param sr suite runner to retrieve results from + * + * @return array of TestResult objects + * + * @since 0.6.1 +*/ +CK_DLL_EXP TestResult **CK_EXPORT srunner_results(SRunner * sr); + +/** + * Print the results contained in an SRunner to stdout. + * + * @param sr suite runner to print results for to stdout + * @param print_mode the print_output (verbosity) to use to report + * the result + * + * @since 0.7.0 + */ +CK_DLL_EXP void CK_EXPORT srunner_print(SRunner * sr, + enum print_output print_mode); + +/** + * Set the suite runner to output the result in log format to the + * given file. + * + * Note: log file setting is an initialize only operation -- it should + * be done immediately after SRunner creation, and the log file can't be + * changed after being set. + * + * This setting does not conflict with the other log output types; + * all logging types can occur concurrently if configured. + * + * @param sr suite runner to log results of in log format + * @param fname file name to output log results to + * + * @since 0.7.1 +*/ +CK_DLL_EXP void CK_EXPORT srunner_set_log(SRunner * sr, const char *fname); + +/** + * Checks if the suite runner is assigned a file for log output. + * + * @param sr suite runner to check + * + * @return 1 iff the suite runner currently is configured to output + * in log format; 0 otherwise + * + * @since 0.7.1 + */ +CK_DLL_EXP int CK_EXPORT srunner_has_log(SRunner * sr); + +/** + * Retrieves the name of the currently assigned file + * for log output, if any exists. + * + * @return the name of the log file, or NULL if none is configured + * + * @since 0.7.1 + */ +CK_DLL_EXP const char *CK_EXPORT srunner_log_fname(SRunner * sr); + +/** + * Set the suite runner to output the result in XML format to the + * given file. + * + * Note: XML file setting is an initialize only operation -- it should + * be done immediately after SRunner creation, and the XML file can't be + * changed after being set. + * + * This setting does not conflict with the other log output types; + * all logging types can occur concurrently if configured. + * + * @param sr suite runner to log results of in XML format + * @param fname file name to output XML results to + * + * @since 0.9.1 +*/ +CK_DLL_EXP void CK_EXPORT srunner_set_xml(SRunner * sr, const char *fname); + +/** + * Checks if the suite runner is assigned a file for XML output. + * + * @param sr suite runner to check + * + * @return 1 iff the suite runner currently is configured to output + * in XML format; 0 otherwise + * + * @since 0.9.1 + */ +CK_DLL_EXP int CK_EXPORT srunner_has_xml(SRunner * sr); + +/** + * Retrieves the name of the currently assigned file + * for XML output, if any exists. + * + * @return the name of the XML file, or NULL if none is configured + * + * @since 0.9.1 + */ +CK_DLL_EXP const char *CK_EXPORT srunner_xml_fname(SRunner * sr); + +/** + * Set the suite runner to output the result in TAP format to the + * given file. + * + * Note: TAP file setting is an initialize only operation -- it should + * be done immediately after SRunner creation, and the TAP file can't be + * changed after being set. + * + * This setting does not conflict with the other log output types; + * all logging types can occur concurrently if configured. + * + * @param sr suite runner to log results of in TAP format + * @param fname file name to output TAP results to + * + * @since 0.9.12 +*/ +CK_DLL_EXP void CK_EXPORT srunner_set_tap(SRunner * sr, const char *fname); + +/** + * Checks if the suite runner is assigned a file for TAP output. + * + * @param sr suite runner to check + * + * @return 1 iff the suite runner currently is configured to output + * in TAP format; 0 otherwise + * + * @since 0.9.12 + */ +CK_DLL_EXP int CK_EXPORT srunner_has_tap(SRunner * sr); + +/** + * Retrieves the name of the currently assigned file + * for TAP output, if any exists. + * + * @return the name of the TAP file, or NULL if none is configured + * + * @since 0.9.12 + */ +CK_DLL_EXP const char *CK_EXPORT srunner_tap_fname(SRunner * sr); + +/** + * Enum describing the current fork usage. + */ +enum fork_status +{ + CK_FORK_GETENV, /**< look in the environment for CK_FORK */ + CK_FORK, /**< call fork to run tests */ + CK_NOFORK /**< don't call fork */ +}; + +/** + * Retrieve the current fork status for the given suite runner + * + * @param sr suite runner to check fork status of + * + * @since 0.8.0 + */ +CK_DLL_EXP enum fork_status CK_EXPORT srunner_fork_status(SRunner * sr); + +/** + * Set the fork status for a given suite runner. + * + * The default fork status is CK_FORK_GETENV, which will look + * for the CK_FORK environment variable, which can be set to + * "yes" or "no". If the environment variable is not present, + * CK_FORK will be used if fork() is available on the system, + * otherwise CK_NOFORK is used. + * + * If set to CK_FORK or CK_NOFORK, the environment variable + * if defined is ignored. + * + * If Check is compiled without support for fork(), attempting + * to set the status to CK_FORK is ignored. + * + * @param sr suite runner to assign the fork status to + * @param fstat fork status to assign + * + * @since 0.8.0 + */ +CK_DLL_EXP void CK_EXPORT srunner_set_fork_status(SRunner * sr, + enum fork_status fstat); + +/** + * Invoke fork() during a test and assign the child to the same + * process group that the rest of the test case uses. + * + * One can invoke fork() directly during a test; however doing so + * may not guarantee that any children processes are destroyed once + * the test finishes. Once a test has completed, all processes in + * the process group will be killed; using this wrapper will prevent + * orphan processes. + * + * If Check is compiled without fork() support this call simply + * return -1 and does nothing. + * + * @return On success, the PID of the child process is returned in + * the parent, and 0 is returned in the child. On failure, + * a value of -1 is returned to the parent process and no + * child process is created. + * + * @since 0.9.3 + */ +CK_DLL_EXP pid_t CK_EXPORT check_fork(void); + +/** + * Wait for the pid and exit. + * + * This is to be used in conjunction with check_fork(). When called, + * will wait for the given process to terminate. If the process + * exited without error, exit(EXIT_SUCCESS) is invoked; otherwise + * exit(EXIT_FAILURE) is invoked. + * + * If Check is compiled without support for fork(), this invokes + * exit(EXIT_FAILURE). + * + * @param pid process to wait for, created by check_fork() + * + * @since 0.9.3 + */ +CK_DLL_EXP void CK_EXPORT check_waitpid_and_exit(pid_t pid) CK_ATTRIBUTE_NORETURN; + +/** + * Set the maximal assertion message size. + * + * This protects the code against unintentional extremely large assertion messages + * (values of up to 4GB were seen in the wild). + * The usual size for a message is less than 80 bytes. + * + * If the environment variable CK_MAX_MSG_SIZE is defined to a positive value, it is used. + * Otherwise, if a positive maximal message size is set via this function, it is used. + * Otherwise, the maximal message size is one assigned at compile time (4K bytes). + * + * @param max_msg_size the maximal assertion message size. + * + * @since 0.12.0 + */ +CK_DLL_EXP void CK_EXPORT check_set_max_msg_size(size_t max_msg_size); + +#ifdef __cplusplus +CK_CPPEND +#endif + +#endif /* CHECK_H */ diff --git a/deps/windows/check_error.h b/deps/windows/check_error.h new file mode 100644 index 0000000..0dc5bb9 --- /dev/null +++ b/deps/windows/check_error.h @@ -0,0 +1,39 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef ERROR_H +#define ERROR_H + +#include "../lib/libcompat.h" +#include + +extern jmp_buf error_jmp_buffer; + +/* Include stdlib.h beforehand */ + +/* Print error message and die + If fmt ends in colon, include system error information */ +void eprintf(const char *fmt, const char *file, int line, + ...) CK_ATTRIBUTE_NORETURN CK_ATTRIBUTE_FORMAT(printf, 1, 4); +/* malloc or die */ +void *emalloc(size_t n); +void *erealloc(void *, size_t n); + +#endif /*ERROR_H */ diff --git a/deps/windows/check_impl.h b/deps/windows/check_impl.h new file mode 100644 index 0000000..f4e8c59 --- /dev/null +++ b/deps/windows/check_impl.h @@ -0,0 +1,140 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_IMPL_H +#define CHECK_IMPL_H + +/* This header should be included by any module that needs + to know the implementation details of the check structures + Include stdio.h, time.h, & list.h before this header +*/ + +#define US_PER_SEC 1000000 +#define NANOS_PER_SECONDS 1000000000 + +/** calculate the difference in useconds out of two "struct timespec"s */ +#define DIFF_IN_USEC(begin, end) \ + ( (((end).tv_sec - (begin).tv_sec) * US_PER_SEC) + \ + ((end).tv_nsec/1000) - ((begin).tv_nsec/1000) ) + +typedef struct TF +{ + const TTest * ttest; + int loop_start; + int loop_end; + int signal; + signed char allowed_exit_value; +} TF; + +struct Suite +{ + const char *name; + List *tclst; /* List of test cases */ +}; + +typedef struct Fixture +{ + int ischecked; + SFun fun; +} Fixture; + +struct TCase +{ + const char *name; + struct timespec timeout; + List *tflst; /* list of test functions */ + List *unch_sflst; + List *unch_tflst; + List *ch_sflst; + List *ch_tflst; + List *tags; +}; + +typedef struct TestStats +{ + int n_checked; + int n_failed; + int n_errors; +} TestStats; + +struct TestResult +{ + enum test_result rtype; /* Type of result */ + enum ck_result_ctx ctx; /* When the result occurred */ + char *file; /* File where the test occured */ + int line; /* Line number where the test occurred */ + int iter; /* The iteration value for looping tests */ + int duration; /* duration of this test in microseconds */ + const char *tcname; /* Test case that generated the result */ + const char *tname; /* Test that generated the result */ + char *msg; /* Failure message */ +}; + +TestResult *tr_create(void); +void tr_reset(TestResult * tr); +void tr_free(TestResult * tr); + +enum cl_event +{ + CLINITLOG_SR, /* Initialize log file */ + CLENDLOG_SR, /* Tests are complete */ + CLSTART_SR, /* Suite runner start */ + CLSTART_S, /* Suite start */ + CLEND_SR, /* Suite runner end */ + CLEND_S, /* Suite end */ + CLSTART_T, /* A test case is about to run */ + CLEND_T /* Test case end */ +}; + +typedef void (*LFun) (SRunner *, FILE *, enum print_output, + void *, enum cl_event); + +typedef struct Log +{ + FILE *lfile; + LFun lfun; + int close; + enum print_output mode; +} Log; + +struct SRunner +{ + List *slst; /* List of Suite objects */ + TestStats *stats; /* Run statistics */ + List *resultlst; /* List of unit test results */ + const char *log_fname; /* name of log file */ + const char *xml_fname; /* name of xml output file */ + const char *tap_fname; /* name of tap output file */ + List *loglst; /* list of Log objects */ + enum fork_status fstat; /* controls if suites are forked or not + NOTE: Don't use this value directly, + instead use srunner_fork_status */ +}; + + +void set_fork_status(enum fork_status fstat); +enum fork_status cur_fork_status(void); + +clockid_t check_get_clockid(void); + +unsigned int tcase_matching_tag(TCase *tc, List *check_for); +List *tag_string_to_list(const char *tags_string); + +#endif /* CHECK_IMPL_H */ diff --git a/deps/windows/check_list.h b/deps/windows/check_list.h new file mode 100644 index 0000000..e0b5c8c --- /dev/null +++ b/deps/windows/check_list.h @@ -0,0 +1,59 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_LIST_H +#define CHECK_LIST_H + +typedef struct List List; + +/* Create an empty list */ +List *check_list_create(void); + +/* Is list at end? */ +int check_list_at_end(List * lp); + +/* Position list at front */ +void check_list_front(List * lp); + +/* Add a value to the front of the list, + positioning newly added value as current value. + More expensive than list_add_end, as it uses memmove. */ +void check_list_add_front(List * lp, void *val); + +/* Add a value to the end of the list, + positioning newly added value as current value */ +void check_list_add_end(List * lp, void *val); + +/* Give the value of the current node */ +void *check_list_val(List * lp); + +/* Position the list at the next node */ +void check_list_advance(List * lp); + +/* Free a list, but don't free values */ +void check_list_free(List * lp); + +void check_list_apply(List * lp, void (*fp) (void *)); + +/* Return true if the list contains the value, false otherwise */ +int check_list_contains(List * lp, void *val); + + +#endif /* CHECK_LIST_H */ diff --git a/deps/windows/check_log.h b/deps/windows/check_log.h new file mode 100644 index 0000000..7223b98 --- /dev/null +++ b/deps/windows/check_log.h @@ -0,0 +1,55 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_LOG_H +#define CHECK_LOG_H + +void log_srunner_start(SRunner * sr); +void log_srunner_end(SRunner * sr); +void log_suite_start(SRunner * sr, Suite * s); +void log_suite_end(SRunner * sr, Suite * s); +void log_test_end(SRunner * sr, TestResult * tr); +void log_test_start(SRunner * sr, TCase * tc, TF * tfun); + +void stdout_lfun(SRunner * sr, FILE * file, enum print_output, + void *obj, enum cl_event evt); + +void lfile_lfun(SRunner * sr, FILE * file, enum print_output, + void *obj, enum cl_event evt); + +void xml_lfun(SRunner * sr, FILE * file, enum print_output, + void *obj, enum cl_event evt); + +void tap_lfun(SRunner * sr, FILE * file, enum print_output, + void *obj, enum cl_event evt); + +void subunit_lfun(SRunner * sr, FILE * file, enum print_output, + void *obj, enum cl_event evt); + +void srunner_register_lfun(SRunner * sr, FILE * lfile, int close, + LFun lfun, enum print_output); + +FILE *srunner_open_lfile(SRunner * sr); +FILE *srunner_open_xmlfile(SRunner * sr); +FILE *srunner_open_tapfile(SRunner * sr); +void srunner_init_logging(SRunner * sr, enum print_output print_mode); +void srunner_end_logging(SRunner * sr); + +#endif /* CHECK_LOG_H */ diff --git a/deps/windows/check_msg.h b/deps/windows/check_msg.h new file mode 100644 index 0000000..c66a35a --- /dev/null +++ b/deps/windows/check_msg.h @@ -0,0 +1,39 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_MSG_NEW_H +#define CHECK_MSG_NEW_H + + +/* Functions implementing messaging during test runs */ + +void send_failure_info(const char *msg); +void send_loc_info(const char *file, int line); +void send_ctx_info(enum ck_result_ctx ctx); +void send_duration_info(int duration); + +TestResult *receive_test_result(int waserror); + +void setup_messaging(void); +void teardown_messaging(void); + +FILE *open_tmp_file(char **name); + +#endif /*CHECK_MSG_NEW_H */ diff --git a/deps/windows/check_pack.h b/deps/windows/check_pack.h new file mode 100644 index 0000000..3d469d2 --- /dev/null +++ b/deps/windows/check_pack.h @@ -0,0 +1,84 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_PACK_H +#define CHECK_PACK_H + + +enum ck_msg_type +{ + CK_MSG_CTX, + CK_MSG_FAIL, + CK_MSG_LOC, + CK_MSG_DURATION, + CK_MSG_LAST +}; + +typedef struct CtxMsg +{ + enum ck_result_ctx ctx; +} CtxMsg; + +typedef struct LocMsg +{ + int line; + char *file; +} LocMsg; + +typedef struct FailMsg +{ + char *msg; +} FailMsg; + +typedef struct DurationMsg +{ + int duration; +} DurationMsg; + +typedef union +{ + CtxMsg ctx_msg; + FailMsg fail_msg; + LocMsg loc_msg; + DurationMsg duration_msg; +} CheckMsg; + +typedef struct RcvMsg +{ + enum ck_result_ctx lastctx; + enum ck_result_ctx failctx; + char *fixture_file; + int fixture_line; + char *test_file; + int test_line; + char *msg; + int duration; +} RcvMsg; + +void rcvmsg_free(RcvMsg * rmsg); + + +int pack(enum ck_msg_type type, char **buf, CheckMsg * msg); +int upack(char *buf, CheckMsg * msg, enum ck_msg_type *type); + +void ppack(FILE * fdes, enum ck_msg_type type, CheckMsg * msg); +RcvMsg *punpack(FILE * fdes); + +#endif /*CHECK_PACK_H */ diff --git a/deps/windows/check_print.h b/deps/windows/check_print.h new file mode 100644 index 0000000..eabd8b9 --- /dev/null +++ b/deps/windows/check_print.h @@ -0,0 +1,32 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_PRINT_H +#define CHECK_PRINT_H + +/* escape XML special characters (" ' < > &) in str and print to file */ +void fprint_xml_esc(FILE * file, const char *str); +void tr_fprint(FILE * file, TestResult * tr, enum print_output print_mode); +void tr_xmlprint(FILE * file, TestResult * tr, enum print_output print_mode); +void srunner_fprint(FILE * file, SRunner * sr, enum print_output print_mode); +enum print_output get_env_printmode(void); + + +#endif /* CHECK_PRINT_H */ diff --git a/deps/windows/check_stdint.h b/deps/windows/check_stdint.h new file mode 100644 index 0000000..e4458bc --- /dev/null +++ b/deps/windows/check_stdint.h @@ -0,0 +1,41 @@ +/*-*- mode:C; -*- */ +/* + * Check: a unit test framework for C + * + * Copyright (C) 2013 Branden Archer + * Copyright (C) 2019 Mikko Johannes Koivunalho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _CHECK_CHECK_STDINT_H +#define _CHECK_CHECK_STDINT_H 1 +#ifndef _GENERATED_STDINT_H +#define _GENERATED_STDINT_H "Check 0.15.2" +/* generated using CMake 3.20.2 from file cmake/check_stdint.h.in */ + +/* Imported CMake variables created during build. */ +#define HAVE_STDINT_H 1 + +#ifdef HAVE_STDINT_H +#define _STDINT_HAVE_STDINT_H 1 +#include +#undef HAVE_STDINT_H +#endif /* defined HAVE_STDINT_H */ + +/* Define only once */ +#endif /* _GENERATED_STDINT_H */ +#endif /* _CHECK_CHECK_STDINT_H */ diff --git a/deps/windows/check_str.h b/deps/windows/check_str.h new file mode 100644 index 0000000..5f52e61 --- /dev/null +++ b/deps/windows/check_str.h @@ -0,0 +1,44 @@ +/* + * Check: a unit test framework for C + * Copyright (C) 2001, 2002 Arien Malec + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef CHECK_STR_H +#define CHECK_STR_H + +#include "../lib/libcompat.h" + +/* Return a string representation of the given TestResult. Return + value has been malloc'd, and must be freed by the caller */ +char *tr_str(TestResult * tr); + +/* Return a string representation of the given TestResult message + without the test id or result type. This is suitable for separate + formatting of the test and the message. Return value has been + malloc'd, and must be freed by the caller */ +char *tr_short_str(TestResult * tr); + +/* Return a string representation of the given SRunner's run + statistics (% passed, num run, passed, errors, failures). Return + value has been malloc'd, and must be freed by the caller +*/ +char *sr_stat_str(SRunner * sr); + +char *ck_strdup_printf(const char *fmt, ...) CK_ATTRIBUTE_FORMAT(printf, 1, 2); + +#endif /* CHECK_STR_H */ diff --git a/gbemu/CMakeLists.txt b/gbemu/CMakeLists.txt new file mode 100644 index 0000000..cf34752 --- /dev/null +++ b/gbemu/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.10) +project(gbemu) +FILE(GLOB SRCS *.c) +add_executable(gbemu ${SRCS}) +target_include_directories(gbemu PUBLIC ../include) +target_link_libraries(gbemu PUBLIC Lib) \ No newline at end of file diff --git a/gbemu/main.c b/gbemu/main.c new file mode 100644 index 0000000..4f31466 --- /dev/null +++ b/gbemu/main.c @@ -0,0 +1,5 @@ +#include + +int main(int argc, char **argv) { + return emu_run(argc, argv); +} \ No newline at end of file diff --git a/include/bus.h b/include/bus.h new file mode 100644 index 0000000..4a5bfc6 --- /dev/null +++ b/include/bus.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +u8 bus_read(u16 address); +void bus_write(u16 address, u8 value); \ No newline at end of file diff --git a/include/cart.h b/include/cart.h new file mode 100644 index 0000000..b8bd113 --- /dev/null +++ b/include/cart.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +typedef struct { + u8 entry[4]; + u8 logo[0x30]; + + char title[16]; + u16 new_lic_code; + u8 sgb_flag; + u8 type; + u8 rom_size; + u8 ram_size; + u8 dest_code; + u8 lic_code; + u8 version; + u8 checksum; + u16 global_checksum; +} rom_header; + +bool cart_load(char *cart); \ No newline at end of file diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..2ed67ba --- /dev/null +++ b/include/common.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#define BIT(a, n) ((a & (1 << n)) ? 1 : 0) +#define BIT_SET(a, n, on) (on ? (a) |= (1 << n) : (a) &= !(1 << n)) +#define BETWEEN(a, b, c) ((a >= b) && (a <= c)) + +void delay(u32 ms); \ No newline at end of file diff --git a/include/cpu.h b/include/cpu.h new file mode 100644 index 0000000..1aa4e62 --- /dev/null +++ b/include/cpu.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void cpu_init(); +bool cpu_step(); \ No newline at end of file diff --git a/include/emu.h b/include/emu.h new file mode 100644 index 0000000..2b6da59 --- /dev/null +++ b/include/emu.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +typedef struct { + bool paused; + bool running; + u64 ticks; +} emu_context; + +int emu_run(int, char**); +emu_context *emu_get_context(); \ No newline at end of file diff --git a/include/ppu.h b/include/ppu.h new file mode 100644 index 0000000..f7c136c --- /dev/null +++ b/include/ppu.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void ppu_init(); +void ppu_tick(); \ No newline at end of file diff --git a/include/timer.h b/include/timer.h new file mode 100644 index 0000000..eff6444 --- /dev/null +++ b/include/timer.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void timer_init(); +void timer_tick(); \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..a831290 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.10) +project(Lib) +file(GLOB SRCS *.c) +add_library(Lib STATIC ${SRCS}) +target_include_directories(Lib PUBLIC ../include ../deps/SDL/include ../deps/SDL_ttf) +target_link_libraries(Lib SDL2-static SDL2_ttf::SDL2_ttf-static) \ No newline at end of file diff --git a/lib/bus.c b/lib/bus.c new file mode 100644 index 0000000..e69de29 diff --git a/lib/cart.c b/lib/cart.c new file mode 100644 index 0000000..102f4a8 --- /dev/null +++ b/lib/cart.c @@ -0,0 +1,6 @@ +#include + +bool cart_load(char *cart) { + printf("Cart loading not implemented yet\n"); + return false; +} \ No newline at end of file diff --git a/lib/cpu.c b/lib/cpu.c new file mode 100644 index 0000000..dbb84fb --- /dev/null +++ b/lib/cpu.c @@ -0,0 +1,10 @@ +#include + +void cpu_init() { + +} + +bool cpu_step() { + printf("Cpu not yet implemented.\n"); + return false; +} \ No newline at end of file diff --git a/lib/emu.c b/lib/emu.c new file mode 100644 index 0000000..5eeb652 --- /dev/null +++ b/lib/emu.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include + +static emu_context ctx; + +emu_context *emu_get_context() { + return &ctx; +} + +void delay(u32 ms) { + SDL_Delay(ms); +} + +int emu_run(int argc, char **argv) { + if (argc < 2) { + printf("Usage: gbemu \n"); + return -1; + } + + if(!cart_load(argv[1])) { + printf("Failed to load ROM file: %s\n", argv[1]); + return -2; + } + + printf("Cart loaded..\n"); + + SDL_Init(SDL_INIT_VIDEO); + printf("SDL INIT\n"); + TTF_Init(); + printf("TTF INIT\n"); + + cpu_init(); + + ctx.running = true; + ctx.paused = false; + ctx.ticks = 0; + + while (ctx.running) { + if (ctx.paused) { + delay(10); + continue; + } + + if (!cpu_step()) { + printf("CPU stopped\n"); + return -3; + } + + ctx.ticks++; + } + + return 0; +} \ No newline at end of file diff --git a/lib/ppu.c b/lib/ppu.c new file mode 100644 index 0000000..d3ab571 --- /dev/null +++ b/lib/ppu.c @@ -0,0 +1,9 @@ +#include + +void ppu_init() { + +} + +void ppu_tick() { + +} \ No newline at end of file diff --git a/lib/timer.c b/lib/timer.c new file mode 100644 index 0000000..e81a980 --- /dev/null +++ b/lib/timer.c @@ -0,0 +1,10 @@ + +#include + +void timer_init() { + +} + +void timer_tick() { + +} \ No newline at end of file diff --git a/roms/01-special.gb b/roms/01-special.gb new file mode 100644 index 0000000..ad3e998 Binary files /dev/null and b/roms/01-special.gb differ diff --git a/roms/02-interrupts.gb b/roms/02-interrupts.gb new file mode 100644 index 0000000..2089594 Binary files /dev/null and b/roms/02-interrupts.gb differ diff --git a/roms/03-op sp,hl.gb b/roms/03-op sp,hl.gb new file mode 100644 index 0000000..50b3cc7 Binary files /dev/null and b/roms/03-op sp,hl.gb differ diff --git a/roms/04-op r,imm.gb b/roms/04-op r,imm.gb new file mode 100644 index 0000000..58ca7b8 Binary files /dev/null and b/roms/04-op r,imm.gb differ diff --git a/roms/05-op rp.gb b/roms/05-op rp.gb new file mode 100644 index 0000000..1c19d92 Binary files /dev/null and b/roms/05-op rp.gb differ diff --git a/roms/06-ld r,r.gb b/roms/06-ld r,r.gb new file mode 100644 index 0000000..d497bfd Binary files /dev/null and b/roms/06-ld r,r.gb differ diff --git a/roms/07-jr,jp,call,ret,rst.gb b/roms/07-jr,jp,call,ret,rst.gb new file mode 100644 index 0000000..5c8d20b Binary files /dev/null and b/roms/07-jr,jp,call,ret,rst.gb differ diff --git a/roms/08-misc instrs.gb b/roms/08-misc instrs.gb new file mode 100644 index 0000000..4da139b Binary files /dev/null and b/roms/08-misc instrs.gb differ diff --git a/roms/09-op r,r.gb b/roms/09-op r,r.gb new file mode 100644 index 0000000..e30e6ec Binary files /dev/null and b/roms/09-op r,r.gb differ diff --git a/roms/10-bit ops.gb b/roms/10-bit ops.gb new file mode 100644 index 0000000..8988458 Binary files /dev/null and b/roms/10-bit ops.gb differ diff --git a/roms/11-op a,(hl).gb b/roms/11-op a,(hl).gb new file mode 100644 index 0000000..0634b7f Binary files /dev/null and b/roms/11-op a,(hl).gb differ diff --git a/roms/cpu_instrs.gb b/roms/cpu_instrs.gb new file mode 100644 index 0000000..7b06221 Binary files /dev/null and b/roms/cpu_instrs.gb differ diff --git a/roms/dmg-acid2.gb b/roms/dmg-acid2.gb new file mode 100644 index 0000000..a25ef94 Binary files /dev/null and b/roms/dmg-acid2.gb differ diff --git a/roms/mem_timing.gb b/roms/mem_timing.gb new file mode 100644 index 0000000..78766b5 Binary files /dev/null and b/roms/mem_timing.gb differ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..882f8df --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10) +project(tests) +FILE(GLOB SRCS *.c) +add_executable(tests ${SRCS}) +target_include_directories(tests PUBLIC ../include ../build/deps/check/src ../build/deps/check) +target_link_libraries(tests PUBLIC Lib check) +add_test(NAME mytests COMMAND tests) \ No newline at end of file diff --git a/tests/test.c b/tests/test.c new file mode 100644 index 0000000..8311738 --- /dev/null +++ b/tests/test.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +#include + +START_TEST(test_nothing) { + bool b = cpu_step(); + ck_assert_uint_eq(b, false); +} END_TEST + +Suite *stack_suite() { + Suite *s = suite_create("emu"); + TCase *tc = tcase_create("core"); + + tcase_add_test(tc, test_nothing); + suite_add_tcase(s, tc); + + return s; +} + +int main(int argc, char **argv) { + Suite *s = stack_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + int nf = srunner_ntests_failed(sr); + srunner_free(sr); + return nf == 0 ? 0 : -1; +} \ No newline at end of file