# mh4n_memory: guest memory (N1-02).
#
# config/native/zero_regions.txt is embedded at configure time as mh4n_zero_regions.inc, a list of
# `{start, last},` initialisers. A native/ tree built without the repository's config/ (a copy
# of native/ on its own) gets the built-in list below instead.
set(MH4N_ZERO_REGIONS_FILE "${MH4N_ROOT}/../config/native/zero_regions.txt")
set(MH4N_ZERO_REGIONS_DEFAULT "0x00000000 0x000fffff  # reads of address 0 (the quest-loading race)")

if (EXISTS "${MH4N_ZERO_REGIONS_FILE}")
    file(READ "${MH4N_ZERO_REGIONS_FILE}" zero_text)
    # ';' and brackets in a comment would split or confuse the CMake list of lines.
    string(REPLACE ";" "," zero_text "${zero_text}")
    string(REPLACE "[" "(" zero_text "${zero_text}")
    string(REPLACE "]" ")" zero_text "${zero_text}")
    string(REPLACE "\n" ";" zero_lines "${zero_text}")
    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${MH4N_ZERO_REGIONS_FILE}")
    set(zero_source "${MH4N_ZERO_REGIONS_FILE}")
else()
    set(zero_lines "${MH4N_ZERO_REGIONS_DEFAULT}")
    set(zero_source "built-in default (config/native/zero_regions.txt not found)")
    message(STATUS "mh4n_memory: ${zero_source}")
endif()

set(zero_inc "")
set(zero_count 0)
foreach (line IN LISTS zero_lines)
    string(REGEX REPLACE "#.*$" "" content "${line}")
    string(STRIP "${content}" content)
    if (content STREQUAL "")
        continue()
    endif()
    if (NOT content MATCHES "^(0[xX][0-9a-fA-F]+)[ \t]+(0[xX][0-9a-fA-F]+)$")
        message(FATAL_ERROR "${zero_source}: expected '<start> <end>  # reason', got '${line}'")
    endif()
    set(first "${CMAKE_MATCH_1}")
    set(last "${CMAKE_MATCH_2}")
    math(EXPR first_value "${first}")
    math(EXPR last_value "${last}")
    math(EXPR misaligned_start "${first_value} & 0xFFF")
    math(EXPR misaligned_end "(${last_value} + 1) & 0xFFF")
    if (first_value GREATER last_value OR last_value GREATER 4294967295
        OR NOT misaligned_start EQUAL 0 OR NOT misaligned_end EQUAL 0)
        message(FATAL_ERROR "${zero_source}: '${line}' is not an ascending, 4 KiB aligned range "
                            "inside the 32-bit address space")
    endif()
    string(APPEND zero_inc "    {${first}u, ${last}u},\n")
    math(EXPR zero_count "${zero_count} + 1")
endforeach()
if (zero_count EQUAL 0)
    message(FATAL_ERROR "${zero_source}: no zero regions")
endif()

set(zero_header "${CMAKE_CURRENT_BINARY_DIR}/generated/mh4n_zero_regions.inc")
# file(CONFIGURE) rewrites the file only when its content changes.
file(CONFIGURE OUTPUT "${zero_header}" @ONLY
     CONTENT "// Generated by native/src/memory/CMakeLists.txt from ${zero_source}\n${zero_inc}")

set(memory_sources
    fault.cpp
    fcram_regions.cpp
    guest_memory.cpp
)
if (WIN32)
    list(APPEND memory_sources host_win32.cpp)
else()
    list(APPEND memory_sources host_posix.cpp)
endif()

mh4n_module(
    SOURCES ${memory_sources}
    THIRD_PARTY Threads::Threads
)
target_include_directories(mh4n_memory PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated")
