mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-14 18:43:01 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 53b59a8a22 | |||
| 100bea644c | |||
| fe71592b68 |
@@ -50,6 +50,7 @@ endif()
|
||||
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(baseband)
|
||||
add_subdirectory(standalone)
|
||||
add_subdirectory(test)
|
||||
|
||||
# NOTE: Dependencies break if the .bin files aren't included in DEPENDS. WTF, CMake?
|
||||
@@ -88,7 +89,7 @@ add_custom_target(
|
||||
program-external-apps
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND ${PROJECT_SOURCE_DIR}/tools/copy_external_apps.sh
|
||||
DEPENDS program
|
||||
DEPENDS program standalone_apps
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
@@ -100,8 +101,9 @@ add_custom_command(
|
||||
COMMAND cp ${FIRMWARE_FILENAME} firmware_tar/FIRMWARE/portapack-mayhem_${VERSION_NOHASH}.bin
|
||||
COMMAND mkdir -p firmware_tar/APPS
|
||||
COMMAND cp application/*.ppma firmware_tar/APPS
|
||||
COMMAND cp standalone/*/*.ppmp firmware_tar/APPS
|
||||
COMMAND cd firmware_tar && tar -cvaf ../${PPFW_FILENAME} *
|
||||
DEPENDS firmware ${FIRMWARE_FILENAME}
|
||||
DEPENDS firmware ${FIRMWARE_FILENAME} standalone_apps
|
||||
# Dont use VERBATIM here as it prevents usage of globbing (*)
|
||||
# There shouldnt be any funny business in the filenames above :)
|
||||
)
|
||||
|
||||
@@ -325,6 +325,7 @@ set(CPPSRC
|
||||
# apps/ui_spectrum_painter.cpp
|
||||
apps/ui_ss_viewer.cpp
|
||||
apps/ui_sstvtx.cpp
|
||||
apps/ui_standalone_view.cpp
|
||||
apps/ui_subghzd.cpp
|
||||
# apps/ui_test.cpp
|
||||
apps/ui_text_editor.cpp
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program 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 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program 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 this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_standalone_view.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
void create_thread(int32_t (*fn)(void*), void* arg, size_t stack_size, int priority) {
|
||||
// TODO: collect memory on terminate, once this is used
|
||||
chThdCreateFromHeap(NULL, stack_size, priority, fn, arg);
|
||||
}
|
||||
|
||||
void fill_rectangle(int x, int y, int width, int height, uint16_t color) {
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle({x, y, width, height}, ui::Color(color));
|
||||
}
|
||||
|
||||
void* alloc(size_t size) {
|
||||
void* p = chHeapAlloc(0x0, size);
|
||||
if (p == nullptr)
|
||||
chDbgPanic("Out of Memory");
|
||||
return p;
|
||||
}
|
||||
|
||||
uint64_t get_switches_state_ulong() {
|
||||
return get_switches_state().to_ulong();
|
||||
}
|
||||
|
||||
standalone_application_api_t api = {
|
||||
/* .malloc = */ &alloc,
|
||||
/* .calloc = */ &calloc,
|
||||
/* .realloc = */ &realloc,
|
||||
/* .free = */ &chHeapFree,
|
||||
/* .create_thread = */ &create_thread,
|
||||
/* .fill_rectangle = */ &fill_rectangle,
|
||||
/* .swizzled_switches = */ &swizzled_switches,
|
||||
/* .get_switches_state = */ &get_switches_state_ulong,
|
||||
};
|
||||
|
||||
StandaloneView::StandaloneView(NavigationView& nav, std::unique_ptr<uint8_t[]> app_image)
|
||||
: nav_(nav), _app_image(std::move(app_image)) {
|
||||
get_application_information()->initialize(api);
|
||||
add_children({&dummy});
|
||||
}
|
||||
|
||||
void StandaloneView::focus() {
|
||||
dummy.focus();
|
||||
}
|
||||
|
||||
void StandaloneView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void StandaloneView::frame_sync() {
|
||||
// skip first refresh
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
} else {
|
||||
get_application_information()->on_event(1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
+15
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
* Copyright (C) 2024 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -19,22 +19,24 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_PACMAN_H__
|
||||
#define __UI_PACMAN_H__
|
||||
#ifndef __UI_STANDALONE_VIEW_H__
|
||||
#define __UI_STANDALONE_VIEW_H__
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "message.hpp"
|
||||
#include "standalone_app.hpp"
|
||||
|
||||
namespace ui::external_app::pacman {
|
||||
namespace ui {
|
||||
|
||||
class PacmanView : public View {
|
||||
class StandaloneView : public View {
|
||||
public:
|
||||
PacmanView(NavigationView& nav);
|
||||
StandaloneView(NavigationView& nav, std::unique_ptr<uint8_t[]> app_image);
|
||||
virtual ~StandaloneView() override { get_application_information()->shutdown(); }
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Pac-Man"; };
|
||||
std::string title() const override { return (const char*)get_application_information()->app_name; };
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void frame_sync();
|
||||
@@ -42,6 +44,10 @@ class PacmanView : public View {
|
||||
private:
|
||||
bool initialized = false;
|
||||
NavigationView& nav_;
|
||||
std::unique_ptr<uint8_t[]> _app_image;
|
||||
standalone_application_information_t* get_application_information() const {
|
||||
return reinterpret_cast<standalone_application_information_t*>(_app_image.get());
|
||||
}
|
||||
|
||||
Button dummy{
|
||||
{240, 0, 0, 0},
|
||||
@@ -54,6 +60,6 @@ class PacmanView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::pacman
|
||||
} // namespace ui
|
||||
|
||||
#endif /*__UI_PACMAN_H__*/
|
||||
#endif /*__UI_STANDALONE_VIEW_H__*/
|
||||
@@ -1,9 +1,5 @@
|
||||
set(EXTCPPSRC
|
||||
|
||||
#pacman
|
||||
external/pacman/main.cpp
|
||||
external/pacman/ui_pacman.cpp
|
||||
|
||||
#tetris
|
||||
external/tetris/main.cpp
|
||||
external/tetris/ui_tetris.cpp
|
||||
@@ -87,7 +83,6 @@ set(EXTCPPSRC
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
pacman
|
||||
afsk_rx
|
||||
calculator
|
||||
font_viewer
|
||||
|
||||
-7
@@ -23,7 +23,6 @@ MEMORY
|
||||
* Also need to consider processor memory map - reading 0xADxxxxxx generates a fault which may be better than unexpected behavior.
|
||||
* External app address ranges below must match those in python file "external_app_info.py".
|
||||
*/
|
||||
ram_external_app_pacman (rwx) : org = 0xADB00000, len = 32k
|
||||
ram_external_app_afsk_rx (rwx) : org = 0xADB10000, len = 32k
|
||||
ram_external_app_calculator (rwx) : org = 0xADB20000, len = 32k
|
||||
ram_external_app_font_viewer(rwx) : org = 0xADB30000, len = 32k
|
||||
@@ -47,12 +46,6 @@ MEMORY
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.external_app_pacman : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_pacman.application_information));
|
||||
*(*ui*external_app*pacman*);
|
||||
} > ram_external_app_pacman
|
||||
|
||||
.external_app_afsk_rx : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_afsk_rx.application_information));
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#include "ui_pacman.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
namespace ui::external_app::pacman {
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
// external code, so ignore warnings
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wreturn-type"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#include "playfield.hpp"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
PacmanView::PacmanView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
add_children({&dummy});
|
||||
}
|
||||
|
||||
void PacmanView::focus() {
|
||||
dummy.focus();
|
||||
}
|
||||
|
||||
void PacmanView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
static Playfield _game;
|
||||
static bool wait_for_button_release{false};
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
_game.Init();
|
||||
}
|
||||
|
||||
auto switches_raw = swizzled_switches() & ((1 << (int)Switch::Right) | (1 << (int)Switch::Left) | (1 << (int)Switch::Down) | (1 << (int)Switch::Up) | (1 << (int)Switch::Sel) | (1 << (int)Switch::Dfu));
|
||||
auto switches_debounced = get_switches_state().to_ulong();
|
||||
|
||||
// For the Select (Start/Pause) button, wait for release to avoid repeat
|
||||
uint8_t buttons_to_wait_for = (1 << (int)Switch::Sel);
|
||||
if (wait_for_button_release) {
|
||||
if ((switches_debounced & buttons_to_wait_for) == 0)
|
||||
wait_for_button_release = false;
|
||||
switches_debounced &= ~buttons_to_wait_for;
|
||||
} else {
|
||||
if (switches_debounced & buttons_to_wait_for)
|
||||
wait_for_button_release = true;
|
||||
}
|
||||
|
||||
// For the directional buttons, use the raw inputs for fastest response time
|
||||
but_RIGHT = (switches_raw & (1 << (int)Switch::Right)) != 0;
|
||||
but_LEFT = (switches_raw & (1 << (int)Switch::Left)) != 0;
|
||||
but_DOWN = (switches_raw & (1 << (int)Switch::Down)) != 0;
|
||||
but_UP = (switches_raw & (1 << (int)Switch::Up)) != 0;
|
||||
|
||||
// For the pause button, use the debounced input to avoid glitches, and OR in the value to make sure that we don't clear it before it's seen
|
||||
but_A |= (switches_debounced & (1 << (int)Switch::Sel)) != 0;
|
||||
|
||||
_game.Step();
|
||||
}
|
||||
|
||||
void PacmanView::frame_sync() {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::pacman
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "sd_card.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "ui_standalone_view.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -10,8 +11,10 @@ namespace ui {
|
||||
// iterates over all ppma-s, and if it is runnable on the current system, it'll call the callback, and pass info.
|
||||
/* static */ void ExternalItemsMenuLoader::load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback) {
|
||||
if (!callback) return;
|
||||
|
||||
if (sd_card::status() != sd_card::Status::Mounted)
|
||||
return;
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppma")) {
|
||||
auto filePath = apps_dir / entry.path();
|
||||
File app;
|
||||
@@ -43,6 +46,37 @@ namespace ui {
|
||||
.appLocation = application_information.menu_location};
|
||||
callback(info);
|
||||
}
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppmp")) {
|
||||
auto filePath = apps_dir / entry.path();
|
||||
File app;
|
||||
|
||||
auto openError = app.open(filePath);
|
||||
if (openError)
|
||||
continue;
|
||||
|
||||
standalone_application_information_t application_information = {};
|
||||
|
||||
auto readResult = app.read(&application_information, sizeof(standalone_application_information_t));
|
||||
if (!readResult)
|
||||
continue;
|
||||
|
||||
if (application_information.header_version < CURRENT_STANDALONE_APPLICATION_API_VERSION)
|
||||
continue;
|
||||
|
||||
// here the app is startable and good.
|
||||
std::string appshortname = filePath.filename().string();
|
||||
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppmp") {
|
||||
// Remove the ".ppmp" suffix
|
||||
appshortname = appshortname.substr(0, appshortname.size() - 5);
|
||||
}
|
||||
AppInfoConsole info{
|
||||
.appCallName = appshortname.c_str(),
|
||||
.appFriendlyName = reinterpret_cast<char*>(&application_information.app_name[0]),
|
||||
.appLocation = application_information.menu_location};
|
||||
|
||||
callback(info);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
|
||||
@@ -103,6 +137,44 @@ namespace ui {
|
||||
external_apps.push_back(gridItem);
|
||||
}
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppmp")) {
|
||||
auto filePath = apps_dir / entry.path();
|
||||
File app;
|
||||
|
||||
auto openError = app.open(filePath);
|
||||
if (openError)
|
||||
continue;
|
||||
|
||||
standalone_application_information_t application_information = {};
|
||||
|
||||
auto readResult = app.read(&application_information, sizeof(standalone_application_information_t));
|
||||
if (!readResult)
|
||||
continue;
|
||||
|
||||
if (application_information.menu_location != app_location)
|
||||
continue;
|
||||
|
||||
if (application_information.header_version > CURRENT_STANDALONE_APPLICATION_API_VERSION)
|
||||
continue;
|
||||
|
||||
GridItem gridItem = {};
|
||||
gridItem.text = reinterpret_cast<char*>(&application_information.app_name[0]);
|
||||
|
||||
gridItem.color = Color((uint16_t)application_information.icon_color);
|
||||
|
||||
auto dyn_bmp = DynamicBitmap<16, 16>{application_information.bitmap_data};
|
||||
gridItem.bitmap = dyn_bmp.bitmap();
|
||||
bitmaps.push_back(std::move(dyn_bmp));
|
||||
|
||||
gridItem.on_select = [&nav, app_location, filePath]() {
|
||||
if (!run_standalone_app(nav, filePath)) {
|
||||
nav.display_modal("Error", "The .ppmp file in your " + apps_dir.string() + "\nfolder can't be read. Please\nupdate your SD Card content.");
|
||||
}
|
||||
};
|
||||
|
||||
external_apps.push_back(gridItem);
|
||||
}
|
||||
|
||||
return external_apps;
|
||||
}
|
||||
|
||||
@@ -187,4 +259,39 @@ namespace ui {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool ExternalItemsMenuLoader::run_standalone_app(ui::NavigationView& nav, std::filesystem::path filePath) {
|
||||
File app;
|
||||
|
||||
auto openError = app.open(filePath);
|
||||
if (openError)
|
||||
return false;
|
||||
|
||||
auto app_image = std::make_unique<uint8_t[]>(app.size());
|
||||
|
||||
// read file in 512 byte chunks
|
||||
for (size_t file_read_index = 0; file_read_index < app.size(); file_read_index += std::filesystem::max_file_block_size) {
|
||||
auto bytes_to_read = std::filesystem::max_file_block_size;
|
||||
if (file_read_index + std::filesystem::max_file_block_size > app.size())
|
||||
bytes_to_read = app.size() - file_read_index;
|
||||
|
||||
auto readResult = app.read(&app_image[file_read_index], bytes_to_read);
|
||||
if (!readResult)
|
||||
return false;
|
||||
|
||||
if (readResult.value() < std::filesystem::max_file_block_size)
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t file_read_index = 0; file_read_index < app.size() / 4; file_read_index++) {
|
||||
uint32_t* ptr = reinterpret_cast<uint32_t*>(&app_image[file_read_index * 4]);
|
||||
|
||||
if (*ptr >= 0xADB10000 && *ptr < (0xADB10000 + 64 * 1024)) {
|
||||
*ptr = *ptr - 0xADB10000 + (uint32_t)app_image.get();
|
||||
}
|
||||
}
|
||||
|
||||
nav.push<StandaloneView>(std::move(app_image));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
#include "standalone_app.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
|
||||
@@ -57,6 +58,7 @@ class ExternalItemsMenuLoader {
|
||||
static std::vector<GridItem> load_external_items(app_location_t, NavigationView&);
|
||||
ExternalItemsMenuLoader() = delete;
|
||||
static bool run_external_app(ui::NavigationView&, std::filesystem::path);
|
||||
static bool run_standalone_app(ui::NavigationView&, std::filesystem::path);
|
||||
static void load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback);
|
||||
|
||||
private:
|
||||
|
||||
@@ -25,20 +25,13 @@
|
||||
#include "ch.h"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "spi_image.hpp"
|
||||
#include "standalone_app.hpp"
|
||||
|
||||
#define CURRENT_HEADER_VERSION 0x00000002
|
||||
#define MIN_HEADER_VERSION_FOR_CHECKSUM 0x00000002
|
||||
|
||||
typedef void (*externalAppEntry_t)(ui::NavigationView& nav);
|
||||
|
||||
enum app_location_t : uint32_t {
|
||||
UTILITIES = 0,
|
||||
RX,
|
||||
TX,
|
||||
DEBUG,
|
||||
HOME
|
||||
};
|
||||
|
||||
struct application_information_t {
|
||||
uint8_t* memory_location;
|
||||
externalAppEntry_t externalAppEntry;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program 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 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program 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 this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_STANDALONE_APP_H__
|
||||
#define __UI_STANDALONE_APP_H__
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
|
||||
#define CURRENT_STANDALONE_APPLICATION_API_VERSION 1
|
||||
|
||||
struct standalone_application_api_t {
|
||||
void* (*malloc)(size_t size);
|
||||
void* (*calloc)(size_t num, size_t size);
|
||||
void* (*realloc)(void* p, size_t size);
|
||||
void (*free)(void* p);
|
||||
void (*create_thread)(int32_t (*fn)(void*), void* arg, size_t stack_size, int priority);
|
||||
void (*fill_rectangle)(int x, int y, int width, int height, uint16_t color);
|
||||
uint8_t (*swizzled_switches)();
|
||||
uint64_t (*get_switches_state)();
|
||||
|
||||
// HOW TO extend this interface:
|
||||
// to keep everything backward compatible: add new fields at the end
|
||||
// and increment CURRENT_STANDALONE_APPLICATION_API_VERSION
|
||||
};
|
||||
|
||||
enum app_location_t : uint32_t {
|
||||
UTILITIES = 0,
|
||||
RX,
|
||||
TX,
|
||||
DEBUG,
|
||||
HOME
|
||||
};
|
||||
|
||||
struct standalone_application_information_t {
|
||||
uint32_t header_version;
|
||||
|
||||
uint8_t app_name[16];
|
||||
uint8_t bitmap_data[32];
|
||||
uint32_t icon_color;
|
||||
app_location_t menu_location;
|
||||
|
||||
/// @brief gets called once at application start
|
||||
void (*initialize)(const standalone_application_api_t& api);
|
||||
|
||||
/// @brief gets called when an event occurs
|
||||
/// @param events bitfield of events
|
||||
/// @note events are defined in firmware/application/event_m0.hpp
|
||||
void (*on_event)(const uint32_t& events);
|
||||
|
||||
/// @brief gets called once at application shutdown
|
||||
void (*shutdown)();
|
||||
};
|
||||
|
||||
#endif /*__UI_STANDALONE_APP_H__*/
|
||||
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(standalone_apps)
|
||||
|
||||
add_subdirectory(pacman)
|
||||
|
||||
add_custom_target(
|
||||
standalone_apps
|
||||
DEPENDS pacman_app
|
||||
)
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
#
|
||||
# Copyright (C) 2024 Bernd Herzog
|
||||
#
|
||||
# This file is part of PortaPack.
|
||||
#
|
||||
# This program 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 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program 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 this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
# Build global options
|
||||
# NOTE: Can be overridden externally.
|
||||
#
|
||||
|
||||
enable_language(C CXX ASM)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
project(pacman_app)
|
||||
|
||||
# Compiler options here.
|
||||
set(USE_OPT "-Os -g --specs=nano.specs")
|
||||
|
||||
# C specific options here (added to USE_OPT).
|
||||
set(USE_COPT "-std=gnu99")
|
||||
|
||||
# C++ specific options here (added to USE_OPT).
|
||||
check_cxx_compiler_flag("-std=c++20" cpp20_supported)
|
||||
if(cpp20_supported)
|
||||
set(USE_CPPOPT "-std=c++20")
|
||||
else()
|
||||
set(USE_CPPOPT "-std=c++17")
|
||||
endif()
|
||||
set(USE_CPPOPT "${USE_CPPOPT} -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized -fno-use-cxa-atexit")
|
||||
|
||||
# Enable this if you want the linker to remove unused code and data
|
||||
set(USE_LINK_GC yes)
|
||||
|
||||
# Linker extra options here.
|
||||
set(USE_LDOPT)
|
||||
|
||||
# Enable this if you want link time optimizations (LTO) - this flag affects chibios only
|
||||
set(USE_LTO no)
|
||||
|
||||
# If enabled, this option allows to compile the application in THUMB mode.
|
||||
set(USE_THUMB yes)
|
||||
|
||||
# Enable this if you want to see the full log while compiling.
|
||||
set(USE_VERBOSE_COMPILE no)
|
||||
|
||||
#
|
||||
# Build global options
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Architecture or project specific options
|
||||
#
|
||||
|
||||
# Enables the use of FPU on Cortex-M4 (no, softfp, hard).
|
||||
set(USE_FPU no)
|
||||
|
||||
#
|
||||
# Architecture or project specific options
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Project, sources and paths
|
||||
#
|
||||
|
||||
# Define linker script file here
|
||||
set(LDSCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/external.ld)
|
||||
|
||||
|
||||
# C sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
# setting.
|
||||
FILE(GLOB_RECURSE Sources_C ${CMAKE_CURRENT_LIST_DIR}/*.c)
|
||||
set(CSRC
|
||||
${Sources_C}
|
||||
)
|
||||
|
||||
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
# setting.
|
||||
FILE(GLOB_RECURSE Sources_CPP ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
|
||||
set(CPPSRC
|
||||
${Sources_CPP}
|
||||
)
|
||||
|
||||
# C sources to be compiled in ARM mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(ACSRC)
|
||||
|
||||
# C++ sources to be compiled in ARM mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(ACPPSRC)
|
||||
|
||||
# C sources to be compiled in THUMB mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(TCSRC)
|
||||
|
||||
# C sources to be compiled in THUMB mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(TCPPSRC)
|
||||
|
||||
# List ASM source files here
|
||||
set(ASMSRC)
|
||||
|
||||
set(INCDIR
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${COMMON}
|
||||
${COMMON}/../application
|
||||
${COMMON}/../application/hw
|
||||
)
|
||||
|
||||
#
|
||||
# Project, sources and paths
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Compiler settings
|
||||
#
|
||||
|
||||
# TODO: Entertain using MCU=cortex-m0.small-multiply for LPC43xx M0 core.
|
||||
# However, on GCC-ARM-Embedded 4.9 2015q2, it seems to produce non-functional
|
||||
# binaries.
|
||||
set(MCU cortex-m0)
|
||||
|
||||
# ARM-specific options here
|
||||
set(AOPT)
|
||||
|
||||
# THUMB-specific options here
|
||||
set(TOPT "-mthumb -DTHUMB")
|
||||
|
||||
# Define C warning options here
|
||||
set(CWARN "-Wall -Wextra -Wstrict-prototypes")
|
||||
|
||||
# Define C++ warning options here
|
||||
set(CPPWARN "-Wall -Wextra -Wno-psabi")
|
||||
|
||||
#
|
||||
# Compiler settings
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Start of default section
|
||||
#
|
||||
|
||||
# List all default C defines here, like -D_DEBUG=1
|
||||
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
|
||||
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
|
||||
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0")
|
||||
|
||||
# List all default ASM defines here, like -D_DEBUG=1
|
||||
set(DADEFS)
|
||||
|
||||
# List all default directories to look for include files here
|
||||
set(DINCDIR)
|
||||
|
||||
# List the default directory to look for the libraries here
|
||||
set(DLIBDIR)
|
||||
|
||||
# List all default libraries here
|
||||
set(DLIBS)
|
||||
|
||||
#
|
||||
# End of default section
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Start of user section
|
||||
#
|
||||
|
||||
# List all user C define here, like -D_DEBUG=1
|
||||
set(UDEFS)
|
||||
|
||||
# Define ASM defines here
|
||||
set(UADEFS)
|
||||
|
||||
# List all user directories here
|
||||
set(UINCDIR)
|
||||
|
||||
# List the user directory to look for the libraries here
|
||||
set(ULIBDIR)
|
||||
|
||||
# List all user libraries here
|
||||
set(ULIBS)
|
||||
|
||||
#
|
||||
# End of user defines
|
||||
##############################################################################
|
||||
|
||||
set(RULESPATH ${CHIBIOS}/os/ports/GCC/ARMCMx)
|
||||
include(${RULESPATH}/rules.cmake)
|
||||
|
||||
##############################################################################
|
||||
|
||||
|
||||
add_executable(${PROJECT_NAME}.elf ${CSRC} ${CPPSRC} ${ASMSRC})
|
||||
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
|
||||
add_definitions(${DEFS})
|
||||
include_directories(. ${INCDIR})
|
||||
link_directories(${LLIBDIR})
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||
|
||||
# redirect std lib memory allocations
|
||||
target_link_libraries(${PROJECT_NAME}.elf "-Wl,-wrap,_malloc_r")
|
||||
target_link_libraries(${PROJECT_NAME}.elf "-Wl,-wrap,_free_r")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.ppmp
|
||||
COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.ppmp
|
||||
DEPENDS ${PROJECT_NAME}.elf
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
${PROJECT_NAME}
|
||||
DEPENDS ${PROJECT_NAME}.ppmp
|
||||
)
|
||||
+5
-5
@@ -17,6 +17,7 @@
|
||||
/* MIT license, all text above must be included in any redistribution. */
|
||||
|
||||
// #include "ili9328.h"
|
||||
#include "standalone_app.hpp"
|
||||
|
||||
typedef uint16_t ushort;
|
||||
|
||||
@@ -46,16 +47,15 @@ uint16_t _paletteW[] =
|
||||
};
|
||||
|
||||
void drawIndexedmap(uint8_t* indexmap, int16_t x, uint16_t y) {
|
||||
ui::Painter painter;
|
||||
|
||||
byte i = 0;
|
||||
word color = (word)_paletteW[indexmap[0]];
|
||||
uint16_t color = (uint16_t)_paletteW[indexmap[0]];
|
||||
for (byte tmpY = 0; tmpY < 8; tmpY++) {
|
||||
byte width = 1;
|
||||
for (byte tmpX = 0; tmpX < 8; tmpX++) {
|
||||
word next_color = (word)_paletteW[indexmap[++i]];
|
||||
uint16_t next_color = (uint16_t)_paletteW[indexmap[++i]];
|
||||
if ((color != next_color && width >= 1) || tmpX == 7) {
|
||||
painter.draw_hline({x + tmpX - width + 1, y + tmpY}, width, ui::Color(color));
|
||||
_api->fill_rectangle(x + tmpX - width + 1, y + tmpY, width, 1, color);
|
||||
// painter.draw_hline({x + tmpX - width + 1, y + tmpY}, width, ui::Color(color));
|
||||
|
||||
color = next_color;
|
||||
width = 0;
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright (C) 2024 Bernd Herzog
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ram : org = 0xADB10000, len = 64k /* DO NOT CHANGE the address. We make the image relocateable on load. It needs to be 0xADB10000 */
|
||||
}
|
||||
|
||||
__ram_start__ = ORIGIN(ram);
|
||||
__ram_size__ = LENGTH(ram);
|
||||
__ram_end__ = __ram_start__ + __ram_size__;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0;
|
||||
_text = .;
|
||||
startup : ALIGN(16) SUBALIGN(16)
|
||||
{
|
||||
KEEP(*(.standalone_application_information));
|
||||
} > ram
|
||||
|
||||
constructors : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(SORT(.init_array.*)))
|
||||
KEEP(*(.init_array))
|
||||
PROVIDE(__init_array_end = .);
|
||||
} > ram
|
||||
|
||||
destructors : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
PROVIDE(__fini_array_start = .);
|
||||
KEEP(*(.fini_array))
|
||||
KEEP(*(SORT(.fini_array.*)))
|
||||
PROVIDE(__fini_array_end = .);
|
||||
} > ram
|
||||
|
||||
.text : ALIGN(16) SUBALIGN(16)
|
||||
{
|
||||
*(.text.startup.*)
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.glue_7t)
|
||||
*(.glue_7)
|
||||
*(.gcc*)
|
||||
} > ram
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
} > ram
|
||||
|
||||
.ARM.exidx : {
|
||||
PROVIDE(__exidx_start = .);
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
PROVIDE(__exidx_end = .);
|
||||
} > ram
|
||||
|
||||
.eh_frame_hdr :
|
||||
{
|
||||
*(.eh_frame_hdr)
|
||||
} > ram
|
||||
|
||||
.eh_frame : ONLY_IF_RO
|
||||
{
|
||||
*(.eh_frame)
|
||||
} > ram
|
||||
|
||||
.textalign : ONLY_IF_RO
|
||||
{
|
||||
. = ALIGN(8);
|
||||
} > ram
|
||||
|
||||
.bss ALIGN(4) : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_bss_start = .);
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_bss_end = .);
|
||||
} > ram
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
_textdata = _etext;
|
||||
|
||||
.data ALIGN(4) : AT (_textdata)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data = .);
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.ramtext)
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_edata = .);
|
||||
} > ram
|
||||
}
|
||||
|
||||
PROVIDE(end = .);
|
||||
_end = .;
|
||||
|
||||
+38
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
* Copyright (C) 2024 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -19,24 +19,15 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_pacman.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
#include "standalone_app.hpp"
|
||||
#include "pacman.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace ui::external_app::pacman {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<PacmanView>();
|
||||
}
|
||||
} // namespace ui::external_app::pacman
|
||||
const standalone_application_api_t* _api;
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_pacman.application_information"), used)) application_information_t _application_information_pacman = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000, // will be filled at compile time
|
||||
/*.externalAppEntry = */ ui::external_app::pacman::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
__attribute__((section(".standalone_application_information"), used)) standalone_application_information_t _standalone_application_information = {
|
||||
/*.header_version = */ CURRENT_STANDALONE_APPLICATION_API_VERSION,
|
||||
|
||||
/*.app_name = */ "Pac-Man",
|
||||
/*.bitmap_data = */ {
|
||||
@@ -73,10 +64,38 @@ __attribute__((section(".external_app.app_pacman.application_information"), used
|
||||
0x00,
|
||||
0x00,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.icon_color = 16 bit: 5R 6G 5B*/ 0x0000FFE0,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
/*.initialize_app = */ initialize,
|
||||
/*.on_event = */ on_event,
|
||||
/*.shutdown = */ shutdown,
|
||||
};
|
||||
}
|
||||
|
||||
/* Implementing abort() eliminates requirement for _getpid(), _kill(), _exit(). */
|
||||
extern "C" void abort() {
|
||||
while (true);
|
||||
}
|
||||
|
||||
// replace memory allocations to use heap from chibios
|
||||
extern "C" void* malloc(size_t size) {
|
||||
return _api->malloc(size);
|
||||
}
|
||||
extern "C" void* calloc(size_t num, size_t size) {
|
||||
return _api->calloc(num, size);
|
||||
}
|
||||
extern "C" void* realloc(void* p, size_t size) {
|
||||
return _api->realloc(p, size);
|
||||
}
|
||||
extern "C" void free(void* p) {
|
||||
_api->free(p);
|
||||
}
|
||||
|
||||
// redirect std lib memory allocations (sprintf, etc.)
|
||||
extern "C" void* __wrap__malloc_r(size_t size) {
|
||||
return _api->malloc(size);
|
||||
}
|
||||
extern "C" void __wrap__free_r(void* p) {
|
||||
_api->free(p);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program 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 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program 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 this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include "pacman.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
// external code, so ignore warnings
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wreturn-type"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#include "playfield.hpp"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
std::unique_ptr<Playfield> _playfield;
|
||||
|
||||
void initialize(const standalone_application_api_t& api) {
|
||||
_api = &api;
|
||||
}
|
||||
|
||||
void on_event(const uint32_t& events) {
|
||||
static bool wait_for_button_release{false};
|
||||
|
||||
if (!_playfield) {
|
||||
_playfield = std::make_unique<Playfield>();
|
||||
_playfield->Init();
|
||||
}
|
||||
|
||||
if (events & 1) {
|
||||
auto switches_raw = _api->swizzled_switches() & ((1 << (int)Switch::Right) | (1 << (int)Switch::Left) | (1 << (int)Switch::Down) | (1 << (int)Switch::Up) | (1 << (int)Switch::Sel) | (1 << (int)Switch::Dfu));
|
||||
auto switches_debounced = _api->get_switches_state();
|
||||
|
||||
// For the Select (Start/Pause) button, wait for release to avoid repeat
|
||||
uint8_t buttons_to_wait_for = (1 << (int)Switch::Sel);
|
||||
if (wait_for_button_release) {
|
||||
if ((switches_debounced & buttons_to_wait_for) == 0)
|
||||
wait_for_button_release = false;
|
||||
switches_debounced &= ~buttons_to_wait_for;
|
||||
} else {
|
||||
if (switches_debounced & buttons_to_wait_for)
|
||||
wait_for_button_release = true;
|
||||
}
|
||||
|
||||
// For the directional buttons, use the raw inputs for fastest response time
|
||||
but_RIGHT = (switches_raw & (1 << (int)Switch::Right)) != 0;
|
||||
but_LEFT = (switches_raw & (1 << (int)Switch::Left)) != 0;
|
||||
but_DOWN = (switches_raw & (1 << (int)Switch::Down)) != 0;
|
||||
but_UP = (switches_raw & (1 << (int)Switch::Up)) != 0;
|
||||
|
||||
// For the pause button, use the debounced input to avoid glitches, and OR in the value to make sure that we don't clear it before it's seen
|
||||
but_A |= (switches_debounced & (1 << (int)Switch::Sel)) != 0;
|
||||
|
||||
_playfield->Step();
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
_playfield.reset();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program 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 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program 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 this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PACMAN_H__
|
||||
#define __PACMAN_H__
|
||||
|
||||
#include "standalone_app.hpp"
|
||||
|
||||
void initialize(const standalone_application_api_t& api);
|
||||
void on_event(const uint32_t& events);
|
||||
void shutdown();
|
||||
|
||||
extern const standalone_application_api_t* _api;
|
||||
|
||||
#endif /*__PACMAN_H__*/
|
||||
+2
@@ -1,4 +1,6 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
/******************************************************************************/
|
||||
/* MAIN GAME VARIABLES */
|
||||
/******************************************************************************/
|
||||
@@ -44,6 +44,7 @@ do
|
||||
echo "Copying external applications to" $mountpoint
|
||||
mkdir -p $mountpoint/APPS
|
||||
cp application/*.ppma $mountpoint/APPS
|
||||
cp standalone/*/*.ppmp $mountpoint/APPS
|
||||
|
||||
echo "Unmounting" $mountpoint
|
||||
umount $mountpoint
|
||||
|
||||
+1
-1
Submodule hackrf updated: f5dd48a5bc...22c9ff10f9
Reference in New Issue
Block a user