mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-07 01:15:14 +01:00
Add VM task and source files to build system, wip: qor API
This commit is contained in:
parent
802f818735
commit
5104f9a6c0
5 changed files with 176 additions and 68 deletions
|
|
@ -42,12 +42,14 @@ set(OST_SRCS
|
||||||
system/ff/ff.c
|
system/ff/ff.c
|
||||||
system/ff/ffsystem.c
|
system/ff/ffsystem.c
|
||||||
system/ff/ff_stubs.c
|
system/ff/ff_stubs.c
|
||||||
|
chip32/chip32_vm.c
|
||||||
)
|
)
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/library
|
${CMAKE_CURRENT_SOURCE_DIR}/library
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/system
|
${CMAKE_CURRENT_SOURCE_DIR}/system
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/system/ff
|
${CMAKE_CURRENT_SOURCE_DIR}/system/ff
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/chip32
|
||||||
)
|
)
|
||||||
|
|
||||||
# ==================================================================================================
|
# ==================================================================================================
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
// CONSTANTS / DEFINES
|
// CONSTANTS / DEFINES
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
|
const uint8_t DEBUG_PIN = 1;
|
||||||
const uint8_t LED_PIN = 14; // GP 14
|
const uint8_t LED_PIN = 14;
|
||||||
|
|
||||||
const uint8_t LCD_DC = 8;
|
const uint8_t LCD_DC = 8;
|
||||||
const uint8_t LCD_CS = 9;
|
const uint8_t LCD_CS = 9;
|
||||||
|
|
@ -99,6 +99,10 @@ void ost_system_initialize()
|
||||||
gpio_init(LED_PIN);
|
gpio_init(LED_PIN);
|
||||||
gpio_set_dir(LED_PIN, GPIO_OUT);
|
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||||
|
|
||||||
|
//------------------- Init DEBUG PIN
|
||||||
|
gpio_init(DEBUG_PIN);
|
||||||
|
gpio_set_dir(DEBUG_PIN, GPIO_OUT);
|
||||||
|
|
||||||
//------------------- Init UART
|
//------------------- Init UART
|
||||||
|
|
||||||
// Set up our UART with the required speed.
|
// Set up our UART with the required speed.
|
||||||
|
|
@ -180,6 +184,25 @@ void system_putc(char ch)
|
||||||
uart_putc_raw(UART_ID, ch);
|
uart_putc_raw(UART_ID, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
clock_t clock()
|
||||||
|
{
|
||||||
|
return (clock_t)time_us_64() / 1000;
|
||||||
|
}
|
||||||
|
static uint64_t stopwatch_start_time;
|
||||||
|
static uint64_t stopwatch_end_time;
|
||||||
|
|
||||||
|
void ost_system_stopwatch_start()
|
||||||
|
{
|
||||||
|
stopwatch_start_time = clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ost_system_stopwatch_stop()
|
||||||
|
{
|
||||||
|
stopwatch_end_time = clock();
|
||||||
|
return (stopwatch_end_time - stopwatch_start_time);
|
||||||
|
}
|
||||||
|
|
||||||
int ost_hal_gpio_get(ost_hal_gpio_t gpio)
|
int ost_hal_gpio_get(ost_hal_gpio_t gpio)
|
||||||
{
|
{
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
|
@ -205,6 +228,9 @@ void ost_hal_gpio_set(ost_hal_gpio_t gpio, int value)
|
||||||
case OST_GPIO_DEBUG_LED:
|
case OST_GPIO_DEBUG_LED:
|
||||||
gpio_put(LED_PIN, value);
|
gpio_put(LED_PIN, value);
|
||||||
break;
|
break;
|
||||||
|
case OST_GPIO_DEBUG_PIN:
|
||||||
|
gpio_put(DEBUG_PIN, value);
|
||||||
|
break;
|
||||||
|
|
||||||
// Nothing to do for these inputes
|
// Nothing to do for these inputes
|
||||||
case OST_GPIO_ROTARY_A:
|
case OST_GPIO_ROTARY_A:
|
||||||
|
|
|
||||||
|
|
@ -6,47 +6,6 @@
|
||||||
#include "qor.h"
|
#include "qor.h"
|
||||||
#include "rotary-button.h"
|
#include "rotary-button.h"
|
||||||
|
|
||||||
#define RUN_TESTS 1
|
|
||||||
|
|
||||||
#ifndef RUN_TESTS
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
// Low level initialization, mainly platform stuff
|
|
||||||
// After this call, debug_printf *MUST* be available
|
|
||||||
ost_system_initialize();
|
|
||||||
debug_printf("\r\n [OST] Starting OpenStoryTeller tests: V%d.%d\r\n", 1, 0);
|
|
||||||
|
|
||||||
// File system access
|
|
||||||
filesystem_mount();
|
|
||||||
|
|
||||||
// Display
|
|
||||||
ost_display_initialize();
|
|
||||||
decompress();
|
|
||||||
|
|
||||||
// Audio
|
|
||||||
|
|
||||||
// Tasker
|
|
||||||
ost_tasker_init();
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
// Raspberry Pico SDK
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
#include "hardware/uart.h"
|
|
||||||
#include "hardware/spi.h"
|
|
||||||
#include "hardware/dma.h"
|
|
||||||
#include "hardware/irq.h"
|
|
||||||
#include "hardware/pio.h"
|
|
||||||
#include "hardware/clocks.h"
|
|
||||||
#include "pico.h"
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
|
|
||||||
#include "sdcard.h"
|
#include "sdcard.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
@ -54,13 +13,76 @@ int main(void)
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "audio_player.h"
|
#include "audio_player.h"
|
||||||
|
#include "chip32_vm.h"
|
||||||
|
|
||||||
void ost_hal_panic()
|
void ost_hal_panic()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
// SD CARD TASK
|
// VIRTUAL MACHINE TASK
|
||||||
|
// ===========================================================================================================
|
||||||
|
static qor_tcb_t VmTcb;
|
||||||
|
static uint32_t VmStack[4096];
|
||||||
|
|
||||||
|
static qor_mbox_t VmMailBox;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t ev;
|
||||||
|
} ost_vm_event_t;
|
||||||
|
ost_vm_event_t VmQueue[10];
|
||||||
|
|
||||||
|
static ost_vm_event_t VmEvent;
|
||||||
|
|
||||||
|
static uint8_t m_rom_data[16 * 1024];
|
||||||
|
static uint8_t m_ram_data[16 * 1024];
|
||||||
|
static chip32_ctx_t m_chip32_ctx;
|
||||||
|
|
||||||
|
uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t signum)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void VmTask(void *args)
|
||||||
|
{
|
||||||
|
|
||||||
|
// VM Initialize
|
||||||
|
m_chip32_ctx.stack_size = 512;
|
||||||
|
|
||||||
|
m_chip32_ctx.rom.mem = m_rom_data;
|
||||||
|
m_chip32_ctx.rom.addr = 0;
|
||||||
|
m_chip32_ctx.rom.size = sizeof(m_rom_data);
|
||||||
|
|
||||||
|
m_chip32_ctx.ram.mem = m_ram_data;
|
||||||
|
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
|
||||||
|
m_chip32_ctx.ram.size = sizeof(m_ram_data);
|
||||||
|
|
||||||
|
m_chip32_ctx.syscall = vm_syscall;
|
||||||
|
|
||||||
|
chip32_initialize(&m_chip32_ctx);
|
||||||
|
|
||||||
|
chip32_result_t run_result;
|
||||||
|
ost_vm_event_t *e = NULL;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
uint32_t res = qor_mbox_wait(&VmMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
|
||||||
|
|
||||||
|
if (res == QOR_MBOX_OK)
|
||||||
|
{
|
||||||
|
if (VmEvent.ev == 1)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
run_result = chip32_step(&m_chip32_ctx);
|
||||||
|
} while (run_result != VM_OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================================================
|
||||||
|
// FILE SYSTEM TASK
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
static qor_tcb_t AudioTcb;
|
static qor_tcb_t AudioTcb;
|
||||||
static uint32_t AudioStack[4096];
|
static uint32_t AudioStack[4096];
|
||||||
|
|
@ -82,14 +104,8 @@ static int dbg_state = 0;
|
||||||
static void audio_callback(void)
|
static void audio_callback(void)
|
||||||
{
|
{
|
||||||
dbg_state = 1 - dbg_state;
|
dbg_state = 1 - dbg_state;
|
||||||
gpio_put(1, dbg_state);
|
|
||||||
qor_mbox_notify(&AudioMailBox, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK);
|
qor_mbox_notify(&AudioMailBox, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK);
|
||||||
}
|
}
|
||||||
#include <time.h>
|
|
||||||
clock_t clock()
|
|
||||||
{
|
|
||||||
return (clock_t)time_us_64() / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_duration(uint32_t millisecondes)
|
void show_duration(uint32_t millisecondes)
|
||||||
{
|
{
|
||||||
|
|
@ -116,16 +132,12 @@ void AudioTask(void *args)
|
||||||
|
|
||||||
ost_audio_register_callback(audio_callback);
|
ost_audio_register_callback(audio_callback);
|
||||||
|
|
||||||
gpio_init(1);
|
|
||||||
gpio_set_dir(1, GPIO_OUT);
|
|
||||||
|
|
||||||
static bool onetime = true;
|
static bool onetime = true;
|
||||||
gpio_put(1, 0);
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n");
|
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n");
|
||||||
clock_t startTime = clock();
|
ost_system_stopwatch_start();
|
||||||
ost_audio_play("out2.wav");
|
ost_audio_play("out2.wav");
|
||||||
|
|
||||||
ost_audio_event_t *e = NULL;
|
ost_audio_event_t *e = NULL;
|
||||||
|
|
@ -145,9 +157,8 @@ void AudioTask(void *args)
|
||||||
|
|
||||||
} while (isPlaying);
|
} while (isPlaying);
|
||||||
|
|
||||||
|
uint32_t executionTime = ost_system_stopwatch_stop();
|
||||||
ost_audio_stop();
|
ost_audio_stop();
|
||||||
clock_t endTime = clock();
|
|
||||||
uint32_t executionTime = endTime - startTime;
|
|
||||||
|
|
||||||
debug_printf("\r\nPackets: %d\r\n", count);
|
debug_printf("\r\nPackets: %d\r\n", count);
|
||||||
show_duration(executionTime);
|
show_duration(executionTime);
|
||||||
|
|
@ -190,14 +201,15 @@ int main()
|
||||||
// 3. Filesystem / SDCard initialization
|
// 3. Filesystem / SDCard initialization
|
||||||
filesystem_mount();
|
filesystem_mount();
|
||||||
|
|
||||||
// 4. Initialize OS and threads
|
// 4. Initialize OS before all other OS calls
|
||||||
qor_init(125000000UL);
|
qor_init(125000000UL);
|
||||||
|
|
||||||
// qor_create_thread(&tcb1, UserTask_1, 2, "UserTask_1");
|
// 5. Initialize the tasks
|
||||||
// qor_create_thread(&tcb2, UserTask_2, 1, "UserTask_2");
|
qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), 2, "VmTask");
|
||||||
qor_create_thread(&AudioTcb, AudioTask, AudioStack, sizeof(AudioStack) / sizeof(AudioStack[0]), 3, "AudioTask"); ///< High priority for audio
|
qor_create_thread(&AudioTcb, AudioTask, AudioStack, sizeof(AudioStack) / sizeof(AudioStack[0]), 3, "AudioTask"); ///< High priority for audio
|
||||||
|
|
||||||
|
// 6. Start the operating system!
|
||||||
qor_start(&IdleTcb, IdleTask, IdleStack, 1024);
|
qor_start(&IdleTcb, IdleTask, IdleStack, 1024);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ extern "C"
|
||||||
OST_GPIO_ROTARY_A,
|
OST_GPIO_ROTARY_A,
|
||||||
OST_GPIO_ROTARY_B,
|
OST_GPIO_ROTARY_B,
|
||||||
OST_GPIO_DEBUG_LED,
|
OST_GPIO_DEBUG_LED,
|
||||||
|
OST_GPIO_DEBUG_PIN,
|
||||||
} ost_hal_gpio_t;
|
} ost_hal_gpio_t;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
@ -47,6 +48,9 @@ extern "C"
|
||||||
void system_putc(char ch);
|
void system_putc(char ch);
|
||||||
void ost_system_delay_ms(uint32_t delay);
|
void ost_system_delay_ms(uint32_t delay);
|
||||||
|
|
||||||
|
void ost_system_stopwatch_start();
|
||||||
|
uint32_t ost_system_stopwatch_stop();
|
||||||
|
|
||||||
void ost_audio_play(const char *filename);
|
void ost_audio_play(const char *filename);
|
||||||
void ost_audio_stop();
|
void ost_audio_stop();
|
||||||
int ost_audio_process();
|
int ost_audio_process();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef QOR_H
|
#ifndef QOR_H
|
||||||
#define QOR_H
|
#define QOR_H
|
||||||
|
|
||||||
|
#define QOR_VERSION "0.1"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
@ -23,24 +25,24 @@ typedef void (*thread_func_t)(void *args);
|
||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
qor_tcb_state_active,
|
qor_tcb_state_active, //!< Thread is active (and can be scheduled or is currently running)
|
||||||
qor_tcb_state_sleep
|
qor_tcb_state_sleep //!< Thread is wating for mailbox or sleep
|
||||||
} qor_tcb_state_t;
|
} qor_tcb_state_t;
|
||||||
|
|
||||||
|
typedef struct qor_mbox_t qor_mbox_t; //!< Foreward declaration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Thread Control Block
|
* @brief Thread Control Block
|
||||||
*
|
*
|
||||||
* IMPORTANT! keep the stack pointer on top, it is required by the task switch assembly
|
* IMPORTANT! keep the stack pointer on top, it is required by the task switch assembly
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct qor_mbox_t qor_mbox_t;
|
|
||||||
typedef struct TCB
|
typedef struct TCB
|
||||||
{
|
{
|
||||||
uint32_t *sp; //!< Stack pointer, valid for threads not running
|
uint32_t *sp; //!< Stack pointer, valid for threads not running, keep it on top
|
||||||
struct TCB *next; //!< Pointer to circular-linked-list of TCBs
|
struct TCB *next; //!< Pointer to circular-linked-list of TCBs
|
||||||
struct TCB *wait_next; //!< Next TCB in waiting list
|
struct TCB *wait_next; //!< Next TCB in waiting list
|
||||||
uint32_t stack_size;
|
uint32_t stack_size; //!< Stack size, in number of uint32_t
|
||||||
qor_tcb_state_t state; //!< TCB active or free
|
qor_tcb_state_t state; //!< TCB active or free
|
||||||
uint32_t wait_time; //!< Timeout for mbox maiting or sleep
|
uint32_t wait_time; //!< Timeout for mbox maiting or sleep
|
||||||
qor_mbox_t *mbox; //!< Pointer to mailbox on which the thread is blocked, NULL if not blocked
|
qor_mbox_t *mbox; //!< Pointer to mailbox on which the thread is blocked, NULL if not blocked
|
||||||
|
|
@ -51,21 +53,55 @@ typedef struct TCB
|
||||||
// Debug/traces
|
// Debug/traces
|
||||||
uint32_t stack_usage;
|
uint32_t stack_usage;
|
||||||
uint32_t *stack_bottom;
|
uint32_t *stack_bottom;
|
||||||
bool so; // stack overflow detected
|
bool so; //!< stack overflow detected
|
||||||
|
|
||||||
} qor_tcb_t;
|
} qor_tcb_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief QoRTOS initialization, call it before anything else
|
||||||
|
*
|
||||||
|
* @param scheduler_frequency_hz: CPU frequency in Hz
|
||||||
|
*/
|
||||||
void qor_init(uint32_t scheduler_frequency_hz);
|
void qor_init(uint32_t scheduler_frequency_hz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param tcb
|
||||||
|
* @param task
|
||||||
|
* @param stack
|
||||||
|
* @param stack_size
|
||||||
|
* @param priority
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
void qor_create_thread(qor_tcb_t *tcb, thread_func_t task, uint32_t *stack, uint32_t stack_size, uint8_t priority, const char *name);
|
void qor_create_thread(qor_tcb_t *tcb, thread_func_t task, uint32_t *stack, uint32_t stack_size, uint8_t priority, const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param idle_tcb
|
||||||
|
* @param idle_task
|
||||||
|
* @param idle_stack
|
||||||
|
* @param idle_stack_size
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
bool qor_start(qor_tcb_t *idle_tcb, thread_func_t idle_task, uint32_t *idle_stack, uint32_t idle_stack_size);
|
bool qor_start(qor_tcb_t *idle_tcb, thread_func_t idle_task, uint32_t *idle_stack, uint32_t idle_stack_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param sleep_duration_ms
|
||||||
|
*/
|
||||||
void qor_sleep(uint32_t sleep_duration_ms);
|
void qor_sleep(uint32_t sleep_duration_ms);
|
||||||
|
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
// MAILBOX API
|
// MAILBOX API
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
struct qor_mbox_t
|
struct qor_mbox_t
|
||||||
{
|
{
|
||||||
qor_tcb_t *head;
|
qor_tcb_t *head;
|
||||||
|
|
@ -76,6 +112,10 @@ struct qor_mbox_t
|
||||||
void **msgBuffer;
|
void **msgBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
|
|
@ -88,12 +128,36 @@ typedef struct
|
||||||
#define QOR_MBOX_ERROR 3
|
#define QOR_MBOX_ERROR 3
|
||||||
#define QOR_MBOX_FULL 4
|
#define QOR_MBOX_FULL 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param mbox
|
||||||
|
* @param msgBuffer
|
||||||
|
* @param maxCount
|
||||||
|
*/
|
||||||
void qor_mbox_init(qor_mbox_t *mbox, void **msgBuffer, uint32_t maxCount);
|
void qor_mbox_init(qor_mbox_t *mbox, void **msgBuffer, uint32_t maxCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param mbox
|
||||||
|
* @param msg
|
||||||
|
* @param wait_ms
|
||||||
|
* @return uint32_t
|
||||||
|
*/
|
||||||
uint32_t qor_mbox_wait(qor_mbox_t *mbox, void **msg, uint32_t wait_ms);
|
uint32_t qor_mbox_wait(qor_mbox_t *mbox, void **msg, uint32_t wait_ms);
|
||||||
|
|
||||||
#define QOR_MBOX_OPTION_SEND_FRONT 1
|
#define QOR_MBOX_OPTION_SEND_FRONT 1
|
||||||
#define QOR_MBOX_OPTION_SEND_BACK 2
|
#define QOR_MBOX_OPTION_SEND_BACK 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param mbox
|
||||||
|
* @param msg
|
||||||
|
* @param notifyOption
|
||||||
|
* @return uint32_t
|
||||||
|
*/
|
||||||
uint32_t qor_mbox_notify(qor_mbox_t *mbox, void *msg, uint32_t notifyOption);
|
uint32_t qor_mbox_notify(qor_mbox_t *mbox, void *msg, uint32_t notifyOption);
|
||||||
|
|
||||||
#endif // QOR_H
|
#endif // QOR_H
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue