cmake_minimum_required(VERSION 3.22)

# This file is kept by CubeMX. Generated sources live in cmake/stm32cubemx.
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Debug)
endif()

set(CMAKE_PROJECT_NAME arcadestick)

# Target information used by the generic STM32 GCC toolchain.
set(CPU Cortex-M3)
set(FPU NO_FPU)
set(DSP NO_DSP)
set(MVE NO_MVE)
set(BYTE_ORDER Little-endian)

include(cmake/gcc-arm-none-eabi.cmake)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

project(${CMAKE_PROJECT_NAME} LANGUAGES C ASM)
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")

add_executable(${CMAKE_PROJECT_NAME})
add_subdirectory(cmake/stm32cubemx)

# DInput uses a project-owned composite class. Keep the CubeMX generated
# target, but replace only its application source list so regeneration cannot
# silently switch the firmware back to the single-interface HID class.
set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY SOURCES
  USB_DEVICE/Target/usbd_conf.c
  USB_DEVICE/App/usb_device_dinput.c
  USB_DEVICE/App/usbd_desc_dinput.c
  USB_DEVICE/App/usbd_dinput.c
  Core/Src/main.c
  Core/Src/dinput_app.c
  Core/Src/stm32f1xx_it.c
  Core/Src/stm32f1xx_hal_msp.c
  Core/Src/sysmem.c
  Core/Src/syscalls.c
  startup_stm32f103xb.s
)

# Options shared by CubeMX application, HAL and USB object libraries.
target_compile_options(stm32cubemx INTERFACE
  ${CPU_FLAGS}
  --specs=nano.specs
  -Wall
  -fstack-usage
)

# Keep reusable HAL/USB code compact in every configuration. Application
# sources still use -O0 in Debug so breakpoints and local variables behave well.
target_compile_options(STM32_Drivers PRIVATE -Os $<$<CONFIG:Debug>:-g3>)
target_compile_options(USB_Device_Library PRIVATE -Os $<$<CONFIG:Debug>:-g3>)

target_link_libraries(${CMAKE_PROJECT_NAME} stm32cubemx)
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE
  $<$<CONFIG:Debug>:-O0>
  $<$<CONFIG:Debug>:-g3>
  $<$<CONFIG:Release>:-Os>
)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
  REFLEKS_DINPUT
  APP_VECTOR_ADDRESS=0x08013000U
)
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES SUFFIX ".elf")

target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
  ${CPU_FLAGS}
  --specs=nano.specs
  --specs=nosys.specs
  -T${CMAKE_CURRENT_SOURCE_DIR}/STM32F103xx_FLASH.ld
  -Wl,--defsym=FIRMWARE_FLASH_ORIGIN=0x08013000
  -Wl,--defsym=FIRMWARE_FLASH_LENGTH=0x0000D000
  -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.map
  -Wl,--gc-sections
  -Wl,--print-memory-usage
  -static
)

set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
  LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/STM32F103xx_FLASH.ld
)

# XInput application image in App1.
set(XINPUT_TARGET arcadestick_xinput)
add_executable(${XINPUT_TARGET}
  USB_DEVICE/Target/usbd_conf.c
  USB_DEVICE/App/usb_device_xinput.c
  USB_DEVICE/App/usbd_desc_xinput.c
  USB_DEVICE/App/usbd_xinput.c
  Core/Src/main.c
  Core/Src/xinput_app.c
  Core/Src/stm32f1xx_it.c
  Core/Src/stm32f1xx_hal_msp.c
  Core/Src/sysmem.c
  Core/Src/syscalls.c
  startup_stm32f103xb.s
)

target_compile_definitions(${XINPUT_TARGET} PRIVATE
  REFLEKS_XINPUT
  APP_VECTOR_ADDRESS=0x08006000U
)
target_include_directories(${XINPUT_TARGET} PRIVATE USB_DEVICE/App Core/Inc)
target_compile_options(${XINPUT_TARGET} PRIVATE
  ${CPU_FLAGS}
  --specs=nano.specs
  -Wall
  -fstack-usage
  $<$<CONFIG:Debug>:-O0>
  $<$<CONFIG:Debug>:-g3>
  $<$<CONFIG:Release>:-Os>
)
target_link_libraries(${XINPUT_TARGET} PRIVATE
  stm32cubemx
  STM32_Drivers
  USB_Device_Library
  ${TOOLCHAIN_LINK_LIBRARIES}
)
set_target_properties(${XINPUT_TARGET} PROPERTIES SUFFIX ".elf")
target_link_options(${XINPUT_TARGET} PRIVATE
  ${CPU_FLAGS}
  --specs=nano.specs
  --specs=nosys.specs
  -T${CMAKE_CURRENT_SOURCE_DIR}/STM32F103xx_FLASH.ld
  -Wl,--defsym=FIRMWARE_FLASH_ORIGIN=0x08006000
  -Wl,--defsym=FIRMWARE_FLASH_LENGTH=0x0000D000
  -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${XINPUT_TARGET}.map
  -Wl,--gc-sections
  -Wl,--print-memory-usage
  -static
)
set_target_properties(${XINPUT_TARGET} PROPERTIES
  LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/STM32F103xx_FLASH.ld
  ADDITIONAL_CLEAN_FILES ${XINPUT_TARGET}.map
)

# USB DFU bootloader at the beginning of flash.
set(BOOTLOADER_TARGET arcadestick_bootloader)
add_executable(${BOOTLOADER_TARGET}
  USB_DEVICE/Target/usbd_conf.c
  USB_DEVICE/App/usb_device_dfu.c
  USB_DEVICE/App/usbd_desc_dfu.c
  USB_DEVICE/App/usbd_dfu_bootloader.c
  Core/Src/bootloader_main.c
  Core/Src/stm32f1xx_it.c
  Core/Src/stm32f1xx_hal_msp.c
  Core/Src/sysmem.c
  Core/Src/syscalls.c
  startup_stm32f103xb.s
)
target_compile_definitions(${BOOTLOADER_TARGET} PRIVATE REFLEKS_DFU)
target_include_directories(${BOOTLOADER_TARGET} PRIVATE USB_DEVICE/App Core/Inc)
target_compile_options(${BOOTLOADER_TARGET} PRIVATE
  ${CPU_FLAGS}
  --specs=nano.specs
  -Wall
  -Os
  -g3
  -fstack-usage
)
target_link_libraries(${BOOTLOADER_TARGET} PRIVATE
  stm32cubemx
  STM32_Drivers
  USB_Device_Library
  ${TOOLCHAIN_LINK_LIBRARIES}
)
set_target_properties(${BOOTLOADER_TARGET} PROPERTIES SUFFIX ".elf")
target_link_options(${BOOTLOADER_TARGET} PRIVATE
  ${CPU_FLAGS}
  --specs=nano.specs
  --specs=nosys.specs
  -T${CMAKE_CURRENT_SOURCE_DIR}/STM32F103xx_FLASH.ld
  -Wl,--defsym=FIRMWARE_FLASH_ORIGIN=0x08000000
  -Wl,--defsym=FIRMWARE_FLASH_LENGTH=0x00006000
  -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${BOOTLOADER_TARGET}.map
  -Wl,--gc-sections
  -Wl,--print-memory-usage
  -static
)
set_target_properties(${BOOTLOADER_TARGET} PROPERTIES
  LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/STM32F103xx_FLASH.ld
  ADDITIONAL_CLEAN_FILES ${BOOTLOADER_TARGET}.map
)

foreach(FIRMWARE_TARGET IN ITEMS
    ${CMAKE_PROJECT_NAME}
    ${XINPUT_TARGET}
    ${BOOTLOADER_TARGET})
  add_custom_command(TARGET ${FIRMWARE_TARGET} POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} -O ihex
            $<TARGET_FILE:${FIRMWARE_TARGET}>
            ${CMAKE_CURRENT_BINARY_DIR}/${FIRMWARE_TARGET}.hex
    COMMAND ${CMAKE_OBJCOPY} -O binary
            $<TARGET_FILE:${FIRMWARE_TARGET}>
            ${CMAKE_CURRENT_BINARY_DIR}/${FIRMWARE_TARGET}.bin
    COMMENT "Generating ${FIRMWARE_TARGET}.hex and ${FIRMWARE_TARGET}.bin"
  )
endforeach()

set(FULL_FIRMWARE_BIN ${CMAKE_CURRENT_BINARY_DIR}/arcadestick_full.bin)
set(FULL_FIRMWARE_HEX ${CMAKE_CURRENT_BINARY_DIR}/arcadestick_full.hex)
add_custom_command(
  OUTPUT ${FULL_FIRMWARE_BIN} ${FULL_FIRMWARE_HEX}
  COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass
          -File ${CMAKE_CURRENT_SOURCE_DIR}/tools/package_firmware.ps1
          -Bootloader ${CMAKE_CURRENT_BINARY_DIR}/${BOOTLOADER_TARGET}.bin
          -App1 ${CMAKE_CURRENT_BINARY_DIR}/${XINPUT_TARGET}.bin
          -App2 ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
          -Output ${FULL_FIRMWARE_BIN}
  COMMAND ${CMAKE_OBJCOPY} -I binary -O ihex
          --change-addresses=0x08000000
          ${FULL_FIRMWARE_BIN} ${FULL_FIRMWARE_HEX}
  DEPENDS
    ${BOOTLOADER_TARGET}
    ${XINPUT_TARGET}
    ${CMAKE_PROJECT_NAME}
    ${CMAKE_CURRENT_SOURCE_DIR}/tools/package_firmware.ps1
  COMMENT "Packaging bootloader, XInput App1 and DInput App2"
  VERBATIM
)
add_custom_target(arcadestick_package ALL
  DEPENDS ${FULL_FIRMWARE_BIN} ${FULL_FIRMWARE_HEX}
)
