diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 500be77..21cb6b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,28 +10,25 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: 'recursive' - name: Setup Dependencies run: | sudo apt install -y -qq libjansson-dev - - - name: Create Build Environment - run: cmake -E make_directory ${{github.workspace}}/build - - name: Configure CMake + - name: Configure shell: bash - working-directory: ${{github.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE + working-directory: ${{github.workspace}} + run: cmake -Bbuild -S . -DCMAKE_BUILD_TYPE=$BUILD_TYPE - name: Build - working-directory: ${{github.workspace}}/build + working-directory: ${{github.workspace}} shell: bash - run: cmake --build . --config $BUILD_TYPE + run: cmake --build ./build --config $BUILD_TYPE - name: Test - working-directory: ${{github.workspace}}/build + working-directory: ${{github.workspace}} shell: bash - run: ctest -C $BUILD_TYPE + run: cmake --build ./build --target test diff --git a/.gitmodules b/.gitmodules index 2b0728d..adf1a7f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,7 @@ [submodule "externals/cwalk"] path = externals/cwalk url = https://github.com/likle/cwalk.git + branch=v1.2.9 +[submodule "externals/fff"] + path = externals/fff + url = https://github.com/meekrosoft/fff.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a7cf61..e5a3e59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.16) +cmake_minimum_required (VERSION 3.30) project(etripator VERSION 0.9.0 @@ -8,7 +8,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") add_compile_definitions(_XOPEN_SOURCE=700) endif() -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") enable_testing() @@ -17,17 +17,16 @@ include(CheckSymbolExists) check_symbol_exists(strdup "string.h" HAVE_STRDUP) if(NOT CMAKE_BUILD_TYPE) + message(STATUS, "No build type specified. Force build type to Debug.") set(CMAKE_BUILD_TYPE "Debug") endif() -set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - find_package(Doxygen) find_package(Jansson) set(CMAKE_C_STANDARDS 11) -add_subdirectory(externals) +add_subdirectory(externals EXCLUDE_FROM_ALL) set(etripator_SRC message.c @@ -40,12 +39,14 @@ set(etripator_SRC section/save.c opcodes.c comment.c + comment/load.c + comment/save.c label.c label/load.c label/save.c irq.c memory.c - memorymap.c + memory_map.c rom.c cd.c ipl.c @@ -64,7 +65,7 @@ set(etripator_HDR label.h irq.h memory.h - memorymap.h + memory_map.h rom.h cd.h ipl.h @@ -72,9 +73,8 @@ set(etripator_HDR add_library(etripator STATIC ${etripator_SRC} ${etripator_HDR}) set_target_properties(etripator PROPERTIES C_STANDARD 11) -target_include_directories(etripator PUBLIC ${JANSSON_INCLUDE_DIRS} ${EXTRA_INCLUDE} externals) target_compile_definitions(etripator PRIVATE _POSIX_C_SOURCE) -target_link_libraries(etripator PUBLIC ${JANSSON_LIBRARIES} cwalk) +target_link_libraries(etripator PUBLIC jansson cwalk) add_executable(etripator_cli cli/etripator.c cli/options.c) set_target_properties(etripator_cli @@ -83,6 +83,7 @@ set_target_properties(etripator_cli C_STANDARD 11 ) target_include_directories(etripator_cli PRIVATE ${CMAKE_SOURCE_DIR}) +target_include_directories(etripator_cli PUBLIC externals) target_link_libraries(etripator_cli etripator argparse) set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/doxyfile.in) diff --git a/README.md b/README.md index c541b40..4414f81 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ * [Gfx unpacking](examples/maerchen_maze) in Maerchen Maze - メルヘンメイズ . * [Memory Base 128 detection](examples/tadaima) in Taidama Yusha Boshuchu - ただいま勇者募集中. * [Joypad routine](examples/sf2) of Street Fighter II' Champion Edition - ストリートファイターII'. -* [Games Express CD Card bank #0](examples/games_express). +* [Games Express CD Card](https://github.com/BlockoS/GamesExpressCDCard). * [Password check](examples/youkai_douchuuki) in Youkai Douchuuki - 妖怪道中記. ## Usage @@ -22,7 +22,7 @@ The options are : * **--help** or **-h** : displays help. * **--out** or **-o < file >** : main asm file containing includes for all sections as long the irq vector table if the irq-detect option is enabled. * **--labels** or **-l < file >** : labels definition filename. -* **--labels-out ** : extracted labels output filename. Otherwise the labels will be written to .YYMMDDhhmmss.lbl.\n" +* **--labels-out ** : extracted labels output filename. Otherwise the labels will be written to `".YYMMDDhhmmss.lbl"` * **--comments ** : comments description filename. * **--address** : print statement logical address and page in an inline comment. * **cfg** : configuration file. It is optional if irq detection is enabled. @@ -189,27 +189,24 @@ This can be acheived using a package manager (apt, brew, chocolate, pacman, ...) wget https://github.com/akheron/jansson/archive/v2.12.zip unzip v2.12.zip cd jansson-2.12 -mkdir -p build/cmake -mkdir -p build/install -cd build/cmake -cmake ../.. -DJANSSON_BUILD_DOCS=OFF \ - -DJANSSON_WITHOUT_TESTS=ON \ - -DCMAKE_INSTALL_PREFIX=../install -cmake --build . --target install -cd ../../.. +mkdir -p build +cmake -Bbuild/cmake -S . \ + -DJANSSON_BUILD_DOCS=OFF \ + -DJANSSON_WITHOUT_TESTS=ON \ + -DCMAKE_INSTALL_PREFIX=build/install +cmake --build build/cmake --target install ``` Now on to building etripator. ``` cd etripator -mkdir -p build/cmake -cd build/cmake -cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install +mkdir -p build +cmake -Bbuild/cmake -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=cmake/install ``` If you build jansson as shown earlier you may configure the project with ``` -cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install \ +cmake -Bbuild/cmake -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=build/install \ -DJANSSON_INCLUDE_DIR=../../jansson-2.12/build/install/include \ -DJANSSON_LIBRARY=../../jansson-2.12/build/install/lib/jansson.lib ``` @@ -218,7 +215,7 @@ cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install \ If everything went right, you can now compile the project with: ``` -cmake --build . --config Release --target install +cmake --build build/cmake --target install ``` ## Authors diff --git a/README.nfo b/README.nfo index fbda2f3..68bbcfd 100644 --- a/README.nfo +++ b/README.nfo @@ -246,27 +246,24 @@ or by building it from source. wget https://github.com/akheron/jansson/archive/v2.12.zip unzip v2.12.zip cd jansson-2.12 -mkdir -p build/cmake -mkdir -p build/install -cd build/cmake -cmake ../.. -DJANSSON_BUILD_DOCS=OFF \ - -DJANSSON_WITHOUT_TESTS=ON \ - -DCMAKE_INSTALL_PREFIX=../install -cmake --build . --target install -cd ../../.. +mkdir -p build +cmake -Bbuild/cmake -S . \ + -DJANSSON_BUILD_DOCS=OFF \ + -DJANSSON_WITHOUT_TESTS=ON \ + -DCMAKE_INSTALL_PREFIX=build/install +cmake --build build/cmake --target install Now on to building etripator. cd etripator -mkdir -p build/cmake -cd build/cmake -cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install +mkdir -p build +cmake -Bbuild/cmake -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=cmake/install If you build jansson as shown earlier you may configure the project with -cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install \ +cmake -Bbuild/cmake -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=build/install \ -DJANSSON_INCLUDE_DIR=../../jansson-2.12/build/install/include \ -DJANSSON_LIBRARY=../../jansson-2.12/build/install/lib/jansson.lib @@ -274,7 +271,7 @@ cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install \ If everything went right, you can now compile the project with: -cmake --build . --config Release --target install +cmake --build build/cmake --target install ¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ diff --git a/cd.c b/cd.c index a10f418..88542f2 100644 --- a/cd.c +++ b/cd.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,42 +36,46 @@ #include "cd.h" #include "message.h" -/* Adds CD RAM to memory map. */ -int cd_memmap(memmap_t *map) { - int i, ret = 0; - /* Allocate CD RAM */ - if(!mem_create(&map->mem[PCE_MEM_CD_RAM], 8 * 8192)) { - ERROR_MSG("Failed to allocate cd memory!\n"); - memmap_destroy(map); +/// Adds CD RAM to memory map. +bool cd_memory_map(MemoryMap *map) { + int i; + bool ret = false; + // Allocate CD RAM + if(!memory_create(&map->memory[PCE_MEMORY_CD_RAM], PCE_CD_RAM_BANK_COUNT * PCE_BANK_SIZE)) { + ERROR_MSG("Failed to allocate cd memory!"); + // Allocate System Card RAM + } else if (!memory_create(&map->memory[PCE_MEMORY_SYSCARD_RAM], PCE_SYSCARD_RAM_BANK_COUNT * PCE_BANK_SIZE)) { + ERROR_MSG("Failed to allocate system card memory!"); } else { - /* CD RAM is mapped to pages 0x80-0x88 (included). */ - for (i = 0; i <= 8; i++) { - map->page[0x80 + i] = &map->mem[PCE_MEM_CD_RAM].data[i * 8192]; + // CD RAM is mapped to pages 0x80-0x88 (included). + for (i = 0; i <= PCE_CD_RAM_BANK_COUNT; i++) { + map->page[PCE_CD_RAM_FIRST_PAGE + i].id = PCE_MEMORY_CD_RAM; + map->page[PCE_CD_RAM_FIRST_PAGE + i].bank = i; } - /* Allocate System Card RAM */ - if (!mem_create(&map->mem[PCE_MEM_SYSCARD_RAM], 24 * 8192)) { - ERROR_MSG("Failed to allocate system card memory!\n"); - memmap_destroy(map); - } else { - /* System Card RAM is mapped to pages 0x68-0x86. */ - for (i = 0; i < 24; i++) { - map->page[0x68 + i] = &map->mem[PCE_MEM_SYSCARD_RAM].data[i * 8192]; - } - ret = 1; + // System Card RAM is mapped to pages 0x68-0x86. + for (i = 0; i < PCE_SYSCARD_RAM_BANK_COUNT; i++) { + map->page[PCE_SYSCARD_RAM_FIRST_PAGE + i].id = PCE_MEMORY_SYSCARD_RAM; + map->page[PCE_SYSCARD_RAM_FIRST_PAGE + i].bank = i; } + ret = true; + } + + if(ret == false) { + memory_map_destroy(map); } return ret; } -/* Load CDROM data from file. */ -int cd_load(const char* filename, size_t start, size_t len, size_t sector_size, uint8_t page, size_t offset, memmap_t* map) { - int ret = 0; + +/// Load CDROM data from file. +bool cd_load(const char* filename, size_t start, size_t len, size_t sector_size, uint8_t page, size_t offset, MemoryMap* map) { + bool ret = false; FILE *in = fopen(filename, "rb"); if(in == NULL) { ERROR_MSG("Unable to open %s : %s", filename, strerror(errno)); } else { size_t remaining = len; size_t physical = (offset & 0x1FFFU) | (page << 0x0D); - for(ret=1; ret && remaining; ) { + for(ret=true; ret && remaining; ) { size_t count = 2048 - (start % 2048); if(count > remaining) { count = remaining; @@ -85,13 +89,17 @@ int cd_load(const char* filename, size_t start, size_t len, size_t sector_size, size_t current_page = physical >> 0x0D; size_t current_addr = physical & 0x1FFF; - ret = 0; - if(fseek(in, (long int)file_offset, SEEK_SET)) { + size_t bank_offset = current_addr + (map->page[current_page].bank * PCE_BANK_SIZE); + + // [todo] test that map->page[current_page].id != PCE_MEMORY_NONE + + ret = false; + if(fseek(in, (long int)file_offset, SEEK_SET) < 0) { ERROR_MSG("Offset out of bound : %s", strerror(errno)); - } else if(fread(map->page[current_page] + current_addr, 1, count, in) != count) { + } else if(fread(map->memory[map->page[current_page].id].data+bank_offset, 1, count, in) != count) { ERROR_MSG("Failed to read %d bytes : %s", count, strerror(errno)); } else { - ret = 1; + ret = true; } start += count; physical += count; diff --git a/cd.h b/cd.h index c70b4ab..67fdc2c 100644 --- a/cd.h +++ b/cd.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,26 +37,30 @@ #define ETRIPATOR_CD_H #include "config.h" -#include "memorymap.h" - -/** - * Adds CD RAM to memory map. - * \param map Memory map. - * \return 1 upon success, 0 if an error occured. - */ -int cd_memmap(memmap_t *map); - -/** - * Load CDROM data from file. - * \param [in] filename CDROM data filename. - * \param [in] start CDROM data offset. - * \param [in] len CDROM data length (in bytes). - * \param [in] sector_size CD sector size. - * \param [in] page Memory page. - * \param [in] offset memory page offset. - * \param [out] memmap Memory map. - * \return 1 upon success, 0 if an error occured. - */ -int cd_load(const char* filename, size_t start, size_t len, size_t sector_size, uint8_t page, size_t offset, memmap_t* map); +#include "memory_map.h" + +#define PCE_CD_RAM_BANK_COUNT 8U +#define PCE_SYSCARD_RAM_BANK_COUNT 24U + +#define PCE_CD_RAM_FIRST_PAGE 0x80U +#define PCE_SYSCARD_RAM_FIRST_PAGE 0x68U + +/// Adds CD RAM to memory map. +/// \param map Memory map. +/// \return true if the CD RAM and SYSCARD RAM memory areas were successfully created. +/// \return false if an error occured. +bool cd_memory_map(MemoryMap *map); + +/// Load CDROM data from file. +/// \param [in] filename CDROM data filename. +/// \param [in] start CDROM data offset. +/// \param [in] len CDROM data length (in bytes). +/// \param [in] sector_size CD sector size. +/// \param [in] page Memory page. +/// \param [in] offset Memory page offset. +/// \param [out] map Memory map. +/// \return true +/// \return false +bool cd_load(const char* filename, size_t start, size_t len, size_t sector_size, uint8_t page, size_t offset, MemoryMap* map); #endif // ETRIPATOR_CD_H diff --git a/cli/etripator.c b/cli/etripator.c index 52fad01..b86fb8c 100644 --- a/cli/etripator.c +++ b/cli/etripator.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include #include @@ -58,341 +58,312 @@ #include "options.h" -/* - exit callback - */ -void exit_callback(void) { msg_printer_destroy(); } - -/* - output labels -*/ -int label_output(cli_opt_t *option, label_repository_t *repository) { - char buffer[256]; +// exit callback +void exit_callback() { + message_printer_destroy(); +} - if (NULL == option->labels_out) { - /* We don't want to destroy the original label file. */ +// save found labels to a file named .YYmmddHHMMSS.lbl +static bool label_output(LabelRepository *repository, CommandLineOptions *options) { + bool ret = false; + char buffer[256U] = {0}; + if (options->labels_out == NULL) { + // Create a new filename as we don't want to destroy the original label file. const char *filename; size_t length; - char dateString[128]; - -#if defined(_MSC_VER) - struct tm now; - __time64_t tv; - _time64(&tv); - errno_t err = _localtime64_s(&now, &tv); - if (err == 0) { - strftime(dateString, 128, "%Y%m%d%H%M%S", &now); - } else { - sprintf(dateString, 128, "19871030133700"); - } -#else - struct timeval tv; - time_t t; - struct tm *now; - gettimeofday(&tv, NULL); - t = tv.tv_sec; - now = localtime(&t); - strftime(dateString, 128, "%Y%m%d%H%M%S", now); -#endif - cwk_path_get_basename(option->rom_filename, &filename, &length); + cwk_path_get_basename(options->rom_filename, &filename, &length); if(filename == NULL) { - filename = option->rom_filename; + filename = options->rom_filename; } - snprintf(buffer, 256, "%s.%s.lbl", filename, dateString); - option->labels_out = buffer; - } - if (!label_repository_save(option->labels_out, repository)) { - ERROR_MSG("Failed to write/update label file: %s", option->labels_out); - return 0; - } - return 1; -} + time_t t = time(NULL); + struct tm *now = localtime(&t); -static console_msg_printer_t g_console_printer; -static file_msg_printer_t g_file_printer; - -/* ---------------------------------------------------------------- */ -int main(int argc, const char **argv) { - cli_opt_t option; + char date_string[128U] = {0}; + strftime(date_string, sizeof(date_string), "%Y%m%d%H%M%S", now); - FILE *out; - FILE *main_file; - int i; - int ret, failure; - - memmap_t map; - - section_t *section; - int section_count; - - atexit(exit_callback); - - msg_printer_init(); - - file_msg_printer_init(&g_file_printer); - console_msg_printer_init(&g_console_printer); - - if (msg_printer_add(&g_file_printer.super)) { - fprintf(stderr, "Failed to setup file printer.\n"); - return EXIT_FAILURE; + snprintf(buffer, 256, "%s.%s.lbl", filename, date_string); + options->labels_out = buffer; } - if (msg_printer_add(&g_console_printer.super)) { - fprintf(stderr, "Failed to setup console printer.\n"); - return EXIT_FAILURE; + if (!label_repository_save(repository, options->labels_out)) { + ERROR_MSG("Failed to write/update label file: %s", options->labels_out); + } else { + ret = true; } - - /* Log CLI */ - { - size_t len = 0; - char *buffer; - for(i=0; ilabels_out == buffer) { + options->labels_out = NULL; } + return ret; +} - failure = 1; - section_count = 0; - section = NULL; +// log command line +static bool log_cli(int argc, const char* argv[]) { + bool ret = false; - /* Extract command line options */ - ret = get_cli_opt(argc, argv, &option); - if (ret <= 0) { - goto error_1; + size_t len = 0; + for(int i=0; icdrom) { + ret = cd_memory_map(map); + /* Data will be loaded during section disassembly */ + } else { + ret = rom_load(options->rom_filename, map); } + return ret; +} - label_repository_t *repository = label_repository_create(); - /* Load labels */ - if (NULL != option.labels_in) { - for(i=0; option.labels_in[i]; i++) { - ret = label_repository_load(option.labels_in[i], repository); - if (!ret) { - ERROR_MSG("An error occured while loading labels from %s : %s", option.labels_in[i], strerror(errno)); - goto error_4; - } +static bool load_irq(MemoryMap *map, SectionArray *arr, const CommandLineOptions *options) { + bool ret = false; + if(!options->extract_irq) { + ret = true; + } else if (options->cdrom) { + IPL ipl = {0}; + if (!ipl_read(&ipl, options->rom_filename)) { + // ... + } else if (!ipl_sections(&ipl, arr)) { + ERROR_MSG("An error occured while setting up sections from IPL data."); + } else { + ret = true; } + } else if (!irq_read(map, arr)) { + ERROR_MSG("An error occured while reading irq vector offsets"); + } else { + ret = true; } + return ret; +} +static bool reset_output(SectionArray *arr) { + bool ret = true; /* For each section reset every existing files */ - for (i = 0; i < section_count; ++i) { - out = fopen(section[i].output, "wb"); - if (NULL == out) { - ERROR_MSG("Can't open %s : %s", section[i].output, strerror(errno)); - goto error_4; + for (int i = 0; ret && (i < arr->count); ++i) { + Section *section = &arr->data[i]; + FILE *out = fopen(section->output, "wb"); + if (out == NULL) { + ERROR_MSG("Can't open %s : %s", section->output, strerror(errno)); + ret = false; + } else { + fclose(out); } - fclose(out); } + return ret; +} +static bool fill_label_reporitory(LabelRepository *labels, SectionArray *arr) { + bool ret = true; /* Add section name to label repository. */ - for (i = 0; i < section_count; ++i) { - ret = label_repository_add(repository, section[i].name, section[i].logical, section[i].page, section[i].description); + for (int i = 0; ret && (i < arr->count); ++i) { + Section *section = &arr->data[i]; + ret = label_repository_add(labels, section->name, section->logical, section->page, section->description); if (!ret) { - ERROR_MSG("Failed to add section name (%s) to labels", section[i].name); - goto error_4; + ERROR_MSG("Failed to add section name (%s) to labels", section->name); } } + return ret; +} - /* Disassemble and output */ - for (i = 0; i < section_count; ++i) { - out = fopen(section[i].output, "ab"); - if (!out) { - ERROR_MSG("Can't open %s : %s", section[i].output, strerror(errno)); - goto error_4; - } +static bool output_main(MemoryMap *map, LabelRepository *labels, const CommandLineOptions *options) { + bool ret = false; + FILE *out = fopen(options->main_filename, "w"); + if (out == NULL) { + ERROR_MSG("Unable to open %s : %s", options->main_filename, strerror(errno)); + } else { + label_dump(out, map, labels); + fclose(out); + ret = true; + } + return ret; +} - if ((0 != option.cdrom) || (section[i].offset != ((section[i].page << 13) | (section[i].logical & 0x1fff)))) { - size_t offset = section[i].offset; - /* Copy CDROM data */ - ret = cd_load(option.rom_filename, section[i].offset, section[i].size, option.sector_size, section[i].page, section[i].logical, &map); - if (0 == ret) { - ERROR_MSG("Failed to load CD data (section %d)", i); - goto error_4; - } - } +static bool code_extract(FILE *out, SectionArray *arr, int index, MemoryMap *map, LabelRepository *labels, CommentRepository *comments, int address) { + bool ret = false; + Section *current = &arr->data[index]; + if(current->size <= 0) { + current->size = compute_size(arr, index, arr->count, map); + } + if (!label_extract(current, map, labels)) { + // ... + } else { + /* Process opcodes */ + uint16_t logical = current->logical; + do { + (void)decode(out, &logical, current, map, labels, comments, address); + } while (logical < (current->logical+current->size)); + fputc('\n', out); + ret = true; + } + return true; +} - if((i > 0) && (section[i].logical < (section[i-1].logical + section[i-1].size)) - && (section[i].page == section[i-1].page) - && (section[i].type != section[i-1].type)) { - WARNING_MSG("Section %s and %s overlaps! %x %x.%x", section[i].name, section[i-1].name); - } +static void print_header(FILE *out, Section *current, Section *previous) { + if(previous && ((previous->logical + previous->size) == current->logical)) { + // ... + } else if((current->type != SECTION_TYPE_DATA) || (current->data.type != DATA_TYPE_BINARY)) { + /* Print header */ + fprintf(out, "\t.%s\n" + "\t.bank $%03x\n" + "\t.org $%04x\n", + (current->type == SECTION_TYPE_CODE) ? "code" : "data", current->page, current->logical); + } +} - if((i > 0) && (0 == strcmp(section[i].output, section[i-1].output)) - && (section[i].page == section[i-1].page) - && (section[i].logical <= (section[i-1].logical + section[i-1].size))) { - // "Merge" sections and adjust size if necessary. - if(section[i].size > 0) { - uint32_t end0 = section[i-1].logical + section[i-1].size; - uint32_t end1 = section[i].logical + section[i].size; - if(end1 > end0) { - section[i].size = end1 - end0; - section[i].logical = end0; - INFO_MSG("Section %s has been merged with %s!", section[i].name, section[i-1].name); - } - else { - // The previous section overlaps the current one. - // We skip it as it has already been processed. - fclose(out); - continue; +static bool disassemble(SectionArray *arr, MemoryMap *map, LabelRepository *labels, CommentRepository *comments, CommandLineOptions *options) { + bool ret = true; + Section *previous = NULL; + Section *current = NULL; + for (int i = 0; ret && (i < arr->count); ++i, previous=current) { + current = &arr->data[i]; + FILE *out = fopen(current->output, "ab"); + if (out == NULL) { + ERROR_MSG("Can't open %s : %s", current->output, strerror(errno)); + } else { + if (options->cdrom && (current->offset != ((current->page << 13) | (current->logical & 0x1fff)))) { + size_t offset = current->offset; + /* Copy CDROM data */ + if (!cd_load(options->rom_filename, current->offset, current->size, options->sector_size, current->page, current->logical, map)) { + ERROR_MSG("Failed to load CD data (section %d)", i); + } else { + ret = false; } } - else { - section[i].logical = section[i-1].logical + section[i-1].size; - INFO_MSG("Section %s has been merged with %s!", section[i].name, section[i-1].name); + + if(ret) { + print_header(out, current, previous); + memory_map_mpr(map, current->mpr); + + if (current->type == SECTION_TYPE_CODE) { + ret = code_extract(out, arr, i, map, labels, comments, options->address); + } else { + ret = data_extract(out, current, map, labels, comments, options->address); + } } + fclose(out); } - else if((section[i].type != Data) || (section[i].data.type != Binary)) { - /* Print header */ - fprintf(out, "\t.%s\n" - "\t.bank $%03x\n" - "\t.org $%04x\n", - (section[i].type == Code) ? "code" : "data", section[i].page, section[i].logical); - } + } + return ret; +} - memmap_mpr(&map, section[i].mpr); - - if (section[i].type == Code) { - if(section[i].size <= 0) { - section[i].size = compute_size(section, i, section_count, &map); - } +/* ---------------------------------------------------------------- */ +int main(int argc, const char **argv) { + int ret = EXIT_FAILURE; - /* Extract labels */ - ret = label_extract(§ion[i], &map, repository); - if (!ret) { - goto error_4; - } - /* Process opcodes */ - uint16_t logical = section[i].logical; - do { - (void)decode(out, &logical, §ion[i], &map, repository, comments_repository, option.address); - } while (logical < (section[i].logical+section[i].size)); - fputc('\n', out); - } else { - ret = data_extract(out, §ion[i], &map, repository, comments_repository, option.address); - if (!ret) { - // [todo] - } - } + CommandLineOptions options = {0}; - fclose(out); - } + MemoryMap map = {0}; + SectionArray section_arr = {0}; - /* Open main asm file */ - main_file = fopen(option.main_filename, "w"); - if (!main_file) { - ERROR_MSG("Unable to open %s : %s", option.main_filename, strerror(errno)); - goto error_4; - } + LabelRepository labels = {0}; - label_dump(main_file, &map, repository); + CommentRepository comments = {0}; - if (!option.cdrom && option.extract_irq) { - fprintf(main_file, "\n\t.data\n\t.bank 0\n\t.org $FFF6\n"); - for (i = 0; i < 5; ++i) { - fprintf(main_file, "\t.dw $%04x\n", section[i].logical); - } - } + section_array_reset(§ion_arr); - fclose(main_file); + atexit(exit_callback); - /* Output labels */ - if (!label_output(&option, repository)) { - goto error_4; - } - failure = 0; - -error_4: - label_repository_destroy(repository); -error_3: - comment_repository_destroy(comments_repository); -error_2: - memmap_destroy(&map); -error_1: - release_cli_opt(&option); - - for (i = 0; i < section_count; ++i) { - free(section[i].name); - free(section[i].output); - section[i].name = NULL; + message_printer_init(); + + if (file_message_printer_init() != true) { + fprintf(stderr, "Failed to setup file printer.\n"); + } else if (console_message_printer_init() != true) { + fprintf(stderr, "Failed to setup console printer.\n"); + } else if (log_cli(argc, argv) != true) { + fprintf(stderr, "Failed to log command line.\n"); + } else if (cli_opt_get(&options, argc, argv) != true) { + // ... + } else if (memory_map_init(&map) != true) { + // ... + } else if (!load_sections(§ion_arr, options.cfg_filename)) { + // ... + } else if (!load_labels(&labels, options.labels_in)) { + // ... + } else if (!load_comments(&comments, options.comments_in)) { + // ... + } else if (!load_rom(&map, &options)) { + // ... + } else if (!load_irq(&map, §ion_arr, &options)) { + // ... + } else if (!reset_output(§ion_arr)) { + // ... + } else if (!fill_label_reporitory(&labels, §ion_arr)) { + // ... + } else { + section_array_tidy(§ion_arr); + if(!output_main(&map, &labels, &options)) { + // ... + } else { + ret = EXIT_SUCCESS; + if(!disassemble(§ion_arr, &map, &labels, &comments, &options)) { + ret = EXIT_FAILURE; + } + if (label_output(&labels, &options)) { + ret = EXIT_FAILURE; + } + } } - free(section); - return failure; + label_repository_destroy(&labels); + comment_repository_destroy(&comments); + memory_map_destroy(&map); + section_array_delete(§ion_arr); + cli_opt_release(&options); + + return ret; } diff --git a/cli/options.c b/cli/options.c index 6bae600..0357d33 100644 --- a/cli/options.c +++ b/cli/options.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -45,8 +45,8 @@ struct payload_t { const char ***array; }; -static int opt_callback(struct argparse *self, const struct argparse_option *option) { - struct payload_t *payload = (struct payload_t*)option->data; +static int opt_callback(struct argparse *self, const struct argparse_option *options) { + struct payload_t *payload = (struct payload_t*)options->data; size_t last = payload->size++; if(payload->capacity <= payload->size) { payload->capacity += 4; @@ -57,87 +57,87 @@ static int opt_callback(struct argparse *self, const struct argparse_option *opt memset(tmp+last, 0, 4*sizeof(const char*)); *payload->array = tmp; } - (*payload->array)[last] = *(char**)option->value; + (*payload->array)[last] = *(char**)options->value; return 1; } -/* Extract command line options */ -int get_cli_opt(int argc, const char** argv, cli_opt_t* option) { +// Extract command line options +bool cli_opt_get(CommandLineOptions *options, int argc, const char** argv) { static const char *const usages[] = { "etripator [options] [--] ", NULL }; - int ret = 0; + bool ret = false; char *dummy; - struct payload_t labels_payload = { 0, 0, &option->labels_in }; - struct payload_t comments_payload = {0, 0, &option->comments_in}; + struct payload_t labels_payload = { 0, 0, &options->labels_in }; + struct payload_t comments_payload = {0, 0, &options->comments_in}; - struct argparse_option options[] = { + struct argparse_option arg_opt[] = { OPT_HELP(), - OPT_BOOLEAN('i', "irq-detect", &option->extract_irq, "automatically detect and extract irq vectors when disassembling a ROM, or extract opening code and gfx from CDROM IPL data", NULL, 0, 0), - OPT_BOOLEAN('c', "cd", &option->cdrom, "cdrom image disassembly. Irq detection and rom. Header jump is not performed", NULL, 0, 0), - OPT_STRING('o', "out", &option->main_filename, "main asm file containing includes for all sections as long the irq vector table if the irq-detect option is enabled", NULL, 0, 0), + OPT_BOOLEAN('i', "irq-detect", &options->extract_irq, "automatically detect and extract irq vectors when disassembling a ROM, or extract opening code and gfx from CDROM IPL data", NULL, 0, 0), + OPT_BOOLEAN('c', "cd", &options->cdrom, "cdrom image disassembly. Irq detection and rom. Header jump is not performed", NULL, 0, 0), + OPT_STRING('o', "out", &options->main_filename, "main asm file containing includes for all sections as long the irq vector table if the irq-detect option is enabled", NULL, 0, 0), OPT_STRING('l', "labels", &dummy, "labels definition filename", opt_callback, (intptr_t)&labels_payload, 0), - OPT_STRING(0, "labels-out", &option->labels_out, "extracted labels output filename. Otherwise the labels will be written to .YYMMDDhhmmss.lbl", NULL, 0, 0), + OPT_STRING(0, "labels-out", &options->labels_out, "extracted labels output filename. Otherwise the labels will be written to .YYMMDDhhmmss.lbl", NULL, 0, 0), OPT_STRING(0, "comments", &dummy, "comments description filename", opt_callback, (intptr_t)&comments_payload, 0), - OPT_BOOLEAN(0, "address", &option->address, "print statement address as comment", NULL, 0, 0), - OPT_INTEGER(0, "sector_size", &option->sector_size, "2352 bytes sectors (cd only)", NULL, 0, 0), + OPT_BOOLEAN(0, "address", &options->address, "print statement address as comment", NULL, 0, 0), + OPT_INTEGER(0, "sector_size", &options->sector_size, "2352 bytes sectors (cd only)", NULL, 0, 0), OPT_END(), }; struct argparse argparse; - option->extract_irq = 0; - option->cdrom = 0; - option->cfg_filename = NULL; - option->rom_filename = NULL; - option->main_filename = "main.asm"; - option->labels_in = NULL; - option->labels_out = NULL; - option->comments_in = NULL; - option->address = 0; - option->sector_size = 2048; - - argparse_init(&argparse, options, usages, 0); + options->extract_irq = 0; + options->cdrom = 0; + options->cfg_filename = NULL; + options->rom_filename = NULL; + options->main_filename = "main.asm"; + options->labels_in = NULL; + options->labels_out = NULL; + options->comments_in = NULL; + options->address = 0; + options->sector_size = 2048; + + argparse_init(&argparse, arg_opt, usages, 0); argparse_describe(&argparse, "\nEtripator : a PC Engine disassembler", " "); argc = argparse_parse(&argparse, argc, argv); if(!argc) { // ... - } else if((option->sector_size != 2048) && (option->sector_size != 2352)) { + } else if((options->sector_size != 2048) && (options->sector_size != 2352)) { ERROR_MSG("invalid sector size (must be 2048 or 2352)."); } else if(argc != 2) { - if((option->extract_irq) && (argc == 1)) { + if((options->extract_irq) && (argc == 1)) { /* Config file is optional with automatic irq vector extraction. */ - option->cfg_filename = NULL; - option->rom_filename = argv[0]; - ret = 1; + options->cfg_filename = NULL; + options->rom_filename = argv[0]; + ret = true; } else { // ... } } else { - option->cfg_filename = argv[0]; - option->rom_filename = argv[1]; - ret = 1; + options->cfg_filename = argv[0]; + options->rom_filename = argv[1]; + ret = true; } - if(ret == 0) { + if(!ret) { argparse_usage(&argparse); } return ret; } -/* Release allocated resources during command line parsing */ -void release_cli_opt(cli_opt_t* option) { - if(option->comments_in) { - free(option->comments_in); +// Release allocated resources during command line parsing +void cli_opt_release(CommandLineOptions *options) { + if(options->comments_in) { + free(options->comments_in); } - if(option->labels_in) { - free(option->labels_in); + if(options->labels_in) { + free(options->labels_in); } - memset(option, 0, sizeof(cli_opt_t)); + memset(options, 0, sizeof(CommandLineOptions)); } diff --git a/cli/options.h b/cli/options.h index 48fdc70..e12b3d2 100644 --- a/cli/options.h +++ b/cli/options.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -50,12 +50,12 @@ typedef struct { const char *labels_out; const char **labels_in; const char **comments_in; -} cli_opt_t; +} CommandLineOptions; -/* Extract command line options */ -int get_cli_opt(int argc, const char** argv, cli_opt_t* option); +/// Extract command line options +bool cli_opt_get(CommandLineOptions *options, int argc, const char** argv); -/* Release allocated resources during command line parsing */ -void release_cli_opt(cli_opt_t* option); +/// Release allocated resources during command line parsing +void cli_opt_release(CommandLineOptions *options); #endif // ETRIPATOR_OPTIONS_H diff --git a/cmake/FindJansson.cmake b/cmake/FindJansson.cmake index 3225923..e31f92c 100755 --- a/cmake/FindJansson.cmake +++ b/cmake/FindJansson.cmake @@ -26,34 +26,43 @@ else (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) /sw/include ) -find_library(JANSSON_LIBRARY - NAMES - jansson - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib + find_library(JANSSON_LIBRARY + NAMES + jansson + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib ) -set(JANSSON_INCLUDE_DIRS - ${JANSSON_INCLUDE_DIR} + set(JANSSON_INCLUDE_DIRS + ${JANSSON_INCLUDE_DIR} ) -if (JANSSON_LIBRARY) - set(JANSSON_LIBRARIES - ${JANSSON_LIBRARIES} - ${JANSSON_LIBRARY} + if (JANSSON_LIBRARY) + set(JANSSON_LIBRARIES + ${JANSSON_LIBRARIES} + ${JANSSON_LIBRARY} ) -endif (JANSSON_LIBRARY) + endif (JANSSON_LIBRARY) +endif (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Jansson DEFAULT_MSG - JANSSON_LIBRARIES JANSSON_INCLUDE_DIRS) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Jansson DEFAULT_MSG + JANSSON_LIBRARIES JANSSON_INCLUDE_DIRS) - # show the JANSSON_INCLUDE_DIRS and JANSSON_LIBRARIES variables only in the advanced view - mark_as_advanced(JANSSON_INCLUDE_DIRS JANSSON_LIBRARIES) +if(JANSSON_FOUND) + add_library(jansson UNKNOWN IMPORTED) + set_target_properties(jansson + PROPERTIES + IMPORTED_LOCATION "${JANSSON_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${JANSSON_INCLUDE_DIRS}" + ) +endif() + +# show the JANSSON_INCLUDE_DIRS and JANSSON_LIBRARIES variables only in the advanced view +mark_as_advanced(JANSSON_INCLUDE_DIRS JANSSON_LIBRARIES) -endif (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) diff --git a/comment.c b/comment.c index 53dd907..9fa0c21 100644 --- a/comment.c +++ b/comment.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,23 +41,13 @@ #define COMMENT_ARRAY_INC 16 -/** - * Comment repository. - */ -struct comment_repository_impl { - size_t size; /**< Size of comment repository */ - size_t last; /**< Last element in the repository */ - comment_t *comments; /**< Comments */ -}; - -/** - * Get comment index by its address. - * \param [in] repository Coment repository. - * \param [in] logical Logical address. - * \param [in] page Memory page. - * \return comment index or -1 if the label was not found. - */ -static int comment_repository_index(comment_repository_t* repository, uint16_t logical, uint8_t page) { +/// Get comment index by its address. +/// \param [in] repository Coment repository. +/// \param [in] logical Logical address. +/// \param [in] page Memory page. +/// \return comment index. +/// \return -1 if the label was not found. +static int comment_repository_index(CommentRepository* repository, uint16_t logical, uint8_t page) { size_t i; for(i=0; ilast; i++) { if( (repository->comments[i].page == page) && @@ -68,65 +58,66 @@ static int comment_repository_index(comment_repository_t* repository, uint16_t l return -1; } -/* Create comment repository. */ -comment_repository_t* comment_repository_create() { - comment_repository_t *repository; - repository = (comment_repository_t*)malloc(sizeof(comment_repository_t)); - if(repository == NULL) { - ERROR_MSG("Failed to create comment repository: %s", strerror(errno)); - } else { - repository->last = 0; - repository->comments = NULL; +// Create comment repository. +bool comment_repository_create(CommentRepository *repository) { + assert(repository != NULL); + bool ret = true; - repository->size = COMMENT_ARRAY_INC; - repository->comments = (comment_t*)malloc(repository->size * sizeof(comment_t)); - if(repository->comments == NULL) { - ERROR_MSG("Failed to create comments: %s", strerror(errno)); - comment_repository_destroy(repository); - free(repository); - repository = NULL; - } - } - return repository; + repository->last = 0; + repository->comments = NULL; + + repository->size = COMMENT_ARRAY_INC; + repository->comments = (Comment*)malloc(repository->size * sizeof(Comment)); + if(repository->comments == NULL) { + ERROR_MSG("Failed to create comments: %s", strerror(errno)); + comment_repository_destroy(repository); + ret = false; + } + return ret; } -/* Release comment repository resources. */ -void comment_repository_destroy(comment_repository_t* repository) { +// Release comment repository resources. +void comment_repository_destroy(CommentRepository* repository) { + assert(repository != NULL); + repository->size = 0; repository->last = 0; if(repository->comments != NULL) { - for(int i=0; ilast; i++) { - if(repository->comments[i].text) { - free(repository->comments[i].text); - } + for(size_t i=0; ilast; i++) { + free(repository->comments[i].text); } free(repository->comments); repository->comments = NULL; } } -/* Add comment to repository. */ -int comment_repository_add(comment_repository_t* repository, uint16_t logical, uint8_t page, const char *text) { - int ret = 1; +// Add comment to repository. +bool comment_repository_add(CommentRepository* repository, uint16_t logical, uint8_t page, const char *text) { + assert(repository != NULL); + assert(text != NULL); + + bool ret = true; int index = comment_repository_index(repository, logical, page); if(index >= 0) { WARNING_MSG("Duplicate comment for logical address $%04x in page $%02x", logical, page); } else { - /* Expand arrays if necessary */ + // Expand arrays if necessary. if(repository->last >= repository->size) { - comment_t *ptr; - repository->size += COMMENT_ARRAY_INC; - ptr = (comment_t*)realloc(repository->comments, repository->size * sizeof(comment_t)); + Comment *ptr; + size_t new_size = repository->size + COMMENT_ARRAY_INC; + ptr = (Comment*)realloc(repository->comments, new_size * sizeof(Comment)); if(ptr == NULL) { + ERROR_MSG("Failed to expand comment buffer: %s", strerror(errno)); comment_repository_destroy(repository); - ret = 0; + ret = false; } else { repository->comments = ptr; + repository->size = new_size; } } - if(ret != 0) { - /* Push addresses & text */ + if(ret) { + // Push addresses & text. repository->comments[repository->last].logical = logical; repository->comments[repository->last].page = page; repository->comments[repository->last].text = strdup(text); @@ -137,37 +128,47 @@ int comment_repository_add(comment_repository_t* repository, uint16_t logical, u return ret; } -/* Find a comment by its address. */ -int comment_repository_find(comment_repository_t* repository, uint16_t logical, uint8_t page, comment_t *out) { +// Find a comment by its address. +bool comment_repository_find(CommentRepository* repository, uint16_t logical, uint8_t page, Comment *out) { + assert(repository != NULL); + assert(out != NULL); + int index = comment_repository_index(repository, logical, page); - if(index < 0) { - memset(out, 0, sizeof(comment_t)); - return 0; + bool ret = (index >= 0); + if(ret) { + memcpy(out, &repository->comments[index], sizeof(Comment)); + } else { + memset(out, 0, sizeof(Comment)); } - memcpy(out, &repository->comments[index], sizeof(comment_t)); - return 1; + return ret; } -/* Get the number of comments stored in the repository. */ -int comment_repository_size(comment_repository_t* repository) { - return repository ? (int)repository->last : 0; +// Get the number of comments stored in the repository. +int comment_repository_size(CommentRepository* repository) { + assert(repository != NULL); + return (int)repository->last; } -/* Retrieve the comment at the specified index. */ -int comment_repository_get(comment_repository_t* repository, int index, comment_t *out) { - if((repository != NULL) && ((index >= 0) && (index < (int)repository->last))) { - memcpy(out, &repository->comments[index], sizeof(comment_t)); - return 1; +// Retrieve the comment at the specified index. +bool comment_repository_get(CommentRepository* repository, int index, Comment *out) { + assert(repository != NULL); + assert(out != NULL); + + bool ret = false; + if((index >= 0) && (index < (int)repository->last)) { + memcpy(out, &repository->comments[index], sizeof(Comment)); + ret = true; } else { - memset(out, 0, sizeof(comment_t)); - return 0; - } + memset(out, 0, sizeof(Comment)); + } + return ret; } -/* Delete comments. */ -int comment_repository_delete(comment_repository_t* repository, uint16_t first, uint16_t end, uint8_t page) { - size_t i; - for(i=0; ilast; i++) { +// Delete comments. +void comment_repository_delete(CommentRepository* repository, uint16_t first, uint16_t end, uint8_t page) { + assert(repository != NULL); + + for(size_t i=0; ilast; i++) { if( (repository->comments[i].page == page) && (repository->comments[i].logical >= first) && (repository->comments[i].logical < end) ) { @@ -176,59 +177,9 @@ int comment_repository_delete(comment_repository_t* repository, uint16_t first, if(repository->comments[i].text) { free(repository->comments[i].text); } - memcpy(&repository->comments[i], &repository->comments[repository->last], sizeof(comment_t)); + memcpy(&repository->comments[i], &repository->comments[repository->last], sizeof(Comment)); i--; } } } - return 1;} - -/* Load comments from file. */ -int comment_repository_load(const char* filename, comment_repository_t* repository) { - json_t* root; - json_error_t err; - json_t* value; - int ret = 0, index = 0; - root = json_load_file(filename, 0, &err); - if(!root) { - ERROR_MSG("Failed to parse %s:%d:%d: %s", filename, err.line, err.column, err.text); - } else { - if(!json_is_array(root)) { - ERROR_MSG("Array expected."); - } else for (index = 0, ret = 1; ret && (index < json_array_size(root)) && (value = json_array_get(root, index)); index++) { - ret = 0; - if(!json_is_object(value)) { - ERROR_MSG("Expected object."); - } else { - int num; - // logical - json_t *tmp = json_object_get(value, "logical"); - if(!json_validate_int(tmp, &num)) { - ERROR_MSG("Invalid or missing logical address."); - } else if((num < 0) || (num > 0xffff)) { - ERROR_MSG("Logical address out of range."); - } else { - uint16_t logical = (uint16_t)num; - // page - tmp = json_object_get(value, "page"); - if(!json_validate_int(tmp, &num)) { - ERROR_MSG("Invalid or missing page."); - } else { - // text (same format as section/label description) - char* text = json_load_description(value, "text"); - if(text == NULL) { - ERROR_MSG("Invalid or missing text."); - } else if((num < 0) || (num > 0xff)) { - ERROR_MSG("Page value out of range."); - } else if(comment_repository_add(repository, logical, (uint8_t)num, text)) { - ret = 1; - } - free(text); - } - } - } - } - json_decref(root); - } - return ret; } diff --git a/comment.h b/comment.h index 45e15e9..d909214 100644 --- a/comment.h +++ b/comment.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,80 +38,80 @@ #include "config.h" -/** - * Comment. - */ +/// Comment. typedef struct { - uint16_t logical; /**< Logical address */ - uint8_t page; /**< Memory page */ - char* text; /**< Comment text */ -} comment_t; - -typedef struct comment_repository_impl comment_repository_t; - -/** - * Create comment repository. - * \return A pointer to a comment repository or NULL if an error occured. - */ -comment_repository_t* comment_repository_create(); - -/** - * Release comment repository resources. - * \param [in,out] repository Comment repository. - */ -void comment_repository_destroy(comment_repository_t* repository); - -/** - * Add comment to repository. - * \param [in,out] repository Comment repository. - * \param [in] logical Logical address. - * \param [in] page Memory page. - * \param [in] text Comment text. - */ -int comment_repository_add(comment_repository_t* repository, uint16_t logical, uint8_t page, const char *text); - -/** - * Find a comment by its address. - * \param [in] repository Comment repository. - * \param [in] logical Logical address. - * \param [in] page Memory page. - * \param [out] out Associated comment (if any). - * \return 1 if a comment was found, 0 otherwise. - */ -int comment_repository_find(comment_repository_t* repository, uint16_t logical, uint8_t page, comment_t *out); - -/** - * Get the number of comments stored in the repository. - * \param [in] repository Comment repository. - * \return Comment count. - */ -int comment_repository_size(comment_repository_t* repository); - -/** - * Retrieve the comment at the specified index. - * \param [in] repository Comment repository. - * \param [in] index Comment index. - * \param [out] out Comment (if any). - * \return 1 if a comment exists for the specified index, 0 otherwise. - */ -int comment_repository_get(comment_repository_t* repository, int index, comment_t *out); - -/** - * Delete comments. - * \param [in] repository Comment repository. - * \param [in] first Start of the logical address range. - * \param [in] end End of the logical address range. - * \param [in] page Memory page. - */ -int comment_repository_delete(comment_repository_t* repository, uint16_t first, uint16_t end, uint8_t page); - -/** - * Load comments from file. - * \param [in] filename Input filename. - * \param [out] repository Comment repository. - * \return 1 if the comments contained in the file was succesfully added to the repository. - * 0 if an error occured. - */ -int comment_repository_load(const char* filename, comment_repository_t* repository); + uint16_t logical; //< Logical address. + uint8_t page; //< Memory page. + char* text; //< Comment text. +} Comment; + +/// Comment repository. +typedef struct { + size_t size; //< Size of comment repository. + size_t last; //< Last element in the repository. + Comment *comments; //< Comments. +} CommentRepository; + +/// Create comment repository. +/// \param [in out] repository Comment repository. +/// \return true if the repository was succesfully initialized +/// \return false if an error occured +bool comment_repository_create(CommentRepository* repository); + +/// Release comment repository resources. +/// \param [in,out] repository Comment repository. +void comment_repository_destroy(CommentRepository* repository); + +/// Add comment to repository. +/// \param [in,out] repository Comment repository. +/// \param [in] logical Logical address. +/// \param [in] page Memory page. +/// \param [in] text Comment text. +/// \return true if the comment was successfully added to the repository. +/// \return false if an error occured. +bool comment_repository_add(CommentRepository* repository, uint16_t logical, uint8_t page, const char *text); + +/// Find a comment by its address. +/// \param [in] repository Comment repository. +/// \param [in] logical Logical address. +/// \param [in] page Memory page. +/// \param [out] out Associated comment (if any). +/// \return true if a comment was found. +/// \return 0 otherwise. +bool comment_repository_find(CommentRepository* repository, uint16_t logical, uint8_t page, Comment *out); + +/// Get the number of comments stored in the repository. +/// \param [in] repository Comment repository. +/// \return Comment count. +int comment_repository_size(CommentRepository* repository); + +/// Retrieve the comment at the specified index. +/// \param [in] repository Comment repository. +/// \param [in] index Comment index. +/// \param [out] out Comment (if any). +/// \return true if a comment exists for the specified index. +/// \return false otherwise. +bool comment_repository_get(CommentRepository* repository, int index, Comment *out); + +/// Delete comments. +/// \param [in] repository Comment repository. +/// \param [in] first Start of the logical address range. +/// \param [in] end End of the logical address range. +/// \param [in] page Memory page. +void comment_repository_delete(CommentRepository* repository, uint16_t first, uint16_t end, uint8_t page); + +/// Load comments from file. +/// \param [in, out] repository Comment repository. +/// \param [in] filename Input filename. +/// \return true if the comments contained in the file was succesfully added to the repository. +/// \return false if an error occured. +bool comment_repository_load(CommentRepository* repository, const char* filename); + +/// Save comments to file. +/// \param [in] repository Comment repository. +/// \param [in] filename Output filename. +/// \return true if the comments were succesfully saved. +/// \return false if an error occured. +bool comment_repository_save(CommentRepository* repository, const char* filename); #endif // ETRIPATOR_COMMENT_H \ No newline at end of file diff --git a/comment/load.c b/comment/load.c new file mode 100644 index 0000000..0f3d7a6 --- /dev/null +++ b/comment/load.c @@ -0,0 +1,96 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include +#include "../message.h" +#include "../jsonhelpers.h" +#include "../comment.h" + +// Load comments from file. +bool comment_repository_load(CommentRepository* repository, const char* filename) { + assert(filename != NULL); + assert(repository != NULL); + + bool ret = false; + + json_t* root; + json_error_t err; + json_t* value; + int index = 0; + root = json_load_file(filename, 0, &err); + if(!root) { + ERROR_MSG("Failed to parse %s:%d:%d: %s", filename, err.line, err.column, err.text); + } else { + if(!json_is_array(root)) { + ERROR_MSG("Array expected."); + } else for (index = 0, ret = true; ret && (index < json_array_size(root)) && (value = json_array_get(root, index)); index++) { + ret = false; + if(!json_is_object(value)) { + ERROR_MSG("Expected object."); + } else { + int num; + // logical + json_t *tmp = json_object_get(value, "logical"); + if(!json_validate_int(tmp, &num)) { + ERROR_MSG("Invalid or missing logical address."); + } else if((num < 0) || (num > 0xffff)) { + ERROR_MSG("Logical address out of range."); + } else { + uint16_t logical = (uint16_t)num; + // page + tmp = json_object_get(value, "page"); + if(!json_validate_int(tmp, &num)) { + ERROR_MSG("Invalid or missing page."); + } else { + // text (same format as section/label description) + char* text = NULL; + if(json_load_description (value, "text", &text) != true) { + ERROR_MSG("Faile to retrieve text."); + } else if(text == NULL) { + ERROR_MSG("Empty text string"); + } else if((num < 0) || (num > 0xFF)) { + ERROR_MSG("Page value out of range."); + } else if(comment_repository_add(repository, logical, (uint8_t)num, text)) { + ret = true; + } + free(text); + } + } + } + } + json_decref(root); + } + return ret; +} diff --git a/comment/save.c b/comment/save.c new file mode 100644 index 0000000..3b62a45 --- /dev/null +++ b/comment/save.c @@ -0,0 +1,91 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include +#include + +#include "../message.h" +#include "../jsonhelpers.h" +#include "../comment.h" + +// Save comments to file. +bool comment_repository_save(CommentRepository* repository, const char* filename) { + assert(filename != NULL); + assert(repository != NULL); + + bool ret = false; + + FILE *stream = fopen(filename, "wb"); + if(stream == NULL) { + ERROR_MSG("Failed to open %s: %s", filename, strerror(errno)); + } else { + int count = comment_repository_size(repository); + fprintf(stream, "[\n"); + for(int i=0; i #include #include +#include #include +#include + #include #include diff --git a/decode.c b/decode.c index af69d38..1b306c6 100644 --- a/decode.c +++ b/decode.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,25 +41,24 @@ #define ETRIPATOR_LAST_COLUMN 80U -static const char *spacing = " "; +static const char g_spacing[] = " "; -static int last_column_spacing(int current_char_count) { +static inline int last_column_spacing(int current_char_count) { return (current_char_count < ETRIPATOR_LAST_COLUMN) ? (ETRIPATOR_LAST_COLUMN - current_char_count) : 1; } static void print_comment(FILE *out, const char *str) { - if (!str) { - return; - } - while (*str) { - fputc(';', out); - fputc(' ', out); - for (; *str && (*str != '\n'); str++) { - fputc(*str, out); - } - fputc('\n', out); - if (*str) { - str++; + if(str != NULL) { + while (*str != '\0') { + fputc(';', out); + fputc(' ', out); + for (; (*str != '\0') && (*str != '\n'); str++) { + fputc(*str, out); + } + fputc('\n', out); + if (*str != '\0') { + str++; + } } } } @@ -88,7 +87,7 @@ static void print_statement_address(FILE *out, int n, uint16_t logical, uint8_t fprintf(out, "%*c; bank: $%03x logical: $%04x", last_column_spacing(n), ' ', page, logical); } -static void print_label(FILE *out, label_t *label) { +static void print_label(FILE *out, Label *label) { int n = last_column_spacing((int)strlen(label->name) + 1); /* Print description */ print_comment(out, label->description); @@ -97,7 +96,7 @@ static void print_label(FILE *out, label_t *label) { } /* Finds any jump address from the current section. */ -int label_extract(section_t *section, memmap_t *map, label_repository_t *repository) { +bool label_extract(Section *section, MemoryMap *map, LabelRepository *repository) { int i; uint8_t inst; uint8_t data[6]; @@ -106,20 +105,20 @@ int label_extract(section_t *section, memmap_t *map, label_repository_t *reposit uint16_t logical; uint8_t page; - const opcode_t *opcode; + const Opcode *opcode; - if (section->type != Code) { + if (section->type != SECTION_TYPE_CODE) { return 1; } /* Walk along section */ for (logical = section->logical; logical < (section->logical + section->size); logical += opcode->size) { /* Read instruction */ - inst = memmap_read(map, logical); + inst = memory_map_read(map, logical); opcode = opcode_get(inst); /* Read data (if any) */ for (i = 0; i < (opcode->size - 1); i++) { - data[i] = memmap_read(map, logical + i + 1); + data[i] = memory_map_read(map, logical + i + 1); } if (opcode_is_local_jump(inst)) { @@ -135,7 +134,7 @@ int label_extract(section_t *section, memmap_t *map, label_repository_t *reposit } delta += opcode->size; jump = logical + delta; - page = memmap_page(map, jump); + page = memory_map_page(map, jump); /* Create label name */ snprintf(buffer, 32, "l%04x_%02d", jump, page); @@ -146,7 +145,7 @@ int label_extract(section_t *section, memmap_t *map, label_repository_t *reposit INFO_MSG("%04x short jump to %04x (%02x)", logical, jump, page); } else if (opcode_is_far_jump(inst)) { uint16_t jump = data[0] | (data[1] << 8); - page = memmap_page(map, jump); + page = memory_map_page(map, jump); /* Create label name */ snprintf(buffer, 32, "l%04x_%02d", jump, page); /* Insert offset to repository */ @@ -160,47 +159,45 @@ int label_extract(section_t *section, memmap_t *map, label_repository_t *reposit return 1; } -static int data_extract_binary(FILE *out, section_t *section, memmap_t *map, label_repository_t *repository) { +static int data_extract_binary(FILE *out, Section *section, MemoryMap *map, LabelRepository *repository) { uint16_t logical; int32_t i; for (i = 0, logical = section->logical; i < section->size; i++, logical++) { - uint8_t data = memmap_read(map, logical); + uint8_t data = memory_map_read(map, logical); fwrite(&data, 1, 1, out); } return 1; } -static int data_extract_hex(FILE *out, section_t *section, memmap_t *map, label_repository_t *repository, - comment_repository_t *comments, int extra_infos) { +static int data_extract_hex(FILE *out, Section *section, MemoryMap *map, LabelRepository *repository, + CommentRepository *comments, int extra_infos) { const int32_t element_size = section->data.element_size; const int32_t elements_per_line = section->data.elements_per_line; int32_t i, j; uint16_t logical; - label_t label; - comment_t comment; - - uint8_t buffer[2] = {0}; - int32_t top = 0; - size_t line_offset = ftell(out); uint8_t line_page = section->page; uint16_t line_logical = section->logical; - int has_comment = 0; - - for (i = 0, j = 0, logical = section->logical; i < section->size; i++, logical++) { - uint8_t page = memmap_page(map, logical); + bool has_comment = false; + + Comment comment = {0}; - int has_label = label_repository_find(repository, logical, page, &label); + uint8_t data[2] = {0}; + int32_t top = 0; - if (has_label) { + for (i = 0, j = 0, logical = section->logical; i < section->size; i++, logical++) { + uint8_t page = memory_map_page(map, logical); + Label label = {0}; + bool has_label = label_repository_find(repository, logical, page, &label); + if (has_label) { // flush any bytes left in the buffer. if (top && (top < element_size)) { - fprintf(out, "\n%s.db $%02x", spacing, buffer[0]); + fprintf(out, "\n%s.db $%02x", g_spacing, data[0]); for (int32_t l = 1; l < top; l++) { // useless as top is always equal to 1 - fprintf(out, ",$%02x", buffer[l]); + fprintf(out, ",$%02x", data[l]); } top = 0; } @@ -211,24 +208,24 @@ static int data_extract_hex(FILE *out, section_t *section, memmap_t *map, label_ j = 0; } - comment_t dummy; + Comment dummy; if (comment_repository_find(comments, logical, page, &dummy)) { if (has_comment) { if (top && (top < element_size)) { - fprintf(out, "\n%s.db $%02x", spacing, buffer[0]); + fprintf(out, "\n%s.db $%02x", g_spacing, data[0]); for (int32_t l = 1; l < top; l++) { // useless as top is always equal to 1 - fprintf(out, ",$%02x", buffer[l]); + fprintf(out, ",$%02x", data[l]); } top = 0; } print_inline_comment(out, (int)(ftell(out) - line_offset), comment.text); } - memcpy(&comment, &dummy, sizeof(comment_t)); - has_comment = 1; + comment = dummy; + has_comment = true; j = 0; } - buffer[top++] = memmap_read(map, logical); + data[top++] = memory_map_read(map, logical); if (top >= element_size) { char sep; @@ -241,7 +238,7 @@ static int data_extract_hex(FILE *out, section_t *section, memmap_t *map, label_ const char *data_decl = (top > 1) ? ".dw" : ".db"; - fprintf(out, "%s%s", spacing, data_decl); + fprintf(out, "%s%s", g_spacing, data_decl); sep = ' '; } else { sep = ','; @@ -250,10 +247,10 @@ static int data_extract_hex(FILE *out, section_t *section, memmap_t *map, label_ fputc('$', out); if (top > 1) { while (top--) { - fprintf(out, "%02x", buffer[top]); + fprintf(out, "%02x", data[top]); } } else { - fprintf(out, "%02x", buffer[0]); + fprintf(out, "%02x", data[0]); } top = 0; j++; @@ -263,7 +260,7 @@ static int data_extract_hex(FILE *out, section_t *section, memmap_t *map, label_ int n = (int)(ftell(out) - line_offset); if (has_comment) { print_inline_comment(out, n, comment.text); - has_comment = 0; + has_comment = false; } else if (extra_infos) { print_statement_address(out, n, line_logical, line_page); } @@ -275,44 +272,40 @@ static int data_extract_hex(FILE *out, section_t *section, memmap_t *map, label_ int n = (int)(ftell(out) - line_offset); if (has_comment) { print_inline_comment(out, n, comment.text); - has_comment = 0; + has_comment = false; } else if (extra_infos) { - print_statement_address(out, (int)(ftell(out) - line_offset), line_logical, line_page); + print_statement_address(out, n, line_logical, line_page); } - fprintf(out, "\n%s.db $%02x", spacing, buffer[0]); + fprintf(out, "\n%s.db $%02x", g_spacing, data[0]); for (int32_t j = 1; j < top; j++) { // useless as top is always equal to 1 - fprintf(out, ",$%02x", buffer[j]); + fprintf(out, ",$%02x", data[j]); } } fputc('\n', out); return 1; } -static int data_extract_string(FILE *out, section_t *section, memmap_t *map, label_repository_t *repository, - comment_repository_t *comments, int extra_infos) { +static int data_extract_string(FILE *out, Section *section, MemoryMap *map, LabelRepository *repository, + CommentRepository *comments, int extra_infos) { const int32_t elements_per_line = section->data.elements_per_line; - int32_t i, j; + int32_t i, j, k; uint16_t logical; int c = 0; - int has_label = 0; - int has_comment = 0; + bool has_comment = false; size_t line_offset = 0; uint16_t line_logical = 0; uint8_t line_page; - comment_t comment = {0}; - - for (i = 0, j = 0, logical = section->logical; i < section->size; i++, logical++) { - uint8_t data = memmap_read(map, logical); - uint8_t page = memmap_page(map, logical); - - label_t label = {0}; - - has_label = label_repository_find(repository, logical, page, &label); + Comment comment = {0}; + for (i = 0, j = 0, k = 0, logical = section->logical; i < section->size; i++, logical++) { + uint8_t data = memory_map_read(map, logical); + uint8_t page = memory_map_page(map, logical); + Label label = {0}; + bool has_label = label_repository_find(repository, logical, page, &label); if (has_label) { if (c) { // close string if neededs fputc('"', out); @@ -325,7 +318,7 @@ static int data_extract_string(FILE *out, section_t *section, memmap_t *map, lab print_label(out, &label); } - comment_t dummy; + Comment dummy = {0}; if (comment_repository_find(comments, logical, page, &dummy)) { if (j) { if (c) { // close string if neededs @@ -337,8 +330,8 @@ static int data_extract_string(FILE *out, section_t *section, memmap_t *map, lab if (has_comment) { print_inline_comment(out, (int)(ftell(out) - line_offset), comment.text); } - memcpy(&comment, &dummy, sizeof(comment_t)); - has_comment = 1; + comment = dummy; + has_comment = true; } // display directives @@ -347,7 +340,7 @@ static int data_extract_string(FILE *out, section_t *section, memmap_t *map, lab line_offset = ftell(out); // record star of line line_logical = logical; line_page = page; - fprintf(out, "%s.db ", spacing); + fprintf(out, "%s.db ", g_spacing); } // print char @@ -375,9 +368,22 @@ static int data_extract_string(FILE *out, section_t *section, memmap_t *map, lab } fprintf(out, "$%02x", data); } + + bool newline = false; + if(section->data.delimiter_size && (k < section->data.delimiter_size)) { + if(data == section->data.delimiter[k]) { + k++; + if(k >= section->data.delimiter_size) { + newline = true; + k = 0; + } + } else { + k = 0; + } + } j++; - if (j == elements_per_line) { + if ((j == elements_per_line) || newline) { j = 0; if (c) { fputc('"', out); @@ -387,7 +393,7 @@ static int data_extract_string(FILE *out, section_t *section, memmap_t *map, lab int n = (int)(ftell(out) - line_offset); if (has_comment) { print_inline_comment(out, n, comment.text); - has_comment = 0; + has_comment = false; } else if (extra_infos) { print_statement_address(out, n, line_logical, line_page); } @@ -400,7 +406,7 @@ static int data_extract_string(FILE *out, section_t *section, memmap_t *map, lab int n = (int)(ftell(out) - line_offset); if (has_comment) { print_inline_comment(out, n, comment.text); - has_comment = 0; + has_comment = false; } else if (extra_infos) { print_statement_address(out, n, line_logical, line_page); } @@ -409,33 +415,31 @@ static int data_extract_string(FILE *out, section_t *section, memmap_t *map, lab return 1; } -static int data_extract_jump_table(FILE *out, section_t *section, memmap_t *map, label_repository_t *repository, - comment_repository_t *comments, int extra_infos) { +static int data_extract_jump_table(FILE *out, Section *section, MemoryMap *map, LabelRepository *repository, + CommentRepository *comments, int extra_infos) { const int32_t elements_per_line = section->data.elements_per_line; int32_t i, j; uint8_t page; uint16_t logical; - label_t label; - comment_t comment; + Label label; + Comment comment; size_t line_offset = ftell(out); uint8_t line_page = section->page; uint16_t line_logical = section->logical; - int has_label = 0; - int has_comment = 0; + bool has_comment = false; uint8_t data[2] = {0}; for (i = 0, j = 0, logical = section->logical; i < section->size; i += 2, logical += 2) { - page = memmap_page(map, logical); - data[0] = memmap_read(map, logical); - data[1] = memmap_read(map, logical + 1); - - has_label = label_repository_find(repository, logical, page, &label); + page = memory_map_page(map, logical); + data[0] = memory_map_read(map, logical); + data[1] = memory_map_read(map, logical + 1); + bool has_label = label_repository_find(repository, logical, page, &label); if (has_label) { if (i) { fputc('\n', out); @@ -444,13 +448,13 @@ static int data_extract_jump_table(FILE *out, section_t *section, memmap_t *map, j = 0; } - comment_t dummy; + Comment dummy = {0}; if (comment_repository_find(comments, logical, page, &dummy)) { if (has_comment) { print_inline_comment(out, (int)(ftell(out) - line_offset), comment.text); } - memcpy(&comment, &dummy, sizeof(comment_t)); - has_comment = 1; + comment = dummy; + has_comment = true; j = 0; } @@ -459,7 +463,7 @@ static int data_extract_jump_table(FILE *out, section_t *section, memmap_t *map, line_offset = ftell(out); line_logical = logical; line_page = page; - fprintf(out, "%s.dw ", spacing); + fprintf(out, "%s.dw ", g_spacing); } if (j) { @@ -467,7 +471,7 @@ static int data_extract_jump_table(FILE *out, section_t *section, memmap_t *map, } uint16_t jump_logical = data[0] | (data[1] << 8); - uint8_t jump_page = memmap_page(map, jump_logical); + uint8_t jump_page = memory_map_page(map, jump_logical); if (label_repository_find(repository, jump_logical, jump_page, &label)) { fprintf(out, "%s", label.name); @@ -487,36 +491,37 @@ static int data_extract_jump_table(FILE *out, section_t *section, memmap_t *map, } } if (j) { + int n = (int)(ftell(out) - line_offset); if (has_comment) { - print_inline_comment(out, (int)(ftell(out) - line_offset), comment.text); + print_inline_comment(out, n, comment.text); } else if (extra_infos) { - print_statement_address(out, (int)(ftell(out) - line_offset), line_logical, line_page); + print_statement_address(out, n, line_logical, line_page); } } - fputc('\n', out); + return 1; } /* Process data section. The result will be output has a binary file or an asm file containing hex values or strings. */ -int data_extract(FILE *out, section_t *section, memmap_t *map, label_repository_t *repository, - comment_repository_t *comments, int extra_infos) { +bool data_extract(FILE *out, Section *section, MemoryMap *map, LabelRepository *repository, + CommentRepository *comments, int extra_infos) { switch (section->data.type) { - case Binary: + case DATA_TYPE_BINARY: return data_extract_binary(out, section, map, repository); - case Hex: + case DATA_TYPE_HEX: return data_extract_hex(out, section, map, repository, comments, extra_infos); - case String: + case DATA_TYPE_STRING: return data_extract_string(out, section, map, repository, comments, extra_infos); - case JumpTable: + case DATA_TYPE_JUMP_TABLE: return data_extract_jump_table(out, section, map, repository, comments, extra_infos); default: - return 0; + return false; } } /* Process code section. */ -int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, label_repository_t *repository, - comment_repository_t *comments, int extra_infos) { +int decode(FILE *out, uint16_t *logical, Section *section, MemoryMap *map, LabelRepository *repository, + CommentRepository *comments, int extra_infos) { int i, delta; uint8_t inst, data[6], is_jump; char eor; @@ -525,17 +530,17 @@ int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, labe uint8_t current_page; uint16_t current_logical; uint16_t next_logical; - label_t label; + Label label; - const opcode_t *opcode; + const Opcode *opcode; eor = 0; memset(data, 0, 6); - page = memmap_page(map, *logical); + page = memory_map_page(map, *logical); /* Opcode */ - inst = memmap_read(map, *logical); + inst = memory_map_read(map, *logical); opcode = opcode_get(inst); current_page = page; @@ -552,13 +557,13 @@ int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, labe size_t start = ftell(out); /* Front spacing */ - fwrite(spacing, 1, 10, out); + fwrite(g_spacing, 1, 10, out); /* Print opcode string */ fwrite(opcode->name, 1, 4, out); /* Add spacing */ - fwrite(spacing, 1, 4, out); + fwrite(g_spacing, 1, 4, out); /* End Of Routine (eor) is set to 1 if the instruction is RTI, RTS or BRK */ eor = ((inst == 0x40) || (inst == 0x60) || (inst == 0x00)); @@ -566,7 +571,7 @@ int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, labe /* Data */ if (opcode->size > 1) { for (i = 0; i < (opcode->size - 1); i++) { - data[i] = memmap_read(map, *logical + i + 1); + data[i] = memory_map_read(map, *logical + i + 1); } } @@ -624,14 +629,14 @@ int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, labe /* BBR* and BBS* */ if ((inst & 0x0F) == 0x0F) { uint16_t zp_offset = 0x2000 + data[0]; // [todo] RAM may not be in mpr1 ... - page = memmap_page(map, zp_offset); + page = memory_map_page(map, zp_offset); if (label_repository_find(repository, zp_offset, page, &label)) { fprintf(out, "<%s, ", label.name); } else { fprintf(out, "<$%02x, ", data[0]); } } - page = memmap_page(map, offset); + page = memory_map_page(map, offset); // Label name should have been set by the label extraction pass. label_repository_find(repository, offset, page, &label); fwrite(label.name, 1, strlen(label.name), out); @@ -656,7 +661,7 @@ int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, labe /* fall through */ case PCE_OP_nn_ZZ: /* #$aa, <$zp */ offset = 0x2000 + data[1]; - page = memmap_page(map, offset); + page = memory_map_page(map, offset); has_label = label_repository_find(repository, offset, page, &label); if (has_label) { fprintf(out, "#$%02x, <%s%s", data[0], label.name, extra); @@ -667,7 +672,7 @@ int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, labe /* fall through */ case PCE_OP_nn_hhll: /* #$aa, $hhll */ offset = (data[1] << 8) + data[2]; - page = memmap_page(map, offset); + page = memory_map_page(map, offset); has_label = label_repository_find(repository, offset, page, &label); if (has_label) { fprintf(out, "#$%02x, %s%s", data[0], label.name, extra); @@ -684,7 +689,7 @@ int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, labe /* fall through */ case PCE_OP_ZZ: /* data[index]; uint32_t start = current->logical; uint32_t logical = start; @@ -818,9 +823,9 @@ int32_t compute_size(section_t *sections, int index, int count, memmap_t *map) { uint32_t max_offset = 0xffffffff; for (i = 0; i < count; i++) { if (i != index) { - if (current->page == sections[i].page) { + if (current->page == sections->data[i].page) { uint32_t offset_current = current->offset & 0x1fff; - uint32_t offset_it = sections[i].offset & 0x1fff; + uint32_t offset_it = sections->data[i].offset & 0x1fff; if ((offset_current < offset_it) && (max_offset > offset_it)) { max_offset = offset_it; } @@ -831,17 +836,17 @@ int32_t compute_size(section_t *sections, int index, int count, memmap_t *map) { if ((logical & 0x1fff) >= max_offset) { break; } - uint8_t page = memmap_page(map, logical); - data[0] = memmap_read(map, logical); - const opcode_t *opcode = opcode_get(data[0]); + uint8_t page = memory_map_page(map, logical); + data[0] = memory_map_read(map, logical); + const Opcode *opcode = opcode_get(data[0]); for (i = 1; i < opcode->size; i++) { - data[i] = memmap_read(map, logical + i); + data[i] = memory_map_read(map, logical + i); } logical += opcode->size; if (opcode_is_far_jump(data[0])) { uint32_t jump = data[1] | (data[2] << 8); if (data[0] == 0x4c) { // jmp hhll - uint8_t jump_page = memmap_page(map, jump); + uint8_t jump_page = memory_map_page(map, jump); if (page == jump_page) { if (jump < logical) { eor = 1; @@ -875,10 +880,10 @@ int32_t compute_size(section_t *sections, int index, int count, memmap_t *map) { } /* Output hardware IO port and RAM labels. */ -void label_dump(FILE *out, memmap_t *map, label_repository_t *repository) { +void label_dump(FILE *out, MemoryMap *map, LabelRepository *repository) { int count = label_repository_size(repository); for (int i = 0; i < count; i++) { - label_t label; + Label label; if (label_repository_get(repository, i, &label)) { // IO port and RAM if ((label.page == 0xff) || (label.page == 0xf8)) { diff --git a/decode.h b/decode.h index 0791ce1..ba59f8d 100644 --- a/decode.h +++ b/decode.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,59 +39,52 @@ #include "config.h" #include "label.h" #include "section.h" -#include "memorymap.h" +#include "memory_map.h" #include "comment.h" -/** - * Finds any jump address from the current section. - * @param [in] section Current section. - * @param [in] map Memory map. - * @param [in out] repository Label repository. - * @return 1 upon success, 0 otherwise. - */ -int label_extract(section_t *section, memmap_t *map, label_repository_t *repository); +/// Finds any jump address from the current section. +/// \param [in] section Current section. +/// \param [in] map Memory map. +/// \param [in out] repository Label repository. +/// \return true upon success. +/// \return false if an error occured. +bool label_extract(Section *section, MemoryMap *map, LabelRepository *repository); -/** - * Process data section. The result will be output has a binary file or an asm file containing hex values or strings. - * @param [out] out File output. - * @param [in] section Current section. - * @param [in] map Memory map. - * @param [in] repository Label repository. - * @param [in] comments Comments repository. - * @param [in] extra_infos Display extra informations as comments (if none set). - * @return 1 upon success, 0 otherwise. - */ -int data_extract(FILE *out, section_t *section, memmap_t *map, label_repository_t *repository, comment_repository_t *comments, int extra_infos); +/// Process data section. The result will be output has a binary file or an asm file containing hex values or strings. +/// \param [out] out File output. +/// \param [in] section Current section. +/// \param [in] map Memory map. +/// \param [in] repository Label repository. +/// \param [in] comments Comments repository. +/// \param [in] extra_infos Display extra informations as comments (if none set). +/// \return true upon success. +/// \return false if an error occured. +bool data_extract(FILE *out, Section *section, MemoryMap *map, LabelRepository *repository, CommentRepository *comments, int extra_infos); -/** - * Process code section. - * @param [out] out File output. - * @param [in out] logical Current logical address. - * @param [in] section Current section. - * @param [in] map Memory map. - * @param [in] repository Label repository. - * @param [in] comments Comments repository. - * @param [in] extra_infos Display extra informations as comments (if none set). - * @return 1 if rts, rti or brk instruction was decoded, 0 otherwise. - */ -int decode(FILE *out, uint16_t *logical, section_t *section, memmap_t *map, label_repository_t *repository, comment_repository_t *comments, int extra_infos); +/// Process code section. +/// \param [out] out File output. +/// \param [in out] logical Current logical address. +/// \param [in] section Current section. +/// \param [in] map Memory map. +/// \param [in] repository Label repository. +/// \param [in] comments Comments repository. +/// \param [in] extra_infos Display extra informations as comments (if none set). +/// \return true if rts, rti or brk instruction was decoded. +/// \return false otherwise. +int decode(FILE *out, uint16_t *logical, Section *section, MemoryMap *map, LabelRepository *repository, CommentRepository *comments, int extra_infos); -/** - * Computes section size. - * @param [in] sections Section array. - * @param [in] index Index of the current section. - * @param [in] count Number of sections. - * @param [in] map Memory map. - * @return Section size. - */ -int32_t compute_size(section_t *sections, int index, int count, memmap_t *map); +/// Computes section size. +/// \param [in] sections Section array. +/// \param [in] index Index of the current section. +/// \param [in] count Number of sections. +/// \param [in] map Memory map. +/// \return Section size. +int32_t compute_size(SectionArray *sections, int index, int count, MemoryMap *map); -/** - * Output hardware IO port and RAM labels. - * @param [out] out File output. - * @param [in] map Memory map. - * @param [in] repository Label repository. - */ -void label_dump(FILE *out, memmap_t *map, label_repository_t *repository); +/// Output hardware IO port and RAM labels. +/// \param [out] out File output. +/// \param [in] map Memory map. +/// \param [in] repository Label repository. +void label_dump(FILE *out, MemoryMap *map, LabelRepository *repository); #endif // ETRIPATOR_DECODE_H diff --git a/examples/games_express/bank0.json b/examples/games_express/bank0.json deleted file mode 100644 index 704e35f..0000000 --- a/examples/games_express/bank0.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "le22a": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "e22a", - "mpr": ["ff", "f8", 0, 0, 0, 0, 1, 0 ] - }, - "le267": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "e267", - "mpr": ["ff", "f8", 0, 0, 0, 0, 1, 0 ] - }, - "le865_00": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "e865", - "mpr": ["ff", "f8", 0, 0, 0, 0, 1, 0 ] - }, - "le903_00": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "e903", - "mpr": ["ff", "f8", 0, 0, 0, 0, 1, 0 ] - }, - "le98c_00": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "e98c", - "mpr": ["ff", "f8", 0, 0, 0, 0, 1, 0 ] - }, - "irq_1": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "eba5", - "mpr": ["ff", "f8", 0, 0, 0, 0, 0, 0 ] - }, - "irq_2": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "eaa3", - "mpr": ["ff", "f8", 0, 0, 0, 0, 0, 0 ] - }, - "irq_nmi": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "ea9b", - "mpr": ["ff", "f8", 0, 0, 0, 0, 0, 0 ] - }, - "irq_reset": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "eab3", - "mpr": ["ff", "f8", 0, 0, 0, 0, 0, 0 ], - "description": [ - "-------------------------------------------------------------------------------", - " \"RESET\" interrupt handler", - "-------------------------------------------------------------------------------" - ] - }, - "led03_00": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "ed03", - "mpr": ["ff", "f8", 0, 0, 0, 0, 1, 0 ] - }, - "lf8a4_00": { - "filename": "games_express.asm", - "type": "code", - "page": 0, - "logical": "f8a4", - "mpr": ["ff", "f8", 0, 0, 0, 0, 1, 0 ] - }, - "irq_vectors": { - "filename": "games_express.asm", - "type": "data", - "page": 0, - "logical" :"fff6", - "size": "a", - "mpr": ["ff", "f8", 0, 0, 0, 0, 0, 0 ], - "data": { "type": "hex", "element_size": 2, "elements_per_line": 1 } - } -} diff --git a/examples/games_express/comments.json b/examples/games_express/comments.json deleted file mode 100644 index 844c2e7..0000000 --- a/examples/games_express/comments.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { "logical": "eab3", "page":"00", "text": "switch CPU to high speed mode"}, - { "logical": "eab4", "page":"00", "text": "disable interrupts"}, - { "logical": "eab5", "page":"00", "text": "clear decimal flag"}, - { "logical": "eab6", "page":"00", "text": "map I/O to the 1st memory page"}, - { "logical": "eaba", "page":"00", "text": "map RAM to the 2nd memory page"}, - { "logical": "eabe", "page":"00", "text": "clear zero page"}, - { "logical": "eac7", "page":"00", "text": "clear bss"}, - { "logical": "ead1", "page":"00", "text": [ - "disable interrupts", - "this looks like the soft reset entry point" - ] - }, - { "logical": "ead2", "page":"00", "text": "disable CPU timer"}, - { "logical": "ead5", "page":"00", "text": "switch CPU to high speed mode"}, - { "logical": "ead6", "page":"00", "text": "reset stack pointer"} -] diff --git a/examples/games_express/labels.json b/examples/games_express/labels.json deleted file mode 100644 index 6d65776..0000000 --- a/examples/games_express/labels.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - { "name":"cd_reset", "logical":"e22a", "page":"00" }, - { "name":"irq_2", "logical":"eaa3", "page":"00" }, - { "name":"leaeb_00", "logical":"eaeb", "page":"00" }, - { "name":"irq_1", "logical":"eba5", "page":"00" }, - { "name":"irq_timer", "logical":"ea9c", "page":"00" }, - { "name":"irq_nmi", "logical":"ea9b", "page":"00" }, - { "name":"irq_reset", "logical":"eab3", "page":"00" }, - { "name":"main", "logical":"f8a4", "page":"00" }, - { "name":"irq1_jmp", "logical":"2200", "page":"f8" }, - { "name":"irq2_jmp", "logical":"2202", "page":"f8" }, - { "name":"timer_jmp", "logical":"2263", "page":"f8" } -] diff --git a/examples/youkai_douchuuki/password.c b/examples/youkai_douchuuki/password.c index 38e65e9..fb9d0e1 100644 --- a/examples/youkai_douchuuki/password.c +++ b/examples/youkai_douchuuki/password.c @@ -1,6 +1,6 @@ /* * This file is part of Etripator, - * copyright (c) 2009--2023 Vincent Cruz. + * copyright (c) 2009--2024 Vincent Cruz. * * Etripator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 1fd6b7a..562606c 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -5,3 +5,5 @@ add_subdirectory(cwalk) add_library(munit STATIC ${CMAKE_CURRENT_LIST_DIR}/munit/munit.c) target_include_directories(munit PUBLIC $) set_property(TARGET munit PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) + +add_subdirectory(fff) diff --git a/externals/argparse b/externals/argparse index fafc503..682d452 160000 --- a/externals/argparse +++ b/externals/argparse @@ -1 +1 @@ -Subproject commit fafc503d23d077bda40c29e8a20ea74707452721 +Subproject commit 682d4520b4bc2b646cdfcf078b2fed00b3d2da30 diff --git a/externals/cwalk b/externals/cwalk index 08e7520..f45a23a 160000 --- a/externals/cwalk +++ b/externals/cwalk @@ -1 +1 @@ -Subproject commit 08e7520d332b700392248227bc07a68c429dc2bf +Subproject commit f45a23a13abf39d94b347d7c83810eca26a5a8d0 diff --git a/externals/fff b/externals/fff new file mode 160000 index 0000000..5111c61 --- /dev/null +++ b/externals/fff @@ -0,0 +1 @@ +Subproject commit 5111c61e1ef7848e3afd3550044a8cf4405f4199 diff --git a/ipl.c b/ipl.c index 28471ab..5149533 100644 --- a/ipl.c +++ b/ipl.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,8 +39,9 @@ #define IPL_HEADER_SIZE 0x80 #define IPL_DATA_SIZE 0xB2 -static int ipl_read_header(ipl_t *out, FILE *in, const char *filename) { - int ret = 0; +static bool ipl_read_header(IPL *out, FILE *in, const char *filename) { + bool ret = false; + if(fread(out->load_start_record, 1, 3, in) != 3) { ERROR_MSG("Failed to read IPLBLK from %s: %s", filename, strerror(errno)); } else if(fread(&out->load_sector_count, 1, 1, in) != 1) { @@ -76,12 +77,12 @@ static int ipl_read_header(ipl_t *out, FILE *in, const char *filename) { } else if(fread(out->extra, 1, 6, in) != 6) { ERROR_MSG("Failed to read EXTRA from %s: %s", filename, strerror(errno)); } else { - ret = 1; + ret = true; } return ret; } -void ipl_print(ipl_t *in) { +void ipl_print(IPL *in) { INFO_MSG("IPLBLK: hi:%02x mid:%02x lo:%02x", in->load_start_record[0], in->load_start_record[1], in->load_start_record[2]); INFO_MSG("IPLBKN: %02x", in->load_sector_count); INFO_MSG("IPLSTA: hi:%02x lo:%02x", in->load_store_address[1], in->load_store_address[0]); @@ -107,31 +108,39 @@ void ipl_print(ipl_t *in) { } else { fprintf(stream, "IPLBLK: .db $%02x, $%02x, $%02x\n", in->load_start_record[0], in->load_start_record[1], in->load_start_record[2]); fprintf(stream, "IPLBKN: .db $%02x\n", in->load_sector_count); - fprintf(stream, "IPLSTA: .dw $%02x%02x\n", in->load_store_address[0], in->load_store_address[1]); - fprintf(stream, "IPLJMP: .dw $%02x%02x\n", in->load_exec_address[0], in->load_exec_address[1]); + fprintf(stream, "IPLSTA: .dw $%02x%02x\n", in->load_store_address[1], in->load_store_address[0]); + fprintf(stream, "IPLJMP: .dw $%02x%02x\n", in->load_exec_address[1], in->load_exec_address[0]); fprintf(stream, "IPLMPR: .db $%02x, $%02x, $%02x, $%02x, $%02x\n", in->mpr[0], in->mpr[1], in->mpr[2], in->mpr[3], in->mpr[4]); fprintf(stream, "OPENMODE: .db $%02x\n", in->opening_mode); fprintf(stream, "GRPBLK: .db $%02x, $%02x, $%02x\n", in->opening_gfx_record[0], in->opening_gfx_record[1], in->opening_gfx_record[2]); fprintf(stream, "GRPBLN: .db $%02x\n", in->opening_gfx_sector_count); - fprintf(stream, "GRPADR: .dw $%02x%02x\n", in->opening_gfx_read_address[0], in->opening_gfx_read_address[1]); + fprintf(stream, "GRPADR: .dw $%02x%02x\n", in->opening_gfx_read_address[1], in->opening_gfx_read_address[0]); fprintf(stream, "ADPBLK: .db $%02x, $%02x, $%02x\n", in->opening_adpcm_record[0], in->opening_adpcm_record[1], in->opening_adpcm_record[2]); fprintf(stream, "ADPBLN: .db %02x\n", in->opening_adpcm_sector_count); - fprintf(stream, "ADPRATE: .db $%02x\b", in->opening_adpcm_sampling_rate); + fprintf(stream, "ADPRATE: .db $%02x\n", in->opening_adpcm_sampling_rate); fprintf(stream, "RESERVED: .db $%02x, $%02x, $%02x, $%02x, $%02x, $%02x, $%02x\n", in->reserved[0], in->reserved[1], in->reserved[2], in->reserved[3], in->reserved[4], in->reserved[5], in->reserved[6]); - fprintf(stream, "ID STR: .db \"%.24s\"\n", in->id); - fprintf(stream, "LEGAL: .db \"%.50s\"\n", in->legal); + fprintf(stream, "ID STR: .db \"%.24s\"", in->id); + for(int i=23; (i>=0) && (in->id[i] == '\0'); i--) { + fprintf(stream, ",0"); + } + fputc('\n', stream); + fprintf(stream, "LEGAL: .db \"%.50s\"", in->legal); + for(int i=49; (i>=0) && (in->legal[i] == '\0'); i--) { + fprintf(stream, ",0"); + } + fputc('\n', stream); fprintf(stream,"PROGRAM NAME: .db \"%.16s\"\n", in->program_name); fprintf(stream,"EXTRA: .db \"%.6s\"\n", in->extra); fclose(stream); } } -/* Read IPL data from file. */ -int ipl_read(ipl_t *out, const char *filename) { +// Read IPL data from file. +bool ipl_read(IPL *out, const char *filename) { + bool ret = false; FILE *in = fopen(filename, "rb"); - int ret = 0; if(in == NULL) { ERROR_MSG("Failed to open %s: %s", filename, strerror(errno)); } else { @@ -139,73 +148,71 @@ int ipl_read(ipl_t *out, const char *filename) { ERROR_MSG("Failed to seek to IPL header in %s: %s", filename, strerror(errno)); } else if(ipl_read_header(out, in, filename)) { ipl_print(out); - ret = 1; + ret = true; } fclose(in); } return ret; } -/* Get irq code offsets from IPL. */ -int ipl_sections(ipl_t *in, section_t **out, int *count) { - int i, j, k, extra; - section_t *section; +// Get irq code offsets from IPL. +bool ipl_sections(IPL *in, SectionArray *out) { + assert(in != NULL); + assert(out != NULL); + assert(out->data != NULL); + static const char *section_name[2] = { "cd_start", "gfx_start" }; static const char *section_filename[2] = { "cd_start.asm", "gfx_start.bin" }; - uint32_t record; - extra = 0; + bool ret =true; + int extra = 0; if(in->load_sector_count) { extra++; } if(in->opening_gfx_sector_count) { extra++; } - if(0 == extra) { + if(extra == 0) { INFO_MSG("No section found from IPL data."); - return 1; - } - - j = *count; - section = (section_t*)realloc(*out, (j+extra) * sizeof(section_t)); - if(NULL == section) { - ERROR_MSG("Failed to add extra sections."); - return 0; - } - *count += extra; - *out = section; - - memset(§ion[j], 0, extra * sizeof(section_t)); - - for(k=0; kmpr[i]; + } else { + size_t j = out->count; + for(int k=0; ret && (kmpr[i]; + } + if(section_array_add(out, &tmp) < 0) { + ret = false; + } + } + if(ret) { + // "CD boot" + if(in->load_sector_count) { + Section *section = &out->data[j++]; + uint32_t record = (in->load_start_record[0] << 16) | (in->load_start_record[1] << 8) | in->load_start_record[2]; + section->name = strdup(section_name[0]); + section->type = SECTION_TYPE_CODE; + section->page = section->mpr[in->load_exec_address[1]>>5]; + section->logical = (in->load_exec_address[1] << 8) | in->load_exec_address[0]; + section->offset = record * 2048; + section->size = in->load_sector_count * 2048; + section->output = strdup(section_filename[0]); + } + // "GFX" + if(in->opening_gfx_sector_count) { + Section *section = &out->data[j++]; + uint32_t record = (in->opening_gfx_record[0] << 16) | (in->opening_gfx_record[1] << 8) | in->opening_gfx_record[2]; + section->name = strdup(section_name[1]); + section->type = SECTION_TYPE_DATA; + section->page = section->mpr[in->opening_gfx_read_address[1]>>5]; + section->logical = (in->opening_gfx_read_address[1] << 8) | in->opening_gfx_read_address[0]; + section->offset = record * 2048; + section->size = in->opening_gfx_sector_count * 2048; + section->output = strdup(section_filename[1]); + section->data.type = DATA_TYPE_BINARY; + section->data.element_size = 1; + section->data.elements_per_line = 16; + } } } - // "CD boot" - if(in->load_sector_count) { - record = (in->load_start_record[0] << 16) | (in->load_start_record[1] << 8) | in->load_start_record[2]; - section[j].name = strdup(section_name[0]); - section[j].type = Code; - section[j].page = section[j].mpr[in->load_exec_address[1]>>5]; - section[j].logical = (in->load_exec_address[1] << 8) | in->load_exec_address[0]; - section[j].offset = record * 2048; - section[j].size = in->load_sector_count * 2048; - section[j].output = strdup(section_filename[0]); - j++; - } - // "GFX" - if(in->opening_gfx_sector_count) { - record = (in->opening_gfx_record[0] << 16) | (in->opening_gfx_record[1] << 8) | in->opening_gfx_record[2]; - section[j].name = strdup(section_name[1]); - section[j].type = Data; - section[j].page = section[j].mpr[in->opening_gfx_read_address[1]>>5]; - section[j].logical = (in->opening_gfx_read_address[1] << 8) | in->opening_gfx_read_address[0]; - section[j].offset = record * 2048; - section[j].size = in->opening_gfx_sector_count * 2048; - section[j].output = strdup(section_filename[1]); - section[j].data.type = Binary; - section[j].data.element_size = 1; - section[j].data.elements_per_line = 16; - } - return 1; + return ret; } diff --git a/ipl.h b/ipl.h index d794a11..59b2cdb 100644 --- a/ipl.h +++ b/ipl.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,82 +39,73 @@ #include "config.h" #include "section.h" -/** - * IPL Information block data format - */ +/// IPL Information block data format typedef struct { - /** IPLBLK - load start record no. of CD (3 bytes high/medium/low) - * this is where the program is stored. **/ + /// IPLBLK - load start record no. of CD (3 bytes high/medium/low) + /// this is where the program is stored. uint8_t load_start_record[3]; - /** IPLBLN - load block length of CD (1 byte) - * number of sectors to read. **/ + /// IPLBLN - load block length of CD (1 byte) + /// number of sectors to read. uint8_t load_sector_count; - /** IPLSTA - program load address (2 bytes low/high) - * main memory address for program read. **/ + /// IPLSTA - program load address (2 bytes low/high) + /// main memory address for program read. uint8_t load_store_address[2]; - /** IPLJMP - program execute address (2 bytes low/high) - * starting address of execution after program read. **/ + /// IPLJMP - program execute address (2 bytes low/high) + /// starting address of execution after program read. uint8_t load_exec_address[2]; - /** IPLMPR - ipl set mpr2-6 (5 bytes) **/ + /// IPLMPR - ipl set mpr2-6 (5 bytes) uint8_t mpr[5]; - /** OPENMODE - opening mode - * bit 0: data read to vram (0: not read, 1: read) - * bit 1: data read to adpcm buffer (0: not read, 1: read) - * bit 5: bg display (0: display on, 1: display off) - * bit 6: adpcm play (0: play, 1: not play) - * bit 7: adpcm play mode (0: single, 1: repeat) **/ - uint8_t opening_mode; - /** GRPBLK - opening graphic data record no. (3 bytes high/medium/low) **/ + /// OPENMODE - opening mode + /// bit 0: data read to vram (0: not read, 1: read) + /// bit 1: data read to adpcm buffer (0: not read, 1: read) + /// bit 5: bg display (0: display on, 1: display off) + /// bit 6: adpcm play (0: play, 1: not play) + /// bit 7: adpcm play mode (0: single, 1: repeat) + uint8_t opening_mode; + /// GRPBLK - opening graphic data record no. (3 bytes high/medium/low) uint8_t opening_gfx_record[3]; - /** GRPBLN - opening graphic data length (1 byte) **/ + /// GRPBLN - opening graphic data length (1 byte) uint8_t opening_gfx_sector_count; - /** GRPADR - opening graphic data read address (2 bytes low/high) **/ + /// GRPADR - opening graphic data read address (2 bytes low/high) uint8_t opening_gfx_read_address[2]; - /** ADPBLK - opening ADPCM data record no. (3 bytes high/medium/low) **/ + /// ADPBLK - opening ADPCM data record no. (3 bytes high/medium/low) uint8_t opening_adpcm_record[3]; - /** ADPBLN - opening ADPCM data length (1 byte) **/ + /// ADPBLN - opening ADPCM data length (1 byte) uint8_t opening_adpcm_sector_count; - /** ADPRATE - opening ADPCM sampling rate (1 byte) **/ + /// ADPRATE - opening ADPCM sampling rate (1 byte) uint8_t opening_adpcm_sampling_rate; - /** RESERVED (7 bytes) **/ + /// RESERVED (7 bytes) uint8_t reserved[7]; - /** ID STR - "PC Engine CD-ROM SYSTEM", 0 **/ + /// ID STR - "PC Engine CD-ROM SYSTEM", 0 uint8_t id[24]; - /** LEGAL - "Copyright HUDSON SOFT / NEC Home Electronics, Ltd.", 0 **/ + /// LEGAL - "Copyright HUDSON SOFT / NEC Home Electronics, Ltd.", 0 uint8_t legal[50]; - /** PROGRAM NAME - program name (16 bytes) **/ + /// PROGRAM NAME - program name (16 bytes) uint8_t program_name[16]; - /** EXTRA - (6 bytes) **/ + /// EXTRA - (6 bytes) uint8_t extra[6]; -} ipl_t; -/* +} IPL; +/* [todo] IPL graphic block color palette - 1 record BAT data - 1 record BG font data - GRPBLN - 2 records */ -/** - * Display IPL infos. - * \param [in] in IPL infos. - */ -void ipl_print(ipl_t *in); +/// Display IPL infos. +/// \param [in] in IPL infos. +void ipl_print(IPL *in); -/** - * Read IPL data from file. - * \param [out] out IPL infos. - * \param [in] filename Input filename. - * \return 0 on error, 1 otherwise. - */ -int ipl_read(ipl_t *out, const char *filename); +/// Read IPL data from file. +/// \param [out] out IPL infos. +/// \param [in] filename Input filename. +/// \return 0 on error, 1 otherwise. +bool ipl_read(IPL *out, const char *filename); -/** - * Get irq code offsets from IPL. - * \param [in] in IPL infos. - * \param [out] section Sections. - * \param [out] count Section count. - * \return 0 on error, 1 otherwise. - */ -int ipl_sections(ipl_t *in, section_t **out, int *count); +/// Get irq code offsets from IPL. +/// \param [in] in IPL infos. +/// \param [out] out Sections. +/// \return 0 on error, 1 otherwise. +bool ipl_sections(IPL *in, SectionArray *out); #endif // IPL_H diff --git a/irq.c b/irq.c index 2db67ac..1a597db 100644 --- a/irq.c +++ b/irq.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ #include "irq.h" #include "message.h" -#define PCE_IRQ_TABLE 0xfff6 +#define PCE_IRQ_TABLE 0xFFF6U #define PCE_IRQ_COUNT 5 static char* g_irq_names[PCE_IRQ_COUNT] = { @@ -47,46 +47,74 @@ static char* g_irq_names[PCE_IRQ_COUNT] = { "irq_reset" }; -/* Get irq code offsets from rom. */ -int irq_read(memmap_t* map, section_t **section, int *count) { - int i; - uint8_t addr[2]; - size_t filename_len; - - uint16_t offset = PCE_IRQ_TABLE; - int j = *count; - section_t *tmp = (section_t*)realloc(*section, (j+PCE_IRQ_COUNT) * sizeof(section_t)); - if(NULL == tmp) { - ERROR_MSG("Failed to allocate extra IRQ sections."); - return 0; - } - *section = tmp; - *count += PCE_IRQ_COUNT; - - for(i=0; imemory[PCE_MEMORY_ROM].data == NULL) { + ERROR_MSG("No ROM in memory map."); + } else if(map->memory[PCE_MEMORY_ROM].length < (PCE_IRQ_TABLE + PCE_IRQ_COUNT)) { + ERROR_MSG("ROM is abnormally small."); + } else { + uint16_t offset = PCE_IRQ_TABLE; + map->mpr[7] = 0; + + ret = true; + + for(size_t i=0; ret && (i #include -int json_validate_int(const json_t* obj, int* out) { - int ret = 0; +bool json_validate_int(const json_t* obj, int* out) { + assert(obj != NULL); + assert(out != NULL); + bool ret = false; if(json_is_string(obj)) { const char *str = json_string_value(obj); for(; (*str!='\0') && isspace(*str); str++) { @@ -50,44 +52,62 @@ int json_validate_int(const json_t* obj, int* out) { errno = 0; *out = strtoul(str, NULL, 16); if(errno == 0) { - ret = 1; + ret = true; } } else if(json_is_integer(obj)) { *out = (int)json_integer_value(obj); - ret = 1; + ret = true; } return ret; } -char* json_load_description(const json_t* obj, const char *key) { - json_t *tmp = json_object_get(obj, key); - char *out = NULL; - if(json_is_string(tmp)) { - out = strdup(json_string_value(tmp)); - } else if (json_is_array(tmp)) { - int index; - json_t* value; +bool json_load_description(const json_t* obj, const char *key, char **out) { + assert(out != NULL); + assert(out && (*out == NULL)); - size_t len = 0; - json_array_foreach(tmp, index, value) { - if(json_is_string(value)) { - const char *str = json_string_value(value); - if(out) { - out[len-1] = '\n'; + bool ret = false; + char *buffer = NULL; + json_t *tmp = json_object_get(obj, key); + + if(tmp == NULL) { + ret = true; + } else { + if(json_is_string(tmp)) { + buffer = strdup(json_string_value(tmp)); + } else if (json_is_array(tmp)) { + int index; + json_t* value; + size_t len = 0; + json_array_foreach(tmp, index, value) { + if(json_is_string(value)) { + const char *str = json_string_value(value); + if(buffer != NULL) { + buffer[len-1] = '\n'; + } + size_t n = len + strlen(str) + 1; + char *ptr = realloc(buffer, n); + if(ptr == NULL) { + free(buffer); + buffer = NULL; + break; + } + memcpy(ptr+len, str, strlen(str)); + ptr[n-1] = '\0'; + buffer = ptr; + len = n; } - size_t n = len + strlen(str) + 1; - char *ptr = realloc(out, n); - memcpy(ptr+len, str, strlen(str)); - ptr[n-1] = '\0'; - out = ptr; - len = n; } } + ret = (buffer != NULL); } - return out; + *out = buffer; + return ret; } void json_print_description(FILE *out, const char *key, const char *str) { + assert(out != NULL); + assert(key != NULL); + assert(str != NULL); fprintf(out, "\"%s\":[", key); while(*str) { fprintf(out, "\n\t\t\t\""); diff --git a/jsonhelpers.h b/jsonhelpers.h index b8d5e45..f5631a7 100644 --- a/jsonhelpers.h +++ b/jsonhelpers.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,8 +40,8 @@ #include -int json_validate_int(const json_t* obj, int* out); -char* json_load_description(const json_t* obj, const char *key); +bool json_validate_int(const json_t* obj, int* out); +bool json_load_description(const json_t* obj, const char *key, char **out); void json_print_description(FILE *out, const char *key, const char *str); #endif // ETRIPATOR_JSON_HELPERS_H \ No newline at end of file diff --git a/label.c b/label.c index 418e950..4eeeec0 100644 --- a/label.c +++ b/label.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,25 +36,17 @@ #include "label.h" #include "message.h" +#include + #define LABEL_ARRAY_INC 16 -/** - * Label repository. - */ -struct label_repository_impl { - size_t size; /**< Size of label repository */ - size_t last; /**< Last element in the repository */ - label_t *labels; /**< Labels */ -}; - -/** - * Get label index by its address. - * \param [in] repository Label repository. - * \param [in] logical Logical address. - * \param [in] page Memory page. - * \return label index or -1 if the label was not found. - */ -static int label_repository_index(label_repository_t* repository, uint16_t logical, uint8_t page) { +/// Get label index by its address. +/// \param [in] repository Label repository. +/// \param [in] logical Logical address. +/// \param [in] page Memory page. +/// \return label index. +/// \return -1 if the label was not found. +static int label_repository_index(LabelRepository *repository, uint16_t logical, uint8_t page) { size_t i; for(i=0; ilast; i++) { if( (repository->labels[i].page == page) && @@ -65,53 +57,41 @@ static int label_repository_index(label_repository_t* repository, uint16_t logic return -1; } -/* Create label repository. */ -label_repository_t* label_repository_create() { - label_repository_t *repository; - repository = (label_repository_t*)malloc(sizeof(label_repository_t)); - if(repository == NULL) { - ERROR_MSG("Failed to create label repository: %s", strerror(errno)); - return NULL; - } - +// Create label repository. +bool label_repository_create(LabelRepository* repository) { + assert(repository != NULL); + bool ret = true; repository->last = 0; - repository->labels = NULL; - repository->size = LABEL_ARRAY_INC; - repository->labels = (label_t*)malloc(repository->size * sizeof(label_t)); + repository->labels = (Label*)malloc(repository->size * sizeof(Label)); if(repository->labels == NULL) { ERROR_MSG("Failed to create label: %s", strerror(errno)); label_repository_destroy(repository); - free(repository); - return NULL; + ret = false; } - - return repository; + return ret; } -/* Delete label repository. */ -void label_repository_destroy(label_repository_t* repository) { +// Delete label repository. +void label_repository_destroy(LabelRepository* repository) { repository->size = 0; repository->last = 0; if(repository->labels != NULL) { - for(int i=0; ilast; i++) { - if(repository->labels[i].name) { - free(repository->labels[i].name); - } - if(repository->labels[i].description) { - free(repository->labels[i].description); - } + for(size_t i=0; ilast; i++) { + free(repository->labels[i].name); + free(repository->labels[i].description); } free(repository->labels); repository->labels = NULL; } } -/* Add label to repository. */ -int label_repository_add(label_repository_t* repository, const char* name, uint16_t logical, uint8_t page, const char *description) { - int ret = 1; +// Add label to repository. +bool label_repository_add(LabelRepository* repository, const char* name, uint16_t logical, uint8_t page, const char *description) { + assert(repository != NULL); + bool ret = true; int index = label_repository_index(repository, logical, page); if(index >= 0) { #if 0 @@ -125,12 +105,12 @@ int label_repository_add(label_repository_t* repository, const char* name, uint1 } else { /* Expand arrays if necessary */ if(repository->last >= repository->size) { - label_t *ptr; + Label *ptr; repository->size += LABEL_ARRAY_INC; - ptr = (label_t*)realloc(repository->labels, repository->size * sizeof(label_t)); + ptr = (Label*)realloc(repository->labels, repository->size * sizeof(Label)); if(ptr == NULL) { label_repository_destroy(repository); - ret = 0; + ret = false; } else { repository->labels = ptr; } @@ -150,35 +130,39 @@ int label_repository_add(label_repository_t* repository, const char* name, uint1 return ret; } -/* Find a label by its address. */ -int label_repository_find(label_repository_t* repository, uint16_t logical, uint8_t page, label_t *out) { +// Find a label by its address. +bool label_repository_find(LabelRepository* repository, uint16_t logical, uint8_t page, Label *out) { int index = label_repository_index(repository, logical, page); - if(index < 0) { - memset(out, 0, sizeof(label_t)); - return 0; + bool ret = (index >= 0); + if(ret) { + memcpy(out, &repository->labels[index], sizeof(Label)); + } else { + memset(out, 0, sizeof(Label)); } - memcpy(out, &repository->labels[index], sizeof(label_t)); - return 1; + return ret; } -/* Get the number of labels stored in the repository. */ -int label_repository_size(label_repository_t* repository) { - return repository ? (int)repository->last : 0; +// Get the number of labels stored in the repository. +int label_repository_size(LabelRepository* repository) { + assert(repository != NULL); + return (int)repository->last; } -/* Retrieve the label at the specified index. */ -int label_repository_get(label_repository_t* repository, int index, label_t *out) { - if((repository != NULL) && ((index >= 0) && (index < (int)repository->last))) { - memcpy(out, &repository->labels[index], sizeof(label_t)); - return 1; +// Retrieve the label at the specified index. +bool label_repository_get(LabelRepository* repository, int index, Label *out) { + bool ret = false; + if((repository != NULL) && ((index >= 0) && (index < (int)repository->last))) { + ret = true; + memcpy(out, &repository->labels[index], sizeof(Label)); } else { - memset(out, 0, sizeof(label_t)); - return 0; + memset(out, 0, sizeof(Label)); } + return ret; } -/* Delete labels */ -int label_repository_delete(label_repository_t* repository, uint16_t first, uint16_t end, uint8_t page) { - size_t i; + +// Delete labels. +void label_repository_delete(LabelRepository* repository, uint16_t first, uint16_t end, uint8_t page) { + size_t i; for(i=0; ilast; i++) { if( (repository->labels[i].page == page) && (repository->labels[i].logical >= first) && @@ -191,10 +175,9 @@ int label_repository_delete(label_repository_t* repository, uint16_t first, uint if(repository->labels[i].description) { free(repository->labels[i].description); } - memcpy(&repository->labels[i], &repository->labels[repository->last], sizeof(label_t)); + memcpy(&repository->labels[i], &repository->labels[repository->last], sizeof(Label)); i--; } } } - return 1; } diff --git a/label.h b/label.h index 46e77ef..55dd1eb 100644 --- a/label.h +++ b/label.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,91 +38,82 @@ #include "config.h" -/** - * Label. - */ +/// Label. typedef struct { - char* name; /**< Offset in the repository name buffer */ - uint16_t logical; /**< Logical address */ - uint8_t page; /**< Memory page */ - char* description; /**< Description (optional) */ -} label_t; - -typedef struct label_repository_impl label_repository_t; - -/** - * Create label repository. - * \return A pointer to a label repository or NULL if an error occured. - */ -label_repository_t* label_repository_create(); - -/** - * Release label repository resources. - * \param [in,out] repository Label repository. - */ -void label_repository_destroy(label_repository_t* repository); - -/** - * Add label (or inline description) to repository. - * \param [in,out] repository Label repository. - * \param [in] name Name. If the name is NULL, then this label is an inline description. - * \param [in] logical Logical address. - * \param [in] page Memory page. - * \param [in] description Description (optional if name is set, mandatory otherwise). - */ -int label_repository_add(label_repository_t* repository, const char* name, uint16_t logical, uint8_t page, const char *description); - -/** - * Find a label by its address. - * \param [in] repository Label repository. - * \param [in] logical Logical address. - * \param [in] page Memory page. - * \param [out] out Associated label (if any). - * \return 1 if a label was found, 0 otherwise. - */ -int label_repository_find(label_repository_t* repository, uint16_t logical, uint8_t page, label_t *out); - -/** - * Get the number of labels stored in the repository. - * \param [in] repository Label repository. - * \return Label count. - */ -int label_repository_size(label_repository_t* repository); - -/** - * Retrieve the label at the specified index. - * \param [in] repository Label repository. - * \param [in] index Label index. - * \param [out] out Label (if any). - * \return 1 if a label exists for the specified index, 0 otherwise. - */ -int label_repository_get(label_repository_t* repository, int index, label_t *out); - -/** - * Delete labels - * \param [in] repository Label repository. - * \param [in] first Start of the logical address range. - * \param [in] end End of the logical address range. - * \param [in] page Memory page. - */ -int label_repository_delete(label_repository_t* repository, uint16_t first, uint16_t end, uint8_t page); - -/** - * Load labels from file. - * \param [in] filename Input filename. - * \param [out] repository Label repository. - * \return 1 if the labels contained in the file was succesfully added to the repository. - * 0 if an error occured. - */ -int label_repository_load(const char* filename, label_repository_t* repository); - -/** - * Save labels to file. - * \param [in] filename Configuration file. - * \param [in] reposity Label repository. - * \return 1 if the labels in the repository were succesfully written to the file. - * 0 if an error occured. - */ -int label_repository_save(const char* filename, label_repository_t* repository); + char* name; //< Offset in the repository name buffer + uint16_t logical; //< Logical address + uint8_t page; //< Memory page + char* description; //< Description (optional) +} Label; + +/// Label repository. +typedef struct { + size_t size; //< Size of label repository. + size_t last; //< Last element in the repository. + Label *labels; //< Labels. +} LabelRepository; + +/// Create label repository. +/// \param [in out] repository Label repository. +/// \return true if the repository was succesfully initialized +/// \return false if an error occured +bool label_repository_create(LabelRepository* repository); + +/// Release label repository resources. +/// \param [in,out] repository Label repository. +void label_repository_destroy(LabelRepository* repository); + +/// Add label (or inline description) to repository. +/// \param [in,out] repository Label repository. +/// \param [in] name Name. If the name is NULL, then this label is an inline description. +/// \param [in] logical Logical address. +/// \param [in] page Memory page. +/// \param [in] description Description (optional if name is set, mandatory otherwise). +/// \return true if the entry was successfully added to the repository. +/// \return false if an error occured. +bool label_repository_add(LabelRepository* repository, const char* name, uint16_t logical, uint8_t page, const char *description); + +/// Find a label by its address. +/// \param [in] repository Label repository. +/// \param [in] logical Logical address. +/// \param [in] page Memory page. +/// \param [out] out Associated label (if any). +/// \return true if a label was found. +/// \return 0 otherwise. +bool label_repository_find(LabelRepository* repository, uint16_t logical, uint8_t page, Label *out); + +/// Get the number of labels stored in the repository. +/// \param [in] repository Label repository. +/// \return Label count. +int label_repository_size(LabelRepository* repository); + +/// Retrieve the label at the specified index. +/// \param [in] repository Label repository. +/// \param [in] index Label index. +/// \param [out] out Label (if any).& +/// \return true if a label exists for the specified index. +/// \return false otherwise. +bool label_repository_get(LabelRepository* repository, int index, Label *out); + +/// Delete labels +/// \param [in] repository Label repository. +/// \param [in] first Start of the logical address range. +/// \param [in] end End of the logical address range. +/// \param [in] page Memory page. +void label_repository_delete(LabelRepository* repository, uint16_t first, uint16_t end, uint8_t page); + +/// Load labels from file. +/// \param [out] repository Label repository. +/// \param [in] filename Input filename. +/// \return true if the labels contained in the file was succesfully added to the repository. +/// \return false if an error occured. +bool label_repository_load(LabelRepository* repository, const char* filename); + +/// Save labels to file. +/// \param [in] reposity Label repository. +/// \param [in] filename Configuration file. +/// \return true if the labels in the repository were succesfully written to the file. +/// \return false if an error occured. +bool label_repository_save(LabelRepository* repository, const char* filename); #endif // ETRIPATOR_LABEL_H diff --git a/label/load.c b/label/load.c index 990daba..1ddc476 100644 --- a/label/load.c +++ b/label/load.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,25 +40,23 @@ #define MAX_LABEL_NAME 128 -/** - * Load labels from file. - * \param [in] filename Input filename. - * \param [out] repository Label repository. - * \return 1 if the labels contained in the file were succesfully added to the repository. - * 0 if an error occured. - */ -int label_repository_load(const char* filename, label_repository_t* repository) { - json_t* root; +// Load labels from file. +bool label_repository_load(LabelRepository* repository, const char* filename) { + assert(repository != NULL); + assert(filename != NULL); + + bool ret = false; + json_error_t err; - json_t* value; - int ret = 0, index = 0; - root = json_load_file(filename, 0, &err); + json_t* root = json_load_file(filename, 0, &err); if(!root) { ERROR_MSG("Failed to parse %s:%d:%d: %s", filename, err.line, err.column, err.text); } else { + json_t* value = NULL; + int index = 0; if(!json_is_array(root)) { ERROR_MSG("Array expected."); - } else for (index = 0, ret = 1; ret && (index < json_array_size(root)) && (value = json_array_get(root, index)); index++) { + } else for (index = 0, ret = true; ret && (index < json_array_size(root)) && (value = json_array_get(root, index)); index++) { ret = 0; if(!json_is_object(value)) { ERROR_MSG("Expected object."); @@ -74,7 +72,7 @@ int label_repository_load(const char* filename, label_repository_t* repository) tmp = json_object_get(value, "logical"); if(!json_validate_int(tmp, &num)) { ERROR_MSG("Invalid or missing logical address."); - } else if((num < 0) || (num > 0xffff)) { + } else if((num < 0) || (num > 0xFFFF)) { ERROR_MSG("Logical address out of range."); } else { uint16_t logical = (uint16_t)num; @@ -84,11 +82,13 @@ int label_repository_load(const char* filename, label_repository_t* repository) ERROR_MSG("Invalid or missing page."); } else { // description - char* description = json_load_description(value, "description"); - if((num < 0) || (num > 0xff)) { + char* description = NULL; + if(json_load_description(value, "description", &description) != true) { + ERROR_MSG("Failed to load label description"); + } else if((num < 0) || (num > 0xff)) { ERROR_MSG("Page value out of range."); } else if(label_repository_add(repository, key, logical, (uint8_t)num, description)) { - ret = 1; + ret = true; } free(description); } diff --git a/label/save.c b/label/save.c index 0d91b47..fc1977d 100644 --- a/label/save.c +++ b/label/save.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,27 +40,30 @@ #include "../jsonhelpers.h" #include "../label.h" -/* Save labels to file. */ -int label_repository_save(const char* filename, label_repository_t* repository) { +// Save labels to file +bool label_repository_save(LabelRepository* repository, const char* filename) { + bool ret = false; FILE *stream = fopen(filename, "wb"); - int i, count = label_repository_size(repository); if(stream == NULL) { ERROR_MSG("Failed to open %s: %s", filename, strerror(errno)); - return 0; - } - fprintf(stream, "[\n"); - for(i=0; idata = (uint8_t*)malloc(len); - if(!mem->data) { - ERROR_MSG("Unable to allocate memory : %s.\n", strerror(errno)); - mem->len = 0; - return 0; - } - mem->len = len; - return 1; -} -/* Destroy memory block. */ -void mem_destroy(mem_t *mem) { - if(mem) { - mem->len = 0; - if(mem->data) { - free(mem->data); - mem->data = NULL; + +// Creates a new memory block. +bool memory_create(Memory *memory, size_t length) { + assert(memory != NULL); + + bool ret = false; + if(length == 0) { + ERROR_MSG("Invalid length"); + } else { + uint8_t *buffer = (uint8_t*)malloc(length); + if(buffer == NULL) { + ERROR_MSG("Unable to allocate %zu bytes: %s.", length, strerror(errno)); + } else { + memory->data = buffer; + memory->length = length; + ret = true; } } + return ret; } -/* Fill memory block bytes with a given byte value. */ -void mem_fill(mem_t *mem, uint8_t c) { - if(mem->data && mem->len) { - memset(mem->data, (int)mem->len, c); + +// Releases memory block resources. +void memory_destroy(Memory *memory) { + assert(memory != NULL); + free(memory->data); + memory->data = NULL; + memory->length = 0; +} + +// Fills a memory block with a given byte value. +bool memory_fill(Memory *memory, uint8_t c) { + assert(memory != NULL); + bool ret = false; + if(memory->data != NULL) { + const size_t n = memory->length; + for(size_t i=0; idata[i] = c; + } + ret = true; } + return ret; } diff --git a/memory.h b/memory.h index 56b85cf..e5bb2da 100644 --- a/memory.h +++ b/memory.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,30 +37,35 @@ #define ETRIPATOR_MEMORY_H #include "config.h" -/** - * Memory block. - */ + +/// @defgroup Memory Memory block +///@{ + +/// Memory block. typedef struct { - size_t len; /**< Byte array length. **/ - uint8_t *data; /**< Byte array. **/ -} mem_t; -/** - * Create memory block. - * \param [out] mem Memory block. - * \param [in] len Memory block size (in bytes). - * \return 1 upon success, 0 if an error occured. - */ -int mem_create(mem_t *mem, size_t len); -/** - * Destroy memory block. - * \param [in] mem Memory block. - */ -void mem_destroy(mem_t *mem); -/** - * Fill memory block bytes with a given byte value. - * \param [in] mem Memory block. - * \param [in] c Byte value. - */ -void mem_fill(mem_t *mem, uint8_t c); + size_t length; ///< Byte array length. + uint8_t *data; ///< Byte array. +} Memory; + +/// Creates a new memory block. +/// \param [out] memory Memory block. +/// \param [in] length Memory block size (in bytes). +/// \return true if the memory block was successfully created. +/// \return false if an error occured. +/// @note memory is left untouched if an error occured. +bool memory_create(Memory *memory, size_t length); + +/// Releases memory block resources. +/// \param [in out] mem Memory block. +void memory_destroy(Memory *memory); + +/// Fills a memory block with a given byte value. +/// \param [in out] mem Memory block. +/// \param [in] c Byte value. +/// \return true if the memory block was successfully filled. +/// \return false if the memory block is invalid (0 size or unallocated). +bool memory_fill(Memory *memory, uint8_t c); + +/// @} #endif // ETRIPATOR_MEMORY_H diff --git a/memory_map.c b/memory_map.c new file mode 100644 index 0000000..7903d5e --- /dev/null +++ b/memory_map.c @@ -0,0 +1,116 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include "memory_map.h" +#include "message.h" + +// Resets memory map. +static void memory_map_clear(MemoryMap *map) { + for(unsigned int i=0; imemory[i].length = 0; + map->memory[i].data = NULL; + } + for(unsigned int i=0; impr[i] = 0xFFU; + } + for(unsigned int i=0; ipage[i].id = PCE_MEMORY_NONE; + map->page[i].bank = 0; + } +} + +// Initializes memory map. +bool memory_map_init(MemoryMap *map) { + bool ret = false; + + memory_map_clear(map); + + // Allocate main (or work) RAM. + if(!memory_create(&map->memory[PCE_MEMORY_BASE_RAM], PCE_BANK_SIZE)) { + ERROR_MSG("Failed to allocate main memory!"); + } else { + // Main RAM is mapped to pages 0xF8 to 0xFB (included). + // Pages 0xF9 to 0xFB mirror page 0xF8. */ + for(unsigned int i=0xf8; i<=0xfb; i++) { + map->page[i].id = PCE_MEMORY_BASE_RAM; + map->page[i].bank = 0; + } + + // ROM and syscard RAM will be initialized later. + ret = true; + } + return ret; +} + +// Releases resources used by the memory map. +void memory_map_destroy(MemoryMap *map) { + assert(map != NULL); + for(unsigned i=0; imemory[i]); + } + memory_map_clear(map); +} + +// Get the memory page associated to a logical address. +uint8_t memory_map_page(MemoryMap* map, uint16_t logical) { + assert(map != NULL); + uint8_t id = (logical >> 13) & 0x07U; + return map->mpr[id]; +} + +// Reads a single byte from memory. +uint8_t memory_map_read(MemoryMap *map, size_t logical) { + assert(map != NULL); + const uint8_t id = memory_map_page(map, (uint16_t)logical); + const Page *page = &map->page[id]; + + uint8_t ret = 0xFFU; + if(page->id != PCE_MEMORY_NONE) { + const size_t offset = (logical & 0x1FFFU) + (page->bank * PCE_BANK_SIZE); + const Memory *mem = &map->memory[page->id]; + if(offset < mem->length) { + ret = mem->data[offset]; + } + } + return ret; +} + +// Update mprs. +void memory_map_mpr(MemoryMap *map, const uint8_t mpr[PCE_MPR_COUNT]) { + assert(map != NULL); + for(unsigned int i=0; impr[i] = mpr[i]; + } +} diff --git a/memorymap.h b/memory_map.h similarity index 59% rename from memorymap.h rename to memory_map.h index 7b6a3a3..b4906fc 100644 --- a/memorymap.h +++ b/memory_map.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,56 +38,60 @@ #include "memory.h" -/** - * PC Engine memory - */ +/// PC Engine memory blocks. enum { - PCE_MEM_ROM = 0, - PCE_MEM_BASE_RAM, - PCE_MEM_CD_RAM, - PCE_MEM_SYSCARD_RAM, - PCE_MEM_COUNT + PCE_MEMORY_NONE = -1, + PCE_MEMORY_ROM = 0, + PCE_MEMORY_BASE_RAM, + PCE_MEMORY_CD_RAM, + PCE_MEMORY_SYSCARD_RAM, + PCE_MEMORY_COUNT }; -/** - * PC Engine memory map. - */ +#define PCE_PAGE_COUNT 0x100U + +#define PCE_MPR_COUNT 8U + +#define PCE_BANK_SIZE 8192U + +/// PC Engine memory page description. +typedef struct { + int id; // name the PCE_MEMORY_* enum ? + size_t bank; +} Page; + +/// PC Engine memory map. typedef struct { - mem_t mem[PCE_MEM_COUNT]; - uint8_t *page[0x100]; - uint8_t mpr[8]; -} memmap_t; - -/** - * Initializes memory map. - * \param map Memory map. - * \return 1 upon success, 0 if an error occured. - */ -int memmap_init(memmap_t *map); -/** - * Releases resources used by the memory map. - * \param map Memory map. - */ -void memmap_destroy(memmap_t *map); -/** - * Get the memory page associated to a logical address. - * \param map Memory map. - * \param logical Logical address. - * \return Memory page. - */ -uint8_t memmap_page(memmap_t* map, uint16_t logical); -/** - * Reads a single byte from memory. - * \param [in] map Memory map. - * \param [in] logical Logical address. - * \return Byte read. - */ -uint8_t memmap_read(memmap_t *map, size_t logical); -/** - * Update mprs. - * \param [in][out] map Memory map. - * \param [in] mpr Memory page registers. - */ -void memmap_mpr(memmap_t *map, const uint8_t *mpr); + Memory memory[PCE_MEMORY_COUNT]; + Page page[PCE_PAGE_COUNT]; + uint8_t mpr[PCE_MPR_COUNT]; +} MemoryMap; + +/// Initializes memory map. +/// \param [in out] map Memory map. +/// \return true if the memory map was succesfully initialized +/// \return false if an error occured +bool memory_map_init(MemoryMap *map); + +/// Releases resources used by the memory map. +/// \param [in out] map Memory map. +void memory_map_destroy(MemoryMap *map); + +/// Get the memory page associated to a logical address. +/// \param [in] map Memory map. +/// \param [in] logical Logical address. +/// \return Memory page. +uint8_t memory_map_page(MemoryMap* map, uint16_t logical); + +/// Reads a single byte from memory. +/// \param [in] map Memory map. +/// \param [in] logical Logical address. +/// \return The value stored at the specified logical address. +uint8_t memory_map_read(MemoryMap *map, size_t logical); + +/// Update the whole mprs list +/// \param [in out] map Memory map. +/// \param [in] mpr Memory page registers. +void memory_map_mpr(MemoryMap *map, const uint8_t mpr[PCE_MPR_COUNT]); #endif // ETRIPATOR_MEMORY_MAP_H diff --git a/message.c b/message.c index caaed91..95f9b65 100644 --- a/message.c +++ b/message.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,48 +33,56 @@ ¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ */ -#include "config.h" #include "message.h" #include -static msg_printer_t* g_msg_printer = NULL; +static MessagePrinter* g_message_printer_head = NULL; /* Setup global message printer list. */ -void msg_printer_init() { - g_msg_printer = NULL; +void message_printer_init() { + g_message_printer_head = NULL; // nothing much atm... } + /* Releases the resources used by message printers. */ -void msg_printer_destroy() { - msg_printer_t* printer; - for(printer=g_msg_printer; NULL != printer; printer=printer->next) { - printer->close(printer); +void message_printer_destroy() { + for(MessagePrinter *it = g_message_printer_head; it != NULL; it = it->next) { + if(it->close) { + it->close(); + } } + g_message_printer_head = NULL; } + /* Adds a new message printer to the global list. */ -int msg_printer_add(msg_printer_t *printer) { - if(printer->open(printer)) { - return 1; +bool message_printer_add(MessagePrinter *printer) { + bool ret = false; + if((printer != NULL) && (printer->open != NULL)) { + ret = printer->open(); + if(ret) { + printer->next = g_message_printer_head; + g_message_printer_head = printer; + } } - printer->next = g_msg_printer; - g_msg_printer = printer; - return 0; + return ret; } + /* Dispatch messages to printers. */ -void print_msg(msg_type_t type, const char* file, size_t line, const char* function, const char* format, ...) { - msg_printer_t* printer; +void message_print(MessageType type, const char* file, size_t line, const char* function, const char* format, ...) { + assert(file != NULL); + assert(function != NULL); const char* filename; size_t length; cwk_path_get_basename(file, &filename, &length); if(filename == NULL) { filename = file; } - for(printer=g_msg_printer; NULL != printer; printer=printer->next) { - if(printer->output) { + for(MessagePrinter *it=g_message_printer_head; it != NULL; it = it->next) { + if(it->output != NULL) { va_list args; va_start(args, format); - printer->output(printer, type, filename, line, function, format, args); + (void)it->output(type, filename, line, function, format, args); va_end(args); } } diff --git a/message.h b/message.h index 10c0967..8b78edb 100644 --- a/message.h +++ b/message.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,80 +38,80 @@ #include "config.h" -/** - * \brief Message types - */ +/// @defgroup Message Message printing +///@{ + +// Message types. typedef enum { - MSG_TYPE_ERROR=0, - MSG_TYPE_WARNING, - MSG_TYPE_INFO -} msg_type_t; - -/** - * \brief Initializes and allocates any resources necessary for the message printer. - * \param [in] impl Message printer. - * \return 0 upon success. - */ -typedef int (*msg_printer_open_t)(void* impl); - -/** - * \brief Deletes, clean up resources used by the message printer. - * \param [in] impl Message printer. - * \return 0 upon success. - */ -typedef int (*msg_printer_close_t)(void* impl); - -/** - * \brief Prints message. - * \param [in] impl Message printer. - * \param [in] type Message type. - * \param [in] file Name of the file where the print message command was issued. - * \param [in] line Line number in the file where the print message command was issued. - * \param [in] function Function where the print message command was issued. - * \param [in] format Format string. - * \param [in] args Argument lists. - * \return 0 upon success. - */ -typedef int (*msg_printer_output_t)(void* impl, msg_type_t type, const char* file, size_t line, const char* function, const char* format, va_list args); - -/** - * \brief - */ -typedef struct msg_printer_t_ { - msg_printer_open_t open; - msg_printer_close_t close; - msg_printer_output_t output; - struct msg_printer_t_* next; -} msg_printer_t; - -#define ERROR_MSG(format, ...) print_msg(MSG_TYPE_ERROR, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) - -#define WARNING_MSG(format, ...) print_msg(MSG_TYPE_WARNING, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) - -#define INFO_MSG(format, ...) print_msg(MSG_TYPE_INFO, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) - -/** - * Setup global message printer list. - */ -void msg_printer_init(); -/** - * Releases the resources used by message printers. - */ -void msg_printer_destroy(); -/** - * Adds a new message printer to the global list. - * \param [in] printer Message printer to be added to the list. - * \return 0 upon success. - */ -int msg_printer_add(msg_printer_t *printer); -/** - * Dispatch messages to printers. - * \param type Message type. - * \param file Name of the file where the print message command was issued. - * \param line Line number in the file where the print message command was issued. - * \param function Function where the print message command was issued. - * \param format Format string. - */ -void print_msg(msg_type_t type, const char* file, size_t line, const char* function, const char* format, ...); + MESSAGE_TYPE_ERROR = 0, + MESSAGE_TYPE_WARNING, + MESSAGE_TYPE_INFO, + MESSAGE_TYPE_DEBUG, + MESSAGE_TYPE_COUNT, +} MessageType; + +struct MessagePrinter; + +/// Initializes and allocates any resources necessary for the message printer. +/// \return true if the message printer was successfully opened. +/// \return false if an error occured. +typedef bool (*MessagePrinterOpen)(); + +/// Releases resources used by the message printer. +/// \return true if the resources used by the message printer were successfully released. +/// \return false if an error occured. +typedef bool (*MessagePrinterClose)(); + +/// \brief Prints message. +/// \param [in] type Message type. +/// \param [in] file Name of the file where the print message command was issued. +/// \param [in] line Line number in the file where the print message command was issued. +/// \param [in] function Function where the print message command was issued. +/// \param [in] format Format string. +/// \param [in] args Argument lists. +/// \return true if the message was successfully formatted and printed. +/// \return false if an error occured. +typedef bool (*MessagePrinterOutput)(MessageType type, const char* file, size_t line, const char* function, const char* format, va_list args); + +/// Message printer implementation. +typedef struct MessagePrinter { + MessagePrinterOpen open; + MessagePrinterClose close; + MessagePrinterOutput output; + struct MessagePrinter* next; +} MessagePrinter; + +#define ERROR_MSG(format, ...) message_print(MESSAGE_TYPE_ERROR, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) + +#define WARNING_MSG(format, ...) message_print(MESSAGE_TYPE_WARNING, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) + +#define INFO_MSG(format, ...) message_print(MESSAGE_TYPE_INFO, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) + +#if NDEBUG +# define DEBUG_MSG(format, ...) +#else +# define DEBUG_MSG(format, ...) message_print(MESSAGE_TYPE_DEBUG, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) +#endif + +/// Setup global message printer list. +void message_printer_init(); +/// Releases resources used by message printers. +void message_printer_destroy(); + +/// Opens and adds a new message printer to the global list. +/// \param [in] printer Message printer to be added to the list. +/// \return true if the message printer was successfully added to the list. +/// \return false if the message printer failed to open and could not be added to the message printer list. +bool message_printer_add(MessagePrinter *printer); + +/// Dispatch message to printers. +/// \param type Message type. +/// \param file Name of the file where the print message command was issued. +/// \param line Line number in the file where the print message command was issued. +/// \param function Function where the print message command was issued. +/// \param format Format string. +void message_print(MessageType type, const char* file, size_t line, const char* function, const char* format, ...); + +/// @} #endif // ETRIPATOR_MESSAGE_H diff --git a/message/console.c b/message/console.c index cbb50f9..e5c8f4c 100644 --- a/message/console.c +++ b/message/console.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,95 +36,80 @@ #include "../config.h" #include "console.h" -/** - * \brief Tests if the console has support for colors and other things. - * \param [in] impl Console message printer. - * \return 0 upon success. - */ -static int console_msg_printer_open(void* impl) { - console_msg_printer_t* printer = (console_msg_printer_t*)impl; - printer->use_escape_code = isatty(fileno(stdout)) ? 1 : 0; - if(!printer->use_escape_code) { - fprintf(stderr, "Escape code disabled.\n"); - } - return 0; +static bool g_use_escape_code = false; + +/// Tests if the console has support for colors and other things. +/// \return true always. +static bool console_message_printer_open() { + g_use_escape_code = isatty(fileno(stdout)) ? true : false; + return true; } -/** - * \brief Closes console msg printer. - * \param [in] impl Console message printer. - * \return 0 upon success. - */ -static int console_msg_printer_close(void* impl) { - (void)impl; // Unused atm. - return 0; +/// Do nothing. +/// \return true always. +static bool console_message_printer_close() { + return true; } -/** - * \brief Prints message to console. - * \param userData User data. - * \param [in] impl Console message printer. - * \param [in] type Message type. - * \param [in] file Name of the file where the print message command was issued. - * \param [in] line Line number in the file where the print message command was issued. - * \param [in] function Function where the print message command was issued. - * \param [in] format Format string. - * \param [in] args Argument lists. - * \return 0 upon success. - */ -static int console_msg_printer_output(void* impl, msg_type_t type, const char* file, size_t line, const char* function, const char* format, va_list args) { - static const char *msg_type_name[] = { +/// Prints message to console. +/// \param [in] type Message type. +/// \param [in] file Name of the file where the print message command was issued. +/// \param [in] line Line number in the file where the print message command was issued. +/// \param [in] function Function where the print message command was issued. +/// \param [in] format Format string. +/// \param [in] args Argument lists. +/// \return true upon success. +static bool console_message_printer_output(MessageType type, const char* file, size_t line, const char* function, const char* format, va_list args) { + static const char *message_type_name[] = { "[Error]", "[Warning]", "[Info]" }; - static const char *msg_type_prefix[] = { + static const char *message_type_prefix[] = { "\x1b[1;31m", "\x1b[1;33m", "\x1b[1;32m" }; - int ret = 1; - if(!impl) { - fprintf(stderr, "Invalid console logger.\n"); + bool ret = true; + if(g_use_escape_code) { + fprintf(stderr, "%s%s\x1b[0m %s:%zd \x1b[0;33m %s \x1b[1;37m : " + , message_type_prefix[type] + , message_type_name[type] + , file + , line + , function + ); } else { - console_msg_printer_t* printer = (console_msg_printer_t*)impl; - if(printer->use_escape_code) { - fprintf(stderr, "%s%s\x1b[0m %s:%zd \x1b[0;33m %s \x1b[1;37m : " - , msg_type_prefix[type] - , msg_type_name[type] - , file - , line - , function - ); - } else { - fprintf(stderr, "%s %s:%zd %s : " - , msg_type_name[type] - , file - , line - , function - ); - } + fprintf(stderr, "%s %s:%zd %s : " + , message_type_name[type] + , file + , line + , function + ); + } - vfprintf(stderr, format, args); + vfprintf(stderr, format, args); - if(printer->use_escape_code) { - fprintf(stderr, "\x1b[0m\n"); - } else { - fputc('\n', stderr); - } - fflush(stderr); - ret = 0; + if(g_use_escape_code) { + fprintf(stderr, "\x1b[0m\n"); + } else { + fputc('\n', stderr); } + fflush(stderr); return ret; } +static MessagePrinter g_console_message_printer = { + .open = console_message_printer_open, + .close = console_message_printer_close, + .output = console_message_printer_output, + .next = NULL, +}; + /* Setups console message writer. */ -int console_msg_printer_init(console_msg_printer_t *printer) { - printer->super.open = console_msg_printer_open; - printer->super.close = console_msg_printer_close; - printer->super.output = console_msg_printer_output; - printer->use_escape_code = 0; - return 0; +bool console_message_printer_init() { + g_use_escape_code = false; + return message_printer_add(&g_console_message_printer); } diff --git a/message/console.h b/message/console.h index be12d8f..dbf839e 100644 --- a/message/console.h +++ b/message/console.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,17 +38,9 @@ #include "../message.h" -typedef struct { - msg_printer_t super; - int use_escape_code; -} console_msg_printer_t; - -/** - * \brief Setups console message writer. - * \param [in] printer Console message printer. - * \return 0 upon success. - */ -int console_msg_printer_init(console_msg_printer_t *printer); +/// Setups file message writer. +/// \return true always. +bool console_message_printer_init(); #endif // ETRIPATOR_MESSAGE_FILE_H diff --git a/message/file.c b/message/file.c index 58919d4..31e54ca 100644 --- a/message/file.c +++ b/message/file.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,88 +38,72 @@ static const char* g_log_filename = "etripator.log"; -/** - * \brief Opens log file. - * \param [in] impl Msg printer implementation. - * \return 0 upon success. - */ -static int file_msg_printer_open(void* impl) { - int ret = 1; - file_msg_printer_t* printer = (file_msg_printer_t*)impl; - if(!printer) { - fprintf(stderr, "Invalid file logger.\n"); +/// Check if the log file can be opened and written to. +/// \return true if the log file was successfully opened. +/// \return false if an error occured. +static bool file_message_printer_open() { + bool ret = false; + int fd = open(g_log_filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if(fd < 0) { + fprintf(stderr, "Failed to open log file %s: %s\n", g_log_filename, strerror(errno)); + } else if(close(fd) < 0) { + fprintf(stderr, "Failed to close log file %s: %s\n", g_log_filename, strerror(errno)); } else { - printer->out = fopen(g_log_filename, "ab"); - if(!printer->out) { - fprintf(stderr, "Failed to open log file %s: %s\n", g_log_filename, strerror(errno)); - } else { - ret = 0; - } + ret = true; } return ret; } -/** - * \brief Closes log file. - * \param [in] impl Msg printer implementation. - * \return 0 upon success. - */ -static int file_msg_printer_close(void* impl) { - int ret = 1; - file_msg_printer_t* printer = (file_msg_printer_t*)impl; - if((!printer) || (!printer->out)) { - fprintf(stderr, "Invalid file logger.\n"); - } else if(fclose(printer->out)) { - fprintf(stderr, "Failed to close log file %s : %s\n", g_log_filename, strerror(errno)); - } else { - printer->out = NULL; - ret = 0; - } - return ret; +/// Do nothing. +/// \return true always. +static bool file_message_printer_close() { + return true; } -/** - * \brief Prints message to file. - * \param [in] impl Msg printer implementation. - * \param [in] type Message type. - * \param [in] file Name of the file where the print message command was issued. - * \param [in] line Line number in the file where the print message command was issued. - * \param [in] function Function where the print message command was issued. - * \param [in] format Format string. - * \param [in] args Argument lists. - * \return 0 upon success. - */ -static int file_msg_printer_output(void* impl, msg_type_t type, const char* file, size_t line, const char* function, const char* format, va_list args) { - static const char *msg_type_name[] = { +/// Prints message to file. +/// \param [in] type Message type. +/// \param [in] file Name of the file where the print message command was issued. +/// \param [in] line Line number in the file where the print message command was issued. +/// \param [in] function Function where the print message command was issued. +/// \param [in] format Format string. +/// \param [in] args Argument lists. +/// \return true if the message was successfully written to the log file. +/// \return false if an error occured. +static bool file_message_printer_output(MessageType type, const char* file, size_t line, const char* function, const char* format, va_list args) { + static const char *message_type_name[MESSAGE_TYPE_COUNT] = { "[Error]", "[Warning]", - "[Info]" + "[Info]", + "[Debug]" }; - int ret = 1; - if(!impl) { - fprintf(stderr, "Invalid file logger.\n"); + bool ret = false; + FILE *out = fopen(g_log_filename, "a+"); + if(out == NULL) { + fprintf(stderr, "Failed to open log file %s: %s\n", g_log_filename, strerror(errno)); } else { - file_msg_printer_t* printer = (file_msg_printer_t*)impl; - - fprintf(printer->out, "%s %s:%zd %s : ", msg_type_name[type], file, line, function); - vfprintf(printer->out, format, args); - fputc('\n', printer->out); - fflush(printer->out); - if(ferror(printer->out)) { + fprintf(out, "%s %s:%zd %s : ", message_type_name[type], file, line, function); + vfprintf(out, format, args); + fputc('\n', out); + fflush(out); + if(ferror(out)) { fprintf(stderr, "Failed to output log to %s: %s\n", g_log_filename, strerror(errno)); } else { - ret = 0; + ret = true; } + fclose(out); } return ret; } +static MessagePrinter g_file_message_printer = { + .open = file_message_printer_open, + .close = file_message_printer_close, + .output = file_message_printer_output, + .next = NULL, +}; + /* Setups file message writer. */ -int file_msg_printer_init(file_msg_printer_t *printer) { - printer->super.open = file_msg_printer_open; - printer->super.close = file_msg_printer_close; - printer->super.output = file_msg_printer_output; - printer->out = NULL; - return 0; +bool file_message_printer_init() { + return message_printer_add(&g_file_message_printer); } diff --git a/message/file.h b/message/file.h index d9224a0..0e70e84 100644 --- a/message/file.h +++ b/message/file.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,17 +38,17 @@ #include "../message.h" -typedef struct { - msg_printer_t super; - FILE *out; -} file_msg_printer_t; - -/** - * \brief Setups file message writer. - * \param [in] impl Msg printer implementation. - * \return 0 upon success. - */ -int file_msg_printer_init(file_msg_printer_t *printer); +/// @addtogroup Message +/// @{ + +/// Setups file message writer. +/// Messages will be appeneded to a file names "etripator.log". +/// This file will be placed in the current working directory. +/// \return true if the log file can be written to. +/// \return false if an error occured. +bool file_message_printer_init(); + +/// @} #endif // ETRIPATOR_MESSAGE_FILE_H diff --git a/opcodes.c b/opcodes.c index 6d7ba26..2dce388 100644 --- a/opcodes.c +++ b/opcodes.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,7 +38,7 @@ #define PCE_TYPE_COUNT 23 #define PCE_ARG_COUNT 7 -/* Opcode output. */ +/// Opcode output string static char* pce_opstring[PCE_TYPE_COUNT][PCE_ARG_COUNT] = { { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, @@ -65,306 +65,298 @@ static char* pce_opstring[PCE_TYPE_COUNT][PCE_ARG_COUNT] = { { "$%02x", NULL, NULL, NULL, NULL, NULL, NULL } }; -/* PC engine opcodes */ -static opcode_t pce_opcode[256] = { - /* 00 */ { "brk ", 1 , 0 }, /* BRK */ - /* 01 */ { "ora ", 2 , 11 }, /* ORA (ZZ, X) */ - /* 02 */ { "sxy ", 1 , 0 }, /* SXY */ - /* 03 */ { "st0 ", 2 , 2 }, /* STO #nn */ - /* 04 */ { "tsb ", 2 , 7 }, /* TSB ZZ */ - /* O5 */ { "ora ", 2 , 7 }, /* ORA ZZ */ - /* 06 */ { "asl ", 2 , 7 }, /* ASL ZZ */ - /* 07 */ { "rmb0", 2 , 7 }, /* RMB0 ZZ */ - /* 08 */ { "php ", 1 , 0 }, /* PHP */ - /* O9 */ { "ora ", 2 , 2 }, /* ORA #nn */ - /* 0A */ { "asl ", 1 , 1 }, /* ASL A */ - /* 0B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 0C */ { "tsb ", 3 , 14 }, /* TRB hhll */ - /* 0D */ { "ora ", 3 , 14 }, /* ORA hhll */ - /* 0E */ { "asl ", 3 , 14 }, /* ASL hhll */ - /* 0F */ { "bbr0", 3 , 20 }, /* BBR0 ZZ, lhhll */ - /* 10 */ { "bpl ", 2 , 19 }, /* BPL lhhll */ - /* 11 */ { "ora ", 2 , 12 }, /* ORA (ZZ), Y */ - /* 12 */ { "ora ", 2 , 10 }, /* ORA (ZZ) */ - /* 13 */ { "st1 ", 2 , 2 }, /* ST1 #nn */ - /* 14 */ { "trb ", 2 , 7 }, /* TRB ZZ */ - /* 15 */ { "ora ", 2 , 8 }, /* ORA ZZ, X */ - /* 16 */ { "asl ", 2 , 8 }, /* ASL ZZ, X */ - /* 17 */ { "rmb1", 2 , 7 }, /* RMB1 ZZ */ - /* 18 */ { "clc ", 1 , 0 }, /* CLC */ - /* 19 */ { "ora ", 3 , 17 }, /* ORA hhll, Y */ - /* 1A */ { "inc ", 1 , 1 }, /* INC A */ - /* 1B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 1C */ { "trb ", 3 , 14 }, /* TRB hhll */ - /* 1D */ { "ora ", 3 , 16 }, /* ORA hhll, X */ - /* 1E */ { "asl ", 3 , 16 }, /* ASL hhll, X */ - /* 1F */ { "bbr1", 3 , 20 }, /* BBR1 ZZ, lhhll */ - /* 20 */ { "jsr ", 3 , 19 }, /* JSR lhhll */ - /* 21 */ { "and ", 2 , 11 }, /* AND (ZZ, X) */ - /* 22 */ { "sax ", 1 , 0 }, /* SAX */ - /* 23 */ { "st2 ", 2 , 2 }, /* ST2 #nn */ - /* 24 */ { "bit ", 2 , 7 }, /* BIT ZZ */ - /* 25 */ { "and ", 2 , 7 }, /* AND ZZ */ - /* 26 */ { "rol ", 2 , 7 }, /* ROL ZZ */ - /* 27 */ { "rmb2", 2 , 7 }, /* RMB2 ZZ */ - /* 28 */ { "plp ", 1 , 0 }, /* PLP */ - /* 29 */ { "and ", 2 , 2 }, /* AND #nn */ - /* 2A */ { "rol ", 1 , 1 }, /* ROL A */ - /* 2B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 2C */ { "bit ", 3 , 14 }, /* BIT hhll */ - /* 2D */ { "and ", 3 , 14 }, /* AND hhll */ - /* 2E */ { "rol ", 3 , 14 }, /* ROL hhll */ - /* 2F */ { "bbr2", 3 , 20 }, /* BBR2 ZZ, lhhll */ - /* 30 */ { "bmi ", 2 , 19 }, /* BMI lhhll */ - /* 31 */ { "and ", 2 , 12 }, /* AND (ZZ), Y */ - /* 32 */ { "and ", 2 , 10 }, /* AND (ZZ) */ - /* 33 */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 34 */ { "bit ", 2 , 8 }, /* BIT ZZ, X */ - /* 35 */ { "and ", 2 , 8 }, /* AND ZZ, X */ - /* 36 */ { "rol ", 2 , 7 }, /* ROL ZZ */ - /* 37 */ { "rmb3", 2 , 7 }, /* RMB3 ZZ */ - /* 38 */ { "sec ", 1 , 0 }, /* SEC */ - /* 39 */ { "and ", 3 , 17 }, /* AND hhll, Y */ - /* 3A */ { "dec ", 1 , 1 }, /* DEC A */ - /* 3B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 3C */ { "bit ", 3 , 16 }, /* BIT hhll, X */ - /* 3D */ { "and ", 3 , 16 }, /* AND hhll, X */ - /* 3E */ { "rol ", 3 , 16 }, /* ROL hhll, X */ - /* 3F */ { "bbr3", 3 , 20 }, /* BBR3 ZZ, lhhll */ - /* 40 */ { "rti ", 1 , 0 }, /* RTI */ - /* 41 */ { "eor ", 2 , 11 }, /* EOR (ZZ, X) */ - /* 42 */ { "say ", 1 , 0 }, /* SAY */ - /* 43 */ { "tma ", 2 , 2 }, /* TMA #nn */ - /* 44 */ { "bsr ", 2 , 19 }, /* BSR lhhll */ - /* 45 */ { "eor ", 2 , 7 }, /* EOR ZZ */ - /* 46 */ { "lsr ", 2 , 7 }, /* LSR ZZ */ - /* 47 */ { "rmb4", 2 , 7 }, /* RMB4 ZZ */ - /* 48 */ { "pha ", 1 , 0 }, /* PHA */ - /* 49 */ { "eor ", 2 , 2 }, /* EOR #nn */ - /* 4A */ { "lsr ", 1 , 1 }, /* LSR A */ - /* 4B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 4C */ { "jmp ", 3 , 19 }, /* JMP lhhll */ - /* 4D */ { "eor ", 3 , 14 }, /* EOR hhll */ - /* 4E */ { "lsr ", 3 , 14 }, /* LSR hhll */ - /* 4F */ { "bbr4", 3 , 20 }, /* BBR4 ZZ, lhhll */ - /* 50 */ { "bvc ", 2 , 19 }, /* BVC lhhll */ - /* 51 */ { "eor ", 2 , 12 }, /* EOR (ZZ),Y */ - /* 52 */ { "eor ", 2 , 10 }, /* EOR (ZZ) */ - /* 53 */ { "tam ", 2 , 2 }, /* TAM #nn */ - /* 54 */ { "csl ", 1 , 0 }, /* CSL */ - /* 55 */ { "eor ", 2 , 8 }, /* EOR ZZ, X */ - /* 56 */ { "lsr ", 2 , 8 }, /* LSR ZZ, X */ - /* 57 */ { "rmb5", 2 , 7 }, /* RMB5 ZZ */ - /* 58 */ { "cli ", 1 , 0 }, /* CLI */ - /* 59 */ { "eor ", 3 , 17 }, /* EOR hhll, Y */ - /* 5A */ { "phy ", 1 , 0 }, /* PHY */ - /* 5B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 5C */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 5D */ { "eor ", 3 , 16 }, /* EOR hhll, X */ - /* 5E */ { "lsr ", 3 , 16 }, /* LSR hhll, X */ - /* 5F */ { "bbr5", 3 , 20 }, /* BBR5 ZZ, lhhll */ - /* 60 */ { "rts ", 1 , 0 }, /* RTS */ - /* 61 */ { "adc ", 2 , 11 }, /* ADC (ZZ, X) */ - /* 62 */ { "cla ", 1 , 0 }, /* CLA */ - /* 63 */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 64 */ { "stz ", 2 , 7 }, /* STZ ZZ */ - /* 65 */ { "adc ", 2 , 7 }, /* ADC ZZ */ - /* 66 */ { "ror ", 2 , 7 }, /* ROR ZZ */ - /* 67 */ { "rmb6", 2 , 7 }, /* RMB6 ZZ */ - /* 68 */ { "pla ", 1 , 0 }, /* PLA */ - /* 69 */ { "adc ", 2 , 2 }, /* ADC #nn */ - /* 6A */ { "ror ", 1 , 1 }, /* ROR A */ - /* 6B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 6C */ { "jmp ", 3 , 15 }, /* JMP (hhll) */ - /* 6D */ { "adc ", 3 , 14 }, /* ADC hhll */ - /* 6E */ { "ror ", 3 , 14 }, /* ROR hhll */ - /* 6F */ { "bbr6", 3 , 20 }, /* BBR6 ZZ, lhhll */ - /* 70 */ { "bvs ", 2 , 19 }, /* BVS lhhll */ - /* 71 */ { "adc ", 2 , 12 }, /* ADC (ZZ), Y */ - /* 72 */ { "adc ", 2 , 10 }, /* ADC (ZZ) */ - /* 73 */ { "tii ", 7 , 18 }, /* TII shsl,dhdl,lhll */ - /* 74 */ { "stz ", 2 , 8 }, /* STZ ZZ, X */ - /* 75 */ { "adc ", 2 , 8 }, /* ADC ZZ, X */ - /* 76 */ { "ror ", 2 , 8 }, /* ROR ZZ, X */ - /* 77 */ { "rmb7", 2 , 7 }, /* RMB7 ZZ */ - /* 78 */ { "sei ", 1 , 0 }, /* SEI */ - /* 79 */ { "adc ", 3 , 17 }, /* ADC hhll, Y */ - /* 7A */ { "ply ", 1 , 0 }, /* PLY */ - /* 7B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 7C */ { "jmp ", 3 , 21 }, /* JMP (hhll, X) */ - /* 7D */ { "adc ", 3 , 16 }, /* ADC hhll, X */ - /* 7E */ { "ror ", 3 , 16 }, /* ROR hhll, X */ - /* 7F */ { "bbr7", 3 , 20 }, /* BBR7 ZZ, lhhll */ - /* 80 */ { "bra ", 2 , 19 }, /* BRA lhhll */ - /* 81 */ { "sta ", 2 , 11 }, /* STA (ZZ, X) */ - /* 82 */ { "clx ", 1 , 0 }, /* CLX */ - /* 83 */ { "tst ", 3 , 3 }, /* TST #nn, ZZ */ - /* 84 */ { "sty ", 2 , 7 }, /* STY ZZ */ - /* 85 */ { "sta ", 2 , 7 }, /* STA ZZ */ - /* 86 */ { "stx ", 2 , 7 }, /* STX ZZ */ - /* 87 */ { "smb0", 2 , 7 }, /* SMB0 ZZ */ - /* 88 */ { "dey ", 1 , 0 }, /* DEY */ - /* 89 */ { "bit ", 2 , 2 }, /* BIT #nn */ - /* 8A */ { "txa ", 1 , 0 }, /* TXA */ - /* 8B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 8C */ { "sty ", 3 , 14 }, /* STY hhll */ - /* 8D */ { "sta ", 3 , 14 }, /* STA hhll */ - /* 8E */ { "stx ", 3 , 14 }, /* STX hhll */ - /* 8F */ { "bbs0", 3 , 20 }, /* BBS0 ZZ, lhhll */ - /* 90 */ { "bcc ", 2 , 19 }, /* BCC lhhll */ - /* 91 */ { "sta ", 2 , 12 }, /* STA (ZZ), Y */ - /* 92 */ { "sta ", 2 , 10 }, /* STA (ZZ) */ - /* 93 */ { "tst ", 4 , 5 }, /* TST #nn, hhll */ - /* 94 */ { "sty ", 2 , 8 }, /* STY ZZ, X */ - /* 95 */ { "sta ", 2 , 8 }, /* STA ZZ, X */ - /* 96 */ { "stx ", 2 , 9 }, /* STX ZZ, Y */ - /* 97 */ { "smb1", 2 , 7 }, /* SMB1 ZZ */ - /* 98 */ { "tya ", 1 , 0 }, /* TYA */ - /* 99 */ { "sta ", 3 , 17 }, /* STA hhll, Y */ - /* 9A */ { "txs ", 1 , 0 }, /* TXS */ - /* 9B */ { ".db ", 1 , 22 }, /* UNUSED */ - /* 9C */ { "stz ", 3 , 14 }, /* STZ hhll */ - /* 9D */ { "sta ", 3 , 16 }, /* STA hhll, X */ - /* 9E */ { "stz ", 3 , 16 }, /* STZ hhll, X */ - /* 9F */ { "bbs1", 3 , 20 }, /* BBS1 ZZ, lhhll */ - /* A0 */ { "ldy ", 2 , 2 }, /* LDY #nn */ - /* A1 */ { "lda ", 2 , 11 }, /* LDA (ZZ, X) */ - /* A2 */ { "ldx ", 2 , 2 }, /* LDA #nn */ - /* A3 */ { "tst ", 3 , 4 }, /* TST #nn, ZZ, X */ - /* A4 */ { "ldy ", 2 , 7 }, /* LDY ZZ */ - /* A5 */ { "lda ", 2 , 7 }, /* LDA ZZ */ - /* A6 */ { "ldx ", 2 , 7 }, /* LDX ZZ */ - /* A7 */ { "smb2", 2 , 7 }, /* SMB2 ZZ */ - /* A8 */ { "tay ", 1 , 0 }, /* TAY */ - /* A9 */ { "lda ", 2 , 2 }, /* LDA #nn */ - /* AA */ { "tax ", 1 , 0 }, /* TAX */ - /* AB */ { ".db ", 1 , 22 }, /* UNUSED */ - /* AC */ { "ldy ", 3 , 14 }, /* LDY hhll */ - /* AD */ { "lda ", 3 , 14 }, /* LDA hhll */ - /* AE */ { "ldx ", 3 , 14 }, /* LDX hhll */ - /* AF */ { "bbs2", 3 , 20 }, /* BBS2 ZZ, lhhll */ - /* B0 */ { "bcs ", 2 , 19 }, /* BCS lhhll */ - /* B1 */ { "lda ", 2 , 12 }, /* LDA (ZZ), Y */ - /* B2 */ { "lda ", 2 , 10 }, /* LDA (ZZ) */ - /* B3 */ { "tst ", 4 , 6 }, /* TST #nn, hhll, X */ - /* B4 */ { "ldy ", 2 , 8 }, /* LDY ZZ, X */ - /* B5 */ { "lda ", 2 , 8 }, /* LDA ZZ, X */ - /* B6 */ { "ldx ", 2 , 9 }, /* LDX ZZ, Y */ - /* B7 */ { "smb3", 2 , 7 }, /* SMB3 ZZ */ - /* B8 */ { "clv ", 1 , 0 }, /* CLV */ - /* B9 */ { "lda ", 3 , 17 }, /* LDA hhll, Y */ - /* BA */ { "tsx ", 1 , 0 }, /* TSX */ - /* BB */ { ".db ", 1 , 22 }, /* UNUSED */ - /* BC */ { "ldy ", 3 , 16 }, /* LDY hhll, X */ - /* BD */ { "lda ", 3 , 16 }, /* LDA hhll, X */ - /* BE */ { "ldx ", 3 , 17 }, /* LDX hhll, Y */ - /* BF */ { "bbs3", 3 , 20 }, /* BBS3 ZZ, lhhll */ - /* C0 */ { "cpy ", 2 , 2 }, /* CPY #nn */ - /* C1 */ { "cmp ", 2 , 11 }, /* CMP (ZZ, X) */ - /* C2 */ { "cly ", 1 , 0 }, /* CLY */ - /* C3 */ { "tdd ", 7 , 18 }, /* TDD shsl,dhdl,lhlh */ - /* C4 */ { "cpy ", 2 , 7 }, /* CPY ZZ */ - /* C5 */ { "cmp ", 2 , 7 }, /* CMP ZZ */ - /* C6 */ { "dec ", 2 , 7 }, /* DEC ZZ */ - /* C7 */ { "smb4", 2 , 7 }, /* SMB4 ZZ */ - /* C8 */ { "iny ", 1 , 0 }, /* INY */ - /* C9 */ { "cmp ", 2 , 2 }, /* CMP #nn */ - /* CA */ { "dex ", 1 , 0 }, /* DEX */ - /* CB */ { ".db ", 1 , 22 }, /* UNUSED */ - /* CC */ { "cpy ", 3 , 14 }, /* CPY hhll */ - /* CD */ { "cmp ", 3 , 14 }, /* CMP hhll */ - /* CE */ { "dec ", 3 , 14 }, /* DEC hhll */ - /* CF */ { "bbs4", 3 , 20 }, /* BBS4 ZZ, lhhll */ - /* D0 */ { "bne ", 2 , 19 }, /* BNE lhhll */ - /* D1 */ { "cmp ", 2 , 12 }, /* CMP (ZZ), Y */ - /* D2 */ { "cmp ", 2 , 10 }, /* CMP (ZZ) */ - /* D3 */ { "tin ", 7 , 18 }, /* TIN shsl,dhdl,lhlh */ - /* D4 */ { "csh ", 1 , 0 }, /* CSH */ - /* D5 */ { "cmp ", 2 , 8 }, /* CMP ZZ, X */ - /* D6 */ { "dec ", 2 , 8 }, /* DEC ZZ, X */ - /* D7 */ { "smb5", 2 , 7 }, /* SMB5 ZZ */ - /* D8 */ { "cld ", 1 , 0 }, /* CLD */ - /* D9 */ { "cmp ", 3 , 17 }, /* CMP hhll, Y */ - /* DA */ { "phx ", 1 , 0 }, /* PHX */ - /* DB */ { ".db ", 1 , 22 }, /* UNUSED */ - /* DC */ { ".db ", 1 , 22 }, /* UNUSED */ - /* DD */ { "cmp ", 3 , 16 }, /* CMP hhll, X */ - /* DE */ { "dec ", 3 , 16 }, /* DEC hhll, X */ - /* DF */ { "bbs5", 3 , 20 }, /* BBS5 ZZ, lhhll */ - /* E0 */ { "cpx ", 2 , 2 }, /* CPX #nn */ - /* E1 */ { "sbc ", 2 , 11 }, /* SBC (ZZ,X) */ - /* E2 */ { ".db ", 1 , 22 }, /* UNUSED */ - /* E3 */ { "tia ", 7 , 18 }, /* TIA shsl,dhdl,lhlh */ - /* E4 */ { "cpx ", 2 , 7 }, /* CPX ZZ */ - /* E5 */ { "sbc ", 2 , 7 }, /* SBC ZZ */ - /* E6 */ { "inc ", 2 , 7 }, /* INC ZZ */ - /* E7 */ { "smb6", 2 , 7 }, /* SMB6 ZZ */ - /* E8 */ { "inx ", 1 , 0 }, /* INX */ - /* E9 */ { "sbc ", 2 , 2 }, /* SBC #nn */ - /* EA */ { "nop ", 1 , 0 }, /* NOP */ - /* EB */ { ".db ", 1 , 22 }, /* UNUSED */ - /* EC */ { "cpx ", 3 , 14 }, /* CPX hhll */ - /* ED */ { "sbc ", 3 , 14 }, /* SBC hhll */ - /* EE */ { "inc ", 3 , 14 }, /* INC hhll */ - /* EF */ { "bbs6", 3 , 20 }, /* BBS6 ZZ, lhhll */ - /* F0 */ { "beq ", 2 , 19 }, /* BEQ lhhll */ - /* F1 */ { "sbc ", 2 , 12 }, /* SBC (ZZ), Y */ - /* F2 */ { "sbc ", 2 , 10 }, /* SBC (ZZ) */ - /* F3 */ { "tai ", 7 , 18 }, /* TAI shsl,dhdl,lhlh */ - /* F4 */ { "set ", 1 , 0 }, /* SET */ - /* F5 */ { "sbc ", 2 , 8 }, /* SBC ZZ, X */ - /* F6 */ { "inc ", 2 , 8 }, /* INC ZZ, X */ - /* F7 */ { "smb7", 2 , 7 }, /* SMB7 ZZ */ - /* F8 */ { "sed ", 1 , 0 }, /* SED */ - /* F9 */ { "sbc ", 3 , 17 }, /* SBC hhll, Y */ - /* FA */ { "plx ", 1 , 0 }, /* PLX */ - /* FB */ { ".db ", 1 , 22 }, /* UNUSED */ - /* FC */ { ".db ", 1 , 22 }, /* UNUSED */ - /* FD */ { "sbc ", 3 , 16 }, /* SBC hhll, X */ - /* FE */ { "inc ", 3 , 16 }, /* INC hhll, X */ - /* FF */ { "bbs7", 3 , 20 } /* BBS7 ZZ, lhhll */ +// PC engine opcodes +static Opcode pce_opcode[256] = { + /* 00 */ { "brk ", 1 , 0 }, // BRK + /* 01 */ { "ora ", 2 , 11 }, // ORA (ZZ, X) + /* 02 */ { "sxy ", 1 , 0 }, // SXY + /* 03 */ { "st0 ", 2 , 2 }, // STO #nn + /* 04 */ { "tsb ", 2 , 7 }, // TSB ZZ + /* O5 */ { "ora ", 2 , 7 }, // ORA ZZ + /* 06 */ { "asl ", 2 , 7 }, // ASL ZZ + /* 07 */ { "rmb0", 2 , 7 }, // RMB0 ZZ + /* 08 */ { "php ", 1 , 0 }, // PHP + /* O9 */ { "ora ", 2 , 2 }, // ORA #nn + /* 0A */ { "asl ", 1 , 1 }, // ASL A + /* 0B */ { ".db ", 1 , 22 }, // UNUSED + /* 0C */ { "tsb ", 3 , 14 }, // TRB hhll + /* 0D */ { "ora ", 3 , 14 }, // ORA hhll + /* 0E */ { "asl ", 3 , 14 }, // ASL hhll + /* 0F */ { "bbr0", 3 , 20 }, // BBR0 ZZ, lhhll + /* 10 */ { "bpl ", 2 , 19 }, // BPL lhhll + /* 11 */ { "ora ", 2 , 12 }, // ORA (ZZ), Y + /* 12 */ { "ora ", 2 , 10 }, // ORA (ZZ) + /* 13 */ { "st1 ", 2 , 2 }, // ST1 #nn + /* 14 */ { "trb ", 2 , 7 }, // TRB ZZ + /* 15 */ { "ora ", 2 , 8 }, // ORA ZZ, X + /* 16 */ { "asl ", 2 , 8 }, // ASL ZZ, X + /* 17 */ { "rmb1", 2 , 7 }, // RMB1 ZZ + /* 18 */ { "clc ", 1 , 0 }, // CLC + /* 19 */ { "ora ", 3 , 17 }, // ORA hhll, Y + /* 1A */ { "inc ", 1 , 1 }, // INC A + /* 1B */ { ".db ", 1 , 22 }, // UNUSED + /* 1C */ { "trb ", 3 , 14 }, // TRB hhll + /* 1D */ { "ora ", 3 , 16 }, // ORA hhll, X + /* 1E */ { "asl ", 3 , 16 }, // ASL hhll, X + /* 1F */ { "bbr1", 3 , 20 }, // BBR1 ZZ, lhhll + /* 20 */ { "jsr ", 3 , 19 }, // JSR lhhll + /* 21 */ { "and ", 2 , 11 }, // AND (ZZ, X) + /* 22 */ { "sax ", 1 , 0 }, // SAX + /* 23 */ { "st2 ", 2 , 2 }, // ST2 #nn + /* 24 */ { "bit ", 2 , 7 }, // BIT ZZ + /* 25 */ { "and ", 2 , 7 }, // AND ZZ + /* 26 */ { "rol ", 2 , 7 }, // ROL ZZ + /* 27 */ { "rmb2", 2 , 7 }, // RMB2 ZZ + /* 28 */ { "plp ", 1 , 0 }, // PLP + /* 29 */ { "and ", 2 , 2 }, // AND #nn + /* 2A */ { "rol ", 1 , 1 }, // ROL A + /* 2B */ { ".db ", 1 , 22 }, // UNUSED + /* 2C */ { "bit ", 3 , 14 }, // BIT hhll + /* 2D */ { "and ", 3 , 14 }, // AND hhll + /* 2E */ { "rol ", 3 , 14 }, // ROL hhll + /* 2F */ { "bbr2", 3 , 20 }, // BBR2 ZZ, lhhll + /* 30 */ { "bmi ", 2 , 19 }, // BMI lhhll + /* 31 */ { "and ", 2 , 12 }, // AND (ZZ), Y + /* 32 */ { "and ", 2 , 10 }, // AND (ZZ) + /* 33 */ { ".db ", 1 , 22 }, // UNUSED + /* 34 */ { "bit ", 2 , 8 }, // BIT ZZ, X + /* 35 */ { "and ", 2 , 8 }, // AND ZZ, X + /* 36 */ { "rol ", 2 , 7 }, // ROL ZZ + /* 37 */ { "rmb3", 2 , 7 }, // RMB3 ZZ + /* 38 */ { "sec ", 1 , 0 }, // SEC + /* 39 */ { "and ", 3 , 17 }, // AND hhll, Y + /* 3A */ { "dec ", 1 , 1 }, // DEC A + /* 3B */ { ".db ", 1 , 22 }, // UNUSED + /* 3C */ { "bit ", 3 , 16 }, // BIT hhll, X + /* 3D */ { "and ", 3 , 16 }, // AND hhll, X + /* 3E */ { "rol ", 3 , 16 }, // ROL hhll, X + /* 3F */ { "bbr3", 3 , 20 }, // BBR3 ZZ, lhhll + /* 40 */ { "rti ", 1 , 0 }, // RTI + /* 41 */ { "eor ", 2 , 11 }, // EOR (ZZ, X) + /* 42 */ { "say ", 1 , 0 }, // SAY + /* 43 */ { "tma ", 2 , 2 }, // TMA #nn + /* 44 */ { "bsr ", 2 , 19 }, // BSR lhhll + /* 45 */ { "eor ", 2 , 7 }, // EOR ZZ + /* 46 */ { "lsr ", 2 , 7 }, // LSR ZZ + /* 47 */ { "rmb4", 2 , 7 }, // RMB4 ZZ + /* 48 */ { "pha ", 1 , 0 }, // PHA + /* 49 */ { "eor ", 2 , 2 }, // EOR #nn + /* 4A */ { "lsr ", 1 , 1 }, // LSR A + /* 4B */ { ".db ", 1 , 22 }, // UNUSED + /* 4C */ { "jmp ", 3 , 19 }, // JMP lhhll + /* 4D */ { "eor ", 3 , 14 }, // EOR hhll + /* 4E */ { "lsr ", 3 , 14 }, // LSR hhll + /* 4F */ { "bbr4", 3 , 20 }, // BBR4 ZZ, lhhll + /* 50 */ { "bvc ", 2 , 19 }, // BVC lhhll + /* 51 */ { "eor ", 2 , 12 }, // EOR (ZZ),Y + /* 52 */ { "eor ", 2 , 10 }, // EOR (ZZ) + /* 53 */ { "tam ", 2 , 2 }, // TAM #nn + /* 54 */ { "csl ", 1 , 0 }, // CSL + /* 55 */ { "eor ", 2 , 8 }, // EOR ZZ, X + /* 56 */ { "lsr ", 2 , 8 }, // LSR ZZ, X + /* 57 */ { "rmb5", 2 , 7 }, // RMB5 ZZ + /* 58 */ { "cli ", 1 , 0 }, // CLI + /* 59 */ { "eor ", 3 , 17 }, // EOR hhll, Y + /* 5A */ { "phy ", 1 , 0 }, // PHY + /* 5B */ { ".db ", 1 , 22 }, // UNUSED + /* 5C */ { ".db ", 1 , 22 }, // UNUSED + /* 5D */ { "eor ", 3 , 16 }, // EOR hhll, X + /* 5E */ { "lsr ", 3 , 16 }, // LSR hhll, X + /* 5F */ { "bbr5", 3 , 20 }, // BBR5 ZZ, lhhll + /* 60 */ { "rts ", 1 , 0 }, // RTS + /* 61 */ { "adc ", 2 , 11 }, // ADC (ZZ, X) + /* 62 */ { "cla ", 1 , 0 }, // CLA + /* 63 */ { ".db ", 1 , 22 }, // UNUSED + /* 64 */ { "stz ", 2 , 7 }, // STZ ZZ + /* 65 */ { "adc ", 2 , 7 }, // ADC ZZ + /* 66 */ { "ror ", 2 , 7 }, // ROR ZZ + /* 67 */ { "rmb6", 2 , 7 }, // RMB6 ZZ + /* 68 */ { "pla ", 1 , 0 }, // PLA + /* 69 */ { "adc ", 2 , 2 }, // ADC #nn + /* 6A */ { "ror ", 1 , 1 }, // ROR A + /* 6B */ { ".db ", 1 , 22 }, // UNUSED + /* 6C */ { "jmp ", 3 , 15 }, // JMP (hhll) + /* 6D */ { "adc ", 3 , 14 }, // ADC hhll + /* 6E */ { "ror ", 3 , 14 }, // ROR hhll + /* 6F */ { "bbr6", 3 , 20 }, // BBR6 ZZ, lhhll + /* 70 */ { "bvs ", 2 , 19 }, // BVS lhhll + /* 71 */ { "adc ", 2 , 12 }, // ADC (ZZ), Y + /* 72 */ { "adc ", 2 , 10 }, // ADC (ZZ) + /* 73 */ { "tii ", 7 , 18 }, // TII shsl,dhdl,lhll + /* 74 */ { "stz ", 2 , 8 }, // STZ ZZ, X + /* 75 */ { "adc ", 2 , 8 }, // ADC ZZ, X + /* 76 */ { "ror ", 2 , 8 }, // ROR ZZ, X + /* 77 */ { "rmb7", 2 , 7 }, // RMB7 ZZ + /* 78 */ { "sei ", 1 , 0 }, // SEI + /* 79 */ { "adc ", 3 , 17 }, // ADC hhll, Y + /* 7A */ { "ply ", 1 , 0 }, // PLY + /* 7B */ { ".db ", 1 , 22 }, // UNUSED + /* 7C */ { "jmp ", 3 , 21 }, // JMP (hhll, X) + /* 7D */ { "adc ", 3 , 16 }, // ADC hhll, X + /* 7E */ { "ror ", 3 , 16 }, // ROR hhll, X + /* 7F */ { "bbr7", 3 , 20 }, // BBR7 ZZ, lhhll + /* 80 */ { "bra ", 2 , 19 }, // BRA lhhll + /* 81 */ { "sta ", 2 , 11 }, // STA (ZZ, X) + /* 82 */ { "clx ", 1 , 0 }, // CLX + /* 83 */ { "tst ", 3 , 3 }, // TST #nn, ZZ + /* 84 */ { "sty ", 2 , 7 }, // STY ZZ + /* 85 */ { "sta ", 2 , 7 }, // STA ZZ + /* 86 */ { "stx ", 2 , 7 }, // STX ZZ + /* 87 */ { "smb0", 2 , 7 }, // SMB0 ZZ + /* 88 */ { "dey ", 1 , 0 }, // DEY + /* 89 */ { "bit ", 2 , 2 }, // BIT #nn + /* 8A */ { "txa ", 1 , 0 }, // TXA + /* 8B */ { ".db ", 1 , 22 }, // UNUSED + /* 8C */ { "sty ", 3 , 14 }, // STY hhll + /* 8D */ { "sta ", 3 , 14 }, // STA hhll + /* 8E */ { "stx ", 3 , 14 }, // STX hhll + /* 8F */ { "bbs0", 3 , 20 }, // BBS0 ZZ, lhhll + /* 90 */ { "bcc ", 2 , 19 }, // BCC lhhll + /* 91 */ { "sta ", 2 , 12 }, // STA (ZZ), Y + /* 92 */ { "sta ", 2 , 10 }, // STA (ZZ) + /* 93 */ { "tst ", 4 , 5 }, // TST #nn, hhll + /* 94 */ { "sty ", 2 , 8 }, // STY ZZ, X + /* 95 */ { "sta ", 2 , 8 }, // STA ZZ, X + /* 96 */ { "stx ", 2 , 9 }, // STX ZZ, Y + /* 97 */ { "smb1", 2 , 7 }, // SMB1 ZZ + /* 98 */ { "tya ", 1 , 0 }, // TYA + /* 99 */ { "sta ", 3 , 17 }, // STA hhll, Y + /* 9A */ { "txs ", 1 , 0 }, // TXS + /* 9B */ { ".db ", 1 , 22 }, // UNUSED + /* 9C */ { "stz ", 3 , 14 }, // STZ hhll + /* 9D */ { "sta ", 3 , 16 }, // STA hhll, X + /* 9E */ { "stz ", 3 , 16 }, // STZ hhll, X + /* 9F */ { "bbs1", 3 , 20 }, // BBS1 ZZ, lhhll + /* A0 */ { "ldy ", 2 , 2 }, // LDY #nn + /* A1 */ { "lda ", 2 , 11 }, // LDA (ZZ, X) + /* A2 */ { "ldx ", 2 , 2 }, // LDA #nn + /* A3 */ { "tst ", 3 , 4 }, // TST #nn, ZZ, X + /* A4 */ { "ldy ", 2 , 7 }, // LDY ZZ + /* A5 */ { "lda ", 2 , 7 }, // LDA ZZ + /* A6 */ { "ldx ", 2 , 7 }, // LDX ZZ + /* A7 */ { "smb2", 2 , 7 }, // SMB2 ZZ + /* A8 */ { "tay ", 1 , 0 }, // TAY + /* A9 */ { "lda ", 2 , 2 }, // LDA #nn + /* AA */ { "tax ", 1 , 0 }, // TAX + /* AB */ { ".db ", 1 , 22 }, // UNUSED + /* AC */ { "ldy ", 3 , 14 }, // LDY hhll + /* AD */ { "lda ", 3 , 14 }, // LDA hhll + /* AE */ { "ldx ", 3 , 14 }, // LDX hhll + /* AF */ { "bbs2", 3 , 20 }, // BBS2 ZZ, lhhll + /* B0 */ { "bcs ", 2 , 19 }, // BCS lhhll * + /* B1 */ { "lda ", 2 , 12 }, // LDA (ZZ), Y + /* B2 */ { "lda ", 2 , 10 }, // LDA (ZZ) + /* B3 */ { "tst ", 4 , 6 }, // TST #nn, hhll, X + /* B4 */ { "ldy ", 2 , 8 }, // LDY ZZ, X + /* B5 */ { "lda ", 2 , 8 }, // LDA ZZ, X + /* B6 */ { "ldx ", 2 , 9 }, // LDX ZZ, Y + /* B7 */ { "smb3", 2 , 7 }, // SMB3 ZZ + /* B8 */ { "clv ", 1 , 0 }, // CLV + /* B9 */ { "lda ", 3 , 17 }, // LDA hhll, Y + /* BA */ { "tsx ", 1 , 0 }, // TSX + /* BB */ { ".db ", 1 , 22 }, // UNUSED + /* BC */ { "ldy ", 3 , 16 }, // LDY hhll, X + /* BD */ { "lda ", 3 , 16 }, // LDA hhll, X + /* BE */ { "ldx ", 3 , 17 }, // LDX hhll, Y + /* BF */ { "bbs3", 3 , 20 }, // BBS3 ZZ, lhhll + /* C0 */ { "cpy ", 2 , 2 }, // CPY #nn + /* C1 */ { "cmp ", 2 , 11 }, // CMP (ZZ, X) + /* C2 */ { "cly ", 1 , 0 }, // CLY + /* C3 */ { "tdd ", 7 , 18 }, // TDD shsl,dhdl,lhlh + /* C4 */ { "cpy ", 2 , 7 }, // CPY ZZ + /* C5 */ { "cmp ", 2 , 7 }, // CMP ZZ + /* C6 */ { "dec ", 2 , 7 }, // DEC ZZ + /* C7 */ { "smb4", 2 , 7 }, // SMB4 ZZ + /* C8 */ { "iny ", 1 , 0 }, // INY + /* C9 */ { "cmp ", 2 , 2 }, // CMP #nn + /* CA */ { "dex ", 1 , 0 }, // DEX + /* CB */ { ".db ", 1 , 22 }, // UNUSED + /* CC */ { "cpy ", 3 , 14 }, // CPY hhll + /* CD */ { "cmp ", 3 , 14 }, // CMP hhll + /* CE */ { "dec ", 3 , 14 }, // DEC hhll + /* CF */ { "bbs4", 3 , 20 }, // BBS4 ZZ, lhhll + /* D0 */ { "bne ", 2 , 19 }, // BNE lhhll + /* D1 */ { "cmp ", 2 , 12 }, // CMP (ZZ), Y + /* D2 */ { "cmp ", 2 , 10 }, // CMP (ZZ) + /* D3 */ { "tin ", 7 , 18 }, // TIN shsl,dhdl,lhlh + /* D4 */ { "csh ", 1 , 0 }, // CSH + /* D5 */ { "cmp ", 2 , 8 }, // CMP ZZ, X + /* D6 */ { "dec ", 2 , 8 }, // DEC ZZ, X + /* D7 */ { "smb5", 2 , 7 }, // SMB5 ZZ + /* D8 */ { "cld ", 1 , 0 }, // CLD + /* D9 */ { "cmp ", 3 , 17 }, // CMP hhll, Y + /* DA */ { "phx ", 1 , 0 }, // PHX + /* DB */ { ".db ", 1 , 22 }, // UNUSED + /* DC */ { ".db ", 1 , 22 }, // UNUSED + /* DD */ { "cmp ", 3 , 16 }, // CMP hhll, X + /* DE */ { "dec ", 3 , 16 }, // DEC hhll, X + /* DF */ { "bbs5", 3 , 20 }, // BBS5 ZZ, lhhll + /* E0 */ { "cpx ", 2 , 2 }, // CPX #nn + /* E1 */ { "sbc ", 2 , 11 }, // SBC (ZZ,X) + /* E2 */ { ".db ", 1 , 22 }, // UNUSED + /* E3 */ { "tia ", 7 , 18 }, // TIA shsl,dhdl,lhlh + /* E4 */ { "cpx ", 2 , 7 }, // CPX ZZ + /* E5 */ { "sbc ", 2 , 7 }, // SBC ZZ + /* E6 */ { "inc ", 2 , 7 }, // INC ZZ + /* E7 */ { "smb6", 2 , 7 }, // SMB6 ZZ + /* E8 */ { "inx ", 1 , 0 }, // INX + /* E9 */ { "sbc ", 2 , 2 }, // SBC #nn + /* EA */ { "nop ", 1 , 0 }, // NOP + /* EB */ { ".db ", 1 , 22 }, // UNUSED + /* EC */ { "cpx ", 3 , 14 }, // CPX hhll + /* ED */ { "sbc ", 3 , 14 }, // SBC hhll + /* EE */ { "inc ", 3 , 14 }, // INC hhll + /* EF */ { "bbs6", 3 , 20 }, // BBS6 ZZ, lhhll + /* F0 */ { "beq ", 2 , 19 }, // BEQ lhhll + /* F1 */ { "sbc ", 2 , 12 }, // SBC (ZZ), Y + /* F2 */ { "sbc ", 2 , 10 }, // SBC (ZZ) + /* F3 */ { "tai ", 7 , 18 }, // TAI shsl,dhdl,lhlh + /* F4 */ { "set ", 1 , 0 }, // SET + /* F5 */ { "sbc ", 2 , 8 }, // SBC ZZ, X + /* F6 */ { "inc ", 2 , 8 }, // INC ZZ, X + /* F7 */ { "smb7", 2 , 7 }, // SMB7 ZZ + /* F8 */ { "sed ", 1 , 0 }, // SED + /* F9 */ { "sbc ", 3 , 17 }, // SBC hhll, Y + /* FA */ { "plx ", 1 , 0 }, // PLX + /* FB */ { ".db ", 1 , 22 }, // UNUSED + /* FC */ { ".db ", 1 , 22 }, // UNUSED + /* FD */ { "sbc ", 3 , 16 }, // SBC hhll, X + /* FE */ { "inc ", 3 , 16 }, // INC hhll, X + /* FF */ { "bbs7", 3 , 20 } // BBS7 ZZ, lhhll }; -/** - * Get opcode description. - */ -const opcode_t* opcode_get(uint8_t op) { +// Get opcode description. +const Opcode* opcode_get(uint8_t op) { return &pce_opcode[op]; } -/** - * - */ -const char* opcode_format(const opcode_t *op, int i) { +// Get opcode description +const char* opcode_format(const Opcode *op, int i) { if((i < 0) || (i >= PCE_ARG_COUNT)) { return NULL; } return pce_opstring[op->type][i]; } -/** - * Is the instruction a local jump ? - */ -int opcode_is_local_jump(uint8_t op) { +// Is the instruction a local jump ? +bool opcode_is_local_jump(uint8_t op) { return - ((op & 0x0F) == 0x0F) || /* BBR* and BBS* */ - (op == 0x90) || /* BCC */ - (op == 0xB0) || /* BCS */ - (op == 0x80) || /* BRA */ - (op == 0xF0) || /* BEQ */ - (op == 0x30) || /* BMI */ - (op == 0xD0) || /* BNE */ - (op == 0x10) || /* BPL */ - (op == 0x44) || /* BSR */ - (op == 0x50) || /* BVC */ - (op == 0x70); /* BVS */ + ((op & 0x0F) == 0x0F) || // BBR* and BBS* + (op == 0x90) || // BCC + (op == 0xB0) || // BCS + (op == 0x80) || // BRA + (op == 0xF0) || // BEQ + (op == 0x30) || // BMI + (op == 0xD0) || // BNE + (op == 0x10) || // BPL + (op == 0x44) || // BSR + (op == 0x50) || // BVC + (op == 0x70); // BVS } -/** - * Is the instruction a "far" jump ? - */ -int opcode_is_far_jump(uint8_t op) { +// Is the instruction a "far" jump ? +bool opcode_is_far_jump(uint8_t op) { return - (op == 0x4C) || /* JMP */ - (op == 0x20); /* JSR */ + (op == 0x4C) || // JMP + (op == 0x20); // JSR } diff --git a/opcodes.h b/opcodes.h index 9b04340..d3b3a39 100644 --- a/opcodes.h +++ b/opcodes.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,32 +38,33 @@ #include "config.h" -/** - * Opcode types: - * -# `OPC` - * -# `OPC A` - * -# `OPC #nn` - * -# `OPC #nn, ZZ` - * -# `OPC #nn, ZZ, X` - * -# `OPC #nn, hhll` - * -# `OPC #nn, hhll, X` - * -# `OPC ZZ` - * -# `OPC ZZ, X` - * -# `OPC ZZ, Y` - * -# `OPC (ZZ)` - * -# `OPC (ZZ, X)` - * -# `OPC (ZZ), Y` - * -# `OPC ZZ, hhll` - * -# `OPC hhll` - * -# `OPC (hhll)` - * -# `OPC hhll, X` - * -# `OPC hhll, Y` - * -# `OPC shsl,dhdl,hhll` - * -# `OPC l_hhll` (label) - * -# `OPC ZZ, l_hhll` (label) - * -# `OPC [hhll, X]` - * -# `.db OPC` (unsupported opcode output as raw binary data) - */ +/// @defgroup Opcode Opcode +///@{ + +/// Opcode types: +/// -# `OPC` +/// -# `OPC A` +/// -# `OPC #nn` +/// -# `OPC #nn, ZZ` +/// -# `OPC #nn, ZZ, X` +/// -# `OPC #nn, hhll` +/// -# `OPC #nn, hhll, X` +/// -# `OPC ZZ` +/// -# `OPC ZZ, X` +/// -# `OPC ZZ, Y` +/// -# `OPC (ZZ)` +/// -# `OPC (ZZ, X)` +/// -# `OPC (ZZ), Y` +/// -# `OPC ZZ, hhll` +/// -# `OPC hhll` +/// -# `OPC (hhll)` +/// -# `OPC hhll, X` +/// -# `OPC hhll, Y` +/// -# `OPC shsl,dhdl,hhll` +/// -# `OPC l_hhll` (label) +/// -# `OPC ZZ, l_hhll` (label) +/// -# `OPC [hhll, X]` +/// -# `.db OPC` (unsupported opcode output as raw binary data) enum PCE_ADDRESSING_MODE { PCE_OP = 0, PCE_OP_A, @@ -90,33 +91,33 @@ enum PCE_ADDRESSING_MODE { PCE_unknown }; -/** - * Opcode - */ +#define OPCODE_NAME_MAX_LEN 5U + +/// Opcode typedef struct { - char name[5]; /**< name. **/ - uint8_t size; /**< size in bytes. **/ - uint8_t type; /**< addressing type. **/ -} opcode_t; + char name[OPCODE_NAME_MAX_LEN]; //< name + uint8_t size; //< size in bytes + uint8_t type; //< addressing type +} Opcode; + +/// Get opcode description +/// \param [in] op Opcode id +/// \return Pointer to opcode description +const Opcode* opcode_get(uint8_t op); -/** - * Get opcode description. - */ -const opcode_t* opcode_get(uint8_t op); +/// Get opcode format string +/// \param [in] op Pointer to opcode description +/// \param [in] i Opcode argument id +/// \return Argument format string +/// \return NULL if the argument id is out of opcode argument count +const char* opcode_format(const Opcode *op, int i); -/** - * - */ -const char* opcode_format(const opcode_t *op, int i); +/// Is the instruction a local jump ? +bool opcode_is_local_jump(uint8_t op); -/** - * Is the instruction a local jump ? - */ -int opcode_is_local_jump(uint8_t op); +/// Is the instruction a "far" jump ? +bool opcode_is_far_jump(uint8_t op); -/** - * Is the instruction a "far" jump ? - */ -int opcode_is_far_jump(uint8_t op); +/// @} #endif // ETRIPATOR_OPCODES_H diff --git a/rom.c b/rom.c index 50d4e48..4a75011 100644 --- a/rom.c +++ b/rom.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,17 +36,15 @@ #include "rom.h" #include "message.h" -static int rom_load_data(const char *filename, memmap_t *map) { - int ret = 0; - size_t size = 0; - FILE *in; +static bool rom_load_data(const char *filename, MemoryMap *map) { + bool ret = false; - /* Open file */ - in = fopen(filename, "rb"); + size_t size = 0; + FILE *in = fopen(filename, "rb"); if (in == NULL) { ERROR_MSG("Unable to open %s : %s", filename, strerror(errno)); } else { - /* Compute file size. */ + // Compute file size. struct stat infos; int fd = fileno(in); if (fd < 0) { @@ -57,29 +55,31 @@ static int rom_load_data(const char *filename, memmap_t *map) { ERROR_MSG("Empty file: %s", filename); } else { size = infos.st_size; - /* Check for possible header */ - if (size & 0x200) { - /* Jump header */ - size &= ~0x200; - if (fseek(in, 0x200, SEEK_SET)) { + // Check for possible header + if (size & 0x200U) { + // Jump header + size &= ~0x200U; + if (fseek(in, 0x200U, SEEK_SET) < 0) { ERROR_MSG("Failed to jump rom header in %s: %s", filename, strerror(errno)); } } } if(size) { - /* Allocate rom storage */ - if (!mem_create(&map->mem[PCE_MEM_ROM], (size + 0x1fff) & ~0x1fff)) { + // Allocate rom storage + Memory *memory = &map->memory[PCE_MEMORY_ROM]; + if (!memory_create(memory, (size + 0x1FFFU) & ~0x1FFFU)) { ERROR_MSG("Failed to allocate ROM storage : %s", strerror(errno)); } else { - /* Fill rom with 0xff */ - memset(map->mem[PCE_MEM_ROM].data, 0xff, map->mem[PCE_MEM_ROM].len); - /* Read ROM data */ - size_t count = (size < map->mem[PCE_MEM_ROM].len) ? size : map->mem[PCE_MEM_ROM].len; - size_t nread = fread(map->mem[PCE_MEM_ROM].data, 1, count, in); + // Fill rom with 0xFF + (void)memory_fill(memory, 0xFFU); + // Read ROM data + size_t count = (size < memory->length) ? size : memory->length; + size_t nread = fread(memory->data, 1, count, in); if (nread != count) { - ERROR_MSG("Failed to read ROM data from %s : %s", filename, strerror(errno)); + ERROR_MSG("Failed to read ROM data from %s (expected %zu, read %zu): %s", filename, count, nread, strerror(errno)); + memory_destroy(memory); } else { - ret = 1; + ret = true; } } } @@ -88,36 +88,40 @@ static int rom_load_data(const char *filename, memmap_t *map) { return ret; } -/* Load ROM from file. */ -int rom_load(const char *filename, memmap_t *map) { +// Load ROM from file and update memory map. +bool rom_load(const char* filename, MemoryMap* map) { + assert(filename != NULL); + assert(map != NULL); FILE *in; - int ret = 0; - if(rom_load_data(filename, map) == 0) { - mem_destroy(&map->mem[PCE_MEM_ROM]); - } else { + bool ret = false; + if(rom_load_data(filename, map)) { unsigned int i; /* Initialize ROM pages. */ - if (map->mem[PCE_MEM_ROM].len == 0x60000) { + if (map->memory[PCE_MEMORY_ROM].length == 0x60000U) { for (i = 0; i < 64; i++) { - map->page[i] = &map->mem[PCE_MEM_ROM].data[(i & 0x1f) * 8192]; + map->page[i].id = PCE_MEMORY_ROM; + map->page[i].bank = i & 0x1FU; } for (i = 64; i < 128; i++) { - map->page[i] = &map->mem[PCE_MEM_ROM].data[((i & 0x0f) + 32) * 8192]; + map->page[i].id = PCE_MEMORY_ROM; + map->page[i].bank = (i & 0x0FU) + 32; } - } else if (map->mem[PCE_MEM_ROM].len == 0x80000) { + } else if (map->memory[PCE_MEMORY_ROM].length == 0x80000U) { for (i = 0; i < 64; i++) { - map->page[i] = &map->mem[PCE_MEM_ROM].data[(i & 0x3f) * 8192]; + map->page[i].id = PCE_MEMORY_ROM; + map->page[i].bank = i & 0x3FU; } for (i = 64; i < 128; i++) { - map->page[i] = &map->mem[PCE_MEM_ROM].data[((i & 0x1f) + 32) * 8192]; + map->page[i].id = PCE_MEMORY_ROM; + map->page[i].bank = (i & 0x1FU) + 32; } } else { for (i = 0; i < 128; i++) { - uint8_t bank = (uint8_t)(i % (map->mem[PCE_MEM_ROM].len / 8192)); - map->page[i] = &map->mem[PCE_MEM_ROM].data[bank * 8192]; + map->page[i].id = PCE_MEMORY_ROM; + map->page[i].bank = i % (map->memory[PCE_MEMORY_ROM].length / PCE_BANK_SIZE); } } - ret = 1; + ret = true; } return ret; } diff --git a/rom.h b/rom.h index 48832f5..c0641e9 100644 --- a/rom.h +++ b/rom.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,14 +36,13 @@ #ifndef ETRIPATOR_ROM_H #define ETRIPATOR_ROM_H -#include "memorymap.h" +#include "memory_map.h" -/** - * Load ROM from file. - * \param [in] filename ROM filename. - * \param [out] map Memory map. - * \return 1 upon success, 0 if an error occured. - */ -int rom_load(const char* filename, memmap_t* map); +/// Load ROM from file and update memory map. +/// \param [in] filename ROM filename. +/// \param [out] map Memory map. +/// \return true if the ROM was successfully loaded. +/// \return false if an error occured. +bool rom_load(const char* filename, MemoryMap* map); #endif // ETRIPATOR_ROM_H diff --git a/section.c b/section.c index f39b850..74e98bb 100644 --- a/section.c +++ b/section.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,53 +43,57 @@ static const int32_t g_default_element_size = 8; static const int32_t g_default_elements_per_line = 16; -static const char *g_supported_section_types[SectionTypeCount] = { - "data", - "code" -}; - -static const char *g_supported_data_types[DataTypeCount] = { - "binary", - "hex", - "string", - "jumptable" -}; - -/* Retrieves section type name. */ -const char* section_type_name(section_type_t type) { - if((type <= UnknownSectionType) || (type >= SectionTypeCount)) { +// Retrieves section type name +const char* section_type_name(SectionType type) { + static const char *name[SECTION_TYPE_COUNT] = { + [SECTION_TYPE_DATA] = "data", + [SECTION_TYPE_CODE] = "code" + }; + + if((type <= SECTION_TYPE_UNKNOWN) || (type >= SECTION_TYPE_COUNT)) { return "unknown"; } - return g_supported_section_types[type]; + return name[type]; } -/* Retrieves data type name. */ -const char* data_type_name(data_type_t type) { - if((type <= UnknownDataType) || (type >= DataTypeCount)) { +// Retrieves data type name +const char* data_type_name(DataType type) { + static const char *name[DATA_TYPE_COUNT] = { + [DATA_TYPE_BINARY] = "binary", + [DATA_TYPE_HEX] = "hex", + [DATA_TYPE_STRING] = "string", + [DATA_TYPE_JUMP_TABLE] = "jumptable" + }; + + if((type <= DATA_TYPE_UNKNOWN) || (type >= DATA_TYPE_COUNT)) { return "unknown"; } - return g_supported_data_types[type]; + return name[type]; } -/* Reset a section to its default values. */ -void section_reset(section_t *s) { +// Reset a section to its default values +void section_reset(Section *s) { + assert(s != NULL); s->name = NULL; - s->type = UnknownSectionType; + s->type = SECTION_TYPE_UNKNOWN; s->page = 0; s->logical = 0; s->offset = 0; s->size = 0; - memset(s->mpr, 0, 8*sizeof(uint8_t)); + for(unsigned int i=0; i<8U; i++) { + s->mpr[i] = 0; + } s->output = NULL; - s->data.type = UnknownDataType; + s->data.type = DATA_TYPE_UNKNOWN; s->data.element_size = g_default_element_size; s->data.elements_per_line = g_default_elements_per_line; s->description = NULL; } static int section_compare(const void *a, const void *b) { - section_t* s0 = (section_t*)a; - section_t* s1 = (section_t*)b; + Section* s0 = (Section*)a; + Section* s1 = (Section*)b; + int cmp = s0->page - s1->page; if(!cmp) { cmp = s0->logical - s1->logical; @@ -97,24 +101,147 @@ static int section_compare(const void *a, const void *b) { return cmp; } -/* Group section per output filename and sort them in bank/org order. */ -void section_sort(section_t *ptr, size_t n) { - qsort(ptr, n, sizeof(section_t), §ion_compare); +// Delete section. +void section_delete(Section *ptr) { + assert(ptr != NULL); + free(ptr->name); + free(ptr->output); + free(ptr->description); + section_reset(ptr); } -/* Delete sections. */ -void section_delete(section_t *ptr, int n) { - int i; - for(i=0; idata != NULL) { + for(size_t i=0; icount; i++) { + section_delete(&arr->data[i]); } - if(ptr[i].output) { - free(ptr[i].output); + } + arr->count = 0; +} + +// Release section array memory. +void section_array_delete(SectionArray *arr) { + assert(arr != NULL); + section_array_reset(arr); + free(arr->data); + arr->data = NULL; + arr->capacity = 0; +} + +// Check if 2 sections overlaps. +// \return 1 Both sections has the same time and overlaps +// \return 0 Sections don't overlap +// \return -1 Sections overlap but they are not of the same type. +static int section_overlap(const Section *a, const Section *b) { + assert(a != NULL); + assert(b != NULL); + + int ret = 0; + + if(a->page == b->page) { + if(a->logical > b->logical) { + const Section *tmp = b; + b = a; + a = tmp; + } + if(a->type != b->type) { + ret = (b->logical < (a->logical + a->size)) ? -1 : 0; + } else if(b->logical <= (a->logical + a->size)) { + if(a->type == SECTION_TYPE_DATA) { + ret = (a->data.type == b->data.type) ? 1 : -1; + } else { + ret = 1; + } } - if(ptr[i].description) { - free(ptr[i].description); + } + return ret; +} + +static inline uint16_t minu16(uint16_t a, uint16_t b) { + return (a < b) ? a : b; +} + +static inline uint16_t maxi32(int32_t a, int32_t b) { + return (a > b) ? a : b; +} + +// Merge 2 sections the 2nd section into the 1st one. +static void section_merge(Section *a, const Section *b) { + assert(a != NULL); + assert(b != NULL); + + uint16_t begin = minu16(a->logical, b->logical); + int32_t end = maxi32(a->logical+a->size, b->logical+b->size); + a->logical = begin; + a->size = end - begin; +} + +// Add a new section. +int section_array_add(SectionArray *arr, const Section* in) { + assert(arr != NULL); + assert(in != NULL); + + int ret = -1; + // Check if we need to expand section array buffer + if(arr->count >= arr->capacity) { + size_t n = arr->capacity + 4U; + Section *ptr = realloc(arr->data, n*sizeof(Section)); + if(ptr == NULL) { + ERROR_MSG("Failed to expand section array buffer", strerror(errno)); + } else { + arr->data = ptr; + arr->capacity = n; + ret = 1; + } + } else { + ret = 1; + } + + if(ret > 0) { + arr->data[arr->count] = *in; + arr->count++; + } + return ret; +} + +// Retrieve the ith section from the array. +const Section* section_array_get(SectionArray *arr, size_t i) { + const Section *ret = NULL; + if(arr != NULL) { + if((arr->data != NULL) && (i < arr->count)) { + ret = &arr->data[i]; + } + } + return ret; +} + +// Merge and sort sections. +void section_array_tidy(SectionArray *arr) { + assert(arr != NULL); + assert(arr->count != 0); + + Section *section = calloc(arr->count, sizeof(Section)); + + qsort(arr->data, arr->count, sizeof(Section), section_compare); + + size_t j = 0; + section[0] = arr->data[0]; + for(size_t i=1; icount; i++) { + int overlap = section_overlap(§ion[j], &arr->data[i]); + if(overlap == 1) { + section_merge(§ion[j], &arr->data[i]); + INFO_MSG("Section %s has been merged with %s!", arr->data[i].name, section[j].name); + section_delete(&arr->data[i]); + } else { + if(overlap == -1) { + WARNING_MSG("Section %s and %s overlaps!", arr->data[i].name, section[j].name); + } + section[++j] = arr->data[i]; } } - free(ptr); + free(arr->data); + arr->data = section; + arr->count = j+1; } diff --git a/section.h b/section.h index 0f1cc09..0869797 100644 --- a/section.h +++ b/section.h @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,98 +38,118 @@ #include "config.h" -/** - * Section type. - */ +/// Section type. typedef enum { - UnknownSectionType = -1, - Data, - Code, - SectionTypeCount -} section_type_t; - -/** - * Retrieves section type name. - */ -const char* section_type_name(section_type_t type); - -/** - * Retrieves data type name. - */ + SECTION_TYPE_UNKNOWN = -1, + SECTION_TYPE_DATA = 0, + SECTION_TYPE_CODE, + SECTION_TYPE_COUNT +} SectionType; + +/// Retrieves section type name +/// \param [in] type Section type +/// \return "data" if type is SECTION_TYPE_DATA +/// \return "code" if type is SECTION_TYPE_CODE +/// \return "unknown" otherwise +const char* section_type_name(SectionType type); + +/// Data type typedef enum { - UnknownDataType = -1, - Binary, - Hex, - String, - JumpTable, - DataTypeCount -} data_type_t; - -/** - * Retrieves data type name. - */ -const char* data_type_name(data_type_t type); - -/** - * Data section configuration. - */ + DATA_TYPE_UNKNOWN = -1, + DATA_TYPE_BINARY = 0, + DATA_TYPE_HEX, + DATA_TYPE_STRING, + DATA_TYPE_JUMP_TABLE, + DATA_TYPE_COUNT +} DataType; + +/// Retrieves data type name +/// \param [in] type Data type +/// \return "binary" if type is DATA_TYPE_BINARY +/// \return "hex" if type is DATA_TYPE_HEX +/// \return "string" if type is DATA_TYPE_STRING +/// \return "jumptable if type is DATA_TYPE_JUMP_TABLE +/// \return "unknown" otherwise +const char* data_type_name(DataType type); + +/// Data section configuration typedef struct { - data_type_t type; /**< type. **/ - int32_t element_size; /**< element size (string=0, byte=1, word=2). **/ - int32_t elements_per_line; /**< number of elements per line. **/ -} data_config_t; - -/** - * Section description. - */ + DataType type; //< type + int32_t element_size; //< element size (string=0, byte=1, word=2) + int32_t elements_per_line; //< number of elements per line + + uint8_t delimiter[8U]; //< string delimiter + int32_t delimiter_size; //< string delimiter length +} DataConfig; + +/// Section description typedef struct { - char *name; /**< name. **/ - section_type_t type; /**< type. **/ - uint8_t page; /**< memory page. **/ - uint16_t logical; /**< logical address. **/ - uint32_t offset; /**< input offset. **/ - int32_t size; /**< size (in bytes). **/ - uint8_t mpr[8]; /**< mpr registers value. **/ - char *output; /**< output filename. **/ - data_config_t data; /**< data configuration (only valid for *data* sections) **/ - char *description; /**< optional description. **/ -} section_t; - -/** - * Reset a section to its default values. - **/ -void section_reset(section_t *s); - -/** - * Group section per output filename and sort them in page/logical address order. - * \param [in][out] ptr Sections. - * \param [in] n Number of sections to sort. - */ -void section_sort(section_t *ptr, size_t n); - -/** - * Delete sections. - */ -void section_delete(section_t *ptr, int n); - -/** - * Load sections from a JSON file. - * \param [in] filename Input filename. - * \param [out] out Loaded sections. - * \param [out] n Number of loaded sections. - * \return 1 if the sections contained in the file were succesfully loaded. - * 0 if an error occured. - */ -int section_load(const char *filename, section_t **out, int *n); - -/** - * Save sections to a JSON file. - * \param [in] filename Output filename. - * \param [in] ptr Sections to be saved. - * \param [in] count Number of sections. - * \return 1 if the sections were succesfully saved. - * 0 if an error occured. - */ -int section_save(const char *filename, section_t *ptr, int n); + char *name; //< Name + SectionType type; //< Type + uint8_t page; //< Memory page + uint16_t logical; //< logical address + uint32_t offset; //< input offset + int32_t size; //< size (in bytes) + uint8_t mpr[8]; //< mpr registers value + char *output; //< output filename + DataConfig data; //< data configuration (only valid for *data* sections) + char *description; //< optional description +} Section; + +/// Section array +typedef struct { + Section *data; ///< Pointer to section array. + size_t count; ///< Number of sections currently in use. + size_t capacity; ///< Number the section array can hold. +} SectionArray; + +/// Reset a section array. +/// \note The internal data pointer will not be freed. +/// \param [in out] arr Section array to be reseted. +void section_array_reset(SectionArray *arr); + +/// Release section array memory. +/// \param [in out] arr Section array. +void section_array_delete(SectionArray *arr); + +/// Add a new section. +/// \param [in out] arr Section array the section will be added to. +/// \param [in] in Section that will be added to the section array. +/// \return 1 if the section was succesfully added. +/// \return 0 if the section was merged with one from the section array. +/// \return -1 if the section can not be merged or if there is not enough memory to add a new one. +int section_array_add(SectionArray *arr, const Section* in); + +/// Merge and sort sections. +/// \param [in out] arr Section array the section will be added to. +void section_array_tidy(SectionArray *arr); + +/// Retrieve the ith section from the array. +/// \param [in] arr Section array. +/// \param [in] i Index of the section to be retrieved. +/// \return A pointer to the section if the index is within the section array bounds. +/// \return NULL if the index is out of the section array bounds. +const Section* section_array_get(SectionArray *arr, size_t i); + +/// Reset a section to its default values. +void section_reset(Section *s); + +/// Delete section. +void section_delete(Section *ptr); + +// Load sections from a JSON file. +// \param [out] arr Loaded sections. +// \param [in] filename Input filename. +// \return true if the sections contained in the file were succesfully loaded. +// \return false if an error occured. +bool section_load(SectionArray *arr, const char *filename); + +// Save sections to a JSON file. +// \param [in] ptr Sections to be saved. +// \param [in] count Number of sections. +// \param [in] filename Output filename. +// \return true if the sections were succesfully saved. +// \return false if an error occured. +bool section_save(const Section *ptr, int n, const char *filename); // [todo] use SectionArray #endif // ETRIPATOR_SECTION_H diff --git a/section/load.c b/section/load.c index 5a664c8..e4e6a36 100644 --- a/section/load.c +++ b/section/load.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,192 +44,330 @@ #include "../jsonhelpers.h" #include "../message.h" -static section_type_t json_validate_section_type(const char *str) { - int i; - for (i = 0; i < SectionTypeCount; i++) { +static inline SectionType json_validate_section_type(const char *str) { + for (int i = 0; i < SECTION_TYPE_COUNT; i++) { if (strncmp(section_type_name(i), str, strlen(section_type_name(i))) == 0) { - return (section_type_t)i; + return (SectionType)i; } } - return UnknownSectionType; + return SECTION_TYPE_UNKNOWN; } -static data_type_t json_validate_data_type(const char *str) { - int i; - for (i = 0; i < DataTypeCount; i++) { +static inline DataType json_validate_data_type(const char *str) { + for (int i = 0; i < DATA_TYPE_COUNT; i++) { if (strncmp(data_type_name(i), str, strlen(data_type_name(i))) == 0) { - return (data_type_t)i; + return (DataType)i; } } - return UnknownDataType; + return DATA_TYPE_UNKNOWN; } -/** - * Initializes section from JSON object. - * @param [in] obj JSON object. - * @param [out] out Section. - * @return 1 upon success or 0 if an error occured. - **/ -static int section_parse(const json_t *obj, section_t *out) { - const json_t *tmp; - int num; - section_reset(out); - if (!json_is_object(obj)) { - ERROR_MSG("Invalid json element."); - return 0; - } - /* type */ - tmp = json_object_get(obj, "type"); - if (!json_is_string(tmp)) { - ERROR_MSG("Invalid data for section type."); - return 0; - } - out->type = json_validate_section_type(json_string_value(tmp)); - if (out->type == UnknownSectionType) { +static bool json_parse_section_type(Section *out, const json_t *obj) { + bool ret = false; + const json_t *tmp = json_object_get(obj, "type"); + if (tmp == NULL) { + ERROR_MSG("Missing section type."); + } else if (!json_is_string(tmp)) { ERROR_MSG("Invalid section type."); - return 0; - } - /* page */ - tmp = json_object_get(obj, "page"); - if (!json_validate_int(tmp, &num)) { - ERROR_MSG("Invalid or missing memory page."); - return 0; - } - if ((num < 0) || (num > 255)) { - ERROR_MSG("Page out of range."); - return 0; - } - out->page = (uint8_t)num; - /* logical address */ - tmp = json_object_get(obj, "logical"); - if (!json_validate_int(tmp, &num)) { - ERROR_MSG("Invalid or missing logical address."); - return 0; - } - if ((num < 0) || (num > 0xffff)) { - ERROR_MSG("Org value out of range."); - return 0; - } - out->logical = (uint16_t)num; - - out->offset = (out->page << 13) | (out->logical & 0x1fff); - - /* offset */ - num = -1; - tmp = json_object_get(obj, "offset"); - if (tmp) { - if (!json_validate_int(tmp, &num)) { - ERROR_MSG("Invalid or missing offset value."); - return 0; + } else { + out->type = json_validate_section_type(json_string_value(tmp)); + if (out->type == SECTION_TYPE_UNKNOWN) { + ERROR_MSG("Invalid section type value."); } else { - out->offset = (uint32_t)num; + ret = true; } } + return ret; +} - /* size */ - tmp = json_object_get(obj, "size"); - if (tmp) { - if (!json_validate_int(tmp, &out->size)) { - ERROR_MSG("Invalid section size."); - return 0; - } +static bool json_parse_section_page(Section *out, const json_t *obj) { + bool ret = false; + int num = -1; + const json_t *tmp = json_object_get(obj, "page"); + if (tmp == NULL) { + ERROR_MSG("Missing section page"); + } else if (!json_validate_int(tmp, &num)) { + ERROR_MSG("Invalid section memory page."); + } else if ((num < 0) || (num > 255)) { + ERROR_MSG("Section memory page out of range."); + } else { + out->page = (uint8_t)num; + ret = true; + } + return ret; +} + +static bool json_parse_section_logical(Section *out, const json_t *obj) { + bool ret = false; + int num = -1; + const json_t *tmp = json_object_get(obj, "logical"); + if (tmp == NULL) { + ERROR_MSG("Missing section logical address"); + } else if (!json_validate_int(tmp, &num)) { + ERROR_MSG("Invalid or missing section logical address."); + } else if ((num < 0) || (num > 0xFFFF)) { + ERROR_MSG("Section logical address out of range."); + } else { + out->logical = (uint16_t)num; + ret = true; + } + return ret; +} + +static bool json_parse_section_offset(Section *out, const json_t *obj) { + bool ret = true; + int num = 1; + json_t *tmp = json_object_get(obj, "offset"); + if(tmp == NULL) { + out->offset = (out->page << 13) | (out->logical & 0x1FFF); + } else if (json_validate_int(tmp, &num)) { + out->offset = (uint32_t)num; + } else { + ERROR_MSG("Invalid or missing offset value."); + ret = false; + } + return ret; +} + +static bool json_parse_section_size(Section *out, const json_t *obj) { + bool ret = true; + int num = -1; + const json_t *tmp = json_object_get(obj, "size"); + if (tmp == NULL) { + // use default value + } else if (json_validate_int(tmp, &num)) { + out->size = (int32_t)num; + } else { + ERROR_MSG("Invalid section size."); + ret = false; } - /* mpr */ - tmp = json_object_get(obj, "mpr"); - if(json_is_array(tmp)) { + return ret; +} + +static bool json_parse_section_mpr(Section *out, const json_t *obj) { + bool ret = false; + const json_t *tmp = json_object_get(obj, "mpr"); + if(tmp == NULL) { + ERROR_MSG("Missing section memory map"); + } else if(!json_is_array(tmp)) { + ERROR_MSG("Invalid section memory map"); + } else { + int num; size_t index; json_t* value; json_array_foreach(tmp, index, value) { - if(index > 7) { - WARNING_MSG("Extra mpr values"); - break; - } - if(json_validate_int(value, &num)) { - if((num < 0) || (num > 0xff)) { - ERROR_MSG("Invalid mpr %d value", index); - return 0; - } + ret = false; + if(index > 7U) { + ERROR_MSG("Too many mpr values"); + } else if(!json_validate_int(value, &num)) { + ERROR_MSG("Invalid type for mpr value"); + } else if((num < 0) || (num > 0xFF)) { + ERROR_MSG("Invalid mpr %d value", index); + } else { out->mpr[index] = (uint8_t)num; + ret = true; + } + if(!ret) { + break; } } } - /* output */ - tmp = json_object_get(obj, "filename"); - if (!json_is_string(tmp)) { - ERROR_MSG("Missing or invalid output filename."); - return 0; + return ret; +} + +static bool json_parse_section_output(Section *out, const json_t *obj) { + bool ret = false; + const json_t *tmp = json_object_get(obj, "filename"); + if (tmp == NULL) { + ERROR_MSG("Misssing section output filename"); + } else if (!json_is_string(tmp)) { + ERROR_MSG("Invalid output filename."); + } else { + out->output = strdup(json_string_value(tmp)); + if(out->output == NULL) { + ERROR_MSG("Failed to parse section output filename: %s", strerror(errno)); + } else { + ret = true; + } } - out->output = strdup(json_string_value(tmp)); - /* data */ - tmp = json_object_get(obj, "data"); - if (tmp) { - json_t* value; - value = json_object_get(tmp, "type"); - if (!json_is_string(value)) { - ERROR_MSG("Invalid data for data type."); - return 0; + return ret; +} + +static bool json_parse_data_config_type(DataConfig *out, const json_t *obj) { + bool ret = false; + const json_t *tmp = json_object_get(obj, "type"); + if (tmp == NULL) { + ERROR_MSG("Missing section data type."); + } else if (!json_is_string(tmp)) { + ERROR_MSG("Invalid section data type."); + } else { + out->type = json_validate_data_type(json_string_value(tmp)); + if (out->type == DATA_TYPE_UNKNOWN) { + ERROR_MSG("Invalid section data type value."); + } else { + ret = true; } - out->data.type = json_validate_data_type(json_string_value(value)); - if (out->data.type == UnknownDataType) { - ERROR_MSG("Invalid data type."); - return 0; - } - value = json_object_get(tmp, "element_size"); - if(value && (out->data.type == Hex)) { - if (!json_validate_int(value, &out->data.element_size) || (out->data.element_size > 2)) { - ERROR_MSG("Invalid element size."); - return 0; + } + return ret; +} + +static bool json_parse_data_config_element_size(DataConfig *out, const json_t *obj) { + bool ret = false; + const json_t *value = json_object_get(obj, "element_size"); + if(value == NULL) { + ret = true; // use default value + } else if(out->type != DATA_TYPE_HEX) { + ERROR_MSG("Element size is only valid for hex data type"); + } else if(!json_validate_int(value, &out->element_size)) { + ERROR_MSG("Invalid element size"); + } else if((out->element_size < 0) || (out->element_size > 2)) { + ERROR_MSG("Invalid element size value: %d", out->element_size); + } else { + ret = true; + } + return ret; +} + +static bool json_parse_data_config_element_per_line(DataConfig *out, const json_t *obj) { + bool ret = false; + const json_t *value = json_object_get(obj, "elements_per_line"); + if(value == NULL) { + ret = true; // use default value + } else if((out->type != DATA_TYPE_HEX) && (out->type != DATA_TYPE_STRING)) { + ERROR_MSG("Number of elements per line is only valid for hex and string data section"); + } else if (!json_validate_int(value, &out->elements_per_line)) { + ERROR_MSG("Invalid number of elements per line."); + } else { + ret = true; + } + return ret; +} + +static bool json_parse_section_data_string_delimiter(DataConfig *out, const json_t *obj) { + bool ret = false; + const json_t *tmp = json_object_get(obj, "delimiter"); + if(tmp == NULL) { + ret = true; // use default value + } else if(out->type != DATA_TYPE_STRING) { + ERROR_MSG("string delimiter is only valid for string data section"); + } else if(!json_is_array(tmp)) { + ERROR_MSG("Invalid string delimiter"); + } else { + int num; + size_t index; + json_t* value; + json_array_foreach(tmp, index, value) { + ret = false; + if(index > 7U) { + ERROR_MSG("too many char in string delimiter"); + } else if(!json_validate_int(value, &num)) { + ERROR_MSG("Invalid type for string delimiter char"); + } else if((num < 0) || (num > 0xFF)) { + ERROR_MSG("Invalid char %d value", index); + } else { + out->delimiter[index] = (uint8_t)num; + ret = true; } - } - value = json_object_get(tmp, "elements_per_line"); - if(value && (out->data.type != Binary)) { - if (!json_validate_int(value, &out->data.elements_per_line)) { - ERROR_MSG("Invalid number of elements per line."); - return 0; + if(!ret) { + break; } } + out->delimiter_size = index; } - /* description (optional) */ - out->description = json_load_description(obj, "description"); + return ret; +} - return 1; +static bool json_parse_section_data_config(Section *out, const json_t *obj) { + bool ret = false; + const json_t *tmp = json_object_get(obj, "data"); + if (tmp == NULL) { + ret = true; + } else if(out->type != SECTION_TYPE_DATA) { + ERROR_MSG("Data configuration only valid for data sections"); + } else if(!json_parse_data_config_type(&out->data, tmp)) { + // ... + } else if(!json_parse_data_config_element_size(&out->data, tmp)) { + // ... + } else if(json_parse_data_config_element_per_line(&out->data, tmp) == false) { + // ... + } else if(json_parse_section_data_string_delimiter(&out->data, tmp) == false) { + // ... + } else { + ret = true; + } + return ret; +} + +/// Initializes section from JSON object. +/// \param [in] obj JSON object. +/// \param [out] out Section. +/// \return 1 upon success +/// \return 0 if an error occured. +static bool section_parse(Section *out, const json_t *obj) { + assert(obj != NULL); + assert(out != NULL); + + bool ret = false; + + section_reset(out); + if (!json_is_object(obj)) { + ERROR_MSG("Invalid json element."); + } else if(!json_parse_section_type(out, obj)) { + // ... + } else if(!json_parse_section_page(out, obj)) { + // ... + } else if(!json_parse_section_logical(out, obj)) { + // ... + } else if(!json_parse_section_offset(out, obj)) { + // ... + } else if(!json_parse_section_size(out, obj)) { + // ... + } else if(!json_parse_section_mpr(out, obj)) { + + } else if(!json_parse_section_output(out, obj)) { + // ... + } else if(!json_parse_section_data_config(out, obj)) { + // ... + } else { + // description (optional) + ret = json_load_description(obj, "description", &out->description); + } + return ret; } -/* Load sections from a JSON file. */ -int section_load(const char *filename, section_t **out, int *n) { - json_t* root; - json_t* obj; +// Load sections from a JSON file. +bool section_load(SectionArray *arr, const char *filename) { + bool ret = false; json_error_t err; - section_t *ptr; + Section *ptr; const char* key; - size_t size; - int ret = 1; - - root = json_load_file(filename, 0, &err); - if(!root) { + + json_t* root = json_load_file(filename, 0, &err); + if(root == NULL) { ERROR_MSG("Failed to parse %s:%d:%d: %s", filename, err.line, err.column, err.text); - return 0; - } + } else { + if(!json_is_object(root)) { + ERROR_MSG("Invalid root element"); + } else { + json_t* obj = NULL; + size_t size = json_object_size(root); + + ret = true; + json_object_foreach(root, key, obj) { + Section tmp = {0}; + section_reset(&tmp); - if(!json_is_object(root)) { - ERROR_MSG("Invalid root element"); - json_decref(root); - return ret; - } - - size = json_object_size(root); - *out = (section_t*)realloc(*out, (*n+size) * sizeof(section_t)); - ptr = *out + *n; - *n += (int)size; - json_object_foreach(root, key, obj) { - section_reset(ptr); - ret = ret && section_parse(obj, ptr); - if(ret) { - ptr->name = strdup(key); + if(!section_parse(&tmp, obj)) { + ret = false; + } else { + tmp.name = strdup(key); + if(section_array_add(arr, &tmp) <= 0) { + section_delete(&tmp); + } + } + } } - ptr++; + json_decref(root); } - - json_decref(root); return ret; } diff --git a/section/save.c b/section/save.c index 480d6ca..634f973 100644 --- a/section/save.c +++ b/section/save.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,45 +41,44 @@ #include #include -/* Save sections to a JSON file. */ -int section_save(const char *filename, section_t *ptr, int n) { - int i; +// Save sections to a JSON file +bool section_save(const Section *ptr, int n, const char *filename) { + bool ret = false; FILE *out = fopen(filename, "wb"); if(!out) { ERROR_MSG("Failed to open %s: %s", filename, strerror(errno)); - return 0; - } - - fprintf(out, "{\n"); - for(i=0; i - WORKING_DIRECTORY $) -set_target_properties(section_tests PROPERTIES C_STANDARD 11) -add_custom_command(TARGET section_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/data $/data) - -add_executable(label_tests label.c) -target_link_libraries(label_tests munit etripator) -target_include_directories(label_tests PRIVATE ${PROJECT_SOURCE_DIR} ${JANSSON_INCLUDE_DIRS} ${EXTRA_INCLUDE}) -add_test(NAME label_tests - COMMAND $) -set_target_properties(label_tests PROPERTIES C_STANDARD 11) - -add_executable(comment_tests comment.c) -target_link_libraries(comment_tests munit etripator) -target_include_directories(comment_tests PRIVATE ${PROJECT_SOURCE_DIR} ${JANSSON_INCLUDE_DIRS} ${EXTRA_INCLUDE}) -add_test(NAME comment_tests - COMMAND $) -set_target_properties(comment_tests PROPERTIES C_STANDARD 11) +function(add_unit_test) + set(options ) + set(oneValueArgs NAME) + set(multiValueArgs SOURCES LIBRARIES INCLUDE_DIRECTORIES WRAP) + cmake_parse_arguments(ut "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(target_name "${ut_NAME}_ut") + + list(REMOVE_DUPLICATES ut_SOURCES) + list(REMOVE_DUPLICATES ut_WRAP) + + add_executable(${target_name} ${ut_SOURCES}) + target_link_libraries(${target_name} PRIVATE ${ut_LIBRARIES} munit) + target_include_directories(${target_name} PRIVATE ${ut_INCLUDE_DIRECTORIES} ${EXTRA_INCLUDE}) + set_target_properties(${target_name} PROPERTIES C_STANDARD 11) + + if(ut_WRAP AND NOT CMAKE_COMPILER_IS_GNUCC) + message(FATAL_ERROR "WRAP can only be used with GCC") + endif() + + foreach(_wrap ${ut_WRAP}) + target_link_options(${target_name} PRIVATE LINKER:--wrap=${_wrap}) + endforeach() + + add_test( + NAME ${target_name} + COMMAND $ + WORKING_DIRECTORY $ + ) + add_custom_command( + TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/data $/data + ) +endfunction() + +add_unit_test( + NAME message + SOURCES + message.c + LIBRARIES cwalk + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +add_unit_test( + NAME memory + SOURCES + memory.c + ${PROJECT_SOURCE_DIR}/memory.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +add_unit_test( + NAME memory_map + SOURCES + memory_map.c + ${PROJECT_SOURCE_DIR}/memory_map.c + ${PROJECT_SOURCE_DIR}/memory.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +if(CMAKE_COMPILER_IS_GNUCC) + add_unit_test( + NAME rom + SOURCES + rom.c + ${PROJECT_SOURCE_DIR}/rom.c + ${PROJECT_SOURCE_DIR}/memory_map.c + ${PROJECT_SOURCE_DIR}/memory.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk fff + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} + WRAP fopen fileno fstat fseek fread fclose + ) +endif() + +add_unit_test( + NAME cd + SOURCES + cd.c + ${PROJECT_SOURCE_DIR}/cd.c + ${PROJECT_SOURCE_DIR}/memory_map.c + ${PROJECT_SOURCE_DIR}/memory.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +add_unit_test( + NAME label + SOURCES + label.c + ${PROJECT_SOURCE_DIR}/label.c + ${PROJECT_SOURCE_DIR}/label/load.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/jsonhelpers.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk jansson + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +add_unit_test( + NAME comment + SOURCES + comment.c + ${PROJECT_SOURCE_DIR}/comment.c + ${PROJECT_SOURCE_DIR}/comment/load.c + ${PROJECT_SOURCE_DIR}/comment/save.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/jsonhelpers.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk jansson + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +add_unit_test( + NAME section + SOURCES + section.c + ${PROJECT_SOURCE_DIR}/section/load.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/jsonhelpers.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk jansson + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +add_unit_test( + NAME opcodes + SOURCES + opcodes.c + ${PROJECT_SOURCE_DIR}/opcodes.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk jansson + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) + +add_unit_test( + NAME ipl + SOURCES + ipl.c + ${PROJECT_SOURCE_DIR}/section.c + ${PROJECT_SOURCE_DIR}/message.c + ${PROJECT_SOURCE_DIR}/message/console.c + LIBRARIES cwalk jansson + INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR} +) diff --git a/test/cd.c b/test/cd.c new file mode 100644 index 0000000..ce765ee --- /dev/null +++ b/test/cd.c @@ -0,0 +1,95 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include + +#include +#include + +#include + +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + return NULL; +} + +void tear_down(void* fixture __attribute__((unused))) { +} + +MunitResult cd_memory_map_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + MemoryMap map = {}; + + munit_assert_true(memory_map_init(&map)); + munit_assert_true(cd_memory_map(&map)); + + munit_assert_not_null(map.memory[PCE_MEMORY_CD_RAM].data); + munit_assert_size(map.memory[PCE_MEMORY_CD_RAM].length, ==, PCE_CD_RAM_BANK_COUNT*PCE_BANK_SIZE); + for(size_t i=0; i ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include + +#include "../ipl.c" + +#include "message.h" +#include "message/console.h" + +static uint8_t g_hcd8004_ipl[] = { + 0x00, 0x00, 0x02, 0x04, 0x00, 0x40, 0x00, 0x40, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x43, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, + 0x20, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4D, 0x00, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x20, 0x48, 0x55, 0x44, 0x53, 0x4F, 0x4E, 0x20, 0x53, 0x4F, 0x46, 0x54, 0x20, 0x2F, 0x20, + 0x4E, 0x45, 0x43, 0x20, 0x48, 0x6F, 0x6D, 0x65, 0x20, 0x45, 0x6C, 0x65, 0x63, 0x74, 0x72, 0x6F, + 0x6E, 0x69, 0x63, 0x73, 0x2C, 0x4C, 0x74, 0x64, 0x2E, 0x00, 0x53, 0x70, 0x61, 0x63, 0x65, 0x20, + 0x41, 0x64, 0x76, 0x65, 0x6E, 0x74, 0x75, 0x72, 0x65, 0x20, 0x43, 0x4F, 0x42, 0x52, 0x41, 0x20, +}; + +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + message_printer_init(); + console_message_printer_init(); + return NULL; +} + +void tear_down(void* fixture __attribute__((unused))) { + message_printer_destroy(); +} + +MunitResult ipl_read_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + IPL ipl = {0}; + const char *filename = "data/HCD8004.ipl"; + + FILE *in = fmemopen(g_hcd8004_ipl, sizeof(g_hcd8004_ipl), "r"); + + munit_assert_not_null(in); + munit_assert_true(ipl_read_header(&ipl, in, filename)); + munit_assert_int(fclose(in), ==, 0); + + munit_assert_uint8(ipl.load_start_record[0], ==, 0x00U); + munit_assert_uint8(ipl.load_start_record[1], ==, 0x00U); + munit_assert_uint8(ipl.load_start_record[2], ==, 0x02U); + + munit_assert_uint8(ipl.load_sector_count, ==, 0x04U); + + munit_assert_uint8(ipl.load_store_address[0], ==, 0x00U); + munit_assert_uint8(ipl.load_store_address[1], ==, 0x40U); + + munit_assert_uint8(ipl.load_exec_address[0], ==, 0x00U); + munit_assert_uint8(ipl.load_exec_address[1], ==, 0x40U); + + munit_assert_uint8(ipl.mpr[0], ==, 0x00U); + munit_assert_uint8(ipl.mpr[1], ==, 0x01U); + munit_assert_uint8(ipl.mpr[2], ==, 0x02U); + munit_assert_uint8(ipl.mpr[3], ==, 0x03U); + munit_assert_uint8(ipl.mpr[4], ==, 0x04U); + + munit_assert_uint8(ipl.opening_mode, ==, 0x00U); + + munit_assert_uint8(ipl.opening_gfx_record[0], ==, 0x00U); + munit_assert_uint8(ipl.opening_gfx_record[1], ==, 0x00U); + munit_assert_uint8(ipl.opening_gfx_record[2], ==, 0x00U); + + munit_assert_uint8(ipl.opening_gfx_sector_count, ==, 0x00U); + + munit_assert_uint8(ipl.opening_gfx_read_address[0], ==, 0x00U); + munit_assert_uint8(ipl.opening_gfx_read_address[1], ==, 0x00U); + + munit_assert_uint8(ipl.opening_adpcm_record[0], ==, 0x00U); + munit_assert_uint8(ipl.opening_adpcm_record[1], ==, 0x00U); + munit_assert_uint8(ipl.opening_adpcm_record[2], ==, 0x00U); + + munit_assert_uint8(ipl.opening_adpcm_sector_count, ==, 0x00U); + + munit_assert_uint8(ipl.opening_adpcm_sampling_rate, ==, 0x00U); + + for(unsigned int i=0; i<7; i++) { + munit_assert_uint8(ipl.reserved[i], ==, 0x00U); + } + + munit_assert_memory_equal(24U, ipl.id, "PC Engine CD-ROM SYSTEM"); + + munit_assert_memory_equal(50U, ipl.legal, "Copyright HUDSON SOFT / NEC Home Electronics,Ltd."); + + munit_assert_memory_equal(16U, ipl.program_name, "Space Adventure "); + + munit_assert_memory_equal(6U, ipl.extra, "COBRA "); + + return MUNIT_OK; +} + +static MunitTest g_ipl_tests[] = { + { "/read", ipl_read_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite g_ipl_test_suite = { + "Label test suite", g_ipl_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main (int argc, char* const* argv) { + return munit_suite_main(&g_ipl_test_suite, NULL, argc, argv); +} \ No newline at end of file diff --git a/test/label.c b/test/label.c index 1c9a306..aa99e6d 100644 --- a/test/label.c +++ b/test/label.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,152 +37,150 @@ #include "label.h" #include "message.h" #include "message/console.h" -#include "message/file.h" -void* setup(const MunitParameter params[], void* user_data) { - (void) params; - (void) user_data; - - console_msg_printer_t *printer = (console_msg_printer_t*)malloc(sizeof(console_msg_printer_t)); - - msg_printer_init(); - console_msg_printer_init(printer); - msg_printer_add((msg_printer_t*)printer); - - return (void*)printer; +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + message_printer_init(); + console_message_printer_init(); + return NULL; } -void tear_down(void* fixture) { - msg_printer_destroy(); - free(fixture); +void tear_down(void* fixture __attribute__((unused))) { + message_printer_destroy(); } -MunitResult label_add_test(const MunitParameter params[], void* fixture) { - (void)params; - (void)fixture; - - int ret; - label_t label; - label_repository_t* repository; - - repository = label_repository_create(); - munit_assert_not_null(repository); - - ret = label_repository_add(repository, "l_0001", 0x0001, 0x00, NULL); - munit_assert_int(ret, !=, 0); - - ret = label_repository_add(repository, "l_0020", 0x0020, 0x00, NULL); - munit_assert_int(ret, !=, 0); +MunitResult label_add_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + Label label = {}; + LabelRepository repository = {0}; + munit_assert_true(label_repository_create(&repository)); - ret = label_repository_add(repository, "l_000a", 0x000a, 0xb1, NULL); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_add(&repository, "l_0001", 0x0001, 0x00, NULL)); + munit_assert_true(label_repository_add(&repository, "l_0020", 0x0020, 0x00, NULL)); + munit_assert_true(label_repository_add(&repository, "l_000a", 0x000a, 0xb1, NULL)); + munit_assert_true(label_repository_add(&repository, "l_cafe", 0xcafe, 0xf7, NULL)); + munit_assert_true(label_repository_add(&repository, "l_0001", 0x0001, 0x00, NULL)); - ret = label_repository_add(repository, "l_cafe", 0xcafe, 0xf7, NULL); - munit_assert_int(ret, !=, 0); - - ret = label_repository_add(repository, "l_0001", 0x0001, 0x00, NULL); - munit_assert_int(ret, !=, 0); - - ret = label_repository_size(repository); - munit_assert_int(ret, ==, 4); + munit_assert_int(label_repository_size(&repository), ==, 4); - ret = label_repository_find(repository, 0x000a, 0xb1, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0x000a, 0xb1, &label)); munit_assert_string_equal(label.name, "l_000a"); - ret = label_repository_find(repository, 0x0020, 0x00, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0x0020, 0x00, &label)); munit_assert_string_equal(label.name, "l_0020"); - ret = label_repository_find(repository, 0xcafe, 0xf7, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0xcafe, 0xf7, &label)); munit_assert_string_equal(label.name, "l_cafe"); - ret = label_repository_find(repository, 0x0001, 0x00, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0x0001, 0x00, &label)); munit_assert_string_equal(label.name, "l_0001"); - ret = label_repository_find(repository, 0xbeef, 0xac, &label); - munit_assert_int(ret, ==, 0); + munit_assert_false(label_repository_find(&repository, 0xbeef, 0xac, &label)); - label_repository_destroy(repository); + label_repository_destroy(&repository); return MUNIT_OK; } -MunitResult label_delete_test(const MunitParameter params[], void* fixture) { - (void)params; - (void)fixture; +MunitResult label_delete_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + Label label = {}; + LabelRepository repository = {0}; + munit_assert_true(label_repository_create(&repository)); + + munit_assert_true(label_repository_add(&repository, "label01", 0x0110, 0x1a, NULL)); + munit_assert_true(label_repository_add(&repository, "label02", 0x0220, 0x1a, NULL)); + munit_assert_true(label_repository_add(&repository, "label03", 0x0330, 0x1b, "description")); + munit_assert_true(label_repository_add(&repository, "label04", 0x0440, 0x1a, NULL)); + munit_assert_true(label_repository_add(&repository, "label05", 0x0550, 0x1b, NULL)); + munit_assert_true(label_repository_add(&repository, "label06", 0x0553, 0x1b, NULL)); + munit_assert_true(label_repository_add(&repository, "label07", 0x0555, 0x1b, NULL)); + munit_assert_true(label_repository_add(&repository, "label08", 0x0557, 0x1b, NULL)); + + munit_assert_int(label_repository_size(&repository), ==, 8); + + label_repository_delete(&repository, 0x04a0, 0x0556, 0x1b); - int ret; - label_t label; - label_repository_t* repository; - - repository = label_repository_create(); - munit_assert_not_null(repository); - - ret = label_repository_add(repository, "label01", 0x0110, 0x1a, NULL); - munit_assert_int(ret, !=, 0); - ret = label_repository_add(repository, "label02", 0x0220, 0x1a, NULL); - munit_assert_int(ret, !=, 0); - ret = label_repository_add(repository, "label03", 0x0330, 0x1b, "description"); - munit_assert_int(ret, !=, 0); - ret = label_repository_add(repository, "label04", 0x0440, 0x1a, NULL); - munit_assert_int(ret, !=, 0); - ret = label_repository_add(repository, "label05", 0x0550, 0x1b, NULL); - munit_assert_int(ret, !=, 0); - ret = label_repository_add(repository, "label06", 0x0553, 0x1b, NULL); - munit_assert_int(ret, !=, 0); - ret = label_repository_add(repository, "label07", 0x0555, 0x1b, NULL); - munit_assert_int(ret, !=, 0); - ret = label_repository_add(repository, "label08", 0x0557, 0x1b, NULL); - munit_assert_int(ret, !=, 0); - - ret = label_repository_size(repository); - munit_assert_int(ret, ==, 8); - - ret = label_repository_delete(repository, 0x04a0, 0x0556, 0x1b); - munit_assert_int(ret, !=, 0); - - ret = label_repository_size(repository); - munit_assert_int(ret, ==, 5); - - ret = label_repository_find(repository, 0x0557, 0x1b, &label); - munit_assert_int(ret, !=, 0); + munit_assert_int(label_repository_size(&repository), ==, 5); + + munit_assert_true(label_repository_find(&repository, 0x0557, 0x1b, &label)); munit_assert_string_equal(label.name, "label08"); - ret = label_repository_find(repository, 0x0440, 0x1a, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0x0440, 0x1a, &label)); munit_assert_string_equal(label.name, "label04"); - ret = label_repository_find(repository, 0x0330, 0x1b, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0x0330, 0x1b, &label)); munit_assert_string_equal(label.name, "label03"); munit_assert_string_equal(label.description, "description"); - ret = label_repository_find(repository, 0x0220, 0x1a, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0x0220, 0x1a, &label)); munit_assert_string_equal(label.name, "label02"); - ret = label_repository_find(repository, 0x0110, 0x1a, &label); - munit_assert_int(ret, !=, 0); + munit_assert_true(label_repository_find(&repository, 0x0110, 0x1a, &label)); munit_assert_string_equal(label.name, "label01"); - ret = label_repository_find(repository, 0x0555, 0x1b, &label); - munit_assert_int(ret, ==, 0); - ret = label_repository_find(repository, 0x0553, 0x1b, &label); - munit_assert_int(ret, ==, 0); - ret = label_repository_find(repository, 0x0550, 0x1b, &label); - munit_assert_int(ret, ==, 0); + munit_assert_false(label_repository_find(&repository, 0x0555, 0x1b, &label)); + munit_assert_false(label_repository_find(&repository, 0x0553, 0x1b, &label)); + munit_assert_false(label_repository_find(&repository, 0x0550, 0x1b, &label)); - label_repository_destroy(repository); + label_repository_destroy(&repository); return MUNIT_OK; } -static MunitTest label_tests[] = { +MunitResult label_load_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + Label label = {}; + LabelRepository repository = {0}; + munit_assert_true(label_repository_create(&repository)); + + munit_assert_false(label_repository_load(&repository, "/not_here/label.json")); + munit_assert_int(label_repository_size(&repository), ==, 0); + + munit_assert_false(label_repository_load(&repository, "data/label_1.json")); + munit_assert_int(label_repository_size(&repository), ==, 1); + + munit_assert_true(label_repository_get(&repository, 0, &label)); + munit_assert_uint16(label.logical, ==, 0x5030U); + munit_assert_uint8(label.page, ==, 4); + munit_assert_string_equal(label.name, "ok"); + munit_assert_null(label.description); + + label_repository_destroy(&repository); + + munit_assert_true(label_repository_load(&repository, "data/label_0.json")); + munit_assert_int(label_repository_size(&repository), ==, 3); + + munit_assert_true(label_repository_find(&repository, 0x31DC, 0xF8, &label)); + munit_assert_uint16(label.logical, ==, 0x31DCU); + munit_assert_uint8(label.page, ==, 0xF8); + munit_assert_string_equal(label.name, "var"); + munit_assert_null(label.description); + + munit_assert_true(label_repository_find(&repository, 0xEABC, 0x00, &label)); + munit_assert_uint16(label.logical, ==, 0xEABCU); + munit_assert_uint8(label.page, ==, 0x00); + munit_assert_string_equal(label.name, "do_something"); + munit_assert_string_equal(label.description, "do something"); + + munit_assert_true(label_repository_find(&repository, 0xD6F7, 0x1F, &label)); + munit_assert_uint16(label.logical, ==, 0xD6F7U); + munit_assert_uint8(label.page, ==, 0x1F); + munit_assert_string_equal(label.name, "run"); + munit_assert_string_equal(label.description, "line0\nline1\nline2\nline3"); + + label_repository_destroy(&repository); + + return MUNIT_OK; +} + +MunitResult label_save_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + // [todo] + return MUNIT_OK; +} + + +static MunitTest Labelests[] = { { "/add", label_add_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { "/delete", label_delete_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { "/load", label_load_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { "/save", label_save_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } }; static const MunitSuite label_suite = { - "Label test suite", label_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE + "Label test suite", Labelests, NULL, 1, MUNIT_SUITE_OPTION_NONE }; int main (int argc, char* const* argv) { diff --git a/test/memory.c b/test/memory.c new file mode 100644 index 0000000..1e0d780 --- /dev/null +++ b/test/memory.c @@ -0,0 +1,115 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include + +#include +#include + +#include + +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + return NULL; +} + +void tear_down(void* fixture __attribute__((unused))) { +} + +MunitResult memory_create_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + Memory mem = {0}; + bool result = false; + + mem.data = NULL; + mem.length = 0xCAFEU; + munit_assert_false(memory_create(&mem, 0U)); + munit_assert_size(mem.length, ==, 0xCAFEU); + munit_assert_ptr_null(mem.data); + + munit_assert_true(memory_create(&mem, 32U)); + munit_assert_size(mem.length, ==, 32U); + munit_assert_ptr_not_null(mem.data); + + memory_destroy(&mem); + munit_assert_size(mem.length, ==, 0U); + munit_assert_ptr_null(mem.data); + + return MUNIT_OK; +} + +MunitResult memory_create_fill(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + Memory mem = {0}; + + munit_assert_false(memory_fill(&mem, 0x7C)); + + munit_assert_true(memory_create(&mem, 256U)); + munit_assert_size(mem.length, ==, 256U); + munit_assert_ptr_not_null(mem.data); + + munit_assert_true(memory_fill(&mem, 0x7C)); + for(size_t i=0; imem[PCE_MEM_BASE_RAM], 8192)) { - ERROR_MSG("Failed to allocate main memory!\n"); - } else { - /* Main RAM is mapped to pages 0xf8-0xfb (included). */ - /* Pages 0xf9 to 0xfb mirror page 0xf8. */ - for(i=0xf8; i<=0xfb; i++) { - map->page[i] = &(map->mem[PCE_MEM_BASE_RAM].data[0]); - } - - /* ROM and syscard RAM will be initialized later. */ - - /* Clear mprs. */ - memset(map->mpr, 0, 8); - map->mpr[0] = 0xff; - map->mpr[1] = 0xf8; - - ret = 1; - } - return ret; -} -/* Releases resources used by the memory map. */ -void memmap_destroy(memmap_t *map) { - int i; - for(i=0; imem[i]); - } - memset(map, 0, sizeof(memmap_t)); -} -/* Get the memory page associated to a logical address. */ -uint8_t memmap_page(memmap_t* map, uint16_t logical) { - uint8_t id = (logical >> 13) & 0x07; - return map->mpr[id]; +#include + +#include +#include + +#include + +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + return NULL; } -/* Reads a single byte from memory. */ -uint8_t memmap_read(memmap_t *map, size_t logical) { - uint8_t i = memmap_page(map, (uint16_t)logical); - return (map->page[i]) ? map->page[i][logical & 0x1fff] : 0xff; + +void tear_down(void* fixture __attribute__((unused))) { } -/* Update mprs. */ -void memmap_mpr(memmap_t *map, const uint8_t *mpr) { - memcpy(map->mpr, mpr, 8); + +MunitResult memory_map_read_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + MemoryMap map = {0}; + + munit_assert_true(memory_map_init(&map)); + + map.mpr[1] = 0xF8U; + + map.memory[PCE_MEMORY_BASE_RAM].data[0x0000] = 0xC5; + map.memory[PCE_MEMORY_BASE_RAM].data[0x0100] = 0x7E; + map.memory[PCE_MEMORY_BASE_RAM].data[0x1000] = 0xA9; + + munit_assert_uint8(memory_map_read(&map, 0x2000U), ==, 0xC5); + munit_assert_uint8(memory_map_read(&map, 0x2100U), ==, 0x7E); + munit_assert_uint8(memory_map_read(&map, 0x3000U), ==, 0xA9); + + memory_map_destroy(&map); + + return MUNIT_OK; } +static MunitTest memory_map_tests[] = { + { "/read", memory_map_read_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite memory_map_suite = { + "Memory map test suite", memory_map_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main (int argc, char* const* argv) { + message_printer_init(); + console_message_printer_init(); + + int ret = munit_suite_main(&memory_map_suite, NULL, argc, argv); + + message_printer_destroy(); + + return ret; +} \ No newline at end of file diff --git a/test/message.c b/test/message.c new file mode 100644 index 0000000..52068c6 --- /dev/null +++ b/test/message.c @@ -0,0 +1,231 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include + +#include "../message.c" + +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + return NULL; +} + +void tear_down(void* fixture __attribute__((unused))) { +} + +MunitResult message_init_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + g_message_printer_head = (MessagePrinter*)0xBEEF; + message_printer_init(); + munit_assert_ptr_null(g_message_printer_head); + + return MUNIT_OK; +} + +static unsigned int dummy_open_0_call_count; +static bool dummy_open_0(MessagePrinter *printer) { + dummy_open_0_call_count++; + return true; +} +static bool dummy_open_1(MessagePrinter *printer) { + dummy_open_0_call_count++; + return false; +} + +MunitResult message_add_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + message_printer_init(); + munit_assert_ptr_null(g_message_printer_head); + + MessagePrinter printer[4] = { + [0] = { .open = dummy_open_0 }, + [1] = { .open = dummy_open_0 }, + [2] = { .open = dummy_open_1 }, + [3] = { .open = dummy_open_0 }, + }; + + dummy_open_0_call_count = 0; + munit_assert_true(message_printer_add(&printer[0])); + munit_assert_uint(dummy_open_0_call_count, ==, 1); + munit_assert_ptr_equal(g_message_printer_head, &printer[0]); + munit_assert_null(g_message_printer_head->next); + + munit_assert_true(message_printer_add(&printer[1])); + munit_assert_uint(dummy_open_0_call_count, ==, 2); + munit_assert_ptr_equal(g_message_printer_head, &printer[1]); + munit_assert_ptr_equal(g_message_printer_head->next, &printer[0]); + munit_assert_null(printer[0].next); + + munit_assert_false(message_printer_add(&printer[2])); + munit_assert_uint(dummy_open_0_call_count, ==, 3); + munit_assert_ptr_equal(g_message_printer_head, &printer[1]); + munit_assert_ptr_equal(g_message_printer_head->next, &printer[0]); + munit_assert_null(g_message_printer_head->next->next); + + munit_assert_true(message_printer_add(&printer[3])); + munit_assert_uint(dummy_open_0_call_count, ==, 4); + munit_assert_ptr_equal(g_message_printer_head, &printer[3]); + munit_assert_ptr_equal(g_message_printer_head->next, &printer[1]); + munit_assert_ptr_equal(g_message_printer_head->next->next, &printer[0]); + munit_assert_null(g_message_printer_head->next->next->next); + + return MUNIT_OK; +} + +static int dummy_close_call_count; + +static bool dummy_close(MessagePrinter *printer __attribute__((unused))) { + dummy_close_call_count++; + return true; +} + +MunitResult message_destroy_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + message_printer_init(); + munit_assert_ptr_null(g_message_printer_head); + + MessagePrinter printer[4] = { + [0] = { .open = dummy_open_0, .close = dummy_close }, + [1] = { .open = dummy_open_0, .close = dummy_close }, + [2] = { .open = dummy_open_0, .close = dummy_close }, + [3] = { .open = dummy_open_0, .close = dummy_close }, + }; + + dummy_open_0_call_count = 0; + dummy_close_call_count = 0; + + message_printer_init(); + munit_assert_true(message_printer_add(&printer[0])); + munit_assert_true(message_printer_add(&printer[1])); + munit_assert_true(message_printer_add(&printer[2])); + munit_assert_true(message_printer_add(&printer[3])); + + munit_assert_ptr_equal(g_message_printer_head, &printer[3]); + + munit_assert_uint(dummy_open_0_call_count, ==, 4); + munit_assert_uint(dummy_close_call_count, ==, 0); + + message_printer_destroy(); + munit_assert_uint(dummy_close_call_count, ==, 4); + munit_assert_null(g_message_printer_head); + + return MUNIT_OK; +} + +static unsigned int dummy_print_index = 0; +static size_t dummy_print_line = 0; +static unsigned int dummy_print_history[4] = {-1}; + +static bool dummy_print_0(MessageType type, const char* file, size_t line, const char* function, const char* format, va_list args) { + dummy_print_history[dummy_print_index++] = 0; + munit_assert_uint(type, ==, MESSAGE_TYPE_INFO); + munit_assert_size(line, ==, dummy_print_line); + return true; +} +static bool dummy_print_1(MessageType type, const char* file, size_t line, const char* function, const char* format, va_list args) { + dummy_print_history[dummy_print_index++] = 1; + munit_assert_uint(type, ==, MESSAGE_TYPE_INFO); + munit_assert_size(line, ==, dummy_print_line); + return true; +} +static bool dummy_print_2(MessageType type, const char* file, size_t line, const char* function, const char* format, va_list args) { + dummy_print_history[dummy_print_index++] = 2; + munit_assert_uint(type, ==, MESSAGE_TYPE_INFO); + munit_assert_size(line, ==, dummy_print_line); + return true; +} +static bool dummy_print_3(MessageType type, const char* file, size_t line, const char* function, const char* format, va_list args) { + dummy_print_history[dummy_print_index++] = 3; + munit_assert_uint(type, ==, MESSAGE_TYPE_INFO); + munit_assert_size(line, ==, dummy_print_line); + return true; +} + +MunitResult message_print_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + message_printer_init(); + munit_assert_ptr_null(g_message_printer_head); + + MessagePrinter printer[4] = { + [0] = { .open = dummy_open_0, .close = dummy_close, .output = dummy_print_0 }, + [1] = { .open = dummy_open_0, .close = dummy_close, .output = dummy_print_1 }, + [2] = { .open = dummy_open_0, .close = dummy_close, .output = dummy_print_2 }, + [3] = { .open = dummy_open_0, .close = dummy_close, .output = dummy_print_3 }, + }; + + dummy_open_0_call_count = 0; + dummy_close_call_count = 0; + dummy_print_index = 0; + + for(unsigned int i=0; i<4; i++) { + dummy_print_history[i] = -1; + } + + message_printer_init(); + munit_assert_true(message_printer_add(&printer[0])); + munit_assert_true(message_printer_add(&printer[1])); + munit_assert_true(message_printer_add(&printer[2])); + munit_assert_true(message_printer_add(&printer[3])); + + munit_assert_ptr_equal(g_message_printer_head, &printer[3]); + + munit_assert_uint(dummy_open_0_call_count, ==, 4); + munit_assert_uint(dummy_close_call_count, ==, 0); + + dummy_print_line = __LINE__; INFO_MSG("test"); + munit_assert_uint(dummy_print_index, ==, 4); + munit_assert_uint(dummy_print_history[0], ==, 3); + munit_assert_uint(dummy_print_history[1], ==, 2); + munit_assert_uint(dummy_print_history[2], ==, 1); + munit_assert_uint(dummy_print_history[3], ==, 0); + + message_printer_destroy(); + munit_assert_uint(dummy_close_call_count, ==, 4); + munit_assert_null(g_message_printer_head); + + return MUNIT_OK; +} + + +static MunitTest message_tests[] = { + { "/init", message_init_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { "/add", message_add_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { "/destroy", message_destroy_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { "/print", message_print_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite message_suite = { + "message testt suite", message_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main (int argc, char* const* argv) { + return munit_suite_main(&message_suite, NULL, argc, argv); +} \ No newline at end of file diff --git a/test/opcodes.c b/test/opcodes.c new file mode 100644 index 0000000..19fc84c --- /dev/null +++ b/test/opcodes.c @@ -0,0 +1,147 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include +#include "opcodes.h" +#include "message.h" +#include "message/console.h" + +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + message_printer_init(); + console_message_printer_init(); + return NULL; +} + +void tear_down(void* fixture __attribute__((unused))) { + message_printer_destroy(); +} + +MunitResult opcodes_get_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + const Opcode* op; + + op = opcode_get(0x00); + munit_assert_not_null(op); + munit_assert_string_equal(op->name, "brk "); + munit_assert_uint8(op->size, ==, 1U); + munit_assert_uint8(op->type, ==, PCE_OP); + + op = opcode_get(0xFF); + munit_assert_not_null(op); + munit_assert_string_equal(op->name, "bbs7"); + munit_assert_uint8(op->size, ==, 3U); + munit_assert_uint8(op->type, ==, PCE_OP_ZZ_lbl); + + op = opcode_get(0x2B); + munit_assert_not_null(op); + munit_assert_string_equal(op->name, ".db "); + munit_assert_uint8(op->size, ==, 1U); + munit_assert_uint8(op->type, ==, PCE_unknown); + + return MUNIT_OK; +} + +MunitResult opcodes_format_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + const Opcode* op; + unsigned int i; + + // tii + op = opcode_get(0x73); + munit_assert_uint8(op->type, ==, PCE_OP_shsl_dhdl_hhll); + for(i=0; i<(op->size-1); i++) { // skip opcode + munit_assert_not_null(opcode_format(op, i)); + } + for(; i<256; i++) { + munit_assert_null(opcode_format(op, i)); + } + + return MUNIT_OK; +} + +MunitResult opcodes_jump_test(const MunitParameter params[] __attribute__((unused)), void* fixture __attribute__((unused))) { + // local jump + for(unsigned int i=0x0FU; i<0x100U; i+=0x10U) { + munit_assert_true(opcode_is_local_jump(i)); // BBR* and BBS* + } + munit_assert_true(opcode_is_local_jump(0x90U)); // BCC + munit_assert_true(opcode_is_local_jump(0xB0U)); // BCS + munit_assert_true(opcode_is_local_jump(0x80U)); // BRA + munit_assert_true(opcode_is_local_jump(0xF0U)); // BEQ + munit_assert_true(opcode_is_local_jump(0x30U)); // BMI + munit_assert_true(opcode_is_local_jump(0xD0U)); // BNE + munit_assert_true(opcode_is_local_jump(0x10U)); // BPL + munit_assert_true(opcode_is_local_jump(0x44U)); // BSR + munit_assert_true(opcode_is_local_jump(0x50U)); // BVC + munit_assert_true(opcode_is_local_jump(0x70U)); // BVS + + munit_assert_false(opcode_is_local_jump(0xEAU)); // NO + munit_assert_false(opcode_is_local_jump(0x4CU)); // JMP + munit_assert_false(opcode_is_local_jump(0x20U)); // JSP + + // far jump + for(unsigned int i=0x0FU; i<0x100U; i+=0x10U) { + munit_assert_false(opcode_is_far_jump(i)); // BBR* and BBS* + } + munit_assert_false(opcode_is_far_jump(0x90U)); // BCC + munit_assert_false(opcode_is_far_jump(0xB0U)); // BCS + munit_assert_false(opcode_is_far_jump(0x80U)); // BRA + munit_assert_false(opcode_is_far_jump(0xF0U)); // BEQ + munit_assert_false(opcode_is_far_jump(0x30U)); // BMI + munit_assert_false(opcode_is_far_jump(0xD0U)); // BNE + munit_assert_false(opcode_is_far_jump(0x10U)); // BPL + munit_assert_false(opcode_is_far_jump(0x44U)); // BSR + munit_assert_false(opcode_is_far_jump(0x50U)); // BVC + munit_assert_false(opcode_is_far_jump(0x70U)); // BVS + + munit_assert_false(opcode_is_far_jump(0xEAU)); // NOP + munit_assert_true(opcode_is_far_jump(0x4CU)); // JMP + munit_assert_true(opcode_is_far_jump(0x20U)); // JSR + + return MUNIT_OK; +} + +static MunitTest opcodes_tests[] = { + { "/get", opcodes_get_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { "/format", opcodes_format_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { "/jump", opcodes_jump_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite opcodes_suite = { + "Opcodes test suite", opcodes_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main (int argc, char* const* argv) { + return munit_suite_main(&opcodes_suite, NULL, argc, argv); +} \ No newline at end of file diff --git a/test/rom.c b/test/rom.c new file mode 100644 index 0000000..b69f3d0 --- /dev/null +++ b/test/rom.c @@ -0,0 +1,159 @@ +/* +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + __/¯\____ ___/\__ _/\__ _/\_ _/\__ _/\___ ___/\__ __/\_ _/\__ + \_ ____/_> ____ \_/ _ \_ \ < /_ \_/ _>> ____ \_ > \_/ _ \_ + _> ___/ ¯>__> <<__// __ _/ |> > <<__// /\ // __ _/ + _> \7 <__/:. \__/:. \> \_/ L/ _____/. 7> .\_/:. \__/ <_/ \_ +|:::::::::::::::::::::::/:::::::::::::>::::::::/::::::::::::::::::::::::/:::::| +|¯¯\::::/\:/¯\::::/¯¯¯¯<::::/\::/¯¯\:/¯¯¯¯¯¯\::\::/¯¯\::::/¯¯\::::/¯¯¯¯<::::/¯| +|__ |¯¯| T _ |¯¯¯| ___ |¯¯| |¯| _ T ______ |¯¯¯¯| _ |¯¯¯| _ |¯¯¯| ___ |¯¯| _| + \|__|/\|/ \|___|/ \|__|/\|_|/ \|/ \| |/ \|___|/ \|___|/dNo\|__|/ + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ + + This file is part of Etripator, + copyright (c) 2009--2024 Vincent Cruz. + + Etripator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Etripator 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Etripator. If not, see . + +¬°¤*,¸¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸ +¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ +*/ +#include + +#include + +#include +#include + +#include + +DEFINE_FFF_GLOBALS; + +// [NOTE] munit is using fileno. If a test fails, munit will loop indefinitely :/ + +FAKE_VALUE_FUNC(FILE*, __wrap_fopen, const char*, const char*) +FAKE_VALUE_FUNC(int, __wrap_fileno, FILE*) +FAKE_VALUE_FUNC(int, __wrap_fstat, int, struct stat*) +FAKE_VALUE_FUNC(int, __wrap_fseek, FILE*, long, int) +FAKE_VALUE_FUNC(size_t, __wrap_fread, void*, size_t, size_t, FILE*) +FAKE_VALUE_FUNC(int, __wrap_fclose, FILE*) + +static int fstat_empty_file(int fd __attribute__((unused)), struct stat* infos) { + memset(infos, 0, sizeof(struct stat)); + infos->st_size = 0; + return 0; +} + +static size_t g_dummy_file_size; + +static int fstat_dummy_file(int fd __attribute__((unused)), struct stat* infos) { + infos->st_size = g_dummy_file_size; + return 0; +} + +static size_t fread_dummy(void* out, size_t size, size_t nmemb, FILE* in __attribute__((unused))) { + uint8_t *ptr = (uint8_t*)out; + uint8_t b = 0; + for(size_t j=0; j 8kb + +static MunitTest rom_tests[] = { + { "/load/small", rom_load_small_test, setup, tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite rom_suite = { + "ROM test suite", rom_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main (int argc, char* const* argv) { + message_printer_init(); + console_message_printer_init(); + + int ret = munit_suite_main(&rom_suite, NULL, argc, argv); + + message_printer_destroy(); + + return ret; +} \ No newline at end of file diff --git a/test/section.c b/test/section.c index 954965d..4c1c735 100644 --- a/test/section.c +++ b/test/section.c @@ -15,7 +15,7 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ This file is part of Etripator, - copyright (c) 2009--2023 Vincent Cruz. + copyright (c) 2009--2024 Vincent Cruz. Etripator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,157 +34,152 @@ ¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯¬°¤*,¸_¸,*¤°¬°¤*,¸,*¤°¬¯ */ #include -#include "section.h" +#include "../section.c" #include "message.h" #include "message/console.h" -void* setup(const MunitParameter params[], void* user_data) { - (void) params; - (void) user_data; - - console_msg_printer_t *printer = (console_msg_printer_t*)malloc(sizeof(console_msg_printer_t)); - - msg_printer_init(); - console_msg_printer_init(printer); - msg_printer_add((msg_printer_t*)printer); - - return (void*)printer; -} -void tear_down(void* fixture) { - msg_printer_destroy(); - free(fixture); +void* setup(const MunitParameter params[] __attribute__((unused)), void* user_data __attribute__((unused))) { + message_printer_init(); + console_message_printer_init(); + return NULL; } -MunitResult section_sort_test(const MunitParameter params[], void* fixture) { - (void)params; - (void)fixture; - - section_t section[8]; - - section[0].output = "0002"; - section[0].page = 0; - section[0].logical = 1; - - section[1].output = "0001"; - section[1].page = 0; - section[1].logical = 0; - - section[2].output = "0002"; - section[2].page = 1; - section[2].logical = 0; - - section[3].output = "0001"; - section[3].page = 1; - section[3].logical = 0; - - section[4].output = "0000"; - section[4].page = 0; - section[4].logical = 2; - - section[5].output = "0000"; - section[5].page = 0; - section[5].logical = 1; - - section[6].output = "0001"; - section[6].page = 0; - section[6].logical = 1; - - section[7].output = "0002"; - section[7].page = 0; - section[7].logical = 0; - - section_sort(section, 8); - - for(int i=0; i<7; i++) { - munit_assert_int(section[i].page, <=, section[i+1].page); - if(section[i].page == section[i+1].page) { - munit_assert_int(section[i].logical, <=, section[i+1].logical); - } - } - - return MUNIT_OK; +void tear_down(void* fixture __attribute__((unused))) { + message_printer_destroy(); } MunitResult section_load_test(const MunitParameter params[], void* fixture) { (void)params; (void)fixture; - static section_t bank0_0[4] = { - { "cdbios_functions", Code, 0, 0xe000, 0x0000, 0x505, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "unknown.0", Data, 0, 0xe504, 0x0504, 0x05, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "ex_colorcmd.impl", Code, 0, 0xe509, 0x0509, 0xce, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "unknown.1", Data, 0, 0xe5d7, 0x05d7, 0x03, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } } + static const Section bank0_0[4] = { + { "cdbios_functions", SECTION_TYPE_CODE, 0, 0xe000, 0x0000, 0x504, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "unknown.0", SECTION_TYPE_DATA, 0, 0xe504, 0x0504, 0x05, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "ex_colorcmd.impl", SECTION_TYPE_CODE, 0, 0xe509, 0x0509, 0xce, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "unknown.1", SECTION_TYPE_DATA, 0, 0xe5d7, 0x05d7, 0x03, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } } }; - static section_t bank0_1[9] = { - { "ex_satclr.impl", Code, 0, 0xe5da, 0x05da, 0x26, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "unknown.2", Data, 0, 0xf8a9, 0x18a9, 0x0f, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "bm_free.impl", Code, 0, 0xf8b8, 0x18b8, 0x575, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "jump_table.0", Data, 0, 0xfe2d, 0x1e2d, 0x2a, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "grp_bios.impl", Code, 0, 0xfe57, 0x1e57, 0x18, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "jump_table.1", Data, 0, 0xfe70, 0x1e70, 0x22, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "ex_memopen.impl", Code, 0, 0xfe92, 0x1e92, 0x30, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "unknown.3", Data, 0, 0xfec2, 0x1ec2, 0x134, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { UnknownDataType, 8, 16 } }, - { "irq_vectors", Data, 0, 0xfff6, 0x1ff6, 0x0a, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { Hex, 2, 1 } } + static const Section bank0_1[9] = { + { "ex_satclr.impl", SECTION_TYPE_CODE, 0, 0xe5da, 0x05da, 0x26, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "unknown.2", SECTION_TYPE_DATA, 0, 0xf8a9, 0x18a9, 0x0f, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "bm_free.impl", SECTION_TYPE_CODE, 0, 0xf8b8, 0x18b8, 0x575, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "jump_table.0", SECTION_TYPE_DATA, 0, 0xfe2d, 0x1e2d, 0x2a, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "grp_bios.impl", SECTION_TYPE_CODE, 0, 0xfe57, 0x1e57, 0x18, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "jump_table.1", SECTION_TYPE_DATA, 0, 0xfe70, 0x1e70, 0x22, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "ex_memopen.impl", SECTION_TYPE_CODE, 0, 0xfe92, 0x1e92, 0x30, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "unknown.3", SECTION_TYPE_DATA, 0, 0xfec2, 0x1ec2, 0x134, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_UNKNOWN, 8, 16 } }, + { "irq_vectors", SECTION_TYPE_DATA, 0, 0xfff6, 0x1ff6, 0x0a, { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }, "syscard.asm", { DATA_TYPE_HEX, 2, 1 } } }; - - static const section_t* bank0[2] = { bank0_0, bank0_1 }; - - section_t *section = NULL; - int count[2] = { 0, 0 }; + + SectionArray arr = {0}; + section_array_reset(&arr); int i, j, k; int ret; - - ret = section_load("./data/bank0_0.json", §ion, &count[0]); + ret = section_load(&arr, "./data/bank0_0.json"); + section_array_tidy(&arr); munit_assert_int(ret, !=, 0); - munit_assert_int(count[0], ==, 4); - - section_sort(section, count[0]); - - for(i=0; i