Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e406a06f61 | |||
| 034867d2a7 | |||
| 76e3d2435e | |||
| 518bc054c4 | |||
| f8fb04db1b | |||
| 84b009c33e | |||
| b26f3bf775 | |||
| f398d62af2 | |||
| fa7d2fd32f | |||
| a274fb04a1 | |||
| a058476b42 | |||
| 2a9b17a539 | |||
| 62716c7dc4 | |||
| b22170b62c | |||
| b5e69abc83 | |||
| b722b8b9c5 | |||
| 38ffdac749 | |||
| 50793ac535 | |||
| 53a2fb046b | |||
| eacc03a38c | |||
| b59d716965 | |||
| 47fc02f05c | |||
| fe5d58b663 | |||
| ce1f06ccbf | |||
| 8cbb95b181 | |||
| b16211f148 | |||
| 2a472fc29f | |||
| 8d176925f8 | |||
| 22f5f1f5b2 |
@@ -0,0 +1,484 @@
|
|||||||
|
# SD Card Best Practices for RP2350 + FatFS
|
||||||
|
|
||||||
|
This document captures critical best practices for working with SD card operations in this project, based on lessons learned during development.
|
||||||
|
|
||||||
|
## 1. SPI Bus Contention (CRITICAL)
|
||||||
|
|
||||||
|
### ⚠️ CRITICAL: Display and SD Card Share the Same SPI Bus
|
||||||
|
|
||||||
|
**The display and SD card use the same SPI bus and CANNOT be accessed simultaneously.** Attempting to do so will cause the Pico to crash or behave unpredictably.
|
||||||
|
|
||||||
|
### Real-World Example: Game Launch Crash
|
||||||
|
|
||||||
|
The serial uploader originally crashed when launching games because:
|
||||||
|
|
||||||
|
1. `SerialUploader::launch_game()` writes file to SD (SPI)
|
||||||
|
2. Immediately calls `select_game_by_name()`
|
||||||
|
3. `LuaGame::load_script()` reads from SD (SPI)
|
||||||
|
4. **Meanwhile**, Core 1 is refreshing the display (also SPI)
|
||||||
|
5. **CRASH** due to simultaneous SPI access
|
||||||
|
|
||||||
|
### Solution: Wait for Display to Be Idle
|
||||||
|
|
||||||
|
Before any SD card operation that isn't already protected, ensure no display refresh is in progress:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// In main loop:
|
||||||
|
if (serial_uploader.wants_to_launch_game() && !is_refresh_in_progress()) {
|
||||||
|
// Safe to launch now - no SPI conflict with display
|
||||||
|
bool game_launched = serial_uploader.complete_launch();
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Patterns for Avoiding Contention
|
||||||
|
|
||||||
|
**Pattern 1: Check `is_refresh_in_progress()` before SD operations**
|
||||||
|
```cpp
|
||||||
|
if (!is_refresh_in_progress()) {
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
// SD card operations
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern 2: Split operations into "prepare" and "execute" phases**
|
||||||
|
```cpp
|
||||||
|
// Phase 1: Prepare (safe, no SD access)
|
||||||
|
void prepare_operation() {
|
||||||
|
state = READY_TO_EXECUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Execute (only when !is_refresh_in_progress())
|
||||||
|
bool execute_operation() {
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
// SD card operations here
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern 3: Keep main loop responsive**
|
||||||
|
```cpp
|
||||||
|
while (1) {
|
||||||
|
// Check if we need to do SD operation
|
||||||
|
if (needs_sd_operation && !is_refresh_in_progress()) {
|
||||||
|
perform_sd_operation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't sleep if waiting for SD operation window
|
||||||
|
bool stay_awake = pending_refresh || needs_sd_operation;
|
||||||
|
if (!stay_awake) {
|
||||||
|
__wfi(); // Sleep until interrupt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### When This Matters Most
|
||||||
|
|
||||||
|
- **Game loading**: Reading Lua scripts from SD during game launch
|
||||||
|
- **Serial uploads**: Writing files and immediately loading them
|
||||||
|
- **Save/load operations**: Writing game state to SD
|
||||||
|
- **Directory scanning**: Re-scanning games while display is active
|
||||||
|
|
||||||
|
### The Core Architecture
|
||||||
|
|
||||||
|
This project uses **dual-core display refresh**:
|
||||||
|
- **Core 0**: Main logic, input processing, game updates, SD card operations
|
||||||
|
- **Core 1**: Display refresh (writes framebuffer to display via SPI)
|
||||||
|
|
||||||
|
Core 1 runs asynchronously, so you must explicitly check `is_refresh_in_progress()` before SD operations.
|
||||||
|
|
||||||
|
## 2. SPI Speed Management
|
||||||
|
|
||||||
|
### Critical Rule: Always Set SD Card Speed Before Operations
|
||||||
|
|
||||||
|
The display and SD card share the same SPI bus but operate at different speeds:
|
||||||
|
- **Display**: 32 MHz (fast)
|
||||||
|
- **SD Card**: 12.5 MHz (slower, more reliable)
|
||||||
|
|
||||||
|
**ALWAYS wrap SD card operations with speed switching:**
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Save current speed and switch to SD card speed
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
|
||||||
|
// ... SD card operations here ...
|
||||||
|
// f_open(), f_write(), f_read(), f_readdir(), etc.
|
||||||
|
|
||||||
|
// Restore previous speed for display
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why This Matters
|
||||||
|
|
||||||
|
Running SD card operations at the wrong SPI speed causes:
|
||||||
|
- Unreliable reads/writes
|
||||||
|
- Corrupted data
|
||||||
|
- `FR_DISK_ERR` errors from FatFS
|
||||||
|
- Hardware-level protocol failures (0xFF data responses)
|
||||||
|
|
||||||
|
### Where Speed Switching is Already Handled
|
||||||
|
|
||||||
|
These functions handle SPI speed internally (you don't need to wrap them):
|
||||||
|
- `LuaGameLoader::register_all_games()`
|
||||||
|
- All functions in `sd_card.c` (low-level operations)
|
||||||
|
|
||||||
|
### Where You MUST Handle Speed Switching
|
||||||
|
|
||||||
|
Any code that calls FatFS functions directly:
|
||||||
|
- `f_open()`, `f_close()`
|
||||||
|
- `f_read()`, `f_write()`
|
||||||
|
- `f_opendir()`, `f_readdir()`, `f_closedir()`
|
||||||
|
- `f_stat()`, `f_mkdir()`, `f_unlink()`
|
||||||
|
- `f_getfree()`, `f_sync()`
|
||||||
|
|
||||||
|
## 2. SD Card Write Protocol
|
||||||
|
|
||||||
|
### The Data Response Polling Issue
|
||||||
|
|
||||||
|
When writing to SD card with `CMD24` (write single block), the data response token may not arrive immediately. You must poll for it:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// After sending data block and CRC:
|
||||||
|
uint8_t response = 0xFF;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
response = sd_card_transfer(0xFF);
|
||||||
|
if (response != 0xFF) {
|
||||||
|
break; // Got the response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if data was accepted
|
||||||
|
if ((response & 0x1F) != SD_DATA_ACCEPTED) {
|
||||||
|
// Write failed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why:** The SD card may need a few clock cycles before sending the data response token. Reading only once may return 0xFF (no response yet).
|
||||||
|
|
||||||
|
### Wait for Card Ready After CMD24
|
||||||
|
|
||||||
|
After sending the write command and before sending data:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// After CMD24 command:
|
||||||
|
uint8_t ready_byte;
|
||||||
|
do {
|
||||||
|
ready_byte = sd_card_transfer(0xFF);
|
||||||
|
timeout_count++;
|
||||||
|
if (timeout_count > 1000) {
|
||||||
|
return false; // Timeout
|
||||||
|
}
|
||||||
|
} while (ready_byte != 0xFF);
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures the card is ready to receive the data block.
|
||||||
|
|
||||||
|
## 3. FatFS Best Practices
|
||||||
|
|
||||||
|
### Always Check Return Codes
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
FRESULT fr = f_open(&fil, path, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
|
if (fr != FR_OK) {
|
||||||
|
printf("ERROR: f_open failed: %d\n", fr);
|
||||||
|
// Clean up and return
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Common FatFS error codes:
|
||||||
|
- `FR_OK (0)`: Success
|
||||||
|
- `FR_DISK_ERR (1)`: Low-level disk error (often SPI speed issue)
|
||||||
|
- `FR_NOT_READY (3)`: Card not initialized
|
||||||
|
- `FR_NO_FILE (4)`: File not found
|
||||||
|
- `FR_NO_PATH (5)`: Path not found
|
||||||
|
- `FR_EXIST (8)`: File/directory already exists
|
||||||
|
|
||||||
|
### Use FA_CREATE_ALWAYS to Overwrite
|
||||||
|
|
||||||
|
For rapid iteration (like our serial uploader):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
f_open(&fil, path, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
|
```
|
||||||
|
|
||||||
|
This overwrites existing files, perfect for development.
|
||||||
|
|
||||||
|
### Write in Chunks for Large Files
|
||||||
|
|
||||||
|
For files larger than 512 bytes, write in chunks:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const uint32_t CHUNK_SIZE = 512;
|
||||||
|
uint32_t total_written = 0;
|
||||||
|
|
||||||
|
while (total_written < total_size) {
|
||||||
|
uint32_t chunk_size = min(CHUNK_SIZE, total_size - total_written);
|
||||||
|
UINT bytes_written;
|
||||||
|
|
||||||
|
fr = f_write(&fil, buffer + total_written, chunk_size, &bytes_written);
|
||||||
|
if (fr != FR_OK || bytes_written != chunk_size) {
|
||||||
|
// Handle error
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_written += bytes_written;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Always Sync and Close
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
f_sync(&fil); // Ensure data is written to card
|
||||||
|
f_close(&fil); // Close file and update directory
|
||||||
|
```
|
||||||
|
|
||||||
|
Skipping `f_sync()` can lead to data loss if power is lost.
|
||||||
|
|
||||||
|
## 4. Memory Management
|
||||||
|
|
||||||
|
### Clean Up After Re-scanning
|
||||||
|
|
||||||
|
When re-scanning games (like after upload), clean up old data:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Clear game launcher entries
|
||||||
|
game_launcher->clear_games();
|
||||||
|
|
||||||
|
// Clear Lua game factory data
|
||||||
|
LuaGameLoader::clear_factory_data();
|
||||||
|
|
||||||
|
// Re-scan
|
||||||
|
LuaGameLoader::register_all_games(game_launcher);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why:** Without cleanup, you get duplicate registrations and memory leaks.
|
||||||
|
|
||||||
|
### Delete Old Game Instances Before Creating New Ones
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (selected_game) {
|
||||||
|
delete selected_game;
|
||||||
|
selected_game = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now create new game
|
||||||
|
selected_game = factory(width, height, renderer, gui, input_manager);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Critical for Lua games:** Each LuaGame has a Lua state. Not cleaning up the old one before creating a new one causes conflicts and freezes.
|
||||||
|
|
||||||
|
## 5. Debugging Tips
|
||||||
|
|
||||||
|
### Add Targeted Debug Output
|
||||||
|
|
||||||
|
When debugging SD operations, add prints at key points:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
printf("✓ Wrote %u bytes to %s\n", bytes_written, filepath);
|
||||||
|
```
|
||||||
|
|
||||||
|
But avoid spamming the console - it slows down operations significantly.
|
||||||
|
|
||||||
|
### Check Hardware Layer First
|
||||||
|
|
||||||
|
If FatFS returns `FR_DISK_ERR`, the issue is usually at the hardware level:
|
||||||
|
1. Check SPI speed (most common issue)
|
||||||
|
2. Check SD card write protection
|
||||||
|
3. Check physical SD card connection
|
||||||
|
4. Verify SD card is properly initialized
|
||||||
|
|
||||||
|
### Use Root Directory for Testing
|
||||||
|
|
||||||
|
When debugging writes, test with root directory first:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
FIL test_file;
|
||||||
|
if (f_open(&test_file, "/test.txt", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) {
|
||||||
|
printf("Root write OK\n");
|
||||||
|
f_close(&test_file);
|
||||||
|
f_unlink("/test.txt");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This isolates directory-related issues.
|
||||||
|
|
||||||
|
## 6. Common Pitfalls
|
||||||
|
|
||||||
|
### ❌ DON'T: Access SD Card During Display Refresh (MOST CRITICAL)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BAD - Will crash the Pico!
|
||||||
|
void launch_game() {
|
||||||
|
scan_games(); // Reads SD card
|
||||||
|
selected_game = create_game(); // Reads Lua script from SD
|
||||||
|
}
|
||||||
|
// Called directly without checking if display is refreshing
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ DO: Wait for Display to Be Idle
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// GOOD - Wait for safe window
|
||||||
|
if (!is_refresh_in_progress()) {
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
scan_games();
|
||||||
|
selected_game = create_game();
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ DON'T: Forget SPI Speed Management
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BAD - Will fail or be unreliable
|
||||||
|
f_open(&fil, "/games/test.lua", FA_WRITE);
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ DO: Always Switch Speeds
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// GOOD
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
f_open(&fil, "/games/test.lua", FA_WRITE);
|
||||||
|
// ... operations ...
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ DON'T: Assume Immediate Data Response
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BAD - May get 0xFF (no response yet)
|
||||||
|
uint8_t response = sd_card_transfer(0xFF);
|
||||||
|
if (response != 0x05) {
|
||||||
|
// Might incorrectly fail
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ DO: Poll for Data Response
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// GOOD - Poll until response arrives
|
||||||
|
uint8_t response = 0xFF;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
response = sd_card_transfer(0xFF);
|
||||||
|
if (response != 0xFF) break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ DON'T: Skip Error Checking
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BAD
|
||||||
|
f_write(&fil, buffer, size, &bytes_written);
|
||||||
|
f_close(&fil);
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ DO: Check Every Return Value
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// GOOD
|
||||||
|
if (f_write(&fil, buffer, size, &bytes_written) != FR_OK) {
|
||||||
|
printf("Write failed\n");
|
||||||
|
f_close(&fil);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ DON'T: Create New Games Without Cleanup
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BAD - Memory leak and Lua state conflicts
|
||||||
|
selected_game = new LuaGame(...);
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ DO: Clean Up First
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// GOOD
|
||||||
|
if (selected_game) {
|
||||||
|
delete selected_game;
|
||||||
|
selected_game = nullptr;
|
||||||
|
}
|
||||||
|
selected_game = new LuaGame(...);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Serial Upload Pattern
|
||||||
|
|
||||||
|
The serial uploader demonstrates the complete pattern:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool SerialUploader::write_file_to_sd() {
|
||||||
|
// 1. Validate input
|
||||||
|
if (!file_buffer || bytes_received == 0) return false;
|
||||||
|
|
||||||
|
// 2. Set SD card SPI speed
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
|
||||||
|
// 3. Ensure directory exists
|
||||||
|
f_mkdir("/games");
|
||||||
|
|
||||||
|
// 4. Open file (overwrite mode for iteration)
|
||||||
|
FIL fil;
|
||||||
|
FRESULT fr = f_open(&fil, filepath, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
|
if (fr != FR_OK) {
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Write in chunks
|
||||||
|
const uint32_t CHUNK_SIZE = 512;
|
||||||
|
uint32_t total_written = 0;
|
||||||
|
while (total_written < bytes_received) {
|
||||||
|
uint32_t chunk = min(CHUNK_SIZE, bytes_received - total_written);
|
||||||
|
UINT written;
|
||||||
|
|
||||||
|
fr = f_write(&fil, file_buffer + total_written, chunk, &written);
|
||||||
|
if (fr != FR_OK || written != chunk) {
|
||||||
|
f_close(&fil);
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_written += written;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Sync and close
|
||||||
|
f_sync(&fil);
|
||||||
|
f_close(&fil);
|
||||||
|
|
||||||
|
// 7. Restore display SPI speed
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. Testing Checklist
|
||||||
|
|
||||||
|
When implementing new SD card functionality:
|
||||||
|
|
||||||
|
- [ ] **SPI bus contention checked** - verify `!is_refresh_in_progress()` before SD operations
|
||||||
|
- [ ] SPI speed switching is in place
|
||||||
|
- [ ] All FatFS return codes are checked
|
||||||
|
- [ ] Files are properly closed after operations
|
||||||
|
- [ ] Memory is cleaned up (no leaks)
|
||||||
|
- [ ] Error messages are informative
|
||||||
|
- [ ] Tested with both small and large files
|
||||||
|
- [ ] Tested overwriting existing files
|
||||||
|
- [ ] Tested with non-existent directories
|
||||||
|
- [ ] Verified data integrity (read back after write)
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
The most important rules:
|
||||||
|
1. **⚠️ CRITICAL: Avoid SPI bus contention** - Check `!is_refresh_in_progress()` before SD operations (display and SD share SPI bus)
|
||||||
|
2. **Always manage SPI speed** around FatFS operations
|
||||||
|
3. **Poll for SD card responses** - don't assume immediate response
|
||||||
|
4. **Check error codes** on every operation
|
||||||
|
5. **Clean up memory** before creating new game instances
|
||||||
|
6. **Write in chunks** for large files
|
||||||
|
7. **Sync before closing** to ensure data is written
|
||||||
|
|
||||||
|
Following these practices will save hours of debugging SD card issues!
|
||||||
@@ -1,3 +1,12 @@
|
|||||||
build
|
build
|
||||||
!.vscode/*
|
!.vscode/*
|
||||||
build*/
|
build*/
|
||||||
|
*.o
|
||||||
|
*.o.d
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
CMakeFiles/
|
||||||
|
*.cmake
|
||||||
|
emulator/build/
|
||||||
|
.DS_Store
|
||||||
+9
-1
@@ -48,6 +48,7 @@ add_executable(basic1
|
|||||||
basic1.cpp
|
basic1.cpp
|
||||||
lib/input_manager.cpp
|
lib/input_manager.cpp
|
||||||
lib/game_launcher.cpp
|
lib/game_launcher.cpp
|
||||||
|
lib/serial_uploader.cpp
|
||||||
games/tic_tac_toe.cpp
|
games/tic_tac_toe.cpp
|
||||||
games/demo_game.cpp
|
games/demo_game.cpp
|
||||||
games/monopoly/monopoly_game.cpp
|
games/monopoly/monopoly_game.cpp
|
||||||
@@ -56,6 +57,7 @@ add_executable(basic1
|
|||||||
games/lua_bindings.cpp
|
games/lua_bindings.cpp
|
||||||
games/lua_game_loader.cpp
|
games/lua_game_loader.cpp
|
||||||
lib/st7796/st7796.c
|
lib/st7796/st7796.c
|
||||||
|
lib/st7789/st7789.c
|
||||||
lib/ft6336u/ft6336u.c
|
lib/ft6336u/ft6336u.c
|
||||||
lib/sd_card/sd_card.c
|
lib/sd_card/sd_card.c
|
||||||
display/low_level_render.cpp
|
display/low_level_render.cpp
|
||||||
@@ -91,7 +93,11 @@ pico_enable_stdio_usb(basic1 1)
|
|||||||
|
|
||||||
# Add the standard library to the build
|
# Add the standard library to the build
|
||||||
target_link_libraries(basic1
|
target_link_libraries(basic1
|
||||||
pico_stdlib)
|
pico_stdlib
|
||||||
|
hardware_dma
|
||||||
|
hardware_spi
|
||||||
|
hardware_pwm
|
||||||
|
)
|
||||||
|
|
||||||
# Add the standard include files to the build
|
# Add the standard include files to the build
|
||||||
target_include_directories(basic1 PRIVATE
|
target_include_directories(basic1 PRIVATE
|
||||||
@@ -102,6 +108,7 @@ target_include_directories(basic1 PRIVATE
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/lib/lua
|
${CMAKE_CURRENT_LIST_DIR}/lib/lua
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lib/fatfs/source
|
${CMAKE_CURRENT_LIST_DIR}/lib/fatfs/source
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lib/st7796
|
${CMAKE_CURRENT_LIST_DIR}/lib/st7796
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/lib/st7789
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lib/ft6336u
|
${CMAKE_CURRENT_LIST_DIR}/lib/ft6336u
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lib/sd_card
|
${CMAKE_CURRENT_LIST_DIR}/lib/sd_card
|
||||||
${CMAKE_CURRENT_LIST_DIR}/display
|
${CMAKE_CURRENT_LIST_DIR}/display
|
||||||
@@ -113,6 +120,7 @@ target_include_directories(basic1 PRIVATE
|
|||||||
target_link_libraries(basic1
|
target_link_libraries(basic1
|
||||||
hardware_spi
|
hardware_spi
|
||||||
hardware_i2c
|
hardware_i2c
|
||||||
|
hardware_pwm
|
||||||
pico_multicore
|
pico_multicore
|
||||||
m
|
m
|
||||||
)
|
)
|
||||||
|
|||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
# basic1 Lua Games Implementation Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Build a collection of games for the basic1 homemade console, implementing them sequentially based on complexity and learning value.
|
||||||
|
|
||||||
|
## Game List & Priority
|
||||||
|
|
||||||
|
### Tier 1: Foundational (Start Here)
|
||||||
|
These teach core mechanics and are quick wins.
|
||||||
|
|
||||||
|
1. **Flappy Bird** ✅ DONE
|
||||||
|
- Mechanics: Gravity, collision, scrolling obstacles
|
||||||
|
- Complexity: Low
|
||||||
|
- Time Estimate: 1-2 hours
|
||||||
|
- Learning: Gravity physics, obstacle spawning, game states
|
||||||
|
- API Usage: Circle/rect drawing, gravity math, simple event handling
|
||||||
|
|
||||||
|
2. **Breakout/Brick Breaker** ✅ DONE
|
||||||
|
- Mechanics: Ball physics, paddle control, grid destruction
|
||||||
|
- Complexity: Low-Medium
|
||||||
|
- Time Estimate: 2-3 hours
|
||||||
|
- Learning: Collision detection, grid management, ball reflection angles
|
||||||
|
- API Usage: Rect grids, ball physics (similar to Pong), score tracking
|
||||||
|
|
||||||
|
3. **Simon Says** ✅ DONE
|
||||||
|
- Mechanics: Color sequence memory, pattern growing
|
||||||
|
- Complexity: Low
|
||||||
|
- Time Estimate: 1-2 hours
|
||||||
|
- Learning: State management, sequence arrays, timing
|
||||||
|
- API Usage: Rect drawing, button input, frame timing
|
||||||
|
|
||||||
|
### Tier 2: Medium Difficulty (Build After Tier 1)
|
||||||
|
These combine mechanics and add complexity.
|
||||||
|
|
||||||
|
4. **Tetris** ✅ DONE
|
||||||
|
- Mechanics: Falling pieces, rotation, line clearing, gravity
|
||||||
|
- Complexity: Medium
|
||||||
|
- Time Estimate: 4-5 hours
|
||||||
|
- Learning: Grid-based movement, rotation matrices, collision
|
||||||
|
- API Usage: Rect grid system, complex state tracking, rapid updates
|
||||||
|
|
||||||
|
5. **Asteroids** ✅ DONE
|
||||||
|
- Mechanics: Spaceship control, rotation, projectiles, spawning
|
||||||
|
- Complexity: Medium
|
||||||
|
- Time Estimate: 3-4 hours
|
||||||
|
- Learning: Vector math, object pooling, spawn waves
|
||||||
|
- API Usage: Line drawing, rotation math, multiple entity management
|
||||||
|
|
||||||
|
### Tier 3: Advanced (Polish & Challenge)
|
||||||
|
These build on previous learnings.
|
||||||
|
|
||||||
|
6. **Memory/Matching Game** ✅ DONE
|
||||||
|
- Mechanics: Tile flipping, pair matching, timer
|
||||||
|
- Complexity: Medium
|
||||||
|
- Time Estimate: 2-3 hours
|
||||||
|
- Learning: Hidden state management, animations
|
||||||
|
- API Usage: Grid layout, simple animations
|
||||||
|
|
||||||
|
7. **Air Hockey** ✅ DONE
|
||||||
|
- Mechanics: 2-player, ball physics, faster pace than Pong
|
||||||
|
- Complexity: Medium
|
||||||
|
- Time Estimate: 2-3 hours
|
||||||
|
- Learning: Improved physics, AI paddle (optional)
|
||||||
|
- API Usage: Ball physics refinement, smooth controls
|
||||||
|
|
||||||
|
### Tier 4: Polish & Variants
|
||||||
|
Once core games work, extend with variants or features.
|
||||||
|
|
||||||
|
8. **2048** ✅ DONE
|
||||||
|
- Mechanics: Grid merging, swipe logic, exponential growth
|
||||||
|
- Complexity: Medium
|
||||||
|
- Time Estimate: 3-4 hours
|
||||||
|
- Learning: Swipe detection, grid animation, merge logic
|
||||||
|
- API Usage: Grid rendering, touch gesture interpretation
|
||||||
|
|
||||||
|
9. **Tic-Tac-Toe** ✅ DONE
|
||||||
|
- Mechanics: Grid selection, AI opponent (minimax)
|
||||||
|
- Complexity: Low-Medium
|
||||||
|
- Time Estimate: 2-3 hours
|
||||||
|
- Learning: Game tree search, AI logic
|
||||||
|
- API Usage: Grid UI, event handling, win detection
|
||||||
|
|
||||||
|
10. **Lunar Lander** ✅ DONE
|
||||||
|
- Mechanics: Thrust, rotation, gravity, soft landing
|
||||||
|
- Complexity: Medium-High
|
||||||
|
- Time Estimate: 3-4 hours
|
||||||
|
- Learning: Advanced physics, control feel
|
||||||
|
- API Usage: Vector math, acceleration, fuel management
|
||||||
|
|
||||||
|
## Implementation Strategy
|
||||||
|
|
||||||
|
**Per Game:**
|
||||||
|
1. Write game file in `/Users/adolforeyna/Projects/basic1/games/lua_examples/`
|
||||||
|
2. Test locally if possible, or prepare for SD card deployment
|
||||||
|
3. Document key mechanics in comments
|
||||||
|
4. Update this plan with completion status
|
||||||
|
5. Save daily progress to `memory/YYYY-MM-DD.md`
|
||||||
|
|
||||||
|
**Patterns to Reuse:**
|
||||||
|
- State machine (MENU → PLAYING → GAME_OVER)
|
||||||
|
- `game.vars` for all state
|
||||||
|
- `game.set_frame_updates(true)` for animation
|
||||||
|
- Touch input handling (left/right screen halves for 2-player, center for single)
|
||||||
|
|
||||||
|
**Testing Checklist (per game):**
|
||||||
|
- [ ] Initializes without crashes
|
||||||
|
- [ ] Main gameplay loop works
|
||||||
|
- [ ] Input is responsive
|
||||||
|
- [ ] Collision detection accurate
|
||||||
|
- [ ] Score/state tracking correct
|
||||||
|
- [ ] Game over condition triggers
|
||||||
|
- [ ] Restart from menu works
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Monochrome display = simple visuals, focus on mechanics
|
||||||
|
- Touch controls are the primary input
|
||||||
|
- Frame-based animation keeps things smooth
|
||||||
|
- Each game builds on previous learnings
|
||||||
|
- Start simple, layer complexity progressively
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Current Status:** All 10 games complete. Ready for SD card deployment.
|
||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
# Lua Game Upload Tool
|
||||||
|
|
||||||
|
Rapidly upload and execute Lua games on your RP2350 via USB serial for quick iteration during development!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Upload Lua game files directly to the SD card via USB serial
|
||||||
|
- Automatically launches the uploaded game immediately
|
||||||
|
- No need to manually swap SD cards or restart the device
|
||||||
|
- Perfect for rapid game development and testing
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Computer Side
|
||||||
|
- Python 3.x
|
||||||
|
- pyserial library: `pip install pyserial`
|
||||||
|
|
||||||
|
### RP2350 Side
|
||||||
|
- Firmware must be built with serial uploader support (already included)
|
||||||
|
- SD card inserted and formatted (FAT32)
|
||||||
|
- USB connection to computer
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### 1. Connect Your Device
|
||||||
|
|
||||||
|
Connect your RP2350 to your computer via USB. The device will appear as a serial port:
|
||||||
|
- **Linux/Mac**: Usually `/dev/ttyACM0` or `/dev/ttyUSB0`
|
||||||
|
- **Windows**: Usually `COM3`, `COM4`, etc.
|
||||||
|
|
||||||
|
### 2. Upload a Lua Game
|
||||||
|
|
||||||
|
Basic usage:
|
||||||
|
```bash
|
||||||
|
python upload_game.py my_game.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
Specify a custom serial port:
|
||||||
|
```bash
|
||||||
|
python upload_game.py my_game.lua /dev/ttyACM0 # Linux/Mac
|
||||||
|
python upload_game.py my_game.lua COM3 # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
The script will:
|
||||||
|
1. Read your Lua file
|
||||||
|
2. Upload it to the RP2350 via serial
|
||||||
|
3. Save it to `/games/<filename>.lua` on the SD card
|
||||||
|
4. Automatically launch the game!
|
||||||
|
|
||||||
|
### 3. View Available Serial Ports
|
||||||
|
|
||||||
|
Run the script without arguments to see available ports:
|
||||||
|
```bash
|
||||||
|
python upload_game.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit your game
|
||||||
|
vim snake.lua
|
||||||
|
|
||||||
|
# Upload and test (the game starts immediately!)
|
||||||
|
python upload_game.py snake.lua
|
||||||
|
|
||||||
|
# Make changes
|
||||||
|
vim snake.lua
|
||||||
|
|
||||||
|
# Upload again (instantly replaces and restarts)
|
||||||
|
python upload_game.py snake.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protocol Details
|
||||||
|
|
||||||
|
The tool uses a simple text-based protocol over USB serial:
|
||||||
|
|
||||||
|
```
|
||||||
|
UPLOAD <filename> <size_in_bytes>
|
||||||
|
<base64_encoded_file_content>
|
||||||
|
END
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```
|
||||||
|
OK <bytes_written>
|
||||||
|
LAUNCHED <game_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "No serial port found"
|
||||||
|
- Make sure your device is connected via USB
|
||||||
|
- Check if the device appears in your system (use `ls /dev/tty*` on Linux/Mac)
|
||||||
|
- On Linux, you may need to add your user to the `dialout` group: `sudo usermod -a -G dialout $USER`
|
||||||
|
|
||||||
|
### "Permission denied"
|
||||||
|
On Linux/Mac, you may need permissions to access the serial port:
|
||||||
|
```bash
|
||||||
|
sudo chmod 666 /dev/ttyACM0 # Quick fix
|
||||||
|
# OR
|
||||||
|
sudo usermod -a -G dialout $USER && newgrp dialout # Permanent fix
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Upload failed" or "ERROR" messages
|
||||||
|
- Make sure the SD card is properly inserted and formatted (FAT32)
|
||||||
|
- Check that there's enough space on the SD card
|
||||||
|
- Verify the Lua file is valid (syntax errors will appear when game launches)
|
||||||
|
|
||||||
|
### Game doesn't appear or launch
|
||||||
|
- Check the serial output from the RP2350 for error messages
|
||||||
|
- Ensure the Lua file has proper metadata comments at the top:
|
||||||
|
```lua
|
||||||
|
-- NAME: My Game
|
||||||
|
-- DESCRIPTION: A fun game
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips for Rapid Development
|
||||||
|
|
||||||
|
1. **Use a serial terminal** alongside uploads to see debug output:
|
||||||
|
```bash
|
||||||
|
screen /dev/ttyACM0 115200
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create a watch script** to auto-upload on file changes:
|
||||||
|
```bash
|
||||||
|
while inotifywait -e modify my_game.lua; do
|
||||||
|
python upload_game.py my_game.lua
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Use printf() in Lua** for debugging:
|
||||||
|
```lua
|
||||||
|
function update(event)
|
||||||
|
print("Event type: " .. event.type)
|
||||||
|
-- your code here
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Size Limits
|
||||||
|
|
||||||
|
- Maximum file size: 64 KB
|
||||||
|
- This should be plenty for most Lua games
|
||||||
|
- If you need more, consider splitting assets or code
|
||||||
|
|
||||||
|
## Related Files
|
||||||
|
|
||||||
|
- `lib/serial_uploader.h` - C++ header for serial upload handler
|
||||||
|
- `lib/serial_uploader.cpp` - C++ implementation
|
||||||
|
- `upload_game.py` - Python upload script
|
||||||
|
|
||||||
|
Happy game development! 🎮
|
||||||
+244
-40
@@ -40,12 +40,16 @@
|
|||||||
#include "pico/multicore.h"
|
#include "pico/multicore.h"
|
||||||
#include "board_config.h" // Board-specific pin configuration
|
#include "board_config.h" // Board-specific pin configuration
|
||||||
#include "sd_card.h"
|
#include "sd_card.h"
|
||||||
|
extern "C" {
|
||||||
|
#include "ff.h" // FatFS
|
||||||
|
}
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "display/low_level_render.h"
|
#include "display/low_level_render.h"
|
||||||
#include "display/low_level_display.h"
|
#include "display/low_level_display.h"
|
||||||
#include "display/low_level_display_epaper.h"
|
#include "display/low_level_display_epaper.h"
|
||||||
|
#include "display/low_level_display_st7796.h"
|
||||||
#include "display/low_level_touch.h"
|
#include "display/low_level_touch.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
@@ -54,6 +58,7 @@
|
|||||||
#include "demo_game.h"
|
#include "demo_game.h"
|
||||||
#include "monopoly_game.h"
|
#include "monopoly_game.h"
|
||||||
#include "lua_game_loader.h"
|
#include "lua_game_loader.h"
|
||||||
|
#include "serial_uploader.h"
|
||||||
|
|
||||||
|
|
||||||
// Binary info for RP2350 - ensures proper boot image structure
|
// Binary info for RP2350 - ensures proper boot image structure
|
||||||
@@ -83,7 +88,7 @@ void core1_entry() {
|
|||||||
while (1) {
|
while (1) {
|
||||||
// Wait for refresh request
|
// Wait for refresh request
|
||||||
if (refresh_requested && refresh_buffer && refresh_display) {
|
if (refresh_requested && refresh_buffer && refresh_display) {
|
||||||
refresh_in_progress = true;
|
// refresh_in_progress is already set by Core 0 to lock the buffer
|
||||||
|
|
||||||
// Get local copies for safe access
|
// Get local copies for safe access
|
||||||
LowLevelDisplay* display = refresh_display;
|
LowLevelDisplay* display = refresh_display;
|
||||||
@@ -95,7 +100,7 @@ void core1_entry() {
|
|||||||
|
|
||||||
// Clear flags
|
// Clear flags
|
||||||
refresh_requested = false;
|
refresh_requested = false;
|
||||||
refresh_in_progress = false;
|
refresh_in_progress = false; // Unlock buffer for Core 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Small delay to avoid busy-waiting
|
// Small delay to avoid busy-waiting
|
||||||
@@ -119,6 +124,10 @@ bool refresh_screen_async(const uint8_t *buffer, LowLevelDisplay* display) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lock the buffer immediately on Core 0 to prevent race condition
|
||||||
|
// Core 1 will unlock it (set to false) when done
|
||||||
|
refresh_in_progress = true;
|
||||||
|
|
||||||
// Queue refresh on Core 1
|
// Queue refresh on Core 1
|
||||||
refresh_buffer = buffer;
|
refresh_buffer = buffer;
|
||||||
refresh_display = display;
|
refresh_display = display;
|
||||||
@@ -148,6 +157,86 @@ struct GameConfig {
|
|||||||
bool debug_verbose; // Print debug messages
|
bool debug_verbose; // Print debug messages
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DISPLAY DIMMING CONFIGURATION
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Display dimming settings
|
||||||
|
#define DIM_TIMEOUT_MS (2 * 60 * 1000) // 2 minutes to dim
|
||||||
|
#define SLEEP_TIMEOUT_MS (10 * 60 * 1000) // 10 minutes to sleep
|
||||||
|
#define DIM_CHECK_INTERVAL_MS 10000 // Check every 10 seconds
|
||||||
|
|
||||||
|
// Display dimming state
|
||||||
|
static uint32_t last_interaction_time = 0; // Last time user interacted
|
||||||
|
static bool is_idle_2min_triggered = false; // Flag for 2min trigger
|
||||||
|
static bool is_idle_10min_triggered = false; // Flag for 10min trigger
|
||||||
|
static volatile bool dim_check_flag = false; // Flag set by timer to check dimming
|
||||||
|
static LowLevelDisplay* global_display = nullptr; // Global display pointer for timer callback
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update last interaction time and notify display driver
|
||||||
|
*
|
||||||
|
* Call this whenever the user interacts with the device (touch, button press).
|
||||||
|
* The display driver handles specific wake/restore logic.
|
||||||
|
*
|
||||||
|
* @param display Pointer to display interface
|
||||||
|
*/
|
||||||
|
static inline void record_user_interaction(LowLevelDisplay* display) {
|
||||||
|
last_interaction_time = to_ms_since_boot(get_absolute_time());
|
||||||
|
|
||||||
|
// Reset idle flags
|
||||||
|
is_idle_2min_triggered = false;
|
||||||
|
is_idle_10min_triggered = false;
|
||||||
|
|
||||||
|
// Notify display driver of interaction
|
||||||
|
display->on_user_interaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Timer callback to periodically check dimming status
|
||||||
|
*
|
||||||
|
* This alarm callback fires every DIM_CHECK_INTERVAL_MS milliseconds
|
||||||
|
* to wake the CPU from __wfi() and check if dimming should occur.
|
||||||
|
* Running in interrupt context, so just sets a flag for main loop.
|
||||||
|
*
|
||||||
|
* @param id Alarm ID (unused)
|
||||||
|
* @param user_data User data pointer (unused)
|
||||||
|
* @return Next alarm time (relative to current time)
|
||||||
|
*/
|
||||||
|
static int64_t dim_check_alarm_callback(alarm_id_t id, void *user_data) {
|
||||||
|
// Set flag to check dimming in main loop
|
||||||
|
dim_check_flag = true;
|
||||||
|
|
||||||
|
// Return interval in microseconds for next alarm
|
||||||
|
// Negative value means schedule relative to now
|
||||||
|
return -(DIM_CHECK_INTERVAL_MS * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if idle thresholds have been met and notify display driver
|
||||||
|
*
|
||||||
|
* Checks elapsed time since last interaction and calls the appropriate
|
||||||
|
* display driver methods (on_idle_2min or on_idle_10min).
|
||||||
|
*
|
||||||
|
* @param display Pointer to display interface
|
||||||
|
*/
|
||||||
|
static inline void check_and_apply_dimming(LowLevelDisplay* display) {
|
||||||
|
uint32_t current_time = to_ms_since_boot(get_absolute_time());
|
||||||
|
uint32_t elapsed = current_time - last_interaction_time;
|
||||||
|
|
||||||
|
// Check for 10 minute timeout (Sleep)
|
||||||
|
if (!is_idle_10min_triggered && elapsed >= SLEEP_TIMEOUT_MS) {
|
||||||
|
display->on_idle_10min();
|
||||||
|
is_idle_10min_triggered = true;
|
||||||
|
is_idle_2min_triggered = true; // Implicitly triggered
|
||||||
|
}
|
||||||
|
// Check for 2 minute timeout (Dim)
|
||||||
|
else if (!is_idle_2min_triggered && elapsed >= DIM_TIMEOUT_MS) {
|
||||||
|
display->on_idle_2min();
|
||||||
|
is_idle_2min_triggered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// INTERRUPT HANDLERS (Keep these minimal!)
|
// INTERRUPT HANDLERS (Keep these minimal!)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -284,6 +373,13 @@ int main()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable dirty rectangle optimization for ST7796 displays
|
||||||
|
if (display->get_type() == DISPLAY_TYPE_ST7796) {
|
||||||
|
LowLevelDisplayST7796* st7796_display = static_cast<LowLevelDisplayST7796*>(display);
|
||||||
|
st7796_display->enable_dirty_rect(true);
|
||||||
|
printf("Dirty rectangle optimization enabled (4 quadrants: TL/TR/BL/BR split)\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Launch Core 1 for display refresh handling
|
// Launch Core 1 for display refresh handling
|
||||||
printf("Launching Core 1 for display refresh...\n");
|
printf("Launching Core 1 for display refresh...\n");
|
||||||
multicore_launch_core1(core1_entry);
|
multicore_launch_core1(core1_entry);
|
||||||
@@ -346,6 +442,10 @@ int main()
|
|||||||
// Create GameLauncher
|
// Create GameLauncher
|
||||||
GameLauncher launcher(V_WIDTH, V_HEIGHT, &renderer, &gui, &input_manager);
|
GameLauncher launcher(V_WIDTH, V_HEIGHT, &renderer, &gui, &input_manager);
|
||||||
|
|
||||||
|
// Create SerialUploader for rapid game iteration
|
||||||
|
SerialUploader serial_uploader(&launcher);
|
||||||
|
printf("Serial uploader initialized\n");
|
||||||
|
|
||||||
// Register available games
|
// Register available games
|
||||||
launcher.register_game("Tic-Tac-Toe", "Classic 2-player game",
|
launcher.register_game("Tic-Tac-Toe", "Classic 2-player game",
|
||||||
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||||
@@ -364,10 +464,33 @@ int main()
|
|||||||
return new DemoGame(w, h, r, g, im);
|
return new DemoGame(w, h, r, g, im);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register Lua games from SD card /games directory
|
// Initialize SD card and mount filesystem
|
||||||
printf("\nScanning for Lua games on SD card...\n");
|
printf("\nInitializing SD card...\n");
|
||||||
int lua_games_found = LuaGameLoader::register_all_games(&launcher);
|
bool sd_available = sd_card_init_with_board_config();
|
||||||
printf("Found %d Lua game(s)\n", lua_games_found);
|
if (sd_available) {
|
||||||
|
printf("SD card initialized successfully\n");
|
||||||
|
|
||||||
|
// Mount FatFS filesystem
|
||||||
|
static FATFS fs;
|
||||||
|
FRESULT res = f_mount(&fs, "0:", 1); // Mount drive 0 immediately
|
||||||
|
if (res == FR_OK) {
|
||||||
|
printf("FatFS mounted successfully\n");
|
||||||
|
|
||||||
|
// Register Lua games from SD card /games directory
|
||||||
|
printf("Scanning for Lua games on SD card...\n");
|
||||||
|
int lua_games_found = LuaGameLoader::register_all_games(&launcher);
|
||||||
|
printf("Found %d Lua game(s)\n", lua_games_found);
|
||||||
|
} else {
|
||||||
|
printf("FatFS mount failed (error %d) - SD card may not be formatted\n", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore display SPI speed (SD card shares same SPI bus)
|
||||||
|
// SD card init sets SPI to 12.5 MHz, but display needs 32 MHz for fast refresh
|
||||||
|
spi_set_baudrate(DISPLAY_SPI_PORT, SPI_BAUDRATE);
|
||||||
|
printf("Display SPI speed restored to %d MHz\n", SPI_BAUDRATE / 1000000);
|
||||||
|
} else {
|
||||||
|
printf("SD card not available - skipping Lua game scan\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Draw launcher menu
|
// Draw launcher menu
|
||||||
launcher.draw();
|
launcher.draw();
|
||||||
@@ -406,13 +529,6 @@ int main()
|
|||||||
printf("Button interrupts enabled (falling edge = press)\n");
|
printf("Button interrupts enabled (falling edge = press)\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Test SD card and FatFS
|
|
||||||
// if (sd_card_init_with_board_config()) {
|
|
||||||
// sd_card_test_fatfs();
|
|
||||||
// } else {
|
|
||||||
// printf("SD Card initialization failed or no card present\n");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// REACTIVE GAME LOOP WITH DUAL-CORE REFRESH
|
// REACTIVE GAME LOOP WITH DUAL-CORE REFRESH
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
@@ -429,18 +545,69 @@ int main()
|
|||||||
uint32_t last_touch_time = 0;
|
uint32_t last_touch_time = 0;
|
||||||
bool pending_refresh = false; // Track if we have a pending refresh
|
bool pending_refresh = false; // Track if we have a pending refresh
|
||||||
|
|
||||||
|
// Initialize last interaction time to current time
|
||||||
|
last_interaction_time = to_ms_since_boot(get_absolute_time());
|
||||||
|
global_display = display;
|
||||||
|
|
||||||
|
// Set up repeating alarm to periodically check dimming status
|
||||||
|
// This wakes the CPU from __wfi() every DIM_CHECK_INTERVAL_MS
|
||||||
|
add_alarm_in_ms(DIM_CHECK_INTERVAL_MS, dim_check_alarm_callback, nullptr, true);
|
||||||
|
|
||||||
|
if (display->get_type() == DISPLAY_TYPE_ST7796) {
|
||||||
|
printf("Power saving: Dim at %d min, Sleep at %d min\n",
|
||||||
|
DIM_TIMEOUT_MS / 60000, SLEEP_TIMEOUT_MS / 60000);
|
||||||
|
} else {
|
||||||
|
printf("Power saving: Sleep at %d min\n", SLEEP_TIMEOUT_MS / 60000);
|
||||||
|
}
|
||||||
|
printf("Dimming check timer set to %d seconds\n", DIM_CHECK_INTERVAL_MS / 1000);
|
||||||
|
|
||||||
printf("\nEntering reactive game loop (Core 0 - input & logic)\n");
|
printf("\nEntering reactive game loop (Core 0 - input & logic)\n");
|
||||||
printf("Display refreshes handled by Core 1\n\n");
|
printf("Display refreshes handled by Core 1\n");
|
||||||
|
printf("Frame rate limited to 30 FPS (33.3ms per frame)\n\n");
|
||||||
|
|
||||||
Game* current_game = nullptr;
|
Game* current_game = nullptr;
|
||||||
uint32_t game_start_time = 0;
|
uint32_t game_start_time = 0;
|
||||||
|
|
||||||
|
// Frame rate limiting (30 FPS = 33.33ms per frame)
|
||||||
|
const uint32_t TARGET_FRAME_TIME_MS = 33; // 1000ms / 30fps ≈ 33ms
|
||||||
|
uint32_t last_frame_time = 0;
|
||||||
|
|
||||||
|
bool needs_refresh = false; // Track if screen needs redraw
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
// Sleep until interrupt wakes us up (very power efficient!)
|
// 0. Process serial uploads (for rapid game iteration)
|
||||||
__wfi(); // Wait For Interrupt - CPU sleeps until any interrupt occurs
|
serial_uploader.process();
|
||||||
|
|
||||||
|
// If serial uploader wants to launch a game, wait until it's safe (no display refresh)
|
||||||
|
if (serial_uploader.wants_to_launch_game() && !is_refresh_in_progress()) {
|
||||||
|
// Safe to launch now - no SPI conflict with display
|
||||||
|
bool game_launched = serial_uploader.complete_launch();
|
||||||
|
if (game_launched) {
|
||||||
|
// A new game was uploaded and launched - trigger redraw
|
||||||
|
needs_refresh = true;
|
||||||
|
current_game = launcher.get_selected_game();
|
||||||
|
// Note: game is already initialized by select_game_by_name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if we should sleep or stay awake for updates
|
||||||
|
bool stay_awake = false;
|
||||||
|
if (pending_refresh) stay_awake = true;
|
||||||
|
if (serial_uploader.wants_to_launch_game()) stay_awake = true; // Don't sleep while waiting to launch
|
||||||
|
|
||||||
|
if (launcher.is_game_selected()) {
|
||||||
|
Game* g = launcher.get_selected_game();
|
||||||
|
if (g && g->wants_frame_updates()) {
|
||||||
|
stay_awake = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stay_awake) {
|
||||||
|
// Sleep until interrupt wakes us up (very power efficient!)
|
||||||
|
__wfi(); // Wait For Interrupt - CPU sleeps until any interrupt occurs
|
||||||
|
}
|
||||||
|
|
||||||
InputEvent input = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
InputEvent input = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
||||||
bool needs_refresh = false;
|
|
||||||
|
|
||||||
// 1. Process button input first (higher priority)
|
// 1. Process button input first (higher priority)
|
||||||
input = input_manager.process_button_input();
|
input = input_manager.process_button_input();
|
||||||
@@ -457,6 +624,9 @@ int main()
|
|||||||
|
|
||||||
// 3. Process input based on current state
|
// 3. Process input based on current state
|
||||||
if (input.valid) {
|
if (input.valid) {
|
||||||
|
// Record user interaction for dimming timer
|
||||||
|
record_user_interaction(display);
|
||||||
|
|
||||||
// if debugging enabled, print input event
|
// if debugging enabled, print input event
|
||||||
if (config.debug_verbose) {
|
if (config.debug_verbose) {
|
||||||
printf("Input Event: type=%d, x=%d, y=%d, gesture=0x%02X, button=%d, pressure=%d\n",
|
printf("Input Event: type=%d, x=%d, y=%d, gesture=0x%02X, button=%d, pressure=%d\n",
|
||||||
@@ -491,7 +661,7 @@ int main()
|
|||||||
}
|
}
|
||||||
} else if (input.type == INPUT_TOUCH_UP) {
|
} else if (input.type == INPUT_TOUCH_UP) {
|
||||||
uint32_t now = to_ms_since_boot(get_absolute_time());
|
uint32_t now = to_ms_since_boot(get_absolute_time());
|
||||||
if (game_start_time > 0 && (now - game_start_time) > 2000) {
|
if (game_start_time > 0 && (now - game_start_time) > 10000) {
|
||||||
// Long press detected - return to menu
|
// Long press detected - return to menu
|
||||||
printf("Long press detected - returning to launcher\n");
|
printf("Long press detected - returning to launcher\n");
|
||||||
launcher.reset();
|
launcher.reset();
|
||||||
@@ -522,31 +692,65 @@ int main()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Redraw and queue async refresh on Core 1
|
if (launcher.is_game_selected()) {
|
||||||
if (needs_refresh || pending_refresh) {
|
// No input, but check if game wants continuous updates
|
||||||
// Clear buffer and redraw entire UI with updated state
|
current_game = launcher.get_selected_game();
|
||||||
memset(bit_buffer, 0, V_WIDTH * V_HEIGHT / 8);
|
if (current_game->wants_frame_updates()) {
|
||||||
|
// Only send frame tick if we're ready to draw the next frame
|
||||||
if (launcher.is_game_selected()) {
|
if (!is_refresh_in_progress()) {
|
||||||
current_game = launcher.get_selected_game();
|
InputEvent frame_tick = {INPUT_FRAME_TICK, 0, 0, 0, 0, 0, true};
|
||||||
current_game->draw();
|
needs_refresh = current_game->update(frame_tick) || needs_refresh;
|
||||||
} else {
|
|
||||||
launcher.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request async refresh (non-blocking - handled by Core 1)
|
|
||||||
bool refresh_started = refresh_screen_async(bit_buffer, display);
|
|
||||||
|
|
||||||
if (refresh_started) {
|
|
||||||
pending_refresh = false; // Refresh queued successfully
|
|
||||||
} else {
|
|
||||||
pending_refresh = true; // Core 1 busy, retry next iteration
|
|
||||||
if (config.debug_verbose) {
|
|
||||||
printf("Refresh pending - Core 1 still busy\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Core 0 continues immediately, Core 1 handles the refresh
|
// 4. Redraw and queue async refresh on Core 1 (with 30 FPS limiting)
|
||||||
|
if (needs_refresh || pending_refresh) {
|
||||||
|
// Check frame rate limiting
|
||||||
|
uint32_t current_time = to_ms_since_boot(get_absolute_time());
|
||||||
|
uint32_t time_since_last_frame = current_time - last_frame_time;
|
||||||
|
|
||||||
|
// Only proceed if enough time has passed since last frame
|
||||||
|
if (time_since_last_frame >= TARGET_FRAME_TIME_MS) {
|
||||||
|
// Only draw if Core 1 is finished with the buffer
|
||||||
|
if (!is_refresh_in_progress()) {
|
||||||
|
// Clear buffer and redraw entire UI with updated state
|
||||||
|
memset(bit_buffer, 0, V_WIDTH * V_HEIGHT / 8);
|
||||||
|
|
||||||
|
if (launcher.is_game_selected()) {
|
||||||
|
current_game = launcher.get_selected_game();
|
||||||
|
current_game->draw();
|
||||||
|
} else {
|
||||||
|
launcher.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request async refresh (non-blocking - handled by Core 1)
|
||||||
|
bool refresh_started = refresh_screen_async(bit_buffer, display);
|
||||||
|
|
||||||
|
if (refresh_started) {
|
||||||
|
pending_refresh = false; // Refresh queued successfully
|
||||||
|
last_frame_time = current_time; // Update frame time
|
||||||
|
} else {
|
||||||
|
pending_refresh = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pending_refresh = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Frame rate limit: skip this frame, wait for next opportunity
|
||||||
|
// Sleep for the remaining time to reach target frame time
|
||||||
|
uint32_t remaining_time = TARGET_FRAME_TIME_MS - time_since_last_frame;
|
||||||
|
if (remaining_time > 1) {
|
||||||
|
sleep_ms(remaining_time - 1); // -1 to account for overhead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Check if display should be dimmed due to inactivity
|
||||||
|
// This flag is set by timer alarm every DIM_CHECK_INTERVAL_MS
|
||||||
|
if (dim_check_flag) {
|
||||||
|
dim_check_flag = false;
|
||||||
|
check_and_apply_dimming(display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -6,6 +6,8 @@
|
|||||||
#include "diskio.h"
|
#include "diskio.h"
|
||||||
#include "sd_card.h"
|
#include "sd_card.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
/* Definitions of physical drive number for each drive */
|
/* Definitions of physical drive number for each drive */
|
||||||
#define DEV_SD 0 /* SD card */
|
#define DEV_SD 0 /* SD card */
|
||||||
@@ -21,7 +23,7 @@ DSTATUS disk_status (
|
|||||||
if (pdrv != DEV_SD) return STA_NOINIT;
|
if (pdrv != DEV_SD) return STA_NOINIT;
|
||||||
|
|
||||||
// Assume SD card is always initialized after sd_card_init() is called
|
// Assume SD card is always initialized after sd_card_init() is called
|
||||||
return 0; // OK
|
return 0; // OK - not write protected, not removed
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
@@ -76,7 +78,10 @@ DRESULT disk_write (
|
|||||||
if (pdrv != DEV_SD) return RES_PARERR;
|
if (pdrv != DEV_SD) return RES_PARERR;
|
||||||
|
|
||||||
for (UINT i = 0; i < count; i++) {
|
for (UINT i = 0; i < count; i++) {
|
||||||
if (!sd_card_write_block(sector + i, (uint8_t*)(buff + (i * 512)))) {
|
bool result = sd_card_write_block(sector + i, (uint8_t*)(buff + (i * 512)));
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
printf("ERROR disk_write failed at sector %lu\n", (unsigned long)(sector + i));
|
||||||
return RES_ERROR;
|
return RES_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,6 +136,11 @@ DRESULT disk_ioctl (
|
|||||||
res = RES_OK;
|
res = RES_OK;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CTRL_TRIM:
|
||||||
|
// Inform device that data on the block of sectors is no longer used (optional)
|
||||||
|
res = RES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
res = RES_PARERR;
|
res = RES_PARERR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,19 @@ public:
|
|||||||
// Optional: Backlight control (if supported)
|
// Optional: Backlight control (if supported)
|
||||||
virtual void set_backlight(bool on) { (void)on; }
|
virtual void set_backlight(bool on) { (void)on; }
|
||||||
|
|
||||||
|
// Optional: Brightness control (if supported)
|
||||||
|
// brightness: 0-100 (percentage), 0=off, 100=full brightness
|
||||||
|
virtual void set_brightness(uint8_t brightness) { (void)brightness; }
|
||||||
|
virtual uint8_t get_brightness() const { return 100; } // Default to full brightness
|
||||||
|
|
||||||
// Optional: Orientation control (not commonly needed for bitmap displays)
|
// Optional: Orientation control (not commonly needed for bitmap displays)
|
||||||
virtual void set_rotation(uint8_t rotation) { (void)rotation; }
|
virtual void set_rotation(uint8_t rotation) { (void)rotation; }
|
||||||
|
|
||||||
|
// Power saving hooks
|
||||||
|
virtual void on_idle_2min() {}
|
||||||
|
virtual void on_idle_10min() {}
|
||||||
|
virtual void on_user_interaction() {}
|
||||||
|
|
||||||
// Factory method - creates display based on type, using board_config.h for pins
|
// Factory method - creates display based on type, using board_config.h for pins
|
||||||
static LowLevelDisplay* create(DisplayType type, int width, int height);
|
static LowLevelDisplay* create(DisplayType type, int width, int height);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -141,3 +141,24 @@ void LowLevelDisplayEPaper::sleep() {
|
|||||||
printf("Putting e-paper display to sleep...\n");
|
printf("Putting e-paper display to sleep...\n");
|
||||||
EPD_4IN2_V2_Sleep();
|
EPD_4IN2_V2_Sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayEPaper::on_idle_2min() {
|
||||||
|
// E-paper doesn't dim
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayEPaper::on_idle_10min() {
|
||||||
|
if (!is_sleeping) {
|
||||||
|
sleep();
|
||||||
|
is_sleeping = true;
|
||||||
|
printf("E-Paper: Entered sleep mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayEPaper::on_user_interaction() {
|
||||||
|
if (is_sleeping) {
|
||||||
|
printf("E-Paper: Waking from sleep...\n");
|
||||||
|
init(); // Re-initialize to wake up
|
||||||
|
is_sleeping = false;
|
||||||
|
printf("E-Paper: Ready\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ public:
|
|||||||
void clear_display(); // Full clear with refresh
|
void clear_display(); // Full clear with refresh
|
||||||
void full_refresh(); // Force full screen refresh (slower but removes ghosting)
|
void full_refresh(); // Force full screen refresh (slower but removes ghosting)
|
||||||
void sleep(); // Put display in low power mode
|
void sleep(); // Put display in low power mode
|
||||||
|
|
||||||
|
// Power saving hooks
|
||||||
|
void on_idle_2min() override;
|
||||||
|
void on_idle_10min() override;
|
||||||
|
void on_user_interaction() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_sleeping = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOW_LEVEL_DISPLAY_EPAPER_H
|
#endif // LOW_LEVEL_DISPLAY_EPAPER_H
|
||||||
|
|||||||
@@ -69,11 +69,51 @@ void LowLevelDisplayST7789::refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LowLevelDisplayST7789::set_backlight(bool on) {
|
void LowLevelDisplayST7789::set_backlight(bool on) {
|
||||||
// TODO: Implement
|
set_brightness(on ? 100 : 0);
|
||||||
(void)on;
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7789::set_brightness(uint8_t brightness) {
|
||||||
|
st7789_set_brightness(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t LowLevelDisplayST7789::get_brightness() const {
|
||||||
|
return st7789_get_brightness();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LowLevelDisplayST7789::set_rotation(uint8_t rotation) {
|
void LowLevelDisplayST7789::set_rotation(uint8_t rotation) {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
(void)rotation;
|
(void)rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7789::on_idle_2min() {
|
||||||
|
if (!is_dimmed && !is_sleeping) {
|
||||||
|
saved_brightness = get_brightness();
|
||||||
|
set_brightness(5); // Dim to 5%
|
||||||
|
is_dimmed = true;
|
||||||
|
printf("ST7789: Dimmed to 5%%\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7789::on_idle_10min() {
|
||||||
|
if (!is_sleeping) {
|
||||||
|
st7789_sleep();
|
||||||
|
is_sleeping = true;
|
||||||
|
is_dimmed = true; // Sleep implies dimmed
|
||||||
|
printf("ST7789: Entered sleep mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7789::on_user_interaction() {
|
||||||
|
if (is_sleeping) {
|
||||||
|
st7789_wake();
|
||||||
|
// Restore brightness if we have a saved value, or default to 100
|
||||||
|
set_brightness(saved_brightness > 0 ? saved_brightness : 100);
|
||||||
|
is_sleeping = false;
|
||||||
|
is_dimmed = false;
|
||||||
|
printf("ST7789: Woke from sleep\n");
|
||||||
|
} else if (is_dimmed) {
|
||||||
|
set_brightness(saved_brightness > 0 ? saved_brightness : 100);
|
||||||
|
is_dimmed = false;
|
||||||
|
printf("ST7789: Restored brightness\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,17 +2,7 @@
|
|||||||
#define LOW_LEVEL_DISPLAY_ST7789_H
|
#define LOW_LEVEL_DISPLAY_ST7789_H
|
||||||
|
|
||||||
#include "low_level_display.h"
|
#include "low_level_display.h"
|
||||||
|
#include "st7789.h"
|
||||||
// ST7789 configuration structure (similar to ST7796)
|
|
||||||
struct st7789_config {
|
|
||||||
void* spi; // SPI instance
|
|
||||||
int gpio_din; // MOSI pin
|
|
||||||
int gpio_clk; // Clock pin
|
|
||||||
int gpio_cs; // Chip select pin
|
|
||||||
int gpio_dc; // Data/Command pin
|
|
||||||
int gpio_rst; // Reset pin
|
|
||||||
int gpio_bl; // Backlight pin
|
|
||||||
};
|
|
||||||
|
|
||||||
class LowLevelDisplayST7789 : public LowLevelDisplay {
|
class LowLevelDisplayST7789 : public LowLevelDisplay {
|
||||||
private:
|
private:
|
||||||
@@ -42,8 +32,22 @@ public:
|
|||||||
// Backlight control
|
// Backlight control
|
||||||
void set_backlight(bool on) override;
|
void set_backlight(bool on) override;
|
||||||
|
|
||||||
|
// Brightness control
|
||||||
|
void set_brightness(uint8_t brightness) override;
|
||||||
|
uint8_t get_brightness() const override;
|
||||||
|
|
||||||
// Orientation control
|
// Orientation control
|
||||||
void set_rotation(uint8_t rotation) override;
|
void set_rotation(uint8_t rotation) override;
|
||||||
|
|
||||||
|
// Power saving hooks
|
||||||
|
void on_idle_2min() override;
|
||||||
|
void on_idle_10min() override;
|
||||||
|
void on_user_interaction() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t saved_brightness = 100;
|
||||||
|
bool is_dimmed = false;
|
||||||
|
bool is_sleeping = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOW_LEVEL_DISPLAY_ST7789_H
|
#endif // LOW_LEVEL_DISPLAY_ST7789_H
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
#include "low_level_display_st7796.h"
|
#include "low_level_display_st7796.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <cstdlib> // For abs()
|
||||||
|
|
||||||
// RGB565 color definitions
|
// RGB565 color definitions
|
||||||
#define COLOR_BLACK 0x0000
|
#define COLOR_BLACK 0x0000
|
||||||
#define COLOR_WHITE 0xFFFF
|
#define COLOR_WHITE 0xFFFF
|
||||||
|
|
||||||
LowLevelDisplayST7796::LowLevelDisplayST7796(const st7796_config* cfg, int w, int h, bool invert)
|
LowLevelDisplayST7796::LowLevelDisplayST7796(const st7796_config* cfg, int w, int h, bool invert)
|
||||||
: config(cfg), width(w), height(h), initialized(false), rgb_buffer(nullptr), invert_color(invert) {
|
: config(cfg), width(w), height(h), initialized(false), rgb_buffer(nullptr), invert_color(invert),
|
||||||
|
prev_bit_buffer(nullptr), dirty_rect_enabled(true) {
|
||||||
|
for (int i = 0; i < MAX_DIRTY_RECTS; i++) {
|
||||||
|
dirty_rects[i].reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LowLevelDisplayST7796::~LowLevelDisplayST7796() {
|
LowLevelDisplayST7796::~LowLevelDisplayST7796() {
|
||||||
@@ -15,6 +21,10 @@ LowLevelDisplayST7796::~LowLevelDisplayST7796() {
|
|||||||
free(rgb_buffer);
|
free(rgb_buffer);
|
||||||
rgb_buffer = nullptr;
|
rgb_buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
if (prev_bit_buffer) {
|
||||||
|
free(prev_bit_buffer);
|
||||||
|
prev_bit_buffer = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LowLevelDisplayST7796::init() {
|
bool LowLevelDisplayST7796::init() {
|
||||||
@@ -49,19 +59,204 @@ void LowLevelDisplayST7796::draw_pixel(int x, int y, bool white) {
|
|||||||
|
|
||||||
void LowLevelDisplayST7796::draw_buffer(const uint8_t* bit_buffer) {
|
void LowLevelDisplayST7796::draw_buffer(const uint8_t* bit_buffer) {
|
||||||
if (!bit_buffer || !rgb_buffer) return;
|
if (!bit_buffer || !rgb_buffer) return;
|
||||||
// Convert 1-bit buffer to RGB565 using persistent buffer
|
|
||||||
for (int y = 0; y < height; y++) {
|
// Calculate buffer size
|
||||||
for (int x = 0; x < width; x++) {
|
size_t bit_buffer_size = (width * height + 7) / 8;
|
||||||
int byte_index = (y * width + x) / 8;
|
|
||||||
int bit_index = 7 - (x % 8);
|
// If dirty rectangle tracking is enabled and we have a previous buffer
|
||||||
bool pixel_white = (bit_buffer[byte_index] >> bit_index) & 0x01;
|
if (dirty_rect_enabled && prev_bit_buffer) {
|
||||||
bool out_white = invert_color ? !pixel_white : pixel_white;
|
// Reset all dirty rectangles
|
||||||
rgb_buffer[y * width + x] = out_white ? COLOR_WHITE : COLOR_BLACK;
|
for (int i = 0; i < MAX_DIRTY_RECTS; i++) {
|
||||||
|
dirty_rects[i].reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split screen into 4 quadrants
|
||||||
|
int mid_x = width / 2;
|
||||||
|
int mid_y = height / 2;
|
||||||
|
|
||||||
|
// Use bitwise XOR to quickly detect changed bytes
|
||||||
|
for (size_t byte_idx = 0; byte_idx < bit_buffer_size; byte_idx++) {
|
||||||
|
uint8_t diff = bit_buffer[byte_idx] ^ prev_bit_buffer[byte_idx];
|
||||||
|
|
||||||
|
// If this byte has changes
|
||||||
|
if (diff != 0) {
|
||||||
|
// Calculate pixel coordinates for this byte
|
||||||
|
int pixel_idx = byte_idx * 8;
|
||||||
|
int base_x = pixel_idx % width;
|
||||||
|
int base_y = pixel_idx / width;
|
||||||
|
|
||||||
|
// Check each changed bit/pixel in this byte
|
||||||
|
for (int bit = 0; bit < 8 && (pixel_idx + bit) < (width * height); bit++) {
|
||||||
|
if (diff & (0x80 >> bit)) {
|
||||||
|
int x = base_x + bit;
|
||||||
|
int y = base_y;
|
||||||
|
|
||||||
|
// Adjust coordinates if we wrapped to next row
|
||||||
|
if (x >= width) {
|
||||||
|
x -= width;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route to appropriate quadrant based on X and Y position
|
||||||
|
// Quadrant 0: Top-left (x < mid_x, y < mid_y)
|
||||||
|
// Quadrant 1: Top-right (x >= mid_x, y < mid_y)
|
||||||
|
// Quadrant 2: Bottom-left (x < mid_x, y >= mid_y)
|
||||||
|
// Quadrant 3: Bottom-right (x >= mid_x, y >= mid_y)
|
||||||
|
int rect_idx = ((y >= mid_y) ? 2 : 0) + ((x >= mid_x) ? 1 : 0);
|
||||||
|
dirty_rects[rect_idx].expand(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any valid dirty rectangles
|
||||||
|
int valid_rects = 0;
|
||||||
|
for (int i = 0; i < MAX_DIRTY_RECTS; i++) {
|
||||||
|
if (dirty_rects[i].is_valid) {
|
||||||
|
valid_rects++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no changes, skip the update
|
||||||
|
if (valid_rects == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimization: Merge adjacent rectangles if beneficial
|
||||||
|
// Check pairs of rectangles and merge if they overlap or are close
|
||||||
|
if (valid_rects >= 2) {
|
||||||
|
// Try merging adjacent quadrants
|
||||||
|
// Check top row (0,1) merge
|
||||||
|
if (dirty_rects[0].is_valid && dirty_rects[1].is_valid) {
|
||||||
|
int gap_x = dirty_rects[1].x0 - dirty_rects[0].x1;
|
||||||
|
int gap_y = abs(dirty_rects[0].y0 - dirty_rects[1].y0) + abs(dirty_rects[0].y1 - dirty_rects[1].y1);
|
||||||
|
|
||||||
|
if (gap_x < 30 && gap_y < 20) {
|
||||||
|
dirty_rects[0].merge(dirty_rects[1]);
|
||||||
|
dirty_rects[1].reset();
|
||||||
|
valid_rects--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check bottom row (2,3) merge
|
||||||
|
if (dirty_rects[2].is_valid && dirty_rects[3].is_valid) {
|
||||||
|
int gap_x = dirty_rects[3].x0 - dirty_rects[2].x1;
|
||||||
|
int gap_y = abs(dirty_rects[2].y0 - dirty_rects[3].y0) + abs(dirty_rects[2].y1 - dirty_rects[3].y1);
|
||||||
|
|
||||||
|
if (gap_x < 30 && gap_y < 20) {
|
||||||
|
dirty_rects[2].merge(dirty_rects[3]);
|
||||||
|
dirty_rects[3].reset();
|
||||||
|
valid_rects--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check left column (0,2) merge
|
||||||
|
if (dirty_rects[0].is_valid && dirty_rects[2].is_valid) {
|
||||||
|
int gap_y = dirty_rects[2].y0 - dirty_rects[0].y1;
|
||||||
|
int gap_x = abs(dirty_rects[0].x0 - dirty_rects[2].x0) + abs(dirty_rects[0].x1 - dirty_rects[2].x1);
|
||||||
|
|
||||||
|
if (gap_y < 30 && gap_x < 20) {
|
||||||
|
dirty_rects[0].merge(dirty_rects[2]);
|
||||||
|
dirty_rects[2].reset();
|
||||||
|
valid_rects--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check right column (1,3) merge
|
||||||
|
if (dirty_rects[1].is_valid && dirty_rects[3].is_valid) {
|
||||||
|
int gap_y = dirty_rects[3].y0 - dirty_rects[1].y1;
|
||||||
|
int gap_x = abs(dirty_rects[1].x0 - dirty_rects[3].x0) + abs(dirty_rects[1].x1 - dirty_rects[3].x1);
|
||||||
|
|
||||||
|
if (gap_y < 30 && gap_x < 20) {
|
||||||
|
dirty_rects[1].merge(dirty_rects[3]);
|
||||||
|
dirty_rects[3].reset();
|
||||||
|
valid_rects--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final pass: merge any remaining valid rectangles if they're very close
|
||||||
|
for (int i = 0; i < MAX_DIRTY_RECTS - 1; i++) {
|
||||||
|
if (!dirty_rects[i].is_valid) continue;
|
||||||
|
for (int j = i + 1; j < MAX_DIRTY_RECTS; j++) {
|
||||||
|
if (!dirty_rects[j].is_valid) continue;
|
||||||
|
|
||||||
|
DirtyRect merged = dirty_rects[i];
|
||||||
|
merged.merge(dirty_rects[j]);
|
||||||
|
|
||||||
|
int combined_area = dirty_rects[i].get_area() + dirty_rects[j].get_area();
|
||||||
|
int merged_area = merged.get_area();
|
||||||
|
|
||||||
|
// Merge if the combined overhead is less than 40%
|
||||||
|
if (merged_area < combined_area * 1.4f) {
|
||||||
|
dirty_rects[i] = merged;
|
||||||
|
dirty_rects[j].reset();
|
||||||
|
valid_rects--;
|
||||||
|
break; // Move to next i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy current buffer to previous buffer for next frame comparison
|
||||||
|
memcpy(prev_bit_buffer, bit_buffer, bit_buffer_size);
|
||||||
|
|
||||||
|
// Process each valid dirty rectangle
|
||||||
|
for (int rect_idx = 0; rect_idx < MAX_DIRTY_RECTS; rect_idx++) {
|
||||||
|
if (!dirty_rects[rect_idx].is_valid) continue;
|
||||||
|
|
||||||
|
DirtyRect& rect = dirty_rects[rect_idx];
|
||||||
|
|
||||||
|
// Convert only the dirty rectangle region to RGB565
|
||||||
|
for (int y = rect.y0; y <= rect.y1; y++) {
|
||||||
|
for (int x = rect.x0; x <= rect.x1; x++) {
|
||||||
|
int byte_index = (y * width + x) / 8;
|
||||||
|
int bit_index = 7 - (x % 8);
|
||||||
|
bool pixel_white = (bit_buffer[byte_index] >> bit_index) & 0x01;
|
||||||
|
bool out_white = invert_color ? !pixel_white : pixel_white;
|
||||||
|
rgb_buffer[y * width + x] = out_white ? COLOR_WHITE : COLOR_BLACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw only this dirty rectangle
|
||||||
|
st7796_set_window(rect.x0, rect.y0, rect.x1, rect.y1);
|
||||||
|
|
||||||
|
// Calculate size of dirty region
|
||||||
|
int dirty_width = rect.get_width();
|
||||||
|
int dirty_height = rect.get_height();
|
||||||
|
|
||||||
|
// Write only the dirty rectangle pixels
|
||||||
|
// We need to extract rows from the full rgb_buffer
|
||||||
|
for (int row = 0; row < dirty_height; row++) {
|
||||||
|
int buffer_offset = (rect.y0 + row) * width + rect.x0;
|
||||||
|
st7796_write_raw((const uint8_t*)&rgb_buffer[buffer_offset], dirty_width * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Full screen update (original behavior)
|
||||||
|
// Convert 1-bit buffer to RGB565 using persistent buffer
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int byte_index = (y * width + x) / 8;
|
||||||
|
int bit_index = 7 - (x % 8);
|
||||||
|
bool pixel_white = (bit_buffer[byte_index] >> bit_index) & 0x01;
|
||||||
|
bool out_white = invert_color ? !pixel_white : pixel_white;
|
||||||
|
rgb_buffer[y * width + x] = out_white ? COLOR_WHITE : COLOR_BLACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Draw entire buffer at once
|
||||||
|
st7796_set_cursor(0, 0);
|
||||||
|
// Use raw write for speed.
|
||||||
|
// Since we only use 0x0000 (Black) and 0xFFFF (White), endianness doesn't matter.
|
||||||
|
// 0x0000 -> 0x00, 0x00 (LE) -> Display sees 0x00, 0x00 (0x0000 correct)
|
||||||
|
// 0xFFFF -> 0xFF, 0xFF (LE) -> Display sees 0xFF, 0xFF (0xFFFF correct)
|
||||||
|
st7796_write_raw((const uint8_t*)rgb_buffer, width * height * 2);
|
||||||
|
|
||||||
|
// If dirty rect is enabled, store this buffer for next comparison
|
||||||
|
if (dirty_rect_enabled && prev_bit_buffer) {
|
||||||
|
memcpy(prev_bit_buffer, bit_buffer, bit_buffer_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Draw entire buffer at once
|
|
||||||
st7796_set_cursor(0, 0);
|
|
||||||
st7796_write(rgb_buffer, width * height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LowLevelDisplayST7796::refresh() {
|
void LowLevelDisplayST7796::refresh() {
|
||||||
@@ -69,9 +264,24 @@ void LowLevelDisplayST7796::refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LowLevelDisplayST7796::set_backlight(bool on) {
|
void LowLevelDisplayST7796::set_backlight(bool on) {
|
||||||
// ST7796 driver doesn't have backlight control yet
|
// Use brightness control: on = 100%, off = 0%
|
||||||
// TODO: Add GPIO control for backlight pin
|
st7796_set_brightness(on ? 100 : 0);
|
||||||
(void)on;
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7796::set_brightness(uint8_t brightness) {
|
||||||
|
st7796_set_brightness(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t LowLevelDisplayST7796::get_brightness() const {
|
||||||
|
return st7796_get_brightness();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7796::sleep() {
|
||||||
|
st7796_sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7796::wake() {
|
||||||
|
st7796_wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LowLevelDisplayST7796::set_rotation(uint8_t rotation) {
|
void LowLevelDisplayST7796::set_rotation(uint8_t rotation) {
|
||||||
@@ -79,3 +289,63 @@ void LowLevelDisplayST7796::set_rotation(uint8_t rotation) {
|
|||||||
// TODO: Add MADCTL register manipulation for rotation
|
// TODO: Add MADCTL register manipulation for rotation
|
||||||
(void)rotation;
|
(void)rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7796::enable_dirty_rect(bool enabled) {
|
||||||
|
dirty_rect_enabled = enabled;
|
||||||
|
|
||||||
|
if (enabled && !prev_bit_buffer) {
|
||||||
|
// Allocate buffer to store previous frame for change detection
|
||||||
|
size_t bit_buffer_size = (width * height + 7) / 8; // 1 bit per pixel
|
||||||
|
prev_bit_buffer = (uint8_t *)malloc(bit_buffer_size);
|
||||||
|
if (prev_bit_buffer) {
|
||||||
|
// Initialize to all zeros (black screen)
|
||||||
|
memset(prev_bit_buffer, 0, bit_buffer_size);
|
||||||
|
printf("ST7796: Dirty rectangle tracking enabled (buffer: %zu bytes, max rects: %d)\n",
|
||||||
|
bit_buffer_size, MAX_DIRTY_RECTS);
|
||||||
|
} else {
|
||||||
|
printf("Error: Failed to allocate %zu bytes for dirty rect buffer\n", bit_buffer_size);
|
||||||
|
dirty_rect_enabled = false;
|
||||||
|
}
|
||||||
|
} else if (!enabled && prev_bit_buffer) {
|
||||||
|
// Disable and free tracking buffer
|
||||||
|
free(prev_bit_buffer);
|
||||||
|
prev_bit_buffer = nullptr;
|
||||||
|
for (int i = 0; i < MAX_DIRTY_RECTS; i++) {
|
||||||
|
dirty_rects[i].reset();
|
||||||
|
}
|
||||||
|
printf("ST7796: Dirty rectangle tracking disabled\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7796::on_idle_2min() {
|
||||||
|
if (!is_dimmed && !is_sleeping) {
|
||||||
|
saved_brightness = get_brightness();
|
||||||
|
set_brightness(5); // Dim to 5%
|
||||||
|
is_dimmed = true;
|
||||||
|
printf("TFT: Dimmed to 5%%\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7796::on_idle_10min() {
|
||||||
|
if (!is_sleeping) {
|
||||||
|
sleep();
|
||||||
|
is_sleeping = true;
|
||||||
|
is_dimmed = true; // Sleep implies dimmed
|
||||||
|
printf("TFT: Entered sleep mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowLevelDisplayST7796::on_user_interaction() {
|
||||||
|
if (is_sleeping) {
|
||||||
|
wake();
|
||||||
|
// Restore brightness if we have a saved value, or default to 100
|
||||||
|
set_brightness(saved_brightness > 0 ? saved_brightness : 100);
|
||||||
|
is_sleeping = false;
|
||||||
|
is_dimmed = false;
|
||||||
|
printf("TFT: Woke from sleep\n");
|
||||||
|
} else if (is_dimmed) {
|
||||||
|
set_brightness(saved_brightness > 0 ? saved_brightness : 100);
|
||||||
|
is_dimmed = false;
|
||||||
|
printf("TFT: Restored brightness\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "low_level_display.h"
|
#include "low_level_display.h"
|
||||||
#include "st7796.h"
|
#include "st7796.h"
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
class LowLevelDisplayST7796 : public LowLevelDisplay {
|
class LowLevelDisplayST7796 : public LowLevelDisplay {
|
||||||
private:
|
private:
|
||||||
@@ -13,6 +14,56 @@ private:
|
|||||||
uint16_t* rgb_buffer; // Persistent buffer for 1-bit to RGB565 conversion
|
uint16_t* rgb_buffer; // Persistent buffer for 1-bit to RGB565 conversion
|
||||||
bool invert_color; // If true, swap black/white
|
bool invert_color; // If true, swap black/white
|
||||||
|
|
||||||
|
// Dirty rectangle tracking for partial updates
|
||||||
|
uint8_t* prev_bit_buffer; // Previous frame buffer for change detection
|
||||||
|
bool dirty_rect_enabled; // Enable/disable dirty rectangle optimization
|
||||||
|
|
||||||
|
struct DirtyRect {
|
||||||
|
int x0, y0; // Top-left corner
|
||||||
|
int x1, y1; // Bottom-right corner (inclusive)
|
||||||
|
bool is_valid;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
x0 = INT_MAX;
|
||||||
|
y0 = INT_MAX;
|
||||||
|
x1 = -1;
|
||||||
|
y1 = -1;
|
||||||
|
is_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void expand(int x, int y) {
|
||||||
|
if (x < x0) x0 = x;
|
||||||
|
if (x > x1) x1 = x;
|
||||||
|
if (y < y0) y0 = y;
|
||||||
|
if (y > y1) y1 = y;
|
||||||
|
is_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_width() const { return is_valid ? (x1 - x0 + 1) : 0; }
|
||||||
|
int get_height() const { return is_valid ? (y1 - y0 + 1) : 0; }
|
||||||
|
int get_area() const { return is_valid ? get_width() * get_height() : 0; }
|
||||||
|
|
||||||
|
bool overlaps(const DirtyRect& other) const {
|
||||||
|
if (!is_valid || !other.is_valid) return false;
|
||||||
|
return !(x1 < other.x0 || x0 > other.x1 || y1 < other.y0 || y0 > other.y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void merge(const DirtyRect& other) {
|
||||||
|
if (!other.is_valid) return;
|
||||||
|
if (!is_valid) {
|
||||||
|
*this = other;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x0 = (x0 < other.x0) ? x0 : other.x0;
|
||||||
|
y0 = (y0 < other.y0) ? y0 : other.y0;
|
||||||
|
x1 = (x1 > other.x1) ? x1 : other.x1;
|
||||||
|
y1 = (y1 > other.y1) ? y1 : other.y1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr int MAX_DIRTY_RECTS = 4;
|
||||||
|
DirtyRect dirty_rects[MAX_DIRTY_RECTS]; // Support up to 4 dirty rectangles (4 quadrants)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LowLevelDisplayST7796(const st7796_config* cfg, int w, int h, bool invert = false);
|
LowLevelDisplayST7796(const st7796_config* cfg, int w, int h, bool invert = false);
|
||||||
~LowLevelDisplayST7796() override;
|
~LowLevelDisplayST7796() override;
|
||||||
@@ -33,12 +84,34 @@ public:
|
|||||||
// Backlight control
|
// Backlight control
|
||||||
void set_backlight(bool on) override;
|
void set_backlight(bool on) override;
|
||||||
|
|
||||||
|
// Brightness control (0-100)
|
||||||
|
void set_brightness(uint8_t brightness) override;
|
||||||
|
uint8_t get_brightness() const override;
|
||||||
|
|
||||||
|
// Power management
|
||||||
|
void sleep(); // Put display to sleep (low power, touch still active)
|
||||||
|
void wake(); // Wake display from sleep
|
||||||
|
|
||||||
// Orientation control
|
// Orientation control
|
||||||
void set_rotation(uint8_t rotation) override;
|
void set_rotation(uint8_t rotation) override;
|
||||||
|
|
||||||
// Color inversion control
|
// Color inversion control
|
||||||
void set_invert_color(bool inv) { invert_color = inv; }
|
void set_invert_color(bool inv) { invert_color = inv; }
|
||||||
bool get_invert_color() const { return invert_color; }
|
bool get_invert_color() const { return invert_color; }
|
||||||
|
|
||||||
|
// Power saving hooks
|
||||||
|
void on_idle_2min() override;
|
||||||
|
void on_idle_10min() override;
|
||||||
|
void on_user_interaction() override;
|
||||||
|
|
||||||
|
// Dirty rectangle optimization control
|
||||||
|
void enable_dirty_rect(bool enabled = true);
|
||||||
|
bool is_dirty_rect_enabled() const { return dirty_rect_enabled; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t saved_brightness = 100;
|
||||||
|
bool is_dimmed = false;
|
||||||
|
bool is_sleeping = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOW_LEVEL_DISPLAY_ST7796_H
|
#endif // LOW_LEVEL_DISPLAY_ST7796_H
|
||||||
|
|||||||
+11
-8
@@ -1,5 +1,5 @@
|
|||||||
# This is the CMakeCache file.
|
# This is the CMakeCache file.
|
||||||
# For build in directory: /Users/adolforeyna/Projects/pico-bare-metal/Adolfo/basic1/emulator
|
# For build in directory: /Users/adolforeyna/Projects/basic1/emulator
|
||||||
# It was generated by CMake: /opt/homebrew/bin/cmake
|
# It was generated by CMake: /opt/homebrew/bin/cmake
|
||||||
# You can edit this file to change values found and used by cmake.
|
# You can edit this file to change values found and used by cmake.
|
||||||
# If you do not want to change any of the values, simply exit the editor.
|
# If you do not want to change any of the values, simply exit the editor.
|
||||||
@@ -85,7 +85,7 @@ CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
|||||||
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=
|
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=
|
||||||
|
|
||||||
//Value Computed by CMake.
|
//Value Computed by CMake.
|
||||||
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/Users/adolforeyna/Projects/pico-bare-metal/Adolfo/basic1/emulator/CMakeFiles/pkgRedirects
|
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/Users/adolforeyna/Projects/basic1/emulator/CMakeFiles/pkgRedirects
|
||||||
|
|
||||||
//Path to a program.
|
//Path to a program.
|
||||||
CMAKE_INSTALL_NAME_TOOL:FILEPATH=/usr/bin/install_name_tool
|
CMAKE_INSTALL_NAME_TOOL:FILEPATH=/usr/bin/install_name_tool
|
||||||
@@ -211,7 +211,7 @@ CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
|||||||
CMAKE_STRIP:FILEPATH=/usr/bin/strip
|
CMAKE_STRIP:FILEPATH=/usr/bin/strip
|
||||||
|
|
||||||
//Path to a program.
|
//Path to a program.
|
||||||
CMAKE_TAPI:FILEPATH=/Library/Developer/CommandLineTools/usr/bin/tapi
|
CMAKE_TAPI:FILEPATH=/Volumes/ReynaFamily/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/tapi
|
||||||
|
|
||||||
//If this value is on, makefiles will be generated without the
|
//If this value is on, makefiles will be generated without the
|
||||||
// .SILENT directive, and all commands will be echoed to the console
|
// .SILENT directive, and all commands will be echoed to the console
|
||||||
@@ -220,16 +220,19 @@ CMAKE_TAPI:FILEPATH=/Library/Developer/CommandLineTools/usr/bin/tapi
|
|||||||
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
|
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
|
||||||
|
|
||||||
//The directory containing a CMake configuration file for SFML.
|
//The directory containing a CMake configuration file for SFML.
|
||||||
SFML_DIR:PATH=SFML_DIR-NOTFOUND
|
SFML_DIR:PATH=/opt/homebrew/lib/cmake/SFML
|
||||||
|
|
||||||
|
//Path to a file.
|
||||||
|
SFML_DOC_DIR:PATH=/opt/homebrew/share/doc/SFML
|
||||||
|
|
||||||
//Value Computed by CMake
|
//Value Computed by CMake
|
||||||
basic1_emulator_BINARY_DIR:STATIC=/Users/adolforeyna/Projects/pico-bare-metal/Adolfo/basic1/emulator
|
basic1_emulator_BINARY_DIR:STATIC=/Users/adolforeyna/Projects/basic1/emulator
|
||||||
|
|
||||||
//Value Computed by CMake
|
//Value Computed by CMake
|
||||||
basic1_emulator_IS_TOP_LEVEL:STATIC=ON
|
basic1_emulator_IS_TOP_LEVEL:STATIC=ON
|
||||||
|
|
||||||
//Value Computed by CMake
|
//Value Computed by CMake
|
||||||
basic1_emulator_SOURCE_DIR:STATIC=/Users/adolforeyna/Projects/pico-bare-metal/Adolfo/basic1/emulator
|
basic1_emulator_SOURCE_DIR:STATIC=/Users/adolforeyna/Projects/basic1/emulator
|
||||||
|
|
||||||
|
|
||||||
########################
|
########################
|
||||||
@@ -241,7 +244,7 @@ CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1
|
|||||||
//ADVANCED property for variable: CMAKE_AR
|
//ADVANCED property for variable: CMAKE_AR
|
||||||
CMAKE_AR-ADVANCED:INTERNAL=1
|
CMAKE_AR-ADVANCED:INTERNAL=1
|
||||||
//This is the directory where this CMakeCache.txt was created
|
//This is the directory where this CMakeCache.txt was created
|
||||||
CMAKE_CACHEFILE_DIR:INTERNAL=/Users/adolforeyna/Projects/pico-bare-metal/Adolfo/basic1/emulator
|
CMAKE_CACHEFILE_DIR:INTERNAL=/Users/adolforeyna/Projects/basic1/emulator
|
||||||
//Major version of cmake used to create the current loaded cache
|
//Major version of cmake used to create the current loaded cache
|
||||||
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4
|
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4
|
||||||
//Minor version of cmake used to create the current loaded cache
|
//Minor version of cmake used to create the current loaded cache
|
||||||
@@ -310,7 +313,7 @@ CMAKE_GENERATOR_PLATFORM:INTERNAL=
|
|||||||
CMAKE_GENERATOR_TOOLSET:INTERNAL=
|
CMAKE_GENERATOR_TOOLSET:INTERNAL=
|
||||||
//Source directory with the top level CMakeLists.txt file for this
|
//Source directory with the top level CMakeLists.txt file for this
|
||||||
// project
|
// project
|
||||||
CMAKE_HOME_DIRECTORY:INTERNAL=/Users/adolforeyna/Projects/pico-bare-metal/Adolfo/basic1/emulator
|
CMAKE_HOME_DIRECTORY:INTERNAL=/Users/adolforeyna/Projects/basic1/emulator
|
||||||
//ADVANCED property for variable: CMAKE_INSTALL_NAME_TOOL
|
//ADVANCED property for variable: CMAKE_INSTALL_NAME_TOOL
|
||||||
CMAKE_INSTALL_NAME_TOOL-ADVANCED:INTERNAL=1
|
CMAKE_INSTALL_NAME_TOOL-ADVANCED:INTERNAL=1
|
||||||
//ADVANCED property for variable: CMAKE_LINKER
|
//ADVANCED property for variable: CMAKE_LINKER
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
set(CMAKE_C_COMPILER "/usr/bin/cc")
|
|
||||||
set(CMAKE_C_COMPILER_ARG1 "")
|
|
||||||
set(CMAKE_C_COMPILER_ID "AppleClang")
|
|
||||||
set(CMAKE_C_COMPILER_VERSION "17.0.0.17000603")
|
|
||||||
set(CMAKE_C_COMPILER_VERSION_INTERNAL "")
|
|
||||||
set(CMAKE_C_COMPILER_WRAPPER "")
|
|
||||||
set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "17")
|
|
||||||
set(CMAKE_C_EXTENSIONS_COMPUTED_DEFAULT "ON")
|
|
||||||
set(CMAKE_C_STANDARD_LATEST "23")
|
|
||||||
set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert;c_std_17;c_std_23")
|
|
||||||
set(CMAKE_C90_COMPILE_FEATURES "c_std_90;c_function_prototypes")
|
|
||||||
set(CMAKE_C99_COMPILE_FEATURES "c_std_99;c_restrict;c_variadic_macros")
|
|
||||||
set(CMAKE_C11_COMPILE_FEATURES "c_std_11;c_static_assert")
|
|
||||||
set(CMAKE_C17_COMPILE_FEATURES "c_std_17")
|
|
||||||
set(CMAKE_C23_COMPILE_FEATURES "c_std_23")
|
|
||||||
|
|
||||||
set(CMAKE_C_PLATFORM_ID "Darwin")
|
|
||||||
set(CMAKE_C_SIMULATE_ID "")
|
|
||||||
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "GNU")
|
|
||||||
set(CMAKE_C_COMPILER_APPLE_SYSROOT "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
|
||||||
set(CMAKE_C_SIMULATE_VERSION "")
|
|
||||||
set(CMAKE_C_COMPILER_ARCHITECTURE_ID "arm64")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_AR "/usr/bin/ar")
|
|
||||||
set(CMAKE_C_COMPILER_AR "")
|
|
||||||
set(CMAKE_RANLIB "/usr/bin/ranlib")
|
|
||||||
set(CMAKE_C_COMPILER_RANLIB "")
|
|
||||||
set(CMAKE_LINKER "/usr/bin/ld")
|
|
||||||
set(CMAKE_LINKER_LINK "")
|
|
||||||
set(CMAKE_LINKER_LLD "")
|
|
||||||
set(CMAKE_C_COMPILER_LINKER "/Library/Developer/CommandLineTools/usr/bin/ld")
|
|
||||||
set(CMAKE_C_COMPILER_LINKER_ID "AppleClang")
|
|
||||||
set(CMAKE_C_COMPILER_LINKER_VERSION 1230.1)
|
|
||||||
set(CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT GNU)
|
|
||||||
set(CMAKE_MT "")
|
|
||||||
set(CMAKE_TAPI "/Library/Developer/CommandLineTools/usr/bin/tapi")
|
|
||||||
set(CMAKE_COMPILER_IS_GNUCC )
|
|
||||||
set(CMAKE_C_COMPILER_LOADED 1)
|
|
||||||
set(CMAKE_C_COMPILER_WORKS TRUE)
|
|
||||||
set(CMAKE_C_ABI_COMPILED TRUE)
|
|
||||||
|
|
||||||
set(CMAKE_C_COMPILER_ENV_VAR "CC")
|
|
||||||
|
|
||||||
set(CMAKE_C_COMPILER_ID_RUN 1)
|
|
||||||
set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m)
|
|
||||||
set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
|
|
||||||
set(CMAKE_C_LINKER_PREFERENCE 10)
|
|
||||||
set(CMAKE_C_LINKER_DEPFILE_SUPPORTED )
|
|
||||||
set(CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED )
|
|
||||||
set(CMAKE_C_LINKER_PUSHPOP_STATE_SUPPORTED )
|
|
||||||
|
|
||||||
# Save compiler ABI information.
|
|
||||||
set(CMAKE_C_SIZEOF_DATA_PTR "8")
|
|
||||||
set(CMAKE_C_COMPILER_ABI "")
|
|
||||||
set(CMAKE_C_BYTE_ORDER "LITTLE_ENDIAN")
|
|
||||||
set(CMAKE_C_LIBRARY_ARCHITECTURE "")
|
|
||||||
|
|
||||||
if(CMAKE_C_SIZEOF_DATA_PTR)
|
|
||||||
set(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_C_COMPILER_ABI)
|
|
||||||
set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_C_LIBRARY_ARCHITECTURE)
|
|
||||||
set(CMAKE_LIBRARY_ARCHITECTURE "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_C_CL_SHOWINCLUDES_PREFIX "")
|
|
||||||
if(CMAKE_C_CL_SHOWINCLUDES_PREFIX)
|
|
||||||
set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_C_CL_SHOWINCLUDES_PREFIX}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "/Library/Developer/CommandLineTools/usr/lib/clang/17/include;/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include;/Library/Developer/CommandLineTools/usr/include")
|
|
||||||
set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "")
|
|
||||||
set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib;/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/swift")
|
|
||||||
set(CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks")
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
set(CMAKE_CXX_COMPILER "/usr/bin/c++")
|
|
||||||
set(CMAKE_CXX_COMPILER_ARG1 "")
|
|
||||||
set(CMAKE_CXX_COMPILER_ID "AppleClang")
|
|
||||||
set(CMAKE_CXX_COMPILER_VERSION "17.0.0.17000603")
|
|
||||||
set(CMAKE_CXX_COMPILER_VERSION_INTERNAL "")
|
|
||||||
set(CMAKE_CXX_COMPILER_WRAPPER "")
|
|
||||||
set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "14")
|
|
||||||
set(CMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT "ON")
|
|
||||||
set(CMAKE_CXX_STANDARD_LATEST "23")
|
|
||||||
set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17;cxx_std_20;cxx_std_23")
|
|
||||||
set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters")
|
|
||||||
set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates")
|
|
||||||
set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates")
|
|
||||||
set(CMAKE_CXX17_COMPILE_FEATURES "cxx_std_17")
|
|
||||||
set(CMAKE_CXX20_COMPILE_FEATURES "cxx_std_20")
|
|
||||||
set(CMAKE_CXX23_COMPILE_FEATURES "cxx_std_23")
|
|
||||||
set(CMAKE_CXX26_COMPILE_FEATURES "")
|
|
||||||
|
|
||||||
set(CMAKE_CXX_PLATFORM_ID "Darwin")
|
|
||||||
set(CMAKE_CXX_SIMULATE_ID "")
|
|
||||||
set(CMAKE_CXX_COMPILER_FRONTEND_VARIANT "GNU")
|
|
||||||
set(CMAKE_CXX_COMPILER_APPLE_SYSROOT "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
|
||||||
set(CMAKE_CXX_SIMULATE_VERSION "")
|
|
||||||
set(CMAKE_CXX_COMPILER_ARCHITECTURE_ID "arm64")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_AR "/usr/bin/ar")
|
|
||||||
set(CMAKE_CXX_COMPILER_AR "")
|
|
||||||
set(CMAKE_RANLIB "/usr/bin/ranlib")
|
|
||||||
set(CMAKE_CXX_COMPILER_RANLIB "")
|
|
||||||
set(CMAKE_LINKER "/usr/bin/ld")
|
|
||||||
set(CMAKE_LINKER_LINK "")
|
|
||||||
set(CMAKE_LINKER_LLD "")
|
|
||||||
set(CMAKE_CXX_COMPILER_LINKER "/Library/Developer/CommandLineTools/usr/bin/ld")
|
|
||||||
set(CMAKE_CXX_COMPILER_LINKER_ID "AppleClang")
|
|
||||||
set(CMAKE_CXX_COMPILER_LINKER_VERSION 1230.1)
|
|
||||||
set(CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT GNU)
|
|
||||||
set(CMAKE_MT "")
|
|
||||||
set(CMAKE_TAPI "/Library/Developer/CommandLineTools/usr/bin/tapi")
|
|
||||||
set(CMAKE_COMPILER_IS_GNUCXX )
|
|
||||||
set(CMAKE_CXX_COMPILER_LOADED 1)
|
|
||||||
set(CMAKE_CXX_COMPILER_WORKS TRUE)
|
|
||||||
set(CMAKE_CXX_ABI_COMPILED TRUE)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_COMPILER_ENV_VAR "CXX")
|
|
||||||
|
|
||||||
set(CMAKE_CXX_COMPILER_ID_RUN 1)
|
|
||||||
set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP;ixx;cppm;ccm;cxxm;c++m)
|
|
||||||
set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
|
|
||||||
|
|
||||||
foreach (lang IN ITEMS C OBJC OBJCXX)
|
|
||||||
if (CMAKE_${lang}_COMPILER_ID_RUN)
|
|
||||||
foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS)
|
|
||||||
list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension})
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_LINKER_PREFERENCE 30)
|
|
||||||
set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1)
|
|
||||||
set(CMAKE_CXX_LINKER_DEPFILE_SUPPORTED )
|
|
||||||
set(CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED )
|
|
||||||
set(CMAKE_CXX_LINKER_PUSHPOP_STATE_SUPPORTED )
|
|
||||||
|
|
||||||
# Save compiler ABI information.
|
|
||||||
set(CMAKE_CXX_SIZEOF_DATA_PTR "8")
|
|
||||||
set(CMAKE_CXX_COMPILER_ABI "")
|
|
||||||
set(CMAKE_CXX_BYTE_ORDER "LITTLE_ENDIAN")
|
|
||||||
set(CMAKE_CXX_LIBRARY_ARCHITECTURE "")
|
|
||||||
|
|
||||||
if(CMAKE_CXX_SIZEOF_DATA_PTR)
|
|
||||||
set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ABI)
|
|
||||||
set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_CXX_LIBRARY_ARCHITECTURE)
|
|
||||||
set(CMAKE_LIBRARY_ARCHITECTURE "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "")
|
|
||||||
if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX)
|
|
||||||
set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1;/Library/Developer/CommandLineTools/usr/lib/clang/17/include;/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include;/Library/Developer/CommandLineTools/usr/include")
|
|
||||||
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "c++")
|
|
||||||
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib;/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/swift")
|
|
||||||
set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks")
|
|
||||||
set(CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR "")
|
|
||||||
|
|
||||||
set(CMAKE_CXX_COMPILER_IMPORT_STD "")
|
|
||||||
### Imported target for C++23 standard library
|
|
||||||
set(CMAKE_CXX23_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE "Unsupported generator: Unix Makefiles")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,15 +0,0 @@
|
|||||||
set(CMAKE_HOST_SYSTEM "Darwin-25.2.0")
|
|
||||||
set(CMAKE_HOST_SYSTEM_NAME "Darwin")
|
|
||||||
set(CMAKE_HOST_SYSTEM_VERSION "25.2.0")
|
|
||||||
set(CMAKE_HOST_SYSTEM_PROCESSOR "arm64")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_SYSTEM "Darwin-25.2.0")
|
|
||||||
set(CMAKE_SYSTEM_NAME "Darwin")
|
|
||||||
set(CMAKE_SYSTEM_VERSION "25.2.0")
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR "arm64")
|
|
||||||
|
|
||||||
set(CMAKE_CROSSCOMPILING "FALSE")
|
|
||||||
|
|
||||||
set(CMAKE_SYSTEM_LOADED 1)
|
|
||||||
@@ -1,934 +0,0 @@
|
|||||||
#ifdef __cplusplus
|
|
||||||
# error "A C++ compiler has been selected for C."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__18CXX)
|
|
||||||
# define ID_VOID_MAIN
|
|
||||||
#endif
|
|
||||||
#if defined(__CLASSIC_C__)
|
|
||||||
/* cv-qualifiers did not exist in K&R C */
|
|
||||||
# define const
|
|
||||||
# define volatile
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(__has_include)
|
|
||||||
/* If the compiler does not have __has_include, pretend the answer is
|
|
||||||
always no. */
|
|
||||||
# define __has_include(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Version number components: V=Version, R=Revision, P=Patch
|
|
||||||
Version date components: YYYY=Year, MM=Month, DD=Day */
|
|
||||||
|
|
||||||
#if defined(__INTEL_COMPILER) || defined(__ICC)
|
|
||||||
# define COMPILER_ID "Intel"
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC__)
|
|
||||||
# define SIMULATE_ID "GNU"
|
|
||||||
# endif
|
|
||||||
/* __INTEL_COMPILER = VRP prior to 2021, and then VVVV for 2021 and later,
|
|
||||||
except that a few beta releases use the old format with V=2021. */
|
|
||||||
# if __INTEL_COMPILER < 2021 || __INTEL_COMPILER == 202110 || __INTEL_COMPILER == 202111
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10)
|
|
||||||
# if defined(__INTEL_COMPILER_UPDATE)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE)
|
|
||||||
# else
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10)
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER_UPDATE)
|
|
||||||
/* The third version component from --version is an update index,
|
|
||||||
but no macro is provided for it. */
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(0)
|
|
||||||
# endif
|
|
||||||
# if defined(__INTEL_COMPILER_BUILD_DATE)
|
|
||||||
/* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE)
|
|
||||||
# endif
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
# elif defined(__GNUG__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC_MINOR__)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif (defined(__clang__) && defined(__INTEL_CLANG_COMPILER)) || defined(__INTEL_LLVM_COMPILER)
|
|
||||||
# define COMPILER_ID "IntelLLVM"
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
# define SIMULATE_ID "GNU"
|
|
||||||
#endif
|
|
||||||
/* __INTEL_LLVM_COMPILER = VVVVRP prior to 2021.2.0, VVVVRRPP for 2021.2.0 and
|
|
||||||
* later. Look for 6 digit vs. 8 digit version number to decide encoding.
|
|
||||||
* VVVV is no smaller than the current year when a version is released.
|
|
||||||
*/
|
|
||||||
#if __INTEL_LLVM_COMPILER < 1000000L
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 10)
|
|
||||||
#else
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/10000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/100 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 100)
|
|
||||||
#endif
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
#elif defined(__GNUG__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC_MINOR__)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__PATHCC__)
|
|
||||||
# define COMPILER_ID "PathScale"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__PATHCC__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__)
|
|
||||||
# if defined(__PATHCC_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__)
|
|
||||||
# define COMPILER_ID "Embarcadero"
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF)
|
|
||||||
|
|
||||||
#elif defined(__BORLANDC__)
|
|
||||||
# define COMPILER_ID "Borland"
|
|
||||||
/* __BORLANDC__ = 0xVRR */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF)
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__) && __WATCOMC__ < 1200
|
|
||||||
# define COMPILER_ID "Watcom"
|
|
||||||
/* __WATCOMC__ = VVRR */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
|
|
||||||
# if (__WATCOMC__ % 10) > 0
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
# define COMPILER_ID "OpenWatcom"
|
|
||||||
/* __WATCOMC__ = VVRP + 1100 */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
|
|
||||||
# if (__WATCOMC__ % 10) > 0
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__SUNPRO_C)
|
|
||||||
# define COMPILER_ID "SunPro"
|
|
||||||
# if __SUNPRO_C >= 0x5100
|
|
||||||
/* __SUNPRO_C = 0xVRRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>12)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF)
|
|
||||||
# else
|
|
||||||
/* __SUNPRO_CC = 0xVRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>8)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xF)
|
|
||||||
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__HP_cc)
|
|
||||||
# define COMPILER_ID "HP"
|
|
||||||
/* __HP_cc = VVRRPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__HP_cc/10000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__HP_cc/100 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__HP_cc % 100)
|
|
||||||
|
|
||||||
#elif defined(__DECC)
|
|
||||||
# define COMPILER_ID "Compaq"
|
|
||||||
/* __DECC_VER = VVRRTPPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__DECC_VER/10000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__DECC_VER/100000 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__DECC_VER % 10000)
|
|
||||||
|
|
||||||
#elif defined(__IBMC__) && defined(__COMPILER_VER__)
|
|
||||||
# define COMPILER_ID "zOS"
|
|
||||||
/* __IBMC__ = VRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
|
|
||||||
|
|
||||||
#elif defined(__open_xl__) && defined(__clang__)
|
|
||||||
# define COMPILER_ID "IBMClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__open_xl_version__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__open_xl_release__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__open_xl_modification__)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__open_xl_ptf_fix_level__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__ibmxl__) && defined(__clang__)
|
|
||||||
# define COMPILER_ID "XLClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__)
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ >= 800
|
|
||||||
# define COMPILER_ID "XL"
|
|
||||||
/* __IBMC__ = VRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
|
|
||||||
|
|
||||||
#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ < 800
|
|
||||||
# define COMPILER_ID "VisualAge"
|
|
||||||
/* __IBMC__ = VRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
|
|
||||||
|
|
||||||
#elif defined(__NVCOMPILER)
|
|
||||||
# define COMPILER_ID "NVHPC"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__NVCOMPILER_MAJOR__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__NVCOMPILER_MINOR__)
|
|
||||||
# if defined(__NVCOMPILER_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__NVCOMPILER_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__PGI)
|
|
||||||
# define COMPILER_ID "PGI"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__PGIC__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__)
|
|
||||||
# if defined(__PGIC_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__cray__)
|
|
||||||
# define COMPILER_ID "CrayClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__cray_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__cray_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__cray_patchlevel__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(_CRAYC)
|
|
||||||
# define COMPILER_ID "Cray"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR)
|
|
||||||
|
|
||||||
#elif defined(__TI_COMPILER_VERSION__)
|
|
||||||
# define COMPILER_ID "TI"
|
|
||||||
/* __TI_COMPILER_VERSION__ = VVVRRRPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000)
|
|
||||||
|
|
||||||
#elif defined(__CLANG_FUJITSU)
|
|
||||||
# define COMPILER_ID "FujitsuClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__FUJITSU)
|
|
||||||
# define COMPILER_ID "Fujitsu"
|
|
||||||
# if defined(__FCC_version__)
|
|
||||||
# define COMPILER_VERSION __FCC_version__
|
|
||||||
# elif defined(__FCC_major__)
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
|
|
||||||
# endif
|
|
||||||
# if defined(__fcc_version)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__fcc_version)
|
|
||||||
# elif defined(__FCC_VERSION)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__FCC_VERSION)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__ghs__)
|
|
||||||
# define COMPILER_ID "GHS"
|
|
||||||
/* __GHS_VERSION_NUMBER = VVVVRP */
|
|
||||||
# ifdef __GHS_VERSION_NUMBER
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__TASKING__)
|
|
||||||
# define COMPILER_ID "Tasking"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__VERSION__/1000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__VERSION__ % 100)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__VERSION__)
|
|
||||||
|
|
||||||
#elif defined(__ORANGEC__)
|
|
||||||
# define COMPILER_ID "OrangeC"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ORANGEC_MAJOR__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ORANGEC_MINOR__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ORANGEC_PATCHLEVEL__)
|
|
||||||
|
|
||||||
#elif defined(__RENESAS__)
|
|
||||||
# define COMPILER_ID "Renesas"
|
|
||||||
/* __RENESAS_VERSION__ = 0xVVRRPP00 */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__RENESAS_VERSION__ >> 24 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__RENESAS_VERSION__ >> 16 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_PATCH HEX(__RENESAS_VERSION__ >> 8 & 0xFF)
|
|
||||||
|
|
||||||
#elif defined(__TINYC__)
|
|
||||||
# define COMPILER_ID "TinyCC"
|
|
||||||
|
|
||||||
#elif defined(__BCC__)
|
|
||||||
# define COMPILER_ID "Bruce"
|
|
||||||
|
|
||||||
#elif defined(__SCO_VERSION__)
|
|
||||||
# define COMPILER_ID "SCO"
|
|
||||||
|
|
||||||
#elif defined(__ARMCC_VERSION) && !defined(__clang__)
|
|
||||||
# define COMPILER_ID "ARMCC"
|
|
||||||
#if __ARMCC_VERSION >= 1000000
|
|
||||||
/* __ARMCC_VERSION = VRRPPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
|
|
||||||
#else
|
|
||||||
/* __ARMCC_VERSION = VRPPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__apple_build_version__)
|
|
||||||
# define COMPILER_ID "AppleClang"
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
# endif
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# endif
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__)
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION)
|
|
||||||
# define COMPILER_ID "ARMClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION/100 % 100)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION)
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__ti__)
|
|
||||||
# define COMPILER_ID "TIClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ti_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ti_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ti_patchlevel__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__ti_version__)
|
|
||||||
|
|
||||||
#elif defined(__clang__)
|
|
||||||
# define COMPILER_ID "Clang"
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
# endif
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__LCC__) && (defined(__GNUC__) || defined(__GNUG__) || defined(__MCST__))
|
|
||||||
# define COMPILER_ID "LCC"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__LCC__ / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__LCC__ % 100)
|
|
||||||
# if defined(__LCC_MINOR__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__LCC_MINOR__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
|
||||||
# define SIMULATE_ID "GNU"
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
# if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
# define COMPILER_ID "GNU"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
# if defined(__GNUC_MINOR__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
# define COMPILER_ID "MSVC"
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# if defined(_MSC_FULL_VER)
|
|
||||||
# if _MSC_VER >= 1400
|
|
||||||
/* _MSC_FULL_VER = VVRRPPPPP */
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000)
|
|
||||||
# else
|
|
||||||
/* _MSC_FULL_VER = VVRRPPPP */
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000)
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# if defined(_MSC_BUILD)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(_ADI_COMPILER)
|
|
||||||
# define COMPILER_ID "ADSP"
|
|
||||||
#if defined(__VERSIONNUM__)
|
|
||||||
/* __VERSIONNUM__ = 0xVVRRPPTT */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__VERSIONNUM__ >> 24 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__VERSIONNUM__ >> 16 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__VERSIONNUM__ >> 8 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__VERSIONNUM__ & 0xFF)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
|
|
||||||
# define COMPILER_ID "IAR"
|
|
||||||
# if defined(__VER__) && defined(__ICCARM__)
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
|
|
||||||
# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__) || defined(__ICCSTM8__))
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100))
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__DCC__) && defined(_DIAB_TOOL)
|
|
||||||
# define COMPILER_ID "Diab"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__VERSION_MAJOR_NUMBER__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__VERSION_MINOR_NUMBER__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__VERSION_ARCH_FEATURE_NUMBER__)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__VERSION_BUG_FIX_NUMBER__)
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__SDCC_VERSION_MAJOR) || defined(SDCC)
|
|
||||||
# define COMPILER_ID "SDCC"
|
|
||||||
# if defined(__SDCC_VERSION_MAJOR)
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__SDCC_VERSION_MAJOR)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__SDCC_VERSION_MINOR)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__SDCC_VERSION_PATCH)
|
|
||||||
# else
|
|
||||||
/* SDCC = VRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(SDCC/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(SDCC/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(SDCC % 10)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
|
|
||||||
/* These compilers are either not known or too old to define an
|
|
||||||
identification macro. Try to identify the platform and guess that
|
|
||||||
it is the native compiler. */
|
|
||||||
#elif defined(__hpux) || defined(__hpua)
|
|
||||||
# define COMPILER_ID "HP"
|
|
||||||
|
|
||||||
#else /* unknown compiler */
|
|
||||||
# define COMPILER_ID ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct the string literal in pieces to prevent the source from
|
|
||||||
getting matched. Store it in a pointer rather than an array
|
|
||||||
because some compilers will just produce instructions to fill the
|
|
||||||
array rather than assigning a pointer to a static array. */
|
|
||||||
char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
|
|
||||||
#ifdef SIMULATE_ID
|
|
||||||
char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __QNXNTO__
|
|
||||||
char const* qnxnto = "INFO" ":" "qnxnto[]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
|
|
||||||
char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STRINGIFY_HELPER(X) #X
|
|
||||||
#define STRINGIFY(X) STRINGIFY_HELPER(X)
|
|
||||||
|
|
||||||
/* Identify known platforms by name. */
|
|
||||||
#if defined(__linux) || defined(__linux__) || defined(linux)
|
|
||||||
# define PLATFORM_ID "Linux"
|
|
||||||
|
|
||||||
#elif defined(__MSYS__)
|
|
||||||
# define PLATFORM_ID "MSYS"
|
|
||||||
|
|
||||||
#elif defined(__CYGWIN__)
|
|
||||||
# define PLATFORM_ID "Cygwin"
|
|
||||||
|
|
||||||
#elif defined(__MINGW32__)
|
|
||||||
# define PLATFORM_ID "MinGW"
|
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
# define PLATFORM_ID "Darwin"
|
|
||||||
|
|
||||||
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
|
|
||||||
# define PLATFORM_ID "Windows"
|
|
||||||
|
|
||||||
#elif defined(__FreeBSD__) || defined(__FreeBSD)
|
|
||||||
# define PLATFORM_ID "FreeBSD"
|
|
||||||
|
|
||||||
#elif defined(__NetBSD__) || defined(__NetBSD)
|
|
||||||
# define PLATFORM_ID "NetBSD"
|
|
||||||
|
|
||||||
#elif defined(__OpenBSD__) || defined(__OPENBSD)
|
|
||||||
# define PLATFORM_ID "OpenBSD"
|
|
||||||
|
|
||||||
#elif defined(__sun) || defined(sun)
|
|
||||||
# define PLATFORM_ID "SunOS"
|
|
||||||
|
|
||||||
#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__)
|
|
||||||
# define PLATFORM_ID "AIX"
|
|
||||||
|
|
||||||
#elif defined(__hpux) || defined(__hpux__)
|
|
||||||
# define PLATFORM_ID "HP-UX"
|
|
||||||
|
|
||||||
#elif defined(__HAIKU__)
|
|
||||||
# define PLATFORM_ID "Haiku"
|
|
||||||
|
|
||||||
#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS)
|
|
||||||
# define PLATFORM_ID "BeOS"
|
|
||||||
|
|
||||||
#elif defined(__QNX__) || defined(__QNXNTO__)
|
|
||||||
# define PLATFORM_ID "QNX"
|
|
||||||
|
|
||||||
#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__)
|
|
||||||
# define PLATFORM_ID "Tru64"
|
|
||||||
|
|
||||||
#elif defined(__riscos) || defined(__riscos__)
|
|
||||||
# define PLATFORM_ID "RISCos"
|
|
||||||
|
|
||||||
#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__)
|
|
||||||
# define PLATFORM_ID "SINIX"
|
|
||||||
|
|
||||||
#elif defined(__UNIX_SV__)
|
|
||||||
# define PLATFORM_ID "UNIX_SV"
|
|
||||||
|
|
||||||
#elif defined(__bsdos__)
|
|
||||||
# define PLATFORM_ID "BSDOS"
|
|
||||||
|
|
||||||
#elif defined(_MPRAS) || defined(MPRAS)
|
|
||||||
# define PLATFORM_ID "MP-RAS"
|
|
||||||
|
|
||||||
#elif defined(__osf) || defined(__osf__)
|
|
||||||
# define PLATFORM_ID "OSF1"
|
|
||||||
|
|
||||||
#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv)
|
|
||||||
# define PLATFORM_ID "SCO_SV"
|
|
||||||
|
|
||||||
#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX)
|
|
||||||
# define PLATFORM_ID "ULTRIX"
|
|
||||||
|
|
||||||
#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX)
|
|
||||||
# define PLATFORM_ID "Xenix"
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
# if defined(__LINUX__)
|
|
||||||
# define PLATFORM_ID "Linux"
|
|
||||||
|
|
||||||
# elif defined(__DOS__)
|
|
||||||
# define PLATFORM_ID "DOS"
|
|
||||||
|
|
||||||
# elif defined(__OS2__)
|
|
||||||
# define PLATFORM_ID "OS2"
|
|
||||||
|
|
||||||
# elif defined(__WINDOWS__)
|
|
||||||
# define PLATFORM_ID "Windows3x"
|
|
||||||
|
|
||||||
# elif defined(__VXWORKS__)
|
|
||||||
# define PLATFORM_ID "VxWorks"
|
|
||||||
|
|
||||||
# else /* unknown platform */
|
|
||||||
# define PLATFORM_ID
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__INTEGRITY)
|
|
||||||
# if defined(INT_178B)
|
|
||||||
# define PLATFORM_ID "Integrity178"
|
|
||||||
|
|
||||||
# else /* regular Integrity */
|
|
||||||
# define PLATFORM_ID "Integrity"
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# elif defined(_ADI_COMPILER)
|
|
||||||
# define PLATFORM_ID "ADSP"
|
|
||||||
|
|
||||||
#else /* unknown platform */
|
|
||||||
# define PLATFORM_ID
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* For windows compilers MSVC and Intel we can determine
|
|
||||||
the architecture of the compiler being used. This is because
|
|
||||||
the compilers do not have flags that can change the architecture,
|
|
||||||
but rather depend on which compiler is being used
|
|
||||||
*/
|
|
||||||
#if defined(_WIN32) && defined(_MSC_VER)
|
|
||||||
# if defined(_M_IA64)
|
|
||||||
# define ARCHITECTURE_ID "IA64"
|
|
||||||
|
|
||||||
# elif defined(_M_ARM64EC)
|
|
||||||
# define ARCHITECTURE_ID "ARM64EC"
|
|
||||||
|
|
||||||
# elif defined(_M_X64) || defined(_M_AMD64)
|
|
||||||
# define ARCHITECTURE_ID "x64"
|
|
||||||
|
|
||||||
# elif defined(_M_IX86)
|
|
||||||
# define ARCHITECTURE_ID "X86"
|
|
||||||
|
|
||||||
# elif defined(_M_ARM64)
|
|
||||||
# define ARCHITECTURE_ID "ARM64"
|
|
||||||
|
|
||||||
# elif defined(_M_ARM)
|
|
||||||
# if _M_ARM == 4
|
|
||||||
# define ARCHITECTURE_ID "ARMV4I"
|
|
||||||
# elif _M_ARM == 5
|
|
||||||
# define ARCHITECTURE_ID "ARMV5I"
|
|
||||||
# else
|
|
||||||
# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# elif defined(_M_MIPS)
|
|
||||||
# define ARCHITECTURE_ID "MIPS"
|
|
||||||
|
|
||||||
# elif defined(_M_SH)
|
|
||||||
# define ARCHITECTURE_ID "SHx"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
# if defined(_M_I86)
|
|
||||||
# define ARCHITECTURE_ID "I86"
|
|
||||||
|
|
||||||
# elif defined(_M_IX86)
|
|
||||||
# define ARCHITECTURE_ID "X86"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
|
|
||||||
# if defined(__ICCARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__ICCRX__)
|
|
||||||
# define ARCHITECTURE_ID "RX"
|
|
||||||
|
|
||||||
# elif defined(__ICCRH850__)
|
|
||||||
# define ARCHITECTURE_ID "RH850"
|
|
||||||
|
|
||||||
# elif defined(__ICCRL78__)
|
|
||||||
# define ARCHITECTURE_ID "RL78"
|
|
||||||
|
|
||||||
# elif defined(__ICCRISCV__)
|
|
||||||
# define ARCHITECTURE_ID "RISCV"
|
|
||||||
|
|
||||||
# elif defined(__ICCAVR__)
|
|
||||||
# define ARCHITECTURE_ID "AVR"
|
|
||||||
|
|
||||||
# elif defined(__ICC430__)
|
|
||||||
# define ARCHITECTURE_ID "MSP430"
|
|
||||||
|
|
||||||
# elif defined(__ICCV850__)
|
|
||||||
# define ARCHITECTURE_ID "V850"
|
|
||||||
|
|
||||||
# elif defined(__ICC8051__)
|
|
||||||
# define ARCHITECTURE_ID "8051"
|
|
||||||
|
|
||||||
# elif defined(__ICCSTM8__)
|
|
||||||
# define ARCHITECTURE_ID "STM8"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__ghs__)
|
|
||||||
# if defined(__PPC64__)
|
|
||||||
# define ARCHITECTURE_ID "PPC64"
|
|
||||||
|
|
||||||
# elif defined(__ppc__)
|
|
||||||
# define ARCHITECTURE_ID "PPC"
|
|
||||||
|
|
||||||
# elif defined(__ARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__x86_64__)
|
|
||||||
# define ARCHITECTURE_ID "x64"
|
|
||||||
|
|
||||||
# elif defined(__i386__)
|
|
||||||
# define ARCHITECTURE_ID "X86"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__ti__)
|
|
||||||
# if defined(__ARM_ARCH)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__TI_COMPILER_VERSION__)
|
|
||||||
# if defined(__TI_ARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__MSP430__)
|
|
||||||
# define ARCHITECTURE_ID "MSP430"
|
|
||||||
|
|
||||||
# elif defined(__TMS320C28XX__)
|
|
||||||
# define ARCHITECTURE_ID "TMS320C28x"
|
|
||||||
|
|
||||||
# elif defined(__TMS320C6X__) || defined(_TMS320C6X)
|
|
||||||
# define ARCHITECTURE_ID "TMS320C6x"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# elif defined(__ADSPSHARC__)
|
|
||||||
# define ARCHITECTURE_ID "SHARC"
|
|
||||||
|
|
||||||
# elif defined(__ADSPBLACKFIN__)
|
|
||||||
# define ARCHITECTURE_ID "Blackfin"
|
|
||||||
|
|
||||||
#elif defined(__TASKING__)
|
|
||||||
|
|
||||||
# if defined(__CTC__) || defined(__CPTC__)
|
|
||||||
# define ARCHITECTURE_ID "TriCore"
|
|
||||||
|
|
||||||
# elif defined(__CMCS__)
|
|
||||||
# define ARCHITECTURE_ID "MCS"
|
|
||||||
|
|
||||||
# elif defined(__CARM__) || defined(__CPARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__CARC__)
|
|
||||||
# define ARCHITECTURE_ID "ARC"
|
|
||||||
|
|
||||||
# elif defined(__C51__)
|
|
||||||
# define ARCHITECTURE_ID "8051"
|
|
||||||
|
|
||||||
# elif defined(__CPCP__)
|
|
||||||
# define ARCHITECTURE_ID "PCP"
|
|
||||||
|
|
||||||
# else
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__RENESAS__)
|
|
||||||
# if defined(__CCRX__)
|
|
||||||
# define ARCHITECTURE_ID "RX"
|
|
||||||
|
|
||||||
# elif defined(__CCRL__)
|
|
||||||
# define ARCHITECTURE_ID "RL78"
|
|
||||||
|
|
||||||
# elif defined(__CCRH__)
|
|
||||||
# define ARCHITECTURE_ID "RH850"
|
|
||||||
|
|
||||||
# else
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
# define ARCHITECTURE_ID
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Convert integer to decimal digit literals. */
|
|
||||||
#define DEC(n) \
|
|
||||||
('0' + (((n) / 10000000)%10)), \
|
|
||||||
('0' + (((n) / 1000000)%10)), \
|
|
||||||
('0' + (((n) / 100000)%10)), \
|
|
||||||
('0' + (((n) / 10000)%10)), \
|
|
||||||
('0' + (((n) / 1000)%10)), \
|
|
||||||
('0' + (((n) / 100)%10)), \
|
|
||||||
('0' + (((n) / 10)%10)), \
|
|
||||||
('0' + ((n) % 10))
|
|
||||||
|
|
||||||
/* Convert integer to hex digit literals. */
|
|
||||||
#define HEX(n) \
|
|
||||||
('0' + ((n)>>28 & 0xF)), \
|
|
||||||
('0' + ((n)>>24 & 0xF)), \
|
|
||||||
('0' + ((n)>>20 & 0xF)), \
|
|
||||||
('0' + ((n)>>16 & 0xF)), \
|
|
||||||
('0' + ((n)>>12 & 0xF)), \
|
|
||||||
('0' + ((n)>>8 & 0xF)), \
|
|
||||||
('0' + ((n)>>4 & 0xF)), \
|
|
||||||
('0' + ((n) & 0xF))
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the version number. */
|
|
||||||
#ifdef COMPILER_VERSION
|
|
||||||
char const* info_version = "INFO" ":" "compiler_version[" COMPILER_VERSION "]";
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the version number components. */
|
|
||||||
#elif defined(COMPILER_VERSION_MAJOR)
|
|
||||||
char const info_version[] = {
|
|
||||||
'I', 'N', 'F', 'O', ':',
|
|
||||||
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[',
|
|
||||||
COMPILER_VERSION_MAJOR,
|
|
||||||
# ifdef COMPILER_VERSION_MINOR
|
|
||||||
'.', COMPILER_VERSION_MINOR,
|
|
||||||
# ifdef COMPILER_VERSION_PATCH
|
|
||||||
'.', COMPILER_VERSION_PATCH,
|
|
||||||
# ifdef COMPILER_VERSION_TWEAK
|
|
||||||
'.', COMPILER_VERSION_TWEAK,
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
']','\0'};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the internal version number. */
|
|
||||||
#ifdef COMPILER_VERSION_INTERNAL
|
|
||||||
char const info_version_internal[] = {
|
|
||||||
'I', 'N', 'F', 'O', ':',
|
|
||||||
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_',
|
|
||||||
'i','n','t','e','r','n','a','l','[',
|
|
||||||
COMPILER_VERSION_INTERNAL,']','\0'};
|
|
||||||
#elif defined(COMPILER_VERSION_INTERNAL_STR)
|
|
||||||
char const* info_version_internal = "INFO" ":" "compiler_version_internal[" COMPILER_VERSION_INTERNAL_STR "]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the version number components. */
|
|
||||||
#ifdef SIMULATE_VERSION_MAJOR
|
|
||||||
char const info_simulate_version[] = {
|
|
||||||
'I', 'N', 'F', 'O', ':',
|
|
||||||
's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[',
|
|
||||||
SIMULATE_VERSION_MAJOR,
|
|
||||||
# ifdef SIMULATE_VERSION_MINOR
|
|
||||||
'.', SIMULATE_VERSION_MINOR,
|
|
||||||
# ifdef SIMULATE_VERSION_PATCH
|
|
||||||
'.', SIMULATE_VERSION_PATCH,
|
|
||||||
# ifdef SIMULATE_VERSION_TWEAK
|
|
||||||
'.', SIMULATE_VERSION_TWEAK,
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
']','\0'};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct the string literal in pieces to prevent the source from
|
|
||||||
getting matched. Store it in a pointer rather than an array
|
|
||||||
because some compilers will just produce instructions to fill the
|
|
||||||
array rather than assigning a pointer to a static array. */
|
|
||||||
char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]";
|
|
||||||
char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define C_STD_99 199901L
|
|
||||||
#define C_STD_11 201112L
|
|
||||||
#define C_STD_17 201710L
|
|
||||||
#define C_STD_23 202311L
|
|
||||||
|
|
||||||
#ifdef __STDC_VERSION__
|
|
||||||
# define C_STD __STDC_VERSION__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(__STDC__) && !defined(__clang__) && !defined(__RENESAS__)
|
|
||||||
# if defined(_MSC_VER) || defined(__ibmxl__) || defined(__IBMC__)
|
|
||||||
# define C_VERSION "90"
|
|
||||||
# else
|
|
||||||
# define C_VERSION
|
|
||||||
# endif
|
|
||||||
#elif C_STD > C_STD_17
|
|
||||||
# define C_VERSION "23"
|
|
||||||
#elif C_STD > C_STD_11
|
|
||||||
# define C_VERSION "17"
|
|
||||||
#elif C_STD > C_STD_99
|
|
||||||
# define C_VERSION "11"
|
|
||||||
#elif C_STD >= C_STD_99
|
|
||||||
# define C_VERSION "99"
|
|
||||||
#else
|
|
||||||
# define C_VERSION "90"
|
|
||||||
#endif
|
|
||||||
const char* info_language_standard_default =
|
|
||||||
"INFO" ":" "standard_default[" C_VERSION "]";
|
|
||||||
|
|
||||||
const char* info_language_extensions_default = "INFO" ":" "extensions_default["
|
|
||||||
#if (defined(__clang__) || defined(__GNUC__) || defined(__xlC__) || \
|
|
||||||
defined(__TI_COMPILER_VERSION__) || defined(__RENESAS__)) && \
|
|
||||||
!defined(__STRICT_ANSI__)
|
|
||||||
"ON"
|
|
||||||
#else
|
|
||||||
"OFF"
|
|
||||||
#endif
|
|
||||||
"]";
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#ifdef ID_VOID_MAIN
|
|
||||||
void main() {}
|
|
||||||
#else
|
|
||||||
# if defined(__CLASSIC_C__)
|
|
||||||
int main(argc, argv) int argc; char *argv[];
|
|
||||||
# else
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
# endif
|
|
||||||
{
|
|
||||||
int require = 0;
|
|
||||||
require += info_compiler[argc];
|
|
||||||
require += info_platform[argc];
|
|
||||||
require += info_arch[argc];
|
|
||||||
#ifdef COMPILER_VERSION_MAJOR
|
|
||||||
require += info_version[argc];
|
|
||||||
#endif
|
|
||||||
#if defined(COMPILER_VERSION_INTERNAL) || defined(COMPILER_VERSION_INTERNAL_STR)
|
|
||||||
require += info_version_internal[argc];
|
|
||||||
#endif
|
|
||||||
#ifdef SIMULATE_ID
|
|
||||||
require += info_simulate[argc];
|
|
||||||
#endif
|
|
||||||
#ifdef SIMULATE_VERSION_MAJOR
|
|
||||||
require += info_simulate_version[argc];
|
|
||||||
#endif
|
|
||||||
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
|
|
||||||
require += info_cray[argc];
|
|
||||||
#endif
|
|
||||||
require += info_language_standard_default[argc];
|
|
||||||
require += info_language_extensions_default[argc];
|
|
||||||
(void)argv;
|
|
||||||
return require;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
#include <AvailabilityMacros.h>
|
|
||||||
@@ -1,949 +0,0 @@
|
|||||||
/* This source file must have a .cpp extension so that all C++ compilers
|
|
||||||
recognize the extension without flags. Borland does not know .cxx for
|
|
||||||
example. */
|
|
||||||
#ifndef __cplusplus
|
|
||||||
# error "A C compiler has been selected for C++."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(__has_include)
|
|
||||||
/* If the compiler does not have __has_include, pretend the answer is
|
|
||||||
always no. */
|
|
||||||
# define __has_include(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Version number components: V=Version, R=Revision, P=Patch
|
|
||||||
Version date components: YYYY=Year, MM=Month, DD=Day */
|
|
||||||
|
|
||||||
#if defined(__INTEL_COMPILER) || defined(__ICC)
|
|
||||||
# define COMPILER_ID "Intel"
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC__)
|
|
||||||
# define SIMULATE_ID "GNU"
|
|
||||||
# endif
|
|
||||||
/* __INTEL_COMPILER = VRP prior to 2021, and then VVVV for 2021 and later,
|
|
||||||
except that a few beta releases use the old format with V=2021. */
|
|
||||||
# if __INTEL_COMPILER < 2021 || __INTEL_COMPILER == 202110 || __INTEL_COMPILER == 202111
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10)
|
|
||||||
# if defined(__INTEL_COMPILER_UPDATE)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE)
|
|
||||||
# else
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10)
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER_UPDATE)
|
|
||||||
/* The third version component from --version is an update index,
|
|
||||||
but no macro is provided for it. */
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(0)
|
|
||||||
# endif
|
|
||||||
# if defined(__INTEL_COMPILER_BUILD_DATE)
|
|
||||||
/* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE)
|
|
||||||
# endif
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
# elif defined(__GNUG__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC_MINOR__)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif (defined(__clang__) && defined(__INTEL_CLANG_COMPILER)) || defined(__INTEL_LLVM_COMPILER)
|
|
||||||
# define COMPILER_ID "IntelLLVM"
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
# define SIMULATE_ID "GNU"
|
|
||||||
#endif
|
|
||||||
/* __INTEL_LLVM_COMPILER = VVVVRP prior to 2021.2.0, VVVVRRPP for 2021.2.0 and
|
|
||||||
* later. Look for 6 digit vs. 8 digit version number to decide encoding.
|
|
||||||
* VVVV is no smaller than the current year when a version is released.
|
|
||||||
*/
|
|
||||||
#if __INTEL_LLVM_COMPILER < 1000000L
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 10)
|
|
||||||
#else
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/10000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/100 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 100)
|
|
||||||
#endif
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
#elif defined(__GNUG__)
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC_MINOR__)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
#endif
|
|
||||||
#if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__PATHCC__)
|
|
||||||
# define COMPILER_ID "PathScale"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__PATHCC__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__)
|
|
||||||
# if defined(__PATHCC_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__)
|
|
||||||
# define COMPILER_ID "Embarcadero"
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF)
|
|
||||||
|
|
||||||
#elif defined(__BORLANDC__)
|
|
||||||
# define COMPILER_ID "Borland"
|
|
||||||
/* __BORLANDC__ = 0xVRR */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF)
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__) && __WATCOMC__ < 1200
|
|
||||||
# define COMPILER_ID "Watcom"
|
|
||||||
/* __WATCOMC__ = VVRR */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
|
|
||||||
# if (__WATCOMC__ % 10) > 0
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
# define COMPILER_ID "OpenWatcom"
|
|
||||||
/* __WATCOMC__ = VVRP + 1100 */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
|
|
||||||
# if (__WATCOMC__ % 10) > 0
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__SUNPRO_CC)
|
|
||||||
# define COMPILER_ID "SunPro"
|
|
||||||
# if __SUNPRO_CC >= 0x5100
|
|
||||||
/* __SUNPRO_CC = 0xVRRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>12)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF)
|
|
||||||
# else
|
|
||||||
/* __SUNPRO_CC = 0xVRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>8)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xF)
|
|
||||||
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__HP_aCC)
|
|
||||||
# define COMPILER_ID "HP"
|
|
||||||
/* __HP_aCC = VVRRPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__HP_aCC/10000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__HP_aCC/100 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__HP_aCC % 100)
|
|
||||||
|
|
||||||
#elif defined(__DECCXX)
|
|
||||||
# define COMPILER_ID "Compaq"
|
|
||||||
/* __DECCXX_VER = VVRRTPPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__DECCXX_VER/10000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__DECCXX_VER/100000 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__DECCXX_VER % 10000)
|
|
||||||
|
|
||||||
#elif defined(__IBMCPP__) && defined(__COMPILER_VER__)
|
|
||||||
# define COMPILER_ID "zOS"
|
|
||||||
/* __IBMCPP__ = VRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
|
|
||||||
|
|
||||||
#elif defined(__open_xl__) && defined(__clang__)
|
|
||||||
# define COMPILER_ID "IBMClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__open_xl_version__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__open_xl_release__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__open_xl_modification__)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__open_xl_ptf_fix_level__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__ibmxl__) && defined(__clang__)
|
|
||||||
# define COMPILER_ID "XLClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__)
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ >= 800
|
|
||||||
# define COMPILER_ID "XL"
|
|
||||||
/* __IBMCPP__ = VRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
|
|
||||||
|
|
||||||
#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ < 800
|
|
||||||
# define COMPILER_ID "VisualAge"
|
|
||||||
/* __IBMCPP__ = VRP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
|
|
||||||
|
|
||||||
#elif defined(__NVCOMPILER)
|
|
||||||
# define COMPILER_ID "NVHPC"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__NVCOMPILER_MAJOR__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__NVCOMPILER_MINOR__)
|
|
||||||
# if defined(__NVCOMPILER_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__NVCOMPILER_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__PGI)
|
|
||||||
# define COMPILER_ID "PGI"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__PGIC__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__)
|
|
||||||
# if defined(__PGIC_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__cray__)
|
|
||||||
# define COMPILER_ID "CrayClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__cray_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__cray_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__cray_patchlevel__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(_CRAYC)
|
|
||||||
# define COMPILER_ID "Cray"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR)
|
|
||||||
|
|
||||||
#elif defined(__TI_COMPILER_VERSION__)
|
|
||||||
# define COMPILER_ID "TI"
|
|
||||||
/* __TI_COMPILER_VERSION__ = VVVRRRPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000)
|
|
||||||
|
|
||||||
#elif defined(__CLANG_FUJITSU)
|
|
||||||
# define COMPILER_ID "FujitsuClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__FUJITSU)
|
|
||||||
# define COMPILER_ID "Fujitsu"
|
|
||||||
# if defined(__FCC_version__)
|
|
||||||
# define COMPILER_VERSION __FCC_version__
|
|
||||||
# elif defined(__FCC_major__)
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
|
|
||||||
# endif
|
|
||||||
# if defined(__fcc_version)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__fcc_version)
|
|
||||||
# elif defined(__FCC_VERSION)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__FCC_VERSION)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__ghs__)
|
|
||||||
# define COMPILER_ID "GHS"
|
|
||||||
/* __GHS_VERSION_NUMBER = VVVVRP */
|
|
||||||
# ifdef __GHS_VERSION_NUMBER
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__TASKING__)
|
|
||||||
# define COMPILER_ID "Tasking"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__VERSION__/1000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__VERSION__ % 100)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__VERSION__)
|
|
||||||
|
|
||||||
#elif defined(__ORANGEC__)
|
|
||||||
# define COMPILER_ID "OrangeC"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ORANGEC_MAJOR__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ORANGEC_MINOR__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ORANGEC_PATCHLEVEL__)
|
|
||||||
|
|
||||||
#elif defined(__RENESAS__)
|
|
||||||
# define COMPILER_ID "Renesas"
|
|
||||||
/* __RENESAS_VERSION__ = 0xVVRRPP00 */
|
|
||||||
# define COMPILER_VERSION_MAJOR HEX(__RENESAS_VERSION__ >> 24 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_MINOR HEX(__RENESAS_VERSION__ >> 16 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_PATCH HEX(__RENESAS_VERSION__ >> 8 & 0xFF)
|
|
||||||
|
|
||||||
#elif defined(__SCO_VERSION__)
|
|
||||||
# define COMPILER_ID "SCO"
|
|
||||||
|
|
||||||
#elif defined(__ARMCC_VERSION) && !defined(__clang__)
|
|
||||||
# define COMPILER_ID "ARMCC"
|
|
||||||
#if __ARMCC_VERSION >= 1000000
|
|
||||||
/* __ARMCC_VERSION = VRRPPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
|
|
||||||
#else
|
|
||||||
/* __ARMCC_VERSION = VRPPPP */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__apple_build_version__)
|
|
||||||
# define COMPILER_ID "AppleClang"
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
# endif
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# endif
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__)
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION)
|
|
||||||
# define COMPILER_ID "ARMClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION/100 % 100)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION)
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__ti__)
|
|
||||||
# define COMPILER_ID "TIClang"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__ti_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__ti_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__ti_patchlevel__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__ti_version__)
|
|
||||||
|
|
||||||
#elif defined(__clang__)
|
|
||||||
# define COMPILER_ID "Clang"
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
# define SIMULATE_ID "MSVC"
|
|
||||||
# endif
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
|
|
||||||
# if defined(_MSC_VER)
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__LCC__) && (defined(__GNUC__) || defined(__GNUG__) || defined(__MCST__))
|
|
||||||
# define COMPILER_ID "LCC"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__LCC__ / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__LCC__ % 100)
|
|
||||||
# if defined(__LCC_MINOR__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__LCC_MINOR__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
|
||||||
# define SIMULATE_ID "GNU"
|
|
||||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
# if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
|
||||||
# define COMPILER_ID "GNU"
|
|
||||||
# if defined(__GNUC__)
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__GNUC__)
|
|
||||||
# else
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__GNUG__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC_MINOR__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__)
|
|
||||||
# endif
|
|
||||||
# if defined(__GNUC_PATCHLEVEL__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
# define COMPILER_ID "MSVC"
|
|
||||||
/* _MSC_VER = VVRR */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100)
|
|
||||||
# if defined(_MSC_FULL_VER)
|
|
||||||
# if _MSC_VER >= 1400
|
|
||||||
/* _MSC_FULL_VER = VVRRPPPPP */
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000)
|
|
||||||
# else
|
|
||||||
/* _MSC_FULL_VER = VVRRPPPP */
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000)
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# if defined(_MSC_BUILD)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(_ADI_COMPILER)
|
|
||||||
# define COMPILER_ID "ADSP"
|
|
||||||
#if defined(__VERSIONNUM__)
|
|
||||||
/* __VERSIONNUM__ = 0xVVRRPPTT */
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__VERSIONNUM__ >> 24 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__VERSIONNUM__ >> 16 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__VERSIONNUM__ >> 8 & 0xFF)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__VERSIONNUM__ & 0xFF)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
|
|
||||||
# define COMPILER_ID "IAR"
|
|
||||||
# if defined(__VER__) && defined(__ICCARM__)
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
|
|
||||||
# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__) || defined(__ICCSTM8__))
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100))
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__)
|
|
||||||
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__DCC__) && defined(_DIAB_TOOL)
|
|
||||||
# define COMPILER_ID "Diab"
|
|
||||||
# define COMPILER_VERSION_MAJOR DEC(__VERSION_MAJOR_NUMBER__)
|
|
||||||
# define COMPILER_VERSION_MINOR DEC(__VERSION_MINOR_NUMBER__)
|
|
||||||
# define COMPILER_VERSION_PATCH DEC(__VERSION_ARCH_FEATURE_NUMBER__)
|
|
||||||
# define COMPILER_VERSION_TWEAK DEC(__VERSION_BUG_FIX_NUMBER__)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* These compilers are either not known or too old to define an
|
|
||||||
identification macro. Try to identify the platform and guess that
|
|
||||||
it is the native compiler. */
|
|
||||||
#elif defined(__hpux) || defined(__hpua)
|
|
||||||
# define COMPILER_ID "HP"
|
|
||||||
|
|
||||||
#else /* unknown compiler */
|
|
||||||
# define COMPILER_ID ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct the string literal in pieces to prevent the source from
|
|
||||||
getting matched. Store it in a pointer rather than an array
|
|
||||||
because some compilers will just produce instructions to fill the
|
|
||||||
array rather than assigning a pointer to a static array. */
|
|
||||||
char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
|
|
||||||
#ifdef SIMULATE_ID
|
|
||||||
char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __QNXNTO__
|
|
||||||
char const* qnxnto = "INFO" ":" "qnxnto[]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
|
|
||||||
char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STRINGIFY_HELPER(X) #X
|
|
||||||
#define STRINGIFY(X) STRINGIFY_HELPER(X)
|
|
||||||
|
|
||||||
/* Identify known platforms by name. */
|
|
||||||
#if defined(__linux) || defined(__linux__) || defined(linux)
|
|
||||||
# define PLATFORM_ID "Linux"
|
|
||||||
|
|
||||||
#elif defined(__MSYS__)
|
|
||||||
# define PLATFORM_ID "MSYS"
|
|
||||||
|
|
||||||
#elif defined(__CYGWIN__)
|
|
||||||
# define PLATFORM_ID "Cygwin"
|
|
||||||
|
|
||||||
#elif defined(__MINGW32__)
|
|
||||||
# define PLATFORM_ID "MinGW"
|
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
# define PLATFORM_ID "Darwin"
|
|
||||||
|
|
||||||
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
|
|
||||||
# define PLATFORM_ID "Windows"
|
|
||||||
|
|
||||||
#elif defined(__FreeBSD__) || defined(__FreeBSD)
|
|
||||||
# define PLATFORM_ID "FreeBSD"
|
|
||||||
|
|
||||||
#elif defined(__NetBSD__) || defined(__NetBSD)
|
|
||||||
# define PLATFORM_ID "NetBSD"
|
|
||||||
|
|
||||||
#elif defined(__OpenBSD__) || defined(__OPENBSD)
|
|
||||||
# define PLATFORM_ID "OpenBSD"
|
|
||||||
|
|
||||||
#elif defined(__sun) || defined(sun)
|
|
||||||
# define PLATFORM_ID "SunOS"
|
|
||||||
|
|
||||||
#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__)
|
|
||||||
# define PLATFORM_ID "AIX"
|
|
||||||
|
|
||||||
#elif defined(__hpux) || defined(__hpux__)
|
|
||||||
# define PLATFORM_ID "HP-UX"
|
|
||||||
|
|
||||||
#elif defined(__HAIKU__)
|
|
||||||
# define PLATFORM_ID "Haiku"
|
|
||||||
|
|
||||||
#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS)
|
|
||||||
# define PLATFORM_ID "BeOS"
|
|
||||||
|
|
||||||
#elif defined(__QNX__) || defined(__QNXNTO__)
|
|
||||||
# define PLATFORM_ID "QNX"
|
|
||||||
|
|
||||||
#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__)
|
|
||||||
# define PLATFORM_ID "Tru64"
|
|
||||||
|
|
||||||
#elif defined(__riscos) || defined(__riscos__)
|
|
||||||
# define PLATFORM_ID "RISCos"
|
|
||||||
|
|
||||||
#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__)
|
|
||||||
# define PLATFORM_ID "SINIX"
|
|
||||||
|
|
||||||
#elif defined(__UNIX_SV__)
|
|
||||||
# define PLATFORM_ID "UNIX_SV"
|
|
||||||
|
|
||||||
#elif defined(__bsdos__)
|
|
||||||
# define PLATFORM_ID "BSDOS"
|
|
||||||
|
|
||||||
#elif defined(_MPRAS) || defined(MPRAS)
|
|
||||||
# define PLATFORM_ID "MP-RAS"
|
|
||||||
|
|
||||||
#elif defined(__osf) || defined(__osf__)
|
|
||||||
# define PLATFORM_ID "OSF1"
|
|
||||||
|
|
||||||
#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv)
|
|
||||||
# define PLATFORM_ID "SCO_SV"
|
|
||||||
|
|
||||||
#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX)
|
|
||||||
# define PLATFORM_ID "ULTRIX"
|
|
||||||
|
|
||||||
#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX)
|
|
||||||
# define PLATFORM_ID "Xenix"
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
# if defined(__LINUX__)
|
|
||||||
# define PLATFORM_ID "Linux"
|
|
||||||
|
|
||||||
# elif defined(__DOS__)
|
|
||||||
# define PLATFORM_ID "DOS"
|
|
||||||
|
|
||||||
# elif defined(__OS2__)
|
|
||||||
# define PLATFORM_ID "OS2"
|
|
||||||
|
|
||||||
# elif defined(__WINDOWS__)
|
|
||||||
# define PLATFORM_ID "Windows3x"
|
|
||||||
|
|
||||||
# elif defined(__VXWORKS__)
|
|
||||||
# define PLATFORM_ID "VxWorks"
|
|
||||||
|
|
||||||
# else /* unknown platform */
|
|
||||||
# define PLATFORM_ID
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__INTEGRITY)
|
|
||||||
# if defined(INT_178B)
|
|
||||||
# define PLATFORM_ID "Integrity178"
|
|
||||||
|
|
||||||
# else /* regular Integrity */
|
|
||||||
# define PLATFORM_ID "Integrity"
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# elif defined(_ADI_COMPILER)
|
|
||||||
# define PLATFORM_ID "ADSP"
|
|
||||||
|
|
||||||
#else /* unknown platform */
|
|
||||||
# define PLATFORM_ID
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* For windows compilers MSVC and Intel we can determine
|
|
||||||
the architecture of the compiler being used. This is because
|
|
||||||
the compilers do not have flags that can change the architecture,
|
|
||||||
but rather depend on which compiler is being used
|
|
||||||
*/
|
|
||||||
#if defined(_WIN32) && defined(_MSC_VER)
|
|
||||||
# if defined(_M_IA64)
|
|
||||||
# define ARCHITECTURE_ID "IA64"
|
|
||||||
|
|
||||||
# elif defined(_M_ARM64EC)
|
|
||||||
# define ARCHITECTURE_ID "ARM64EC"
|
|
||||||
|
|
||||||
# elif defined(_M_X64) || defined(_M_AMD64)
|
|
||||||
# define ARCHITECTURE_ID "x64"
|
|
||||||
|
|
||||||
# elif defined(_M_IX86)
|
|
||||||
# define ARCHITECTURE_ID "X86"
|
|
||||||
|
|
||||||
# elif defined(_M_ARM64)
|
|
||||||
# define ARCHITECTURE_ID "ARM64"
|
|
||||||
|
|
||||||
# elif defined(_M_ARM)
|
|
||||||
# if _M_ARM == 4
|
|
||||||
# define ARCHITECTURE_ID "ARMV4I"
|
|
||||||
# elif _M_ARM == 5
|
|
||||||
# define ARCHITECTURE_ID "ARMV5I"
|
|
||||||
# else
|
|
||||||
# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# elif defined(_M_MIPS)
|
|
||||||
# define ARCHITECTURE_ID "MIPS"
|
|
||||||
|
|
||||||
# elif defined(_M_SH)
|
|
||||||
# define ARCHITECTURE_ID "SHx"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
# if defined(_M_I86)
|
|
||||||
# define ARCHITECTURE_ID "I86"
|
|
||||||
|
|
||||||
# elif defined(_M_IX86)
|
|
||||||
# define ARCHITECTURE_ID "X86"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
|
|
||||||
# if defined(__ICCARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__ICCRX__)
|
|
||||||
# define ARCHITECTURE_ID "RX"
|
|
||||||
|
|
||||||
# elif defined(__ICCRH850__)
|
|
||||||
# define ARCHITECTURE_ID "RH850"
|
|
||||||
|
|
||||||
# elif defined(__ICCRL78__)
|
|
||||||
# define ARCHITECTURE_ID "RL78"
|
|
||||||
|
|
||||||
# elif defined(__ICCRISCV__)
|
|
||||||
# define ARCHITECTURE_ID "RISCV"
|
|
||||||
|
|
||||||
# elif defined(__ICCAVR__)
|
|
||||||
# define ARCHITECTURE_ID "AVR"
|
|
||||||
|
|
||||||
# elif defined(__ICC430__)
|
|
||||||
# define ARCHITECTURE_ID "MSP430"
|
|
||||||
|
|
||||||
# elif defined(__ICCV850__)
|
|
||||||
# define ARCHITECTURE_ID "V850"
|
|
||||||
|
|
||||||
# elif defined(__ICC8051__)
|
|
||||||
# define ARCHITECTURE_ID "8051"
|
|
||||||
|
|
||||||
# elif defined(__ICCSTM8__)
|
|
||||||
# define ARCHITECTURE_ID "STM8"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__ghs__)
|
|
||||||
# if defined(__PPC64__)
|
|
||||||
# define ARCHITECTURE_ID "PPC64"
|
|
||||||
|
|
||||||
# elif defined(__ppc__)
|
|
||||||
# define ARCHITECTURE_ID "PPC"
|
|
||||||
|
|
||||||
# elif defined(__ARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__x86_64__)
|
|
||||||
# define ARCHITECTURE_ID "x64"
|
|
||||||
|
|
||||||
# elif defined(__i386__)
|
|
||||||
# define ARCHITECTURE_ID "X86"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__clang__) && defined(__ti__)
|
|
||||||
# if defined(__ARM_ARCH)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__TI_COMPILER_VERSION__)
|
|
||||||
# if defined(__TI_ARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__MSP430__)
|
|
||||||
# define ARCHITECTURE_ID "MSP430"
|
|
||||||
|
|
||||||
# elif defined(__TMS320C28XX__)
|
|
||||||
# define ARCHITECTURE_ID "TMS320C28x"
|
|
||||||
|
|
||||||
# elif defined(__TMS320C6X__) || defined(_TMS320C6X)
|
|
||||||
# define ARCHITECTURE_ID "TMS320C6x"
|
|
||||||
|
|
||||||
# else /* unknown architecture */
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# elif defined(__ADSPSHARC__)
|
|
||||||
# define ARCHITECTURE_ID "SHARC"
|
|
||||||
|
|
||||||
# elif defined(__ADSPBLACKFIN__)
|
|
||||||
# define ARCHITECTURE_ID "Blackfin"
|
|
||||||
|
|
||||||
#elif defined(__TASKING__)
|
|
||||||
|
|
||||||
# if defined(__CTC__) || defined(__CPTC__)
|
|
||||||
# define ARCHITECTURE_ID "TriCore"
|
|
||||||
|
|
||||||
# elif defined(__CMCS__)
|
|
||||||
# define ARCHITECTURE_ID "MCS"
|
|
||||||
|
|
||||||
# elif defined(__CARM__) || defined(__CPARM__)
|
|
||||||
# define ARCHITECTURE_ID "ARM"
|
|
||||||
|
|
||||||
# elif defined(__CARC__)
|
|
||||||
# define ARCHITECTURE_ID "ARC"
|
|
||||||
|
|
||||||
# elif defined(__C51__)
|
|
||||||
# define ARCHITECTURE_ID "8051"
|
|
||||||
|
|
||||||
# elif defined(__CPCP__)
|
|
||||||
# define ARCHITECTURE_ID "PCP"
|
|
||||||
|
|
||||||
# else
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__RENESAS__)
|
|
||||||
# if defined(__CCRX__)
|
|
||||||
# define ARCHITECTURE_ID "RX"
|
|
||||||
|
|
||||||
# elif defined(__CCRL__)
|
|
||||||
# define ARCHITECTURE_ID "RL78"
|
|
||||||
|
|
||||||
# elif defined(__CCRH__)
|
|
||||||
# define ARCHITECTURE_ID "RH850"
|
|
||||||
|
|
||||||
# else
|
|
||||||
# define ARCHITECTURE_ID ""
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
# define ARCHITECTURE_ID
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Convert integer to decimal digit literals. */
|
|
||||||
#define DEC(n) \
|
|
||||||
('0' + (((n) / 10000000)%10)), \
|
|
||||||
('0' + (((n) / 1000000)%10)), \
|
|
||||||
('0' + (((n) / 100000)%10)), \
|
|
||||||
('0' + (((n) / 10000)%10)), \
|
|
||||||
('0' + (((n) / 1000)%10)), \
|
|
||||||
('0' + (((n) / 100)%10)), \
|
|
||||||
('0' + (((n) / 10)%10)), \
|
|
||||||
('0' + ((n) % 10))
|
|
||||||
|
|
||||||
/* Convert integer to hex digit literals. */
|
|
||||||
#define HEX(n) \
|
|
||||||
('0' + ((n)>>28 & 0xF)), \
|
|
||||||
('0' + ((n)>>24 & 0xF)), \
|
|
||||||
('0' + ((n)>>20 & 0xF)), \
|
|
||||||
('0' + ((n)>>16 & 0xF)), \
|
|
||||||
('0' + ((n)>>12 & 0xF)), \
|
|
||||||
('0' + ((n)>>8 & 0xF)), \
|
|
||||||
('0' + ((n)>>4 & 0xF)), \
|
|
||||||
('0' + ((n) & 0xF))
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the version number. */
|
|
||||||
#ifdef COMPILER_VERSION
|
|
||||||
char const* info_version = "INFO" ":" "compiler_version[" COMPILER_VERSION "]";
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the version number components. */
|
|
||||||
#elif defined(COMPILER_VERSION_MAJOR)
|
|
||||||
char const info_version[] = {
|
|
||||||
'I', 'N', 'F', 'O', ':',
|
|
||||||
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[',
|
|
||||||
COMPILER_VERSION_MAJOR,
|
|
||||||
# ifdef COMPILER_VERSION_MINOR
|
|
||||||
'.', COMPILER_VERSION_MINOR,
|
|
||||||
# ifdef COMPILER_VERSION_PATCH
|
|
||||||
'.', COMPILER_VERSION_PATCH,
|
|
||||||
# ifdef COMPILER_VERSION_TWEAK
|
|
||||||
'.', COMPILER_VERSION_TWEAK,
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
']','\0'};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the internal version number. */
|
|
||||||
#ifdef COMPILER_VERSION_INTERNAL
|
|
||||||
char const info_version_internal[] = {
|
|
||||||
'I', 'N', 'F', 'O', ':',
|
|
||||||
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_',
|
|
||||||
'i','n','t','e','r','n','a','l','[',
|
|
||||||
COMPILER_VERSION_INTERNAL,']','\0'};
|
|
||||||
#elif defined(COMPILER_VERSION_INTERNAL_STR)
|
|
||||||
char const* info_version_internal = "INFO" ":" "compiler_version_internal[" COMPILER_VERSION_INTERNAL_STR "]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct a string literal encoding the version number components. */
|
|
||||||
#ifdef SIMULATE_VERSION_MAJOR
|
|
||||||
char const info_simulate_version[] = {
|
|
||||||
'I', 'N', 'F', 'O', ':',
|
|
||||||
's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[',
|
|
||||||
SIMULATE_VERSION_MAJOR,
|
|
||||||
# ifdef SIMULATE_VERSION_MINOR
|
|
||||||
'.', SIMULATE_VERSION_MINOR,
|
|
||||||
# ifdef SIMULATE_VERSION_PATCH
|
|
||||||
'.', SIMULATE_VERSION_PATCH,
|
|
||||||
# ifdef SIMULATE_VERSION_TWEAK
|
|
||||||
'.', SIMULATE_VERSION_TWEAK,
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
']','\0'};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct the string literal in pieces to prevent the source from
|
|
||||||
getting matched. Store it in a pointer rather than an array
|
|
||||||
because some compilers will just produce instructions to fill the
|
|
||||||
array rather than assigning a pointer to a static array. */
|
|
||||||
char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]";
|
|
||||||
char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define CXX_STD_98 199711L
|
|
||||||
#define CXX_STD_11 201103L
|
|
||||||
#define CXX_STD_14 201402L
|
|
||||||
#define CXX_STD_17 201703L
|
|
||||||
#define CXX_STD_20 202002L
|
|
||||||
#define CXX_STD_23 202302L
|
|
||||||
|
|
||||||
#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG)
|
|
||||||
# if _MSVC_LANG > CXX_STD_17
|
|
||||||
# define CXX_STD _MSVC_LANG
|
|
||||||
# elif _MSVC_LANG == CXX_STD_17 && defined(__cpp_aggregate_paren_init)
|
|
||||||
# define CXX_STD CXX_STD_20
|
|
||||||
# elif _MSVC_LANG > CXX_STD_14 && __cplusplus > CXX_STD_17
|
|
||||||
# define CXX_STD CXX_STD_20
|
|
||||||
# elif _MSVC_LANG > CXX_STD_14
|
|
||||||
# define CXX_STD CXX_STD_17
|
|
||||||
# elif defined(__INTEL_CXX11_MODE__) && defined(__cpp_aggregate_nsdmi)
|
|
||||||
# define CXX_STD CXX_STD_14
|
|
||||||
# elif defined(__INTEL_CXX11_MODE__)
|
|
||||||
# define CXX_STD CXX_STD_11
|
|
||||||
# else
|
|
||||||
# define CXX_STD CXX_STD_98
|
|
||||||
# endif
|
|
||||||
#elif defined(_MSC_VER) && defined(_MSVC_LANG)
|
|
||||||
# if _MSVC_LANG > __cplusplus
|
|
||||||
# define CXX_STD _MSVC_LANG
|
|
||||||
# else
|
|
||||||
# define CXX_STD __cplusplus
|
|
||||||
# endif
|
|
||||||
#elif defined(__NVCOMPILER)
|
|
||||||
# if __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init)
|
|
||||||
# define CXX_STD CXX_STD_20
|
|
||||||
# else
|
|
||||||
# define CXX_STD __cplusplus
|
|
||||||
# endif
|
|
||||||
#elif defined(__INTEL_COMPILER) || defined(__PGI)
|
|
||||||
# if __cplusplus == CXX_STD_11 && defined(__cpp_namespace_attributes)
|
|
||||||
# define CXX_STD CXX_STD_17
|
|
||||||
# elif __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi)
|
|
||||||
# define CXX_STD CXX_STD_14
|
|
||||||
# else
|
|
||||||
# define CXX_STD __cplusplus
|
|
||||||
# endif
|
|
||||||
#elif (defined(__IBMCPP__) || defined(__ibmxl__)) && defined(__linux__)
|
|
||||||
# if __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi)
|
|
||||||
# define CXX_STD CXX_STD_14
|
|
||||||
# else
|
|
||||||
# define CXX_STD __cplusplus
|
|
||||||
# endif
|
|
||||||
#elif __cplusplus == 1 && defined(__GXX_EXPERIMENTAL_CXX0X__)
|
|
||||||
# define CXX_STD CXX_STD_11
|
|
||||||
#else
|
|
||||||
# define CXX_STD __cplusplus
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char* info_language_standard_default = "INFO" ":" "standard_default["
|
|
||||||
#if CXX_STD > CXX_STD_23
|
|
||||||
"26"
|
|
||||||
#elif CXX_STD > CXX_STD_20
|
|
||||||
"23"
|
|
||||||
#elif CXX_STD > CXX_STD_17
|
|
||||||
"20"
|
|
||||||
#elif CXX_STD > CXX_STD_14
|
|
||||||
"17"
|
|
||||||
#elif CXX_STD > CXX_STD_11
|
|
||||||
"14"
|
|
||||||
#elif CXX_STD >= CXX_STD_11
|
|
||||||
"11"
|
|
||||||
#else
|
|
||||||
"98"
|
|
||||||
#endif
|
|
||||||
"]";
|
|
||||||
|
|
||||||
const char* info_language_extensions_default = "INFO" ":" "extensions_default["
|
|
||||||
#if (defined(__clang__) || defined(__GNUC__) || defined(__xlC__) || \
|
|
||||||
defined(__TI_COMPILER_VERSION__) || defined(__RENESAS__)) && \
|
|
||||||
!defined(__STRICT_ANSI__)
|
|
||||||
"ON"
|
|
||||||
#else
|
|
||||||
"OFF"
|
|
||||||
#endif
|
|
||||||
"]";
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
int require = 0;
|
|
||||||
require += info_compiler[argc];
|
|
||||||
require += info_platform[argc];
|
|
||||||
require += info_arch[argc];
|
|
||||||
#ifdef COMPILER_VERSION_MAJOR
|
|
||||||
require += info_version[argc];
|
|
||||||
#endif
|
|
||||||
#if defined(COMPILER_VERSION_INTERNAL) || defined(COMPILER_VERSION_INTERNAL_STR)
|
|
||||||
require += info_version_internal[argc];
|
|
||||||
#endif
|
|
||||||
#ifdef SIMULATE_ID
|
|
||||||
require += info_simulate[argc];
|
|
||||||
#endif
|
|
||||||
#ifdef SIMULATE_VERSION_MAJOR
|
|
||||||
require += info_simulate_version[argc];
|
|
||||||
#endif
|
|
||||||
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
|
|
||||||
require += info_cray[argc];
|
|
||||||
#endif
|
|
||||||
require += info_language_standard_default[argc];
|
|
||||||
require += info_language_extensions_default[argc];
|
|
||||||
(void)argv;
|
|
||||||
return require;
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
#include <AvailabilityMacros.h>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
# This file is generated by cmake for dependency checking of the CMakeCache.txt file
|
|
||||||
@@ -41,3 +41,22 @@ target_compile_definitions(basic1_emulator PRIVATE LUA_32BITS=1)
|
|||||||
target_include_directories(basic1_emulator PRIVATE . .. ../display ../fonts ../games ../lib ../lib/lua)
|
target_include_directories(basic1_emulator PRIVATE . .. ../display ../fonts ../games ../lib ../lib/lua)
|
||||||
|
|
||||||
target_link_libraries(basic1_emulator SFML::Graphics SFML::Window SFML::System)
|
target_link_libraries(basic1_emulator SFML::Graphics SFML::Window SFML::System)
|
||||||
|
|
||||||
|
# Copy Lua example files to build directory
|
||||||
|
file(GLOB LUA_EXAMPLE_FILES "../games/lua_examples/*.lua")
|
||||||
|
foreach(LUA_FILE ${LUA_EXAMPLE_FILES})
|
||||||
|
get_filename_component(FILENAME ${LUA_FILE} NAME)
|
||||||
|
add_custom_command(TARGET basic1_emulator POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy
|
||||||
|
${LUA_FILE}
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/games/lua_examples/${FILENAME}
|
||||||
|
COMMENT "Copying Lua example: ${FILENAME}"
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Also ensure directory exists
|
||||||
|
add_custom_command(TARGET basic1_emulator POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/games/lua_examples
|
||||||
|
COMMENT "Creating games/lua_examples directory"
|
||||||
|
)
|
||||||
|
|||||||
+1307
-27
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,61 @@
|
|||||||
|
# Install script for directory: /Users/adolforeyna/Projects/basic1/emulator
|
||||||
|
|
||||||
|
# Set the install prefix
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "/usr/local")
|
||||||
|
endif()
|
||||||
|
string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
# Set the install configuration name.
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
|
||||||
|
if(BUILD_TYPE)
|
||||||
|
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
|
||||||
|
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_CONFIG_NAME "")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set the component getting installed.
|
||||||
|
if(NOT CMAKE_INSTALL_COMPONENT)
|
||||||
|
if(COMPONENT)
|
||||||
|
message(STATUS "Install component: \"${COMPONENT}\"")
|
||||||
|
set(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_COMPONENT)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Is this installation the result of a crosscompile?
|
||||||
|
if(NOT DEFINED CMAKE_CROSSCOMPILING)
|
||||||
|
set(CMAKE_CROSSCOMPILING "FALSE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set path to fallback-tool for dependency-resolution.
|
||||||
|
if(NOT DEFINED CMAKE_OBJDUMP)
|
||||||
|
set(CMAKE_OBJDUMP "/usr/bin/objdump")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT
|
||||||
|
"${CMAKE_INSTALL_MANIFEST_FILES}")
|
||||||
|
if(CMAKE_INSTALL_LOCAL_ONLY)
|
||||||
|
file(WRITE "/Users/adolforeyna/Projects/basic1/emulator/install_local_manifest.txt"
|
||||||
|
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
|
||||||
|
endif()
|
||||||
|
if(CMAKE_INSTALL_COMPONENT)
|
||||||
|
if(CMAKE_INSTALL_COMPONENT MATCHES "^[a-zA-Z0-9_.+-]+$")
|
||||||
|
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
|
||||||
|
else()
|
||||||
|
string(MD5 CMAKE_INST_COMP_HASH "${CMAKE_INSTALL_COMPONENT}")
|
||||||
|
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INST_COMP_HASH}.txt")
|
||||||
|
unset(CMAKE_INST_COMP_HASH)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_MANIFEST "install_manifest.txt")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_INSTALL_LOCAL_ONLY)
|
||||||
|
file(WRITE "/Users/adolforeyna/Projects/basic1/emulator/${CMAKE_INSTALL_MANIFEST}"
|
||||||
|
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
|
||||||
|
endif()
|
||||||
@@ -15,6 +15,7 @@ public:
|
|||||||
virtual bool update(const InputEvent& event) = 0;
|
virtual bool update(const InputEvent& event) = 0;
|
||||||
virtual void draw() = 0;
|
virtual void draw() = 0;
|
||||||
virtual bool wants_to_exit() const { return false; }
|
virtual bool wants_to_exit() const { return false; }
|
||||||
|
virtual bool wants_frame_updates() const { return false; }
|
||||||
|
|
||||||
// Public members for Lua bindings access
|
// Public members for Lua bindings access
|
||||||
uint16_t width;
|
uint16_t width;
|
||||||
|
|||||||
+95
-17
@@ -7,8 +7,9 @@
|
|||||||
extern Font font_5x5_obj;
|
extern Font font_5x5_obj;
|
||||||
GameLauncher::GameLauncher(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
|
GameLauncher::GameLauncher(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
|
||||||
: width(width), height(height), renderer(renderer), gui(gui), input_manager(input_manager),
|
: width(width), height(height), renderer(renderer), gui(gui), input_manager(input_manager),
|
||||||
selected_index(0), selected_game(nullptr) {}
|
selected_index(0), selected_game(nullptr), current_page(0) {}
|
||||||
void GameLauncher::register_game(const char* name, const char* description, Game* (*factory)(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)) {
|
void GameLauncher::register_game(const char* name, const char* description,
|
||||||
|
std::function<Game*(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)> factory) {
|
||||||
GameEntry entry;
|
GameEntry entry;
|
||||||
entry.name = name;
|
entry.name = name;
|
||||||
entry.description = description;
|
entry.description = description;
|
||||||
@@ -19,30 +20,70 @@ void GameLauncher::register_game(const char* name, const char* description, Game
|
|||||||
void GameLauncher::draw() {
|
void GameLauncher::draw() {
|
||||||
LowLevelWindow* window = gui->draw_new_window(10, 10, width - 20, height - 20, "Game Launcher");
|
LowLevelWindow* window = gui->draw_new_window(10, 10, width - 20, height - 20, "Game Launcher");
|
||||||
renderer->set_font(&font_5x5_obj);
|
renderer->set_font(&font_5x5_obj);
|
||||||
renderer->draw_string_scaled(30, 40, "Select a Game:", 2);
|
|
||||||
for (size_t i = 0; i < games.size(); i++) {
|
int total_pages = get_total_pages();
|
||||||
int y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
|
char title[64];
|
||||||
bool is_selected = ((int)i == selected_index);
|
snprintf(title, sizeof(title), "Select a Game: (Page %d/%d)", current_page + 1, total_pages);
|
||||||
|
renderer->draw_string_scaled(30, 40, title, 2);
|
||||||
|
|
||||||
|
int page_start = get_page_start_index();
|
||||||
|
int page_end = get_page_end_index();
|
||||||
|
|
||||||
|
for (int i = page_start; i < page_end && i < (int)games.size(); i++) {
|
||||||
|
int item_index = i - page_start;
|
||||||
|
int y = MENU_Y_START + (item_index * MENU_ITEM_HEIGHT);
|
||||||
|
bool is_selected = (i == selected_index);
|
||||||
gui->draw_button(window, 20, y, games[i].name, is_selected, true);
|
gui->draw_button(window, 20, y, games[i].name, is_selected, true);
|
||||||
renderer->set_font(&font_5x5_obj);
|
renderer->set_font(&font_5x5_obj);
|
||||||
renderer->set_text_color(true);
|
renderer->set_text_color(true);
|
||||||
renderer->draw_string_scaled(50, y + 36, games[i].description, 1);
|
renderer->draw_string_scaled(50, y + 36, games[i].description, 1);
|
||||||
}
|
}
|
||||||
const char* instructions;
|
|
||||||
if (input_manager->has_buttons()) {
|
if (total_pages > 1) {
|
||||||
instructions = "Touch game or use KEY0/KEY1";
|
int button_y = height - 65;
|
||||||
|
gui->draw_button(window, PREV_BUTTON_X, button_y, "< PREV", false, true);
|
||||||
|
gui->draw_button(window, NEXT_BUTTON_X, button_y, "NEXT >", false, true);
|
||||||
|
renderer->set_font(&font_5x5_obj);
|
||||||
|
renderer->draw_string_scaled(30, height - 25, "Touch buttons or KEY0/KEY1", 1);
|
||||||
} else {
|
} else {
|
||||||
instructions = "Touch game to play";
|
renderer->set_font(&font_5x5_obj);
|
||||||
|
renderer->draw_string_scaled(30, height - 35, "KEY0: Navigate | KEY1: Select | Touch to play", 1);
|
||||||
}
|
}
|
||||||
renderer->set_font(&font_5x5_obj);
|
|
||||||
renderer->draw_string_scaled(30, height - 35, instructions, 2);
|
|
||||||
}
|
}
|
||||||
bool GameLauncher::update(const InputEvent& event) {
|
bool GameLauncher::update(const InputEvent& event) {
|
||||||
bool needs_refresh = false;
|
bool needs_refresh = false;
|
||||||
|
int total_pages = get_total_pages();
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case INPUT_TOUCH_DOWN: {
|
case INPUT_TOUCH_DOWN: {
|
||||||
for (size_t i = 0; i < games.size(); i++) {
|
// Check if touch is on navigation buttons (if multiple pages)
|
||||||
int y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
|
if (total_pages > 1) {
|
||||||
|
if (event.x >= PREV_BUTTON_X && event.x < PREV_BUTTON_X + BUTTON_WIDTH &&
|
||||||
|
event.y >= NAV_BUTTON_Y && event.y < NAV_BUTTON_Y + BUTTON_HEIGHT) {
|
||||||
|
if (current_page > 0) {
|
||||||
|
current_page--;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
needs_refresh = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (event.x >= NEXT_BUTTON_X && event.x < NEXT_BUTTON_X + BUTTON_WIDTH &&
|
||||||
|
event.y >= NAV_BUTTON_Y && event.y < NAV_BUTTON_Y + BUTTON_HEIGHT) {
|
||||||
|
if (current_page + 1 < total_pages) {
|
||||||
|
current_page++;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
needs_refresh = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int page_start = get_page_start_index();
|
||||||
|
int page_end = get_page_end_index();
|
||||||
|
|
||||||
|
for (int i = page_start; i < page_end && i < (int)games.size(); i++) {
|
||||||
|
int item_index = i - page_start;
|
||||||
|
int y = MENU_Y_START + (item_index * MENU_ITEM_HEIGHT);
|
||||||
if (event.y >= y - 5 && event.y < y + MENU_ITEM_HEIGHT - 5) {
|
if (event.y >= y - 5 && event.y < y + MENU_ITEM_HEIGHT - 5) {
|
||||||
selected_game = games[i].factory(width, height, renderer, gui, input_manager);
|
selected_game = games[i].factory(width, height, renderer, gui, input_manager);
|
||||||
if (selected_game) {
|
if (selected_game) {
|
||||||
@@ -55,10 +96,33 @@ bool GameLauncher::update(const InputEvent& event) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case INPUT_BUTTON_0: {
|
case INPUT_BUTTON_0: {
|
||||||
if (games.size() > 0) {
|
int page_start = get_page_start_index();
|
||||||
selected_index = (selected_index + 1) % games.size();
|
int page_end = get_page_end_index();
|
||||||
needs_refresh = true;
|
|
||||||
|
if (page_end - page_start > 1) {
|
||||||
|
int old_index = selected_index;
|
||||||
|
selected_index++;
|
||||||
|
if (selected_index >= page_end) {
|
||||||
|
if (current_page + 1 < total_pages) {
|
||||||
|
current_page++;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
} else {
|
||||||
|
current_page = 0;
|
||||||
|
selected_index = 0;
|
||||||
|
}
|
||||||
|
} else if (selected_index < page_start) {
|
||||||
|
selected_index = page_start;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (current_page + 1 < total_pages) {
|
||||||
|
current_page++;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
} else {
|
||||||
|
current_page = 0;
|
||||||
|
selected_index = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
needs_refresh = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case INPUT_BUTTON_1: {
|
case INPUT_BUTTON_1: {
|
||||||
@@ -83,4 +147,18 @@ void GameLauncher::reset() {
|
|||||||
selected_game = nullptr;
|
selected_game = nullptr;
|
||||||
}
|
}
|
||||||
selected_index = 0;
|
selected_index = 0;
|
||||||
|
current_page = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GameLauncher::get_total_pages() const {
|
||||||
|
if (games.empty()) return 1;
|
||||||
|
return (games.size() + GAMES_PER_PAGE - 1) / GAMES_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GameLauncher::get_page_start_index() const {
|
||||||
|
return current_page * GAMES_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GameLauncher::get_page_end_index() const {
|
||||||
|
return (current_page + 1) * GAMES_PER_PAGE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,19 @@ private:
|
|||||||
std::vector<GameEntry> games;
|
std::vector<GameEntry> games;
|
||||||
int selected_index;
|
int selected_index;
|
||||||
Game* selected_game;
|
Game* selected_game;
|
||||||
|
int current_page;
|
||||||
|
|
||||||
static const int MENU_Y_START = 60;
|
static const int MENU_Y_START = 60;
|
||||||
static const int MENU_ITEM_HEIGHT = 40;
|
static const int MENU_ITEM_HEIGHT = 40;
|
||||||
static const int MENU_PADDING = 10;
|
static const int MENU_PADDING = 10;
|
||||||
|
static const int GAMES_PER_PAGE = 4;
|
||||||
|
static const int NAV_BUTTON_Y = 235;
|
||||||
|
static const int PREV_BUTTON_X = 30;
|
||||||
|
static const int NEXT_BUTTON_X = 200;
|
||||||
|
static const int BUTTON_WIDTH = 150;
|
||||||
|
static const int BUTTON_HEIGHT = 40;
|
||||||
|
|
||||||
|
int get_total_pages() const;
|
||||||
|
int get_page_start_index() const;
|
||||||
|
int get_page_end_index() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
-- NAME: Bouncing Ball
|
||||||
|
-- DESC: Physics demo with state management
|
||||||
|
|
||||||
|
-- States
|
||||||
|
local STATE_PAUSED = 0
|
||||||
|
local STATE_RUNNING = 1
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_PAUSED
|
||||||
|
game.vars.ball_x = game.width() / 2
|
||||||
|
game.vars.ball_y = game.height() / 2
|
||||||
|
game.vars.vel_x = 3
|
||||||
|
game.vars.vel_y = 2
|
||||||
|
game.vars.radius = 10
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
|
||||||
|
-- Enable continuous frame updates for smooth animation
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Bouncing Ball initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
-- Toggle pause on tap
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
if game.vars.state == STATE_PAUSED then
|
||||||
|
game.vars.state = STATE_RUNNING
|
||||||
|
else
|
||||||
|
game.vars.state = STATE_PAUSED
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics if running (on any frame tick)
|
||||||
|
if event.type == INPUT.FRAME_TICK and game.vars.state == STATE_RUNNING then
|
||||||
|
-- Move ball
|
||||||
|
game.vars.ball_x = game.vars.ball_x + game.vars.vel_x
|
||||||
|
game.vars.ball_y = game.vars.ball_y + game.vars.vel_y
|
||||||
|
|
||||||
|
-- Bounce off walls
|
||||||
|
if game.vars.ball_x - game.vars.radius < 0 or game.vars.ball_x + game.vars.radius > game.width() then
|
||||||
|
game.vars.vel_x = -game.vars.vel_x
|
||||||
|
game.vars.ball_x = math.max(game.vars.radius, math.min(game.width() - game.vars.radius, game.vars.ball_x))
|
||||||
|
end
|
||||||
|
|
||||||
|
if game.vars.ball_y - game.vars.radius < 0 or game.vars.ball_y + game.vars.radius > game.height() then
|
||||||
|
game.vars.vel_y = -game.vars.vel_y
|
||||||
|
game.vars.ball_y = math.max(game.vars.radius, math.min(game.height() - game.vars.radius, game.vars.ball_y))
|
||||||
|
end
|
||||||
|
|
||||||
|
game.vars.frame_count = game.vars.frame_count + 1
|
||||||
|
return true -- Always redraw when running
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false)
|
||||||
|
|
||||||
|
-- Draw ball
|
||||||
|
renderer.circle(game.vars.ball_x, game.vars.ball_y, game.vars.radius, true, true)
|
||||||
|
|
||||||
|
-- Draw trail (previous positions)
|
||||||
|
local trail_radius = game.vars.radius - 2
|
||||||
|
if trail_radius > 2 then
|
||||||
|
renderer.circle(game.vars.ball_x - game.vars.vel_x,
|
||||||
|
game.vars.ball_y - game.vars.vel_y,
|
||||||
|
trail_radius, true, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw status
|
||||||
|
if game.vars.state == STATE_PAUSED then
|
||||||
|
renderer.text(10, 10, "PAUSED - Tap to start", true)
|
||||||
|
else
|
||||||
|
renderer.text(10, 10, "Frames: " .. tostring(game.vars.frame_count), true)
|
||||||
|
renderer.text(10, 25, "Tap to pause", true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw velocity vector
|
||||||
|
local arrow_x = game.vars.ball_x + game.vars.vel_x * 3
|
||||||
|
local arrow_y = game.vars.ball_y + game.vars.vel_y * 3
|
||||||
|
renderer.line(game.vars.ball_x, game.vars.ball_y, arrow_x, arrow_y, true, 2)
|
||||||
|
end
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
-- NAME: Touch Counter
|
||||||
|
-- DESC: Simple tap counter demo
|
||||||
|
|
||||||
|
-- Initialize game state
|
||||||
|
function init()
|
||||||
|
game.vars.count = 0
|
||||||
|
game.vars.last_x = 0
|
||||||
|
game.vars.last_y = 0
|
||||||
|
print("Counter initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update game logic based on input
|
||||||
|
function update(event)
|
||||||
|
-- Check if touch/button pressed
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.count = game.vars.count + 1
|
||||||
|
game.vars.last_x = event.x
|
||||||
|
game.vars.last_y = event.y
|
||||||
|
print("Count: " .. game.vars.count)
|
||||||
|
return true -- Request redraw
|
||||||
|
end
|
||||||
|
|
||||||
|
return false -- No redraw needed
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw the game
|
||||||
|
function draw()
|
||||||
|
-- Clear screen
|
||||||
|
renderer.clear(true)
|
||||||
|
|
||||||
|
-- Draw title
|
||||||
|
renderer.text(20, 20, "Touch Counter", true)
|
||||||
|
|
||||||
|
-- Draw count (centered)
|
||||||
|
local count_text = "Count: " .. tostring(game.vars.count)
|
||||||
|
renderer.text(game.width() / 2 - 40, game.height() / 2 - 10, count_text, true)
|
||||||
|
|
||||||
|
-- Draw last touch position
|
||||||
|
if game.vars.count > 0 then
|
||||||
|
local pos_text = "Last: (" .. tostring(game.vars.last_x) .. ", " .. tostring(game.vars.last_y) .. ")"
|
||||||
|
renderer.text(20, game.height() - 30, pos_text, true)
|
||||||
|
|
||||||
|
-- Draw marker at last touch
|
||||||
|
renderer.circle(game.vars.last_x, game.vars.last_y, 5, true, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw instructions
|
||||||
|
renderer.text(20, 50, "Tap screen to increment", true)
|
||||||
|
end
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
-- NAME: Snake Game
|
||||||
|
-- DESC: Classic snake with state machine
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local CELL_SIZE = 10
|
||||||
|
local GRID_W = 40
|
||||||
|
local GRID_H = 28
|
||||||
|
|
||||||
|
-- Initialize game
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.high_score = 0
|
||||||
|
|
||||||
|
-- Snake as array of {x, y} positions
|
||||||
|
game.vars.snake = {}
|
||||||
|
game.vars.snake[1] = {x = 20, y = 14}
|
||||||
|
game.vars.snake[2] = {x = 19, y = 14}
|
||||||
|
game.vars.snake[3] = {x = 18, y = 14}
|
||||||
|
|
||||||
|
game.vars.dir_x = 1
|
||||||
|
game.vars.dir_y = 0
|
||||||
|
|
||||||
|
game.vars.food_x = 30
|
||||||
|
game.vars.food_y = 14
|
||||||
|
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
game.vars.move_speed = 10 -- Frames between moves
|
||||||
|
|
||||||
|
-- Enable continuous frame updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Snake Game initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update game logic
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
-- State: MENU
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
game.vars.score = 0
|
||||||
|
init_snake()
|
||||||
|
spawn_food()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- State: PLAYING
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle input for direction change
|
||||||
|
if event.type == INPUT.TOUCH_DOWN then
|
||||||
|
local head = game.vars.snake[1]
|
||||||
|
local head_screen_x = head.x * CELL_SIZE
|
||||||
|
local head_screen_y = head.y * CELL_SIZE
|
||||||
|
|
||||||
|
local dx = event.x - head_screen_x
|
||||||
|
local dy = event.y - head_screen_y
|
||||||
|
|
||||||
|
-- Change direction based on touch relative to head
|
||||||
|
if math.abs(dx) > math.abs(dy) then
|
||||||
|
if dx > 0 and game.vars.dir_x ~= -1 then
|
||||||
|
game.vars.dir_x = 1
|
||||||
|
game.vars.dir_y = 0
|
||||||
|
elseif dx < 0 and game.vars.dir_x ~= 1 then
|
||||||
|
game.vars.dir_x = -1
|
||||||
|
game.vars.dir_y = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if dy > 0 and game.vars.dir_y ~= -1 then
|
||||||
|
game.vars.dir_x = 0
|
||||||
|
game.vars.dir_y = 1
|
||||||
|
elseif dy < 0 and game.vars.dir_y ~= 1 then
|
||||||
|
game.vars.dir_x = 0
|
||||||
|
game.vars.dir_y = -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move snake every N frames
|
||||||
|
game.vars.frame_count = game.vars.frame_count + 1
|
||||||
|
if game.vars.frame_count >= game.vars.move_speed then
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
move_snake()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- State: GAME_OVER
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw game
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false)
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
-- Draw: MENU
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text(game.width() / 2 - 30, game.height() / 2 - 20, "SNAKE", true)
|
||||||
|
renderer.text(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||||
|
|
||||||
|
if game.vars.high_score > 0 then
|
||||||
|
local hs_text = "High: " .. tostring(game.vars.high_score)
|
||||||
|
renderer.text(game.width() / 2 - 30, game.height() / 2 + 20, hs_text, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw: PLAYING
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Draw snake
|
||||||
|
for i = 1, #game.vars.snake do
|
||||||
|
local seg = game.vars.snake[i]
|
||||||
|
local filled = (i == 1) -- Head filled, body outline
|
||||||
|
renderer.rect(seg.x * CELL_SIZE, seg.y * CELL_SIZE, CELL_SIZE, CELL_SIZE, true, filled)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw food
|
||||||
|
renderer.circle(game.vars.food_x * CELL_SIZE + CELL_SIZE / 2,
|
||||||
|
game.vars.food_y * CELL_SIZE + CELL_SIZE / 2,
|
||||||
|
CELL_SIZE / 2, true, true)
|
||||||
|
|
||||||
|
-- Draw score
|
||||||
|
local score_text = "Score: " .. tostring(game.vars.score)
|
||||||
|
renderer.text(5, 5, score_text, true)
|
||||||
|
|
||||||
|
-- Draw: GAME_OVER
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
renderer.text(game.width() / 2 - 40, game.height() / 2 - 20, "GAME OVER", true)
|
||||||
|
|
||||||
|
local score_text = "Score: " .. tostring(game.vars.score)
|
||||||
|
renderer.text(game.width() / 2 - 40, game.height() / 2, score_text, true)
|
||||||
|
|
||||||
|
renderer.text(game.width() / 2 - 60, game.height() / 2 + 20, "Tap to Continue", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper: Initialize snake
|
||||||
|
function init_snake()
|
||||||
|
game.vars.snake = {}
|
||||||
|
game.vars.snake[1] = {x = 20, y = 14}
|
||||||
|
game.vars.snake[2] = {x = 19, y = 14}
|
||||||
|
game.vars.snake[3] = {x = 18, y = 14}
|
||||||
|
game.vars.dir_x = 1
|
||||||
|
game.vars.dir_y = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper: Spawn food at random position
|
||||||
|
function spawn_food()
|
||||||
|
game.vars.food_x = math.random(0, GRID_W - 1)
|
||||||
|
game.vars.food_y = math.random(0, GRID_H - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper: Move snake
|
||||||
|
function move_snake()
|
||||||
|
local head = game.vars.snake[1]
|
||||||
|
local new_head = {
|
||||||
|
x = head.x + game.vars.dir_x,
|
||||||
|
y = head.y + game.vars.dir_y
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Check wall collision
|
||||||
|
if new_head.x < 0 or new_head.x >= GRID_W or new_head.y < 0 or new_head.y >= GRID_H then
|
||||||
|
game_over()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check self collision
|
||||||
|
for i = 1, #game.vars.snake do
|
||||||
|
local seg = game.vars.snake[i]
|
||||||
|
if new_head.x == seg.x and new_head.y == seg.y then
|
||||||
|
game_over()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check food collision
|
||||||
|
local ate_food = false
|
||||||
|
if new_head.x == game.vars.food_x and new_head.y == game.vars.food_y then
|
||||||
|
ate_food = true
|
||||||
|
game.vars.score = game.vars.score + 10
|
||||||
|
spawn_food()
|
||||||
|
|
||||||
|
-- Increase speed slightly
|
||||||
|
if game.vars.move_speed > 3 then
|
||||||
|
game.vars.move_speed = game.vars.move_speed - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move snake
|
||||||
|
table.insert(game.vars.snake, 1, new_head)
|
||||||
|
|
||||||
|
if not ate_food then
|
||||||
|
table.remove(game.vars.snake) -- Remove tail
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper: Game over
|
||||||
|
function game_over()
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
|
||||||
|
if game.vars.score > game.vars.high_score then
|
||||||
|
game.vars.high_score = game.vars.score
|
||||||
|
end
|
||||||
|
|
||||||
|
print("Game Over! Score: " .. game.vars.score)
|
||||||
|
end
|
||||||
@@ -171,7 +171,17 @@ bool LuaGame::wants_to_exit() const {
|
|||||||
|
|
||||||
return exit;
|
return exit;
|
||||||
}
|
}
|
||||||
|
bool LuaGame::wants_frame_updates() const {
|
||||||
|
if (!L) return false;
|
||||||
|
|
||||||
|
// Check registry for frame updates flag
|
||||||
|
lua_pushstring(L, "__wants_frame_updates");
|
||||||
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||||
|
bool wants_updates = lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return wants_updates;
|
||||||
|
}
|
||||||
bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) {
|
bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) {
|
||||||
int result = lua_pcall(L, nargs, nresults, 0);
|
int result = lua_pcall(L, nargs, nresults, 0);
|
||||||
if (result != LUA_OK) {
|
if (result != LUA_OK) {
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace fs = std::filesystem;
|
|||||||
// Structure to hold script path for factory closure
|
// Structure to hold script path for factory closure
|
||||||
struct LuaGameFactoryData {
|
struct LuaGameFactoryData {
|
||||||
char script_path[256];
|
char script_path[256];
|
||||||
|
char name[64];
|
||||||
|
char description[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<LuaGameFactoryData*> factory_data_list;
|
static std::vector<LuaGameFactoryData*> factory_data_list;
|
||||||
@@ -124,23 +126,22 @@ int LuaGameLoader::register_all_games(GameLauncher* launcher) {
|
|||||||
|
|
||||||
std::string script_path = entry.path().string();
|
std::string script_path = entry.path().string();
|
||||||
|
|
||||||
// Parse metadata
|
|
||||||
char name[64];
|
|
||||||
char description[128];
|
|
||||||
parse_metadata(script_path.c_str(), name, description);
|
|
||||||
|
|
||||||
printf("LuaGameLoader: Found %s - '%s'\n", entry.path().filename().string().c_str(), name);
|
|
||||||
|
|
||||||
// Create factory data (persistent for game lifetime)
|
// Create factory data (persistent for game lifetime)
|
||||||
LuaGameFactoryData* data = new LuaGameFactoryData();
|
LuaGameFactoryData* data = new LuaGameFactoryData();
|
||||||
strncpy(data->script_path, script_path.c_str(), sizeof(data->script_path) - 1);
|
strncpy(data->script_path, script_path.c_str(), sizeof(data->script_path) - 1);
|
||||||
data->script_path[sizeof(data->script_path) - 1] = '\0';
|
data->script_path[sizeof(data->script_path) - 1] = '\0';
|
||||||
|
|
||||||
|
// Parse metadata directly into persistent storage
|
||||||
|
parse_metadata(script_path.c_str(), data->name, data->description);
|
||||||
|
|
||||||
|
printf("LuaGameLoader: Found %s - '%s'\n", entry.path().filename().string().c_str(), data->name);
|
||||||
|
|
||||||
factory_data_list.push_back(data);
|
factory_data_list.push_back(data);
|
||||||
|
|
||||||
// Register with launcher - using lambda factory pattern
|
// Register with launcher - using lambda factory pattern
|
||||||
launcher->register_game(
|
launcher->register_game(
|
||||||
name,
|
data->name,
|
||||||
description[0] ? description : "Lua Script",
|
data->description[0] ? data->description : "Lua Script",
|
||||||
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
|
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
|
||||||
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
|
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
|
||||||
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);
|
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);
|
||||||
|
|||||||
+21
-8
@@ -62,13 +62,13 @@ int main() {
|
|||||||
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
||||||
|
|
||||||
while (const auto sfEvent = display.pollEvent()) {
|
while (const auto sfEvent = display.pollEvent()) {
|
||||||
if (const auto* closed = sfEvent->getIf<sf::Event::Closed>()) {
|
if (sfEvent->is<sf::Event::Closed>()) {
|
||||||
display.close();
|
display.close();
|
||||||
running = false;
|
running = false;
|
||||||
} else if (const auto* mousePressed = sfEvent->getIf<sf::Event::MouseButtonPressed>()) {
|
} else if (const auto* mouse = sfEvent->getIf<sf::Event::MouseButtonPressed>()) {
|
||||||
event.type = INPUT_TOUCH_DOWN;
|
event.type = INPUT_TOUCH_DOWN;
|
||||||
event.x = mousePressed->position.x;
|
event.x = mouse->position.x;
|
||||||
event.y = mousePressed->position.y;
|
event.y = mouse->position.y;
|
||||||
event.valid = true;
|
event.valid = true;
|
||||||
|
|
||||||
// Check for virtual buttons
|
// Check for virtual buttons
|
||||||
@@ -76,14 +76,14 @@ int main() {
|
|||||||
if (input_manager.check_virtual_buttons(event.x, event.y, virtual_type)) {
|
if (input_manager.check_virtual_buttons(event.x, event.y, virtual_type)) {
|
||||||
event.type = virtual_type;
|
event.type = virtual_type;
|
||||||
}
|
}
|
||||||
} else if (const auto* keyPressed = sfEvent->getIf<sf::Event::KeyPressed>()) {
|
} else if (const auto* key = sfEvent->getIf<sf::Event::KeyPressed>()) {
|
||||||
if (keyPressed->code == sf::Keyboard::Key::Space) {
|
if (key->code == sf::Keyboard::Key::Space) {
|
||||||
event.type = INPUT_BUTTON_0;
|
event.type = INPUT_BUTTON_0;
|
||||||
event.valid = true;
|
event.valid = true;
|
||||||
} else if (keyPressed->code == sf::Keyboard::Key::Enter) {
|
} else if (key->code == sf::Keyboard::Key::Enter) {
|
||||||
event.type = INPUT_BUTTON_1;
|
event.type = INPUT_BUTTON_1;
|
||||||
event.valid = true;
|
event.valid = true;
|
||||||
} else if (keyPressed->code == sf::Keyboard::Key::Escape) {
|
} else if (key->code == sf::Keyboard::Key::Escape) {
|
||||||
// Simulate long-press exit
|
// Simulate long-press exit
|
||||||
if (launcher.is_game_selected()) {
|
if (launcher.is_game_selected()) {
|
||||||
launcher.reset();
|
launcher.reset();
|
||||||
@@ -109,6 +109,19 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if game wants frame tick updates (independent of user input)
|
||||||
|
if (launcher.is_game_selected()) {
|
||||||
|
current_game = launcher.get_selected_game();
|
||||||
|
if (current_game->wants_frame_updates()) {
|
||||||
|
InputEvent frame_event = {INPUT_FRAME_TICK, 0, 0, 0, 0, 0, true};
|
||||||
|
needs_redraw = current_game->update(frame_event) || needs_redraw;
|
||||||
|
if (current_game->wants_to_exit()) {
|
||||||
|
launcher.reset();
|
||||||
|
needs_redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Always redraw every frame for emulator
|
// Always redraw every frame for emulator
|
||||||
renderer.clear_buffer();
|
renderer.clear_buffer();
|
||||||
if (launcher.is_game_selected()) {
|
if (launcher.is_game_selected()) {
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
@@ -125,6 +125,23 @@ static int lua_renderer_text(lua_State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// renderer.text_scaled(x, y, text, on, scale)
|
||||||
|
static int lua_renderer_text_scaled(lua_State* L) {
|
||||||
|
LuaGame* game = get_game(L);
|
||||||
|
if (!game) return 0;
|
||||||
|
|
||||||
|
int x = luaL_checkinteger(L, 1);
|
||||||
|
int y = luaL_checkinteger(L, 2);
|
||||||
|
const char* text = luaL_checkstring(L, 3);
|
||||||
|
bool on = lua_toboolean(L, 4);
|
||||||
|
int scale = lua_isnone(L, 5) ? 1 : luaL_checkinteger(L, 5);
|
||||||
|
|
||||||
|
game->renderer->set_text_color(on);
|
||||||
|
game->renderer->draw_string_scaled(x, y, text, scale, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// renderer.triangle(x0, y0, x1, y1, x2, y2, on, filled)
|
// renderer.triangle(x0, y0, x1, y1, x2, y2, on, filled)
|
||||||
static int lua_renderer_triangle(lua_State* L) {
|
static int lua_renderer_triangle(lua_State* L) {
|
||||||
LuaGame* game = get_game(L);
|
LuaGame* game = get_game(L);
|
||||||
@@ -186,6 +203,21 @@ static int lua_game_exit(lua_State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// game.set_frame_updates(enabled) - enable/disable continuous frame tick events
|
||||||
|
static int lua_game_set_frame_updates(lua_State* L) {
|
||||||
|
LuaGame* game = get_game(L);
|
||||||
|
if (!game) return 0;
|
||||||
|
|
||||||
|
bool enabled = lua_toboolean(L, 1);
|
||||||
|
|
||||||
|
// Set frame updates flag (will be checked in wants_frame_updates())
|
||||||
|
lua_pushstring(L, "__wants_frame_updates");
|
||||||
|
lua_pushboolean(L, enabled);
|
||||||
|
lua_settable(L, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// INPUT TYPE CONSTANTS
|
// INPUT TYPE CONSTANTS
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -222,6 +254,10 @@ static void register_input_constants(lua_State* L) {
|
|||||||
lua_pushinteger(L, 6);
|
lua_pushinteger(L, 6);
|
||||||
lua_settable(L, -3);
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "FRAME_TICK");
|
||||||
|
lua_pushinteger(L, 7);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
lua_setglobal(L, "INPUT");
|
lua_setglobal(L, "INPUT");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +298,10 @@ void lua_bindings_register(lua_State* L, LuaGame* game) {
|
|||||||
lua_pushcfunction(L, lua_renderer_text);
|
lua_pushcfunction(L, lua_renderer_text);
|
||||||
lua_settable(L, -3);
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "text_scaled");
|
||||||
|
lua_pushcfunction(L, lua_renderer_text_scaled);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
lua_pushstring(L, "triangle");
|
lua_pushstring(L, "triangle");
|
||||||
lua_pushcfunction(L, lua_renderer_triangle);
|
lua_pushcfunction(L, lua_renderer_triangle);
|
||||||
lua_settable(L, -3);
|
lua_settable(L, -3);
|
||||||
@@ -283,6 +323,10 @@ void lua_bindings_register(lua_State* L, LuaGame* game) {
|
|||||||
lua_pushcfunction(L, lua_game_exit);
|
lua_pushcfunction(L, lua_game_exit);
|
||||||
lua_settable(L, -3);
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "set_frame_updates");
|
||||||
|
lua_pushcfunction(L, lua_game_set_frame_updates);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
// Create empty vars table for persistent state
|
// Create empty vars table for persistent state
|
||||||
lua_pushstring(L, "vars");
|
lua_pushstring(L, "vars");
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
|
|||||||
@@ -0,0 +1,415 @@
|
|||||||
|
-- NAME: 2048
|
||||||
|
-- DESC: Merge tiles to reach 2048
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
local STATE_WIN = 3
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local GRID_SIZE = 4
|
||||||
|
|
||||||
|
-- Calculate tile size based on screen size
|
||||||
|
local function get_tile_size()
|
||||||
|
-- Use smallest dimension for square grid
|
||||||
|
local min_dim = math.min(game.width(), game.height())
|
||||||
|
local padding = math.floor(min_dim / 8)
|
||||||
|
local available = min_dim - (padding * 2)
|
||||||
|
local tile_size = math.floor(available / GRID_SIZE)
|
||||||
|
return tile_size
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_grid_start_x()
|
||||||
|
local tile_size = get_tile_size()
|
||||||
|
local grid_width = tile_size * GRID_SIZE
|
||||||
|
return math.floor((game.width() - grid_width) / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_grid_start_y()
|
||||||
|
local tile_size = get_tile_size()
|
||||||
|
local grid_height = tile_size * GRID_SIZE
|
||||||
|
return math.floor((game.height() - grid_height) / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
|
||||||
|
-- Grid (4x4, 0 = empty)
|
||||||
|
game.vars.grid = {}
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
game.vars.grid[y] = {}
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
game.vars.moved = false
|
||||||
|
game.vars.won = false
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
-- game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("2048 initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN then
|
||||||
|
-- Determine swipe direction
|
||||||
|
local direction = get_swipe_direction(event.x, event.y)
|
||||||
|
if direction then
|
||||||
|
game.vars.moved = false
|
||||||
|
move_tiles(direction)
|
||||||
|
|
||||||
|
if game.vars.moved then
|
||||||
|
spawn_tile()
|
||||||
|
|
||||||
|
-- Check win/lose
|
||||||
|
if has_tile(2048) then
|
||||||
|
if not game.vars.won then
|
||||||
|
game.vars.state = STATE_WIN
|
||||||
|
game.vars.won = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not can_move() then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_WIN then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
-- Can continue playing or go to menu
|
||||||
|
if event.y < game.height() / 2 then
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
else
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false)
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
local tile_size = get_tile_size()
|
||||||
|
local start_x = get_grid_start_x()
|
||||||
|
local start_y = get_grid_start_y()
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 15, game.height() / 2 - 30, "2048", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 + 30, "Welcome Adolfo", true, 2)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING or state == STATE_WIN or state == STATE_GAME_OVER then
|
||||||
|
-- Draw grid
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
local tile_x = start_x + (x - 1) * tile_size
|
||||||
|
local tile_y = start_y + (y - 1) * tile_size
|
||||||
|
local value = game.vars.grid[y][x]
|
||||||
|
|
||||||
|
if value == 0 then
|
||||||
|
-- Empty tile
|
||||||
|
renderer.rect(tile_x, tile_y, tile_size, tile_size, true, false)
|
||||||
|
else
|
||||||
|
local reduce_size
|
||||||
|
if value == 2 then
|
||||||
|
reduce_size = 10
|
||||||
|
elseif value == 4 then
|
||||||
|
reduce_size = 8
|
||||||
|
elseif value == 8 then
|
||||||
|
reduce_size = 6
|
||||||
|
elseif value == 16 then
|
||||||
|
reduce_size = 4
|
||||||
|
elseif value == 32 then
|
||||||
|
reduce_size = 2
|
||||||
|
else
|
||||||
|
reduce_size = 0
|
||||||
|
end
|
||||||
|
-- Empty tile
|
||||||
|
renderer.rect(tile_x, tile_y, tile_size, tile_size, true, false)
|
||||||
|
-- Filled tile
|
||||||
|
renderer.rect(tile_x+reduce_size, tile_y+reduce_size, tile_size-reduce_size*2, tile_size-reduce_size*2, true, true)
|
||||||
|
|
||||||
|
-- Draw value (simplified)
|
||||||
|
local text = tostring(value)
|
||||||
|
if string.len(text) < 2 then
|
||||||
|
renderer.text_scaled(tile_x + tile_size / 2 - 4, tile_y + tile_size / 2 - 8, text, false, 2)
|
||||||
|
else
|
||||||
|
renderer.text_scaled(tile_x + tile_size / 2 - 12, tile_y + tile_size / 2 - 8, text, false, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw score
|
||||||
|
renderer.text_scaled(10, 10, "Score: " .. tostring(game.vars.score), true, 2)
|
||||||
|
|
||||||
|
if state == STATE_WIN then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 30, game.height() / 2 - 20, "YOU WIN!", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 60, game.height() / 2, "Tap up: Continue", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 + 15, "Tap down: Menu", true, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 40, game.height() / 2, "GAME OVER", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 30, game.height() / 2 + 15, "Score: " .. tostring(game.vars.score), true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 60, game.height() / 2 + 30, "Tap to Menu", true, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_swipe_direction(x, y)
|
||||||
|
-- Simple tap-based direction (could be enhanced with swipe tracking)
|
||||||
|
local mid_x = game.width() / 2
|
||||||
|
local mid_y = game.height() / 2
|
||||||
|
|
||||||
|
local dx = x - mid_x
|
||||||
|
local dy = y - mid_y
|
||||||
|
|
||||||
|
if math.abs(dx) > math.abs(dy) then
|
||||||
|
if dx > 0 then return "right" end
|
||||||
|
return "left"
|
||||||
|
else
|
||||||
|
if dy > 0 then return "down" end
|
||||||
|
return "up"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function move_tiles(direction)
|
||||||
|
local old_grid = {}
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
old_grid[y] = {}
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
old_grid[y][x] = game.vars.grid[y][x]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if direction == "left" then
|
||||||
|
move_left()
|
||||||
|
elseif direction == "right" then
|
||||||
|
move_right()
|
||||||
|
elseif direction == "up" then
|
||||||
|
move_up()
|
||||||
|
elseif direction == "down" then
|
||||||
|
move_down()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if grid changed
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
if old_grid[y][x] ~= game.vars.grid[y][x] then
|
||||||
|
game.vars.moved = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function move_left()
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
-- Compact
|
||||||
|
local row = {}
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
if game.vars.grid[y][x] ~= 0 then
|
||||||
|
table.insert(row, game.vars.grid[y][x])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Merge
|
||||||
|
local i = 1
|
||||||
|
while i < #row do
|
||||||
|
if row[i] == row[i + 1] then
|
||||||
|
row[i] = row[i] * 2
|
||||||
|
game.vars.score = game.vars.score + row[i]
|
||||||
|
table.remove(row, i + 1)
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fill back
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
if x <= #row then
|
||||||
|
game.vars.grid[y][x] = row[x]
|
||||||
|
else
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function move_right()
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
local row = {}
|
||||||
|
for x = GRID_SIZE, 1, -1 do
|
||||||
|
if game.vars.grid[y][x] ~= 0 then
|
||||||
|
table.insert(row, game.vars.grid[y][x])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
while i < #row do
|
||||||
|
if row[i] == row[i + 1] then
|
||||||
|
row[i] = row[i] * 2
|
||||||
|
game.vars.score = game.vars.score + row[i]
|
||||||
|
table.remove(row, i + 1)
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = GRID_SIZE, 1, -1 do
|
||||||
|
if GRID_SIZE - x + 1 <= #row then
|
||||||
|
game.vars.grid[y][x] = row[GRID_SIZE - x + 1]
|
||||||
|
else
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function move_up()
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
local col = {}
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
if game.vars.grid[y][x] ~= 0 then
|
||||||
|
table.insert(col, game.vars.grid[y][x])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
while i < #col do
|
||||||
|
if col[i] == col[i + 1] then
|
||||||
|
col[i] = col[i] * 2
|
||||||
|
game.vars.score = game.vars.score + col[i]
|
||||||
|
table.remove(col, i + 1)
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
if y <= #col then
|
||||||
|
game.vars.grid[y][x] = col[y]
|
||||||
|
else
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function move_down()
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
local col = {}
|
||||||
|
for y = GRID_SIZE, 1, -1 do
|
||||||
|
if game.vars.grid[y][x] ~= 0 then
|
||||||
|
table.insert(col, game.vars.grid[y][x])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
while i < #col do
|
||||||
|
if col[i] == col[i + 1] then
|
||||||
|
col[i] = col[i] * 2
|
||||||
|
game.vars.score = game.vars.score + col[i]
|
||||||
|
table.remove(col, i + 1)
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
for y = GRID_SIZE, 1, -1 do
|
||||||
|
if GRID_SIZE - y + 1 <= #col then
|
||||||
|
game.vars.grid[y][x] = col[GRID_SIZE - y + 1]
|
||||||
|
else
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function spawn_tile()
|
||||||
|
local empty = {}
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
if game.vars.grid[y][x] == 0 then
|
||||||
|
table.insert(empty, {x = x, y = y})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #empty > 0 then
|
||||||
|
local pos = empty[math.random(1, #empty)]
|
||||||
|
game.vars.grid[pos.y][pos.x] = math.random() > 0.9 and 4 or 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function has_tile(value)
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
if game.vars.grid[y][x] == value then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function can_move()
|
||||||
|
-- Check if any empty tiles
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
if game.vars.grid[y][x] == 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if any merges possible
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
local val = game.vars.grid[y][x]
|
||||||
|
if x < GRID_SIZE and game.vars.grid[y][x + 1] == val then return true end
|
||||||
|
if y < GRID_SIZE and game.vars.grid[y + 1][x] == val then return true end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.won = false
|
||||||
|
|
||||||
|
for y = 1, GRID_SIZE do
|
||||||
|
for x = 1, GRID_SIZE do
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
spawn_tile()
|
||||||
|
spawn_tile()
|
||||||
|
end
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
-- NAME: Air Hockey
|
||||||
|
-- DESC: Fast-paced 2-player hockey
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local PADDLE_WIDTH = 6
|
||||||
|
local PADDLE_HEIGHT = 35
|
||||||
|
local PUCK_RADIUS = 3
|
||||||
|
local MAX_SCORE = 7
|
||||||
|
local PUCK_SPEED = 4
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
|
||||||
|
-- Left paddle (player 1)
|
||||||
|
game.vars.paddle_left_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_left_score = 0
|
||||||
|
|
||||||
|
-- Right paddle (player 2)
|
||||||
|
game.vars.paddle_right_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_right_score = 0
|
||||||
|
|
||||||
|
-- Puck
|
||||||
|
game.vars.puck_x = game.width() / 2
|
||||||
|
game.vars.puck_y = game.height() / 2
|
||||||
|
game.vars.puck_vel_x = PUCK_SPEED
|
||||||
|
game.vars.puck_vel_y = 1
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Air Hockey initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle paddle input via touch
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.TOUCH_MOVE then
|
||||||
|
if event.x < game.width() / 2 then
|
||||||
|
-- Left paddle
|
||||||
|
game.vars.paddle_left_y = math.max(0, math.min(game.height() - PADDLE_HEIGHT, event.y - PADDLE_HEIGHT / 2))
|
||||||
|
else
|
||||||
|
-- Right paddle
|
||||||
|
game.vars.paddle_right_y = math.max(0, math.min(game.height() - PADDLE_HEIGHT, event.y - PADDLE_HEIGHT / 2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics on frame tick
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
update_puck()
|
||||||
|
check_collisions()
|
||||||
|
|
||||||
|
-- Check win
|
||||||
|
if game.vars.paddle_left_score >= MAX_SCORE or game.vars.paddle_right_score >= MAX_SCORE then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 35, game.height() / 2 - 30, "AIR HOCKEY", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 60, game.height() / 2 + 20, "First to " .. tostring(MAX_SCORE), true)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING or state == STATE_GAME_OVER then
|
||||||
|
-- Draw center line
|
||||||
|
for y = 0, game.height(), 4 do
|
||||||
|
renderer.pixel(game.width() / 2, y, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw goal areas (top/bottom highlights)
|
||||||
|
renderer.line(0, 5, game.width(), 5, true, 1)
|
||||||
|
renderer.line(0, game.height() - 5, game.width(), game.height() - 5, true, 1)
|
||||||
|
|
||||||
|
-- Draw paddles
|
||||||
|
renderer.rect(5, game.vars.paddle_left_y, PADDLE_WIDTH, PADDLE_HEIGHT, true, true)
|
||||||
|
renderer.rect(game.width() - 5 - PADDLE_WIDTH, game.vars.paddle_right_y, PADDLE_WIDTH, PADDLE_HEIGHT, true, true)
|
||||||
|
|
||||||
|
-- Draw puck (convert to integers)
|
||||||
|
renderer.circle(math.floor(game.vars.puck_x + 0.5), math.floor(game.vars.puck_y + 0.5), PUCK_RADIUS, true, true)
|
||||||
|
|
||||||
|
-- Draw scores
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 30, 5, tostring(game.vars.paddle_left_score), true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 + 20, 5, tostring(game.vars.paddle_right_score), true)
|
||||||
|
|
||||||
|
if state == STATE_GAME_OVER then
|
||||||
|
local winner = game.vars.paddle_left_score > game.vars.paddle_right_score and "Player 1" or "Player 2"
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2 - 20, "GAME OVER", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2, winner .. " Wins!", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 60, game.height() / 2 + 20, "Tap to Menu", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_puck()
|
||||||
|
-- Move puck
|
||||||
|
game.vars.puck_x = game.vars.puck_x + game.vars.puck_vel_x
|
||||||
|
game.vars.puck_y = game.vars.puck_y + game.vars.puck_vel_y
|
||||||
|
|
||||||
|
-- Bounce off top/bottom
|
||||||
|
if game.vars.puck_y - PUCK_RADIUS < 0 or game.vars.puck_y + PUCK_RADIUS > game.height() then
|
||||||
|
game.vars.puck_vel_y = -game.vars.puck_vel_y
|
||||||
|
game.vars.puck_y = math.max(PUCK_RADIUS, math.min(game.height() - PUCK_RADIUS, game.vars.puck_y))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Goal: left side
|
||||||
|
if game.vars.puck_x < 0 then
|
||||||
|
game.vars.paddle_right_score = game.vars.paddle_right_score + 1
|
||||||
|
reset_puck()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Goal: right side
|
||||||
|
if game.vars.puck_x > game.width() then
|
||||||
|
game.vars.paddle_left_score = game.vars.paddle_left_score + 1
|
||||||
|
reset_puck()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_collisions()
|
||||||
|
-- Left paddle collision
|
||||||
|
if game.vars.puck_x - PUCK_RADIUS < 5 + PADDLE_WIDTH then
|
||||||
|
if game.vars.puck_y > game.vars.paddle_left_y and game.vars.puck_y < game.vars.paddle_left_y + PADDLE_HEIGHT then
|
||||||
|
if game.vars.puck_vel_x < 0 then
|
||||||
|
game.vars.puck_vel_x = -game.vars.puck_vel_x + 0.5 -- Speed up slightly
|
||||||
|
|
||||||
|
-- Add spin
|
||||||
|
local hit_pos = (game.vars.puck_y - game.vars.paddle_left_y) / PADDLE_HEIGHT
|
||||||
|
game.vars.puck_vel_y = (hit_pos - 0.5) * 6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Right paddle collision
|
||||||
|
if game.vars.puck_x + PUCK_RADIUS > game.width() - 5 - PADDLE_WIDTH then
|
||||||
|
if game.vars.puck_y > game.vars.paddle_right_y and game.vars.puck_y < game.vars.paddle_right_y + PADDLE_HEIGHT then
|
||||||
|
if game.vars.puck_vel_x > 0 then
|
||||||
|
game.vars.puck_vel_x = -game.vars.puck_vel_x - 0.5 -- Speed up slightly
|
||||||
|
|
||||||
|
-- Add spin
|
||||||
|
local hit_pos = (game.vars.puck_y - game.vars.paddle_right_y) / PADDLE_HEIGHT
|
||||||
|
game.vars.puck_vel_y = (hit_pos - 0.5) * 6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_puck()
|
||||||
|
game.vars.puck_x = game.width() / 2
|
||||||
|
game.vars.puck_y = game.height() / 2
|
||||||
|
game.vars.puck_vel_x = PUCK_SPEED * (math.random() > 0.5 and 1 or -1)
|
||||||
|
game.vars.puck_vel_y = (math.random() - 0.5) * 2
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.paddle_left_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_left_score = 0
|
||||||
|
game.vars.paddle_right_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_right_score = 0
|
||||||
|
reset_puck()
|
||||||
|
end
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
-- NAME: Asteroids
|
||||||
|
-- DESC: Destroy asteroids, avoid collisions
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local SHIP_SPEED = 2
|
||||||
|
local SHIP_ROTATION_SPEED = 8
|
||||||
|
local BULLET_SPEED = 5
|
||||||
|
local ASTEROID_SPAWN_RATE = 120
|
||||||
|
local ASTEROID_SPEEDS = {1.5, 2, 2.5, 3}
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.level = 1
|
||||||
|
|
||||||
|
-- Ship
|
||||||
|
game.vars.ship_x = game.width() / 2
|
||||||
|
game.vars.ship_y = game.height() / 2
|
||||||
|
game.vars.ship_angle = 0 -- Radians
|
||||||
|
game.vars.ship_vel_x = 0
|
||||||
|
game.vars.ship_vel_y = 0
|
||||||
|
game.vars.thrusting = false
|
||||||
|
|
||||||
|
-- Bullets
|
||||||
|
game.vars.bullets = {}
|
||||||
|
game.vars.bullet_cooldown = 0
|
||||||
|
|
||||||
|
-- Asteroids
|
||||||
|
game.vars.asteroids = {}
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Asteroids initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle input
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.TOUCH_MOVE then
|
||||||
|
if event.x < game.width() / 2 then
|
||||||
|
-- Left side: rotate counter-clockwise
|
||||||
|
game.vars.ship_angle = game.vars.ship_angle - SHIP_ROTATION_SPEED * 0.017
|
||||||
|
else
|
||||||
|
-- Right side: rotate clockwise
|
||||||
|
game.vars.ship_angle = game.vars.ship_angle + SHIP_ROTATION_SPEED * 0.017
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Thrust
|
||||||
|
game.vars.thrusting = true
|
||||||
|
else
|
||||||
|
game.vars.thrusting = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics on frame tick
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
update_ship()
|
||||||
|
update_bullets()
|
||||||
|
update_asteroids()
|
||||||
|
check_collisions()
|
||||||
|
|
||||||
|
-- Spawn asteroids
|
||||||
|
game.vars.frame_count = game.vars.frame_count + 1
|
||||||
|
if game.vars.frame_count >= ASTEROID_SPAWN_RATE then
|
||||||
|
spawn_asteroid(game.width() / 2, game.height() / 2, 3)
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 35, game.height() / 2 - 30, "ASTEROIDS", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING or state == STATE_GAME_OVER then
|
||||||
|
-- Draw asteroids (convert to integers)
|
||||||
|
for i = 1, #game.vars.asteroids do
|
||||||
|
local ast = game.vars.asteroids[i]
|
||||||
|
renderer.circle(math.floor(ast.x + 0.5), math.floor(ast.y + 0.5), ast.size, true, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw bullets
|
||||||
|
for i = 1, #game.vars.bullets do
|
||||||
|
local bullet = game.vars.bullets[i]
|
||||||
|
renderer.pixel(bullet.x, bullet.y, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw ship
|
||||||
|
local ship_size = 6
|
||||||
|
local nose_x = game.vars.ship_x + math.cos(game.vars.ship_angle) * ship_size
|
||||||
|
local nose_y = game.vars.ship_y + math.sin(game.vars.ship_angle) * ship_size
|
||||||
|
local back_x = game.vars.ship_x - math.cos(game.vars.ship_angle) * (ship_size / 2)
|
||||||
|
local back_y = game.vars.ship_y - math.sin(game.vars.ship_angle) * (ship_size / 2)
|
||||||
|
|
||||||
|
renderer.line(game.vars.ship_x, game.vars.ship_y, nose_x, nose_y, true, 1)
|
||||||
|
renderer.line(back_x, back_y, nose_x, nose_y, true, 1)
|
||||||
|
|
||||||
|
-- Draw thrust indicator
|
||||||
|
if game.vars.thrusting then
|
||||||
|
local flame_x = game.vars.ship_x - math.cos(game.vars.ship_angle) * ship_size
|
||||||
|
local flame_y = game.vars.ship_y - math.sin(game.vars.ship_angle) * ship_size
|
||||||
|
renderer.line(game.vars.ship_x, game.vars.ship_y, flame_x, flame_y, true, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw score
|
||||||
|
renderer.text_scaled(10, 10, "Score: " .. tostring(game.vars.score, 2), true)
|
||||||
|
renderer.text_scaled(10, 20, "Level: " .. tostring(game.vars.level, 2), true)
|
||||||
|
|
||||||
|
if state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2, "GAME OVER", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2 + 20, "Tap to Menu", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_ship()
|
||||||
|
if game.vars.thrusting then
|
||||||
|
game.vars.ship_vel_x = game.vars.ship_vel_x + math.cos(game.vars.ship_angle) * SHIP_SPEED
|
||||||
|
game.vars.ship_vel_y = game.vars.ship_vel_y + math.sin(game.vars.ship_angle) * SHIP_SPEED
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Friction
|
||||||
|
game.vars.ship_vel_x = game.vars.ship_vel_x * 0.98
|
||||||
|
game.vars.ship_vel_y = game.vars.ship_vel_y * 0.98
|
||||||
|
|
||||||
|
-- Move ship
|
||||||
|
game.vars.ship_x = game.vars.ship_x + game.vars.ship_vel_x
|
||||||
|
game.vars.ship_y = game.vars.ship_y + game.vars.ship_vel_y
|
||||||
|
|
||||||
|
-- Wrap around screen
|
||||||
|
if game.vars.ship_x < 0 then game.vars.ship_x = game.width() end
|
||||||
|
if game.vars.ship_x > game.width() then game.vars.ship_x = 0 end
|
||||||
|
if game.vars.ship_y < 0 then game.vars.ship_y = game.height() end
|
||||||
|
if game.vars.ship_y > game.height() then game.vars.ship_y = 0 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_bullets()
|
||||||
|
-- Update existing bullets
|
||||||
|
for i = #game.vars.bullets, 1, -1 do
|
||||||
|
local bullet = game.vars.bullets[i]
|
||||||
|
bullet.x = bullet.x + bullet.vel_x
|
||||||
|
bullet.y = bullet.y + bullet.vel_y
|
||||||
|
|
||||||
|
-- Remove if off-screen
|
||||||
|
if bullet.x < 0 or bullet.x > game.width() or bullet.y < 0 or bullet.y > game.height() then
|
||||||
|
table.remove(game.vars.bullets, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fire bullet
|
||||||
|
game.vars.bullet_cooldown = math.max(0, game.vars.bullet_cooldown - 1)
|
||||||
|
if game.vars.thrusting and game.vars.bullet_cooldown == 0 then
|
||||||
|
local bullet_x = game.vars.ship_x + math.cos(game.vars.ship_angle) * 8
|
||||||
|
local bullet_y = game.vars.ship_y + math.sin(game.vars.ship_angle) * 8
|
||||||
|
|
||||||
|
table.insert(game.vars.bullets, {
|
||||||
|
x = bullet_x,
|
||||||
|
y = bullet_y,
|
||||||
|
vel_x = math.cos(game.vars.ship_angle) * BULLET_SPEED + game.vars.ship_vel_x,
|
||||||
|
vel_y = math.sin(game.vars.ship_angle) * BULLET_SPEED + game.vars.ship_vel_y
|
||||||
|
})
|
||||||
|
|
||||||
|
game.vars.bullet_cooldown = 5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_asteroids()
|
||||||
|
for i = 1, #game.vars.asteroids do
|
||||||
|
local ast = game.vars.asteroids[i]
|
||||||
|
ast.x = ast.x + ast.vel_x
|
||||||
|
ast.y = ast.y + ast.vel_y
|
||||||
|
|
||||||
|
-- Wrap around screen
|
||||||
|
if ast.x < -ast.size then ast.x = game.width() + ast.size end
|
||||||
|
if ast.x > game.width() + ast.size then ast.x = -ast.size end
|
||||||
|
if ast.y < -ast.size then ast.y = game.height() + ast.size end
|
||||||
|
if ast.y > game.height() + ast.size then ast.y = -ast.size end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_collisions()
|
||||||
|
-- Bullet-asteroid collisions
|
||||||
|
for b = #game.vars.bullets, 1, -1 do
|
||||||
|
local bullet = game.vars.bullets[b]
|
||||||
|
for a = #game.vars.asteroids, 1, -1 do
|
||||||
|
local ast = game.vars.asteroids[a]
|
||||||
|
|
||||||
|
local dx = bullet.x - ast.x
|
||||||
|
local dy = bullet.y - ast.y
|
||||||
|
local dist = math.sqrt(dx * dx + dy * dy)
|
||||||
|
|
||||||
|
if dist < ast.size then
|
||||||
|
-- Hit!
|
||||||
|
table.remove(game.vars.bullets, b)
|
||||||
|
table.remove(game.vars.asteroids, a)
|
||||||
|
game.vars.score = game.vars.score + (4 - ast.size) * 50
|
||||||
|
|
||||||
|
-- Spawn smaller asteroids
|
||||||
|
if ast.size > 1 then
|
||||||
|
for _ = 1, 2 do
|
||||||
|
spawn_asteroid(ast.x, ast.y, ast.size - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ship-asteroid collisions
|
||||||
|
for i = 1, #game.vars.asteroids do
|
||||||
|
local ast = game.vars.asteroids[i]
|
||||||
|
|
||||||
|
local dx = game.vars.ship_x - ast.x
|
||||||
|
local dy = game.vars.ship_y - ast.y
|
||||||
|
local dist = math.sqrt(dx * dx + dy * dy)
|
||||||
|
|
||||||
|
if dist < ast.size + 6 then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function spawn_asteroid(x, y, size)
|
||||||
|
if size < 1 then return end
|
||||||
|
|
||||||
|
local speed = ASTEROID_SPEEDS[size]
|
||||||
|
local angle = math.random() * math.pi * 2
|
||||||
|
|
||||||
|
table.insert(game.vars.asteroids, {
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
size = size,
|
||||||
|
vel_x = math.cos(angle) * speed,
|
||||||
|
vel_y = math.sin(angle) * speed
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.level = 1
|
||||||
|
game.vars.ship_x = game.width() / 2
|
||||||
|
game.vars.ship_y = game.height() / 2
|
||||||
|
game.vars.ship_angle = 0
|
||||||
|
game.vars.ship_vel_x = 0
|
||||||
|
game.vars.ship_vel_y = 0
|
||||||
|
game.vars.bullets = {}
|
||||||
|
game.vars.asteroids = {}
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
|
||||||
|
spawn_asteroid(game.width() / 2 - 40, game.height() / 2 - 40, 3)
|
||||||
|
spawn_asteroid(game.width() / 2 + 40, game.height() / 2 + 40, 3)
|
||||||
|
end
|
||||||
+13
-10
@@ -14,6 +14,9 @@ function init()
|
|||||||
game.vars.radius = 10
|
game.vars.radius = 10
|
||||||
game.vars.frame_count = 0
|
game.vars.frame_count = 0
|
||||||
|
|
||||||
|
-- Enable continuous frame updates for smooth animation
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
print("Bouncing Ball initialized")
|
print("Bouncing Ball initialized")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -28,8 +31,8 @@ function update(event)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Update physics if running
|
-- Update physics if running (on any frame tick)
|
||||||
if game.vars.state == STATE_RUNNING then
|
if event.type == INPUT.FRAME_TICK and game.vars.state == STATE_RUNNING then
|
||||||
-- Move ball
|
-- Move ball
|
||||||
game.vars.ball_x = game.vars.ball_x + game.vars.vel_x
|
game.vars.ball_x = game.vars.ball_x + game.vars.vel_x
|
||||||
game.vars.ball_y = game.vars.ball_y + game.vars.vel_y
|
game.vars.ball_y = game.vars.ball_y + game.vars.vel_y
|
||||||
@@ -55,27 +58,27 @@ end
|
|||||||
function draw()
|
function draw()
|
||||||
renderer.clear(false)
|
renderer.clear(false)
|
||||||
|
|
||||||
-- Draw ball
|
-- Draw ball (convert to integers)
|
||||||
renderer.circle(game.vars.ball_x, game.vars.ball_y, game.vars.radius, true, true)
|
renderer.circle(math.floor(game.vars.ball_x + 0.5), math.floor(game.vars.ball_y + 0.5), game.vars.radius, true, true)
|
||||||
|
|
||||||
-- Draw trail (previous positions)
|
-- Draw trail (previous positions)
|
||||||
local trail_radius = game.vars.radius - 2
|
local trail_radius = game.vars.radius - 2
|
||||||
if trail_radius > 2 then
|
if trail_radius > 2 then
|
||||||
renderer.circle(game.vars.ball_x - game.vars.vel_x,
|
renderer.circle(math.floor(game.vars.ball_x - game.vars.vel_x + 0.5),
|
||||||
game.vars.ball_y - game.vars.vel_y,
|
math.floor(game.vars.ball_y - game.vars.vel_y + 0.5),
|
||||||
trail_radius, true, false)
|
trail_radius, true, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Draw status
|
-- Draw status
|
||||||
if game.vars.state == STATE_PAUSED then
|
if game.vars.state == STATE_PAUSED then
|
||||||
renderer.text(10, 10, "PAUSED - Tap to start", true)
|
renderer.text_scaled(10, 10, "PAUSED - Tap to start", true, 2)
|
||||||
else
|
else
|
||||||
renderer.text(10, 10, "Frames: " .. tostring(game.vars.frame_count), true)
|
renderer.text_scaled(10, 10, "Frames: " .. tostring(game.vars.frame_count, 2), true)
|
||||||
renderer.text(10, 25, "Tap to pause", true)
|
renderer.text_scaled(10, 25, "Tap to pause", true, 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Draw velocity vector
|
-- Draw velocity vector
|
||||||
local arrow_x = game.vars.ball_x + game.vars.vel_x * 3
|
local arrow_x = game.vars.ball_x + game.vars.vel_x * 3
|
||||||
local arrow_y = game.vars.ball_y + game.vars.vel_y * 3
|
local arrow_y = game.vars.ball_y + game.vars.vel_y * 3
|
||||||
renderer.line(game.vars.ball_x, game.vars.ball_y, arrow_x, arrow_y, true, 2)
|
renderer.line(math.floor(game.vars.ball_x + 0.5), math.floor(game.vars.ball_y + 0.5), math.floor(arrow_x + 0.5), math.floor(arrow_y + 0.5), true, 2)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
-- NAME: Breakout
|
||||||
|
-- DESC: Break bricks with bouncing ball and paddle
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
local STATE_LEVEL_COMPLETE = 3
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local PADDLE_WIDTH = 40
|
||||||
|
local PADDLE_HEIGHT = 6
|
||||||
|
local BALL_RADIUS = 4
|
||||||
|
local BRICK_WIDTH = 16
|
||||||
|
local BRICK_HEIGHT = 6
|
||||||
|
local BRICK_COLS = 16
|
||||||
|
local BRICK_ROWS = 5
|
||||||
|
local BRICK_START_Y = 20
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.lives = 3
|
||||||
|
|
||||||
|
-- Paddle
|
||||||
|
game.vars.paddle_x = (game.width() / 2) - (PADDLE_WIDTH / 2)
|
||||||
|
|
||||||
|
-- Ball
|
||||||
|
game.vars.ball_x = game.width() / 2
|
||||||
|
game.vars.ball_y = game.height() - 30
|
||||||
|
game.vars.ball_vel_x = 2
|
||||||
|
game.vars.ball_vel_y = -3
|
||||||
|
|
||||||
|
-- Bricks (true = exists, false = broken)
|
||||||
|
game.vars.bricks = {}
|
||||||
|
game.vars.bricks_remaining = 0
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Breakout initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle paddle input
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.TOUCH_MOVE then
|
||||||
|
game.vars.paddle_x = math.max(0, math.min(game.width() - PADDLE_WIDTH, event.x - PADDLE_WIDTH / 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics on frame tick
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
update_ball()
|
||||||
|
check_brick_collisions()
|
||||||
|
|
||||||
|
-- Check win
|
||||||
|
if game.vars.bricks_remaining <= 0 then
|
||||||
|
game.vars.state = STATE_LEVEL_COMPLETE
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_LEVEL_COMPLETE then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 30, game.height() / 2 - 30, "BREAKOUT", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Draw bricks
|
||||||
|
for row = 1, BRICK_ROWS do
|
||||||
|
for col = 1, BRICK_COLS do
|
||||||
|
local idx = (row - 1) * BRICK_COLS + col
|
||||||
|
if game.vars.bricks[idx] then
|
||||||
|
local x = (col - 1) * BRICK_WIDTH
|
||||||
|
local y = BRICK_START_Y + (row - 1) * BRICK_HEIGHT
|
||||||
|
renderer.rect(x, y, BRICK_WIDTH, BRICK_HEIGHT, true, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw paddle
|
||||||
|
renderer.rect(game.vars.paddle_x, game.height() - PADDLE_HEIGHT - 2, PADDLE_WIDTH, PADDLE_HEIGHT, true, true)
|
||||||
|
|
||||||
|
-- Draw ball (convert to integers)
|
||||||
|
renderer.circle(math.floor(game.vars.ball_x + 0.5), math.floor(game.vars.ball_y + 0.5), BALL_RADIUS, true, true)
|
||||||
|
|
||||||
|
-- Draw score and lives
|
||||||
|
renderer.text_scaled(5, 5, "Score: " .. tostring(game.vars.score, 2), true)
|
||||||
|
renderer.text_scaled(game.width(, 2) - 50, 5, "Lives: " .. tostring(game.vars.lives), true)
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2 - 20, "GAME OVER", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 30, game.height() / 2, "Score: " .. tostring(game.vars.score), true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 60, game.height() / 2 + 20, "Tap to Menu", true)
|
||||||
|
|
||||||
|
elseif state == STATE_LEVEL_COMPLETE then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2 - 20, "LEVEL COMPLETE!", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 30, game.height() / 2, "Score: " .. tostring(game.vars.score), true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 60, game.height() / 2 + 20, "Tap to Menu", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_ball()
|
||||||
|
-- Move ball
|
||||||
|
game.vars.ball_x = game.vars.ball_x + game.vars.ball_vel_x
|
||||||
|
game.vars.ball_y = game.vars.ball_y + game.vars.ball_vel_y
|
||||||
|
|
||||||
|
-- Bounce off walls
|
||||||
|
if game.vars.ball_x - BALL_RADIUS < 0 or game.vars.ball_x + BALL_RADIUS > game.width() then
|
||||||
|
game.vars.ball_vel_x = -game.vars.ball_vel_x
|
||||||
|
game.vars.ball_x = math.max(BALL_RADIUS, math.min(game.width() - BALL_RADIUS, game.vars.ball_x))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bounce off top
|
||||||
|
if game.vars.ball_y - BALL_RADIUS < 0 then
|
||||||
|
game.vars.ball_vel_y = -game.vars.ball_vel_y
|
||||||
|
game.vars.ball_y = BALL_RADIUS
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check paddle collision
|
||||||
|
if game.vars.ball_y + BALL_RADIUS > game.height() - PADDLE_HEIGHT - 2 then
|
||||||
|
if game.vars.ball_x > game.vars.paddle_x and game.vars.ball_x < game.vars.paddle_x + PADDLE_WIDTH then
|
||||||
|
if game.vars.ball_vel_y > 0 then
|
||||||
|
game.vars.ball_vel_y = -game.vars.ball_vel_y
|
||||||
|
|
||||||
|
-- Add spin based on hit position
|
||||||
|
local hit_pos = (game.vars.ball_x - game.vars.paddle_x) / PADDLE_WIDTH
|
||||||
|
game.vars.ball_vel_x = (hit_pos - 0.5) * 4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fall off bottom = lose life
|
||||||
|
if game.vars.ball_y > game.height() then
|
||||||
|
game.vars.lives = game.vars.lives - 1
|
||||||
|
if game.vars.lives <= 0 then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
else
|
||||||
|
reset_ball()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_brick_collisions()
|
||||||
|
for row = 1, BRICK_ROWS do
|
||||||
|
for col = 1, BRICK_COLS do
|
||||||
|
local idx = (row - 1) * BRICK_COLS + col
|
||||||
|
if game.vars.bricks[idx] then
|
||||||
|
local brick_x = (col - 1) * BRICK_WIDTH
|
||||||
|
local brick_y = BRICK_START_Y + (row - 1) * BRICK_HEIGHT
|
||||||
|
|
||||||
|
-- Simple AABB collision with ball
|
||||||
|
if game.vars.ball_x + BALL_RADIUS > brick_x and
|
||||||
|
game.vars.ball_x - BALL_RADIUS < brick_x + BRICK_WIDTH and
|
||||||
|
game.vars.ball_y + BALL_RADIUS > brick_y and
|
||||||
|
game.vars.ball_y - BALL_RADIUS < brick_y + BRICK_HEIGHT then
|
||||||
|
|
||||||
|
-- Destroy brick
|
||||||
|
game.vars.bricks[idx] = false
|
||||||
|
game.vars.bricks_remaining = game.vars.bricks_remaining - 1
|
||||||
|
game.vars.score = game.vars.score + 10
|
||||||
|
|
||||||
|
-- Bounce ball (simple: vertical bounce)
|
||||||
|
game.vars.ball_vel_y = -game.vars.ball_vel_y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_ball()
|
||||||
|
game.vars.ball_x = game.vars.paddle_x + PADDLE_WIDTH / 2
|
||||||
|
game.vars.ball_y = game.height() - 30
|
||||||
|
game.vars.ball_vel_x = 2
|
||||||
|
game.vars.ball_vel_y = -3
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.lives = 3
|
||||||
|
game.vars.paddle_x = (game.width() / 2) - (PADDLE_WIDTH / 2)
|
||||||
|
|
||||||
|
-- Create brick grid
|
||||||
|
game.vars.bricks = {}
|
||||||
|
game.vars.bricks_remaining = BRICK_ROWS * BRICK_COLS
|
||||||
|
for i = 1, BRICK_ROWS * BRICK_COLS do
|
||||||
|
game.vars.bricks[i] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
reset_ball()
|
||||||
|
end
|
||||||
@@ -29,21 +29,21 @@ function draw()
|
|||||||
renderer.clear(true)
|
renderer.clear(true)
|
||||||
|
|
||||||
-- Draw title
|
-- Draw title
|
||||||
renderer.text(20, 20, "Touch Counter", true)
|
renderer.text_scaled(20, 20, "Touch Counter", true, 2)
|
||||||
|
|
||||||
-- Draw count (centered)
|
-- Draw count (centered)
|
||||||
local count_text = "Count: " .. tostring(game.vars.count)
|
local count_text = "Count: " .. tostring(game.vars.count)
|
||||||
renderer.text(game.width() / 2 - 40, game.height() / 2 - 10, count_text, true)
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2 - 10, count_text, true)
|
||||||
|
|
||||||
-- Draw last touch position
|
-- Draw last touch position
|
||||||
if game.vars.count > 0 then
|
if game.vars.count > 0 then
|
||||||
local pos_text = "Last: (" .. tostring(game.vars.last_x) .. ", " .. tostring(game.vars.last_y) .. ")"
|
local pos_text = "Last: (" .. tostring(game.vars.last_x) .. ", " .. tostring(game.vars.last_y) .. ")"
|
||||||
renderer.text(20, game.height() - 30, pos_text, true)
|
renderer.text_scaled(20, game.height(, 2) - 30, pos_text, true)
|
||||||
|
|
||||||
-- Draw marker at last touch
|
-- Draw marker at last touch (convert to integers)
|
||||||
renderer.circle(game.vars.last_x, game.vars.last_y, 5, true, false)
|
renderer.circle(math.floor(game.vars.last_x + 0.5), math.floor(game.vars.last_y + 0.5), 5, true, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Draw instructions
|
-- Draw instructions
|
||||||
renderer.text(20, 50, "Tap screen to increment", true)
|
renderer.text_scaled(20, 50, "Tap screen to increment", true, 2)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
-- NAME: Flappy Bird
|
||||||
|
-- DESC: Tap to flap, avoid pipes
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local BIRD_SIZE = 8
|
||||||
|
local BIRD_GRAVITY = 0.3
|
||||||
|
local BIRD_FLAP_POWER = -7
|
||||||
|
local PIPE_WIDTH = 20
|
||||||
|
local PIPE_GAP = 50
|
||||||
|
local PIPE_SPEED = 3
|
||||||
|
local SPAWN_RATE = 80 -- Frames between pipe spawns
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
|
||||||
|
-- Bird
|
||||||
|
game.vars.bird_y = game.height() / 2
|
||||||
|
game.vars.bird_vel = 0
|
||||||
|
|
||||||
|
-- Pipes (array of {x, gap_y})
|
||||||
|
game.vars.pipes = {}
|
||||||
|
game.vars.last_pipe_frame = 0
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Flappy Bird initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle flap input
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.bird_vel = BIRD_FLAP_POWER
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics on frame tick
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
update_bird()
|
||||||
|
update_pipes()
|
||||||
|
check_collisions()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2 - 30, "FLAPPY BIRD", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Draw bird (convert to integer)
|
||||||
|
renderer.circle(20, math.floor(game.vars.bird_y + 0.5), BIRD_SIZE, true, true)
|
||||||
|
|
||||||
|
-- Draw pipes
|
||||||
|
for i = 1, #game.vars.pipes do
|
||||||
|
local pipe = game.vars.pipes[i]
|
||||||
|
|
||||||
|
-- Top pipe
|
||||||
|
renderer.rect(pipe.x, 0, PIPE_WIDTH, pipe.gap_y, true, true)
|
||||||
|
|
||||||
|
-- Bottom pipe
|
||||||
|
local bottom_start = pipe.gap_y + PIPE_GAP
|
||||||
|
renderer.rect(pipe.x, bottom_start, PIPE_WIDTH, game.height() - bottom_start, true, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw score
|
||||||
|
renderer.text_scaled(10, 10, "Score: " .. tostring(game.vars.score, 2), true)
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2 - 30, "GAME OVER", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 30, game.height() / 2, "Score: " .. tostring(game.vars.score), true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 60, game.height() / 2 + 20, "Tap to Restart", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_bird()
|
||||||
|
-- Apply gravity
|
||||||
|
game.vars.bird_vel = game.vars.bird_vel + BIRD_GRAVITY
|
||||||
|
game.vars.bird_y = game.vars.bird_y + game.vars.bird_vel
|
||||||
|
|
||||||
|
-- Clamp to screen (game over if hit top/bottom)
|
||||||
|
if game.vars.bird_y - BIRD_SIZE < 0 or game.vars.bird_y + BIRD_SIZE > game.height() then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_pipes()
|
||||||
|
game.vars.frame_count = game.vars.frame_count + 1
|
||||||
|
|
||||||
|
-- Spawn new pipe
|
||||||
|
if game.vars.frame_count - game.vars.last_pipe_frame >= SPAWN_RATE then
|
||||||
|
local gap_y = math.random(30, game.height() - PIPE_GAP - 30)
|
||||||
|
table.insert(game.vars.pipes, {x = game.width(), gap_y = gap_y})
|
||||||
|
game.vars.last_pipe_frame = game.vars.frame_count
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move and remove off-screen pipes
|
||||||
|
for i = #game.vars.pipes, 1, -1 do
|
||||||
|
local pipe = game.vars.pipes[i]
|
||||||
|
pipe.x = pipe.x - PIPE_SPEED
|
||||||
|
|
||||||
|
-- Score when pipe passes bird
|
||||||
|
if pipe.x == 20 then
|
||||||
|
game.vars.score = game.vars.score + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove if off-screen
|
||||||
|
if pipe.x < -PIPE_WIDTH then
|
||||||
|
table.remove(game.vars.pipes, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_collisions()
|
||||||
|
-- Check collision with pipes
|
||||||
|
for i = 1, #game.vars.pipes do
|
||||||
|
local pipe = game.vars.pipes[i]
|
||||||
|
|
||||||
|
-- Bird hitbox: circle at (20, bird_y) with radius BIRD_SIZE
|
||||||
|
-- Check if within pipe's X range
|
||||||
|
if 20 + BIRD_SIZE > pipe.x and 20 - BIRD_SIZE < pipe.x + PIPE_WIDTH then
|
||||||
|
-- Check Y collision
|
||||||
|
if game.vars.bird_y - BIRD_SIZE < pipe.gap_y or
|
||||||
|
game.vars.bird_y + BIRD_SIZE > pipe.gap_y + PIPE_GAP then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.bird_y = game.height() / 2
|
||||||
|
game.vars.bird_vel = 0
|
||||||
|
game.vars.pipes = {}
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
game.vars.last_pipe_frame = 0
|
||||||
|
end
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
-- NAME: Lunar Lander
|
||||||
|
-- DESC: Land safely with limited fuel
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_LANDED = 2
|
||||||
|
local STATE_CRASHED = 3
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local GRAVITY = 0.15
|
||||||
|
local THRUST_POWER = 0.3
|
||||||
|
local MAX_FUEL = 100
|
||||||
|
local SAFE_LANDING_SPEED = 1.5
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
|
||||||
|
-- Lander
|
||||||
|
game.vars.lander_x = game.width() / 2
|
||||||
|
game.vars.lander_y = 10
|
||||||
|
game.vars.lander_vel_y = 0
|
||||||
|
game.vars.fuel = MAX_FUEL
|
||||||
|
game.vars.thrusting = false
|
||||||
|
|
||||||
|
-- Terrain
|
||||||
|
game.vars.landing_zone_x = game.width() / 2 - 20
|
||||||
|
game.vars.landing_zone_w = 40
|
||||||
|
game.vars.terrain_y = game.height() - 15
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Lunar Lander initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle thrust input
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.TOUCH_MOVE then
|
||||||
|
if game.vars.fuel > 0 then
|
||||||
|
game.vars.thrusting = true
|
||||||
|
game.vars.fuel = game.vars.fuel - 1
|
||||||
|
else
|
||||||
|
game.vars.thrusting = false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
game.vars.thrusting = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics on frame tick
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
update_lander()
|
||||||
|
check_landing()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_LANDED or state == STATE_CRASHED then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false)
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2 - 30, "LUNAR LANDER", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 70, game.height() / 2 - 5, "Land in the zone safely", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2 + 20, "Tap to Start", true)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING or state == STATE_LANDED or state == STATE_CRASHED then
|
||||||
|
-- Draw terrain
|
||||||
|
renderer.rect(0, game.vars.terrain_y, game.width(), game.height() - game.vars.terrain_y, true, true)
|
||||||
|
|
||||||
|
-- Draw landing zone (outline)
|
||||||
|
renderer.rect(game.vars.landing_zone_x, game.vars.terrain_y - 2, game.vars.landing_zone_w, 2, true, true)
|
||||||
|
|
||||||
|
-- Draw lander (triangle)
|
||||||
|
local lander_w = 8
|
||||||
|
local lander_h = 6
|
||||||
|
renderer.line(game.vars.lander_x - lander_w / 2, game.vars.lander_y + lander_h,
|
||||||
|
game.vars.lander_x, game.vars.lander_y, true, 1)
|
||||||
|
renderer.line(game.vars.lander_x, game.vars.lander_y,
|
||||||
|
game.vars.lander_x + lander_w / 2, game.vars.lander_y + lander_h, true, 1)
|
||||||
|
renderer.line(game.vars.lander_x - lander_w / 2, game.vars.lander_y + lander_h,
|
||||||
|
game.vars.lander_x + lander_w / 2, game.vars.lander_y + lander_h, true, 1)
|
||||||
|
|
||||||
|
-- Draw thrust flame
|
||||||
|
if game.vars.thrusting then
|
||||||
|
renderer.line(game.vars.lander_x - 2, game.vars.lander_y + lander_h,
|
||||||
|
game.vars.lander_x - 1, game.vars.lander_y + lander_h + 3, true, 1)
|
||||||
|
renderer.line(game.vars.lander_x + 2, game.vars.lander_y + lander_h,
|
||||||
|
game.vars.lander_x + 1, game.vars.lander_y + lander_h + 3, true, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw stats
|
||||||
|
renderer.text_scaled(5, 5, "Fuel: " .. tostring(math.floor(game.vars.fuel, 2)), true)
|
||||||
|
renderer.text_scaled(5, 15, "Speed: " .. tostring(math.floor(game.vars.lander_vel_y * 10, 2)), true)
|
||||||
|
|
||||||
|
if state == STATE_LANDED then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2 - 20, "LANDED!", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 30, game.height() / 2, "Score: " .. tostring(game.vars.score), true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 60, game.height() / 2 + 20, "Tap to Menu", true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE_CRASHED then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2 - 20, "CRASHED!", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 60, game.height() / 2 + 20, "Tap to Menu", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update_lander()
|
||||||
|
-- Apply gravity
|
||||||
|
game.vars.lander_vel_y = game.vars.lander_vel_y + GRAVITY
|
||||||
|
|
||||||
|
-- Apply thrust
|
||||||
|
if game.vars.thrusting then
|
||||||
|
game.vars.lander_vel_y = game.vars.lander_vel_y - THRUST_POWER
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update position
|
||||||
|
game.vars.lander_y = game.vars.lander_y + game.vars.lander_vel_y
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_landing()
|
||||||
|
-- Check if lander reached terrain
|
||||||
|
if game.vars.lander_y + 6 >= game.vars.terrain_y then
|
||||||
|
local lander_left = game.vars.lander_x - 4
|
||||||
|
local lander_right = game.vars.lander_x + 4
|
||||||
|
local zone_left = game.vars.landing_zone_x
|
||||||
|
local zone_right = game.vars.landing_zone_x + game.vars.landing_zone_w
|
||||||
|
|
||||||
|
-- Check if in landing zone
|
||||||
|
if lander_left >= zone_left and lander_right <= zone_right then
|
||||||
|
-- Check landing speed
|
||||||
|
if game.vars.lander_vel_y <= SAFE_LANDING_SPEED then
|
||||||
|
game.vars.state = STATE_LANDED
|
||||||
|
game.vars.score = 100 + math.floor(game.vars.fuel)
|
||||||
|
else
|
||||||
|
game.vars.state = STATE_CRASHED
|
||||||
|
end
|
||||||
|
else
|
||||||
|
game.vars.state = STATE_CRASHED
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.lander_x = game.width() / 2
|
||||||
|
game.vars.lander_y = 10
|
||||||
|
game.vars.lander_vel_y = 0
|
||||||
|
game.vars.fuel = MAX_FUEL
|
||||||
|
game.vars.thrusting = false
|
||||||
|
game.vars.score = 0
|
||||||
|
end
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
-- NAME: Memory Match
|
||||||
|
-- DESC: Find matching pairs
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local GRID_COLS = 4
|
||||||
|
local GRID_ROWS = 4
|
||||||
|
local FLIP_DURATION = 15 -- Frames to show flip animation
|
||||||
|
|
||||||
|
-- Calculate card size based on screen size
|
||||||
|
local function get_card_size()
|
||||||
|
-- Use available space with padding, based on smallest dimension
|
||||||
|
local min_dim = math.min(game.width(), game.height())
|
||||||
|
local padding = math.floor(min_dim / 8)
|
||||||
|
local available_w = game.width() - (padding * 2)
|
||||||
|
local available_h = game.height() - 80 -- Leave room for text
|
||||||
|
|
||||||
|
-- Calculate based on grid
|
||||||
|
local card_width = math.floor(available_w / GRID_COLS)
|
||||||
|
local card_height = math.floor(available_h / GRID_ROWS)
|
||||||
|
|
||||||
|
-- Use the smaller dimension for square cards
|
||||||
|
return math.min(card_width, card_height)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_grid_start_x()
|
||||||
|
local card_size = get_card_size()
|
||||||
|
local grid_width = card_size * GRID_COLS
|
||||||
|
return math.floor((game.width() - grid_width) / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_grid_start_y()
|
||||||
|
return 60
|
||||||
|
end
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.moves = 0
|
||||||
|
|
||||||
|
-- Card grid
|
||||||
|
game.vars.cards = {} -- {id, face_up, matched}
|
||||||
|
game.vars.selected = {} -- Indices of selected cards
|
||||||
|
game.vars.flip_frame = 0
|
||||||
|
game.vars.waiting = false -- Waiting to flip back incorrect pair
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Memory Match initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle card selection
|
||||||
|
if event.type == INPUT.TOUCH_DOWN and not game.vars.waiting then
|
||||||
|
local card_idx = get_card_at(event.x, event.y)
|
||||||
|
if card_idx and not game.vars.cards[card_idx].matched and not game.vars.cards[card_idx].face_up then
|
||||||
|
-- Select card
|
||||||
|
game.vars.cards[card_idx].face_up = true
|
||||||
|
table.insert(game.vars.selected, card_idx)
|
||||||
|
|
||||||
|
if #game.vars.selected == 2 then
|
||||||
|
game.vars.moves = game.vars.moves + 1
|
||||||
|
|
||||||
|
-- Check for match
|
||||||
|
if game.vars.cards[game.vars.selected[1]].id == game.vars.cards[game.vars.selected[2]].id then
|
||||||
|
-- Match!
|
||||||
|
game.vars.cards[game.vars.selected[1]].matched = true
|
||||||
|
game.vars.cards[game.vars.selected[2]].matched = true
|
||||||
|
game.vars.score = game.vars.score + 1
|
||||||
|
game.vars.selected = {}
|
||||||
|
|
||||||
|
-- Check win
|
||||||
|
if game.vars.score == (GRID_COLS * GRID_ROWS) / 2 then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- No match, wait then flip back
|
||||||
|
game.vars.waiting = true
|
||||||
|
game.vars.flip_frame = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle flip-back animation
|
||||||
|
if game.vars.waiting and event.type == INPUT.FRAME_TICK then
|
||||||
|
game.vars.flip_frame = game.vars.flip_frame + 1
|
||||||
|
|
||||||
|
if game.vars.flip_frame >= FLIP_DURATION then
|
||||||
|
-- Flip cards back
|
||||||
|
game.vars.cards[game.vars.selected[1]].face_up = false
|
||||||
|
game.vars.cards[game.vars.selected[2]].face_up = false
|
||||||
|
game.vars.selected = {}
|
||||||
|
game.vars.waiting = false
|
||||||
|
game.vars.flip_frame = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
local card_size = get_card_size()
|
||||||
|
local start_x = get_grid_start_x()
|
||||||
|
local start_y = get_grid_start_y()
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 40, game.height() / 2 - 40, "MEMORY MATCH", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 - 10, "Find all pairs", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 + 20, "Tap to Start", true, 2)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING or state == STATE_GAME_OVER then
|
||||||
|
-- Draw grid
|
||||||
|
for row = 0, GRID_ROWS - 1 do
|
||||||
|
for col = 0, GRID_COLS - 1 do
|
||||||
|
local idx = row * GRID_COLS + col + 1
|
||||||
|
local card = game.vars.cards[idx]
|
||||||
|
local x = start_x + col * card_size
|
||||||
|
local y = start_y + row * card_size
|
||||||
|
|
||||||
|
if card.face_up or card.matched then
|
||||||
|
-- Show card value
|
||||||
|
renderer.rect(x, y, card_size, card_size, true, true)
|
||||||
|
local text = tostring(card.id)
|
||||||
|
renderer.text_scaled(x + math.floor(card_size / 2 - 2), y + math.floor(card_size / 2 - 2), text, false, 2)
|
||||||
|
else
|
||||||
|
-- Face down (outline)
|
||||||
|
renderer.rect(x, y, card_size, card_size, true, false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw stats
|
||||||
|
renderer.text_scaled(10, 10, "Pairs: " .. tostring(game.vars.score) .. "/" .. tostring((GRID_COLS * GRID_ROWS) / 2), true, 2)
|
||||||
|
renderer.text_scaled(10, 25, "Moves: " .. tostring(game.vars.moves), true, 2)
|
||||||
|
|
||||||
|
if state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 40, 5, "YOU WIN!", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() - 20, "Tap to Menu", true, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_card_at(x, y)
|
||||||
|
local card_size = get_card_size()
|
||||||
|
local start_x = get_grid_start_x()
|
||||||
|
local start_y = get_grid_start_y()
|
||||||
|
|
||||||
|
for row = 0, GRID_ROWS - 1 do
|
||||||
|
for col = 0, GRID_COLS - 1 do
|
||||||
|
local card_x = start_x + col * card_size
|
||||||
|
local card_y = start_y + row * card_size
|
||||||
|
|
||||||
|
if x >= card_x and x < card_x + card_size and
|
||||||
|
y >= card_y and y < card_y + card_size then
|
||||||
|
return row * GRID_COLS + col + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.moves = 0
|
||||||
|
game.vars.selected = {}
|
||||||
|
game.vars.flip_frame = 0
|
||||||
|
game.vars.waiting = false
|
||||||
|
|
||||||
|
-- Create shuffled card pairs
|
||||||
|
local card_ids = {}
|
||||||
|
local num_pairs = (GRID_COLS * GRID_ROWS) / 2
|
||||||
|
for i = 1, num_pairs do
|
||||||
|
table.insert(card_ids, i)
|
||||||
|
table.insert(card_ids, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Shuffle
|
||||||
|
for i = #card_ids, 2, -1 do
|
||||||
|
local j = math.random(1, i)
|
||||||
|
card_ids[i], card_ids[j] = card_ids[j], card_ids[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create card objects
|
||||||
|
game.vars.cards = {}
|
||||||
|
for i = 1, #card_ids do
|
||||||
|
game.vars.cards[i] = {
|
||||||
|
id = card_ids[i],
|
||||||
|
face_up = false,
|
||||||
|
matched = false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
-- NAME: Pong
|
||||||
|
-- DESC: Classic two-player Pong game
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local PADDLE_WIDTH = 8
|
||||||
|
local PADDLE_HEIGHT = 40
|
||||||
|
local BALL_RADIUS = 5
|
||||||
|
local MAX_SCORE = 5
|
||||||
|
|
||||||
|
-- Initialize game
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
|
||||||
|
-- Left paddle (player 1)
|
||||||
|
game.vars.paddle_left_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_left_score = 0
|
||||||
|
|
||||||
|
-- Right paddle (player 2)
|
||||||
|
game.vars.paddle_right_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_right_score = 0
|
||||||
|
|
||||||
|
-- Ball
|
||||||
|
game.vars.ball_x = game.width() / 2
|
||||||
|
game.vars.ball_y = game.height() / 2
|
||||||
|
game.vars.ball_vel_x = 3
|
||||||
|
game.vars.ball_vel_y = 2
|
||||||
|
|
||||||
|
-- Enable continuous frame updates for smooth animation
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Pong initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update game logic
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
-- State: MENU
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- State: PLAYING
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle paddle input via touch
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.TOUCH_MOVE then
|
||||||
|
-- Left side touch moves left paddle, right side touch moves right paddle
|
||||||
|
if event.x < game.width() / 2 then
|
||||||
|
-- Move left paddle (constrain within bounds)
|
||||||
|
game.vars.paddle_left_y = math.max(0, math.min(game.height() - PADDLE_HEIGHT, event.y - PADDLE_HEIGHT / 2))
|
||||||
|
else
|
||||||
|
-- Move right paddle (constrain within bounds)
|
||||||
|
game.vars.paddle_right_y = math.max(0, math.min(game.height() - PADDLE_HEIGHT, event.y - PADDLE_HEIGHT / 2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics on frame tick
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
update_ball()
|
||||||
|
|
||||||
|
-- Check win condition
|
||||||
|
if game.vars.paddle_left_score >= MAX_SCORE or game.vars.paddle_right_score >= MAX_SCORE then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
|
||||||
|
return true -- Always redraw when playing
|
||||||
|
end
|
||||||
|
|
||||||
|
-- State: GAME_OVER
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw game
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
-- Draw: MENU
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 15, game.height() / 2 - 30, "PONG", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 70, game.height() / 2 + 20, "First to " .. tostring(MAX_SCORE), true, 2)
|
||||||
|
|
||||||
|
-- Draw: PLAYING
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Draw center line
|
||||||
|
for y = 0, game.height(), 5 do
|
||||||
|
renderer.pixel(game.width() / 2, y, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw paddles
|
||||||
|
renderer.rect(10, game.vars.paddle_left_y, PADDLE_WIDTH, PADDLE_HEIGHT, true, true)
|
||||||
|
renderer.rect(game.width() - 10 - PADDLE_WIDTH, game.vars.paddle_right_y, PADDLE_WIDTH, PADDLE_HEIGHT, true, true)
|
||||||
|
|
||||||
|
-- Draw ball (convert to integers)
|
||||||
|
renderer.circle(math.floor(game.vars.ball_x + 0.5), math.floor(game.vars.ball_y + 0.5), BALL_RADIUS, true, true)
|
||||||
|
|
||||||
|
-- Draw scores
|
||||||
|
local left_score_text = tostring(game.vars.paddle_left_score)
|
||||||
|
local right_score_text = tostring(game.vars.paddle_right_score)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 30, 5, left_score_text, true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 + 20, 5, right_score_text, true, 2)
|
||||||
|
|
||||||
|
-- Draw: GAME_OVER
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 40, game.height() / 2 - 30, "GAME OVER", true, 2)
|
||||||
|
|
||||||
|
local winner = "Player 1 Wins!"
|
||||||
|
if game.vars.paddle_right_score > game.vars.paddle_left_score then
|
||||||
|
winner = "Player 2 Wins!"
|
||||||
|
end
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2, winner, true, 2)
|
||||||
|
|
||||||
|
local final_text = game.vars.paddle_left_score .. " - " .. game.vars.paddle_right_score
|
||||||
|
renderer.text_scaled(game.width() / 2 - 25, game.height() / 2 + 20, final_text, true, 2)
|
||||||
|
|
||||||
|
renderer.text_scaled(game.width() / 2 - 60, game.height() / 2 + 40, "Tap to Menu", true, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper: Update ball physics
|
||||||
|
function update_ball()
|
||||||
|
-- Move ball
|
||||||
|
game.vars.ball_x = game.vars.ball_x + game.vars.ball_vel_x
|
||||||
|
game.vars.ball_y = game.vars.ball_y + game.vars.ball_vel_y
|
||||||
|
|
||||||
|
-- Bounce off top/bottom
|
||||||
|
if game.vars.ball_y - BALL_RADIUS < 0 or game.vars.ball_y + BALL_RADIUS > game.height() then
|
||||||
|
game.vars.ball_vel_y = -game.vars.ball_vel_y
|
||||||
|
game.vars.ball_y = math.max(BALL_RADIUS, math.min(game.height() - BALL_RADIUS, game.vars.ball_y))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check left paddle collision
|
||||||
|
if game.vars.ball_x - BALL_RADIUS < 10 + PADDLE_WIDTH then
|
||||||
|
if game.vars.ball_y > game.vars.paddle_left_y and game.vars.ball_y < game.vars.paddle_left_y + PADDLE_HEIGHT then
|
||||||
|
if game.vars.ball_vel_x < 0 then
|
||||||
|
game.vars.ball_vel_x = -game.vars.ball_vel_x
|
||||||
|
|
||||||
|
-- Add spin based on where ball hits paddle
|
||||||
|
local hit_pos = (game.vars.ball_y - game.vars.paddle_left_y) / PADDLE_HEIGHT
|
||||||
|
game.vars.ball_vel_y = (hit_pos - 0.5) * 6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check right paddle collision
|
||||||
|
if game.vars.ball_x + BALL_RADIUS > game.width() - 10 - PADDLE_WIDTH then
|
||||||
|
if game.vars.ball_y > game.vars.paddle_right_y and game.vars.ball_y < game.vars.paddle_right_y + PADDLE_HEIGHT then
|
||||||
|
if game.vars.ball_vel_x > 0 then
|
||||||
|
game.vars.ball_vel_x = -game.vars.ball_vel_x
|
||||||
|
|
||||||
|
-- Add spin based on where ball hits paddle
|
||||||
|
local hit_pos = (game.vars.ball_y - game.vars.paddle_right_y) / PADDLE_HEIGHT
|
||||||
|
game.vars.ball_vel_y = (hit_pos - 0.5) * 6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Score on left side miss
|
||||||
|
if game.vars.ball_x < 0 then
|
||||||
|
game.vars.paddle_right_score = game.vars.paddle_right_score + 1
|
||||||
|
reset_ball()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Score on right side miss
|
||||||
|
if game.vars.ball_x > game.width() then
|
||||||
|
game.vars.paddle_left_score = game.vars.paddle_left_score + 1
|
||||||
|
reset_ball()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper: Reset ball to center
|
||||||
|
function reset_ball()
|
||||||
|
game.vars.ball_x = game.width() / 2
|
||||||
|
game.vars.ball_y = game.height() / 2
|
||||||
|
game.vars.ball_vel_x = 3 * (math.random() > 0.5 and 1 or -1)
|
||||||
|
game.vars.ball_vel_y = 2 * (math.random() > 0.5 and 1 or -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper: Reset game
|
||||||
|
function reset_game()
|
||||||
|
game.vars.paddle_left_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_left_score = 0
|
||||||
|
game.vars.paddle_right_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
||||||
|
game.vars.paddle_right_score = 0
|
||||||
|
reset_ball()
|
||||||
|
end
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
-- NAME: Simon Says
|
||||||
|
-- DESC: Repeat the color sequence
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_SHOWING = 2
|
||||||
|
local STATE_GAME_OVER = 3
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local SHOW_DURATION = 30 -- Frames to show each button
|
||||||
|
local WAIT_DURATION = 20 -- Frames between shows
|
||||||
|
|
||||||
|
-- Button positions (calculated dynamically based on screen size)
|
||||||
|
local function get_buttons()
|
||||||
|
local padding = math.floor(math.min(game.width(), game.height()) / 10)
|
||||||
|
local available = math.min(game.width(), game.height()) - (padding * 3)
|
||||||
|
local button_size = math.floor(available / 2)
|
||||||
|
local spacing = padding
|
||||||
|
|
||||||
|
return {
|
||||||
|
{x = padding, y = padding, size = button_size, color = 1}, -- Top-left
|
||||||
|
{x = padding + button_size + spacing, y = padding, size = button_size, color = 2}, -- Top-right
|
||||||
|
{x = padding, y = padding + button_size + spacing, size = button_size, color = 3}, -- Bottom-left
|
||||||
|
{x = padding + button_size + spacing, y = padding + button_size + spacing, size = button_size, color = 4} -- Bottom-right
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
|
||||||
|
-- Sequence of button presses
|
||||||
|
game.vars.sequence = {}
|
||||||
|
game.vars.player_seq = {}
|
||||||
|
|
||||||
|
-- Animation state
|
||||||
|
game.vars.showing_idx = 0
|
||||||
|
game.vars.show_frame = 0
|
||||||
|
game.vars.show_button = nil
|
||||||
|
|
||||||
|
-- Input state
|
||||||
|
game.vars.waiting_for_input = false
|
||||||
|
game.vars.input_idx = 0
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Simon Says initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Start showing sequence
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
game.vars.show_frame = game.vars.show_frame + 1
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
elseif state == STATE_SHOWING then
|
||||||
|
-- Animate sequence
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
game.vars.show_frame = game.vars.show_frame + 1
|
||||||
|
|
||||||
|
-- Move to next button in sequence
|
||||||
|
if game.vars.show_frame > SHOW_DURATION + WAIT_DURATION then
|
||||||
|
game.vars.showing_idx = game.vars.showing_idx + 1
|
||||||
|
game.vars.show_frame = 0
|
||||||
|
game.vars.show_button = nil
|
||||||
|
|
||||||
|
-- Done showing sequence, wait for player input
|
||||||
|
if game.vars.showing_idx > #game.vars.sequence then
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
game.vars.waiting_for_input = true
|
||||||
|
game.vars.input_idx = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Highlight button during show duration
|
||||||
|
if game.vars.show_frame <= SHOW_DURATION then
|
||||||
|
game.vars.show_button = game.vars.sequence[game.vars.showing_idx]
|
||||||
|
else
|
||||||
|
game.vars.show_button = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle player input
|
||||||
|
if game.vars.waiting_for_input and event.type == INPUT.TOUCH_DOWN then
|
||||||
|
local button = get_button_at(event.x, event.y)
|
||||||
|
if button then
|
||||||
|
game.vars.player_seq[#game.vars.player_seq + 1] = button
|
||||||
|
game.vars.input_idx = game.vars.input_idx + 1
|
||||||
|
|
||||||
|
-- Check if correct
|
||||||
|
if game.vars.sequence[game.vars.input_idx] ~= button then
|
||||||
|
-- Wrong! Game over
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if completed sequence
|
||||||
|
if game.vars.input_idx == #game.vars.sequence then
|
||||||
|
-- Advance to next round
|
||||||
|
game.vars.sequence[#game.vars.sequence + 1] = math.random(1, 4)
|
||||||
|
game.vars.player_seq = {}
|
||||||
|
game.vars.waiting_for_input = false
|
||||||
|
game.vars.showing_idx = 0
|
||||||
|
game.vars.show_frame = 0
|
||||||
|
game.vars.show_button = nil
|
||||||
|
game.vars.state = STATE_SHOWING
|
||||||
|
game.vars.score = game.vars.score + 1
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
local buttons = get_buttons()
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 40, game.height() / 2 - 40, "SIMON SAYS", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 - 10, "Repeat the sequence", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 + 20, "Tap to Start", true, 2)
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Draw buttons with highlight
|
||||||
|
for i = 1, 4 do
|
||||||
|
local btn = buttons[i]
|
||||||
|
local filled = (game.vars.show_button == i)
|
||||||
|
renderer.rect(btn.x, btn.y, btn.size, btn.size, true, filled)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw score
|
||||||
|
renderer.text_scaled(10, 10, "Level: " .. tostring(game.vars.score + 1), true, 2)
|
||||||
|
|
||||||
|
if state == STATE_PLAYING and game.vars.waiting_for_input then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 40, game.height() - 20, "Your turn!", true, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 40, game.height() / 2 - 20, "GAME OVER", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 30, game.height() / 2, "Level: " .. tostring(game.vars.score + 1), true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 60, game.height() / 2 + 20, "Tap to Restart", true, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_button_at(x, y)
|
||||||
|
local buttons = get_buttons()
|
||||||
|
for i = 1, 4 do
|
||||||
|
local btn = buttons[i]
|
||||||
|
if x >= btn.x and x < btn.x + btn.size and
|
||||||
|
y >= btn.y and y < btn.y + btn.size then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.sequence = {math.random(1, 4)}
|
||||||
|
game.vars.player_seq = {}
|
||||||
|
game.vars.showing_idx = 0
|
||||||
|
game.vars.show_frame = 0
|
||||||
|
game.vars.show_button = nil
|
||||||
|
game.vars.waiting_for_input = false
|
||||||
|
game.vars.input_idx = 0
|
||||||
|
game.vars.state = STATE_SHOWING
|
||||||
|
end
|
||||||
@@ -30,7 +30,10 @@ function init()
|
|||||||
game.vars.food_y = 14
|
game.vars.food_y = 14
|
||||||
|
|
||||||
game.vars.frame_count = 0
|
game.vars.frame_count = 0
|
||||||
game.vars.move_speed = 10 -- Frames between moves
|
game.vars.move_speed = 20 -- Frames between moves (doubled from 10 for half speed)
|
||||||
|
|
||||||
|
-- Enable continuous frame updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
print("Snake Game initialized")
|
print("Snake Game initialized")
|
||||||
end
|
end
|
||||||
@@ -124,9 +127,9 @@ function draw()
|
|||||||
renderer.rect(seg.x * CELL_SIZE, seg.y * CELL_SIZE, CELL_SIZE, CELL_SIZE, true, filled)
|
renderer.rect(seg.x * CELL_SIZE, seg.y * CELL_SIZE, CELL_SIZE, CELL_SIZE, true, filled)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Draw food
|
-- Draw food (convert center to integers)
|
||||||
renderer.circle(game.vars.food_x * CELL_SIZE + CELL_SIZE / 2,
|
renderer.circle(math.floor(game.vars.food_x * CELL_SIZE + CELL_SIZE / 2 + 0.5),
|
||||||
game.vars.food_y * CELL_SIZE + CELL_SIZE / 2,
|
math.floor(game.vars.food_y * CELL_SIZE + CELL_SIZE / 2 + 0.5),
|
||||||
CELL_SIZE / 2, true, true)
|
CELL_SIZE / 2, true, true)
|
||||||
|
|
||||||
-- Draw score
|
-- Draw score
|
||||||
@@ -164,15 +167,13 @@ end
|
|||||||
function move_snake()
|
function move_snake()
|
||||||
local head = game.vars.snake[1]
|
local head = game.vars.snake[1]
|
||||||
local new_head = {
|
local new_head = {
|
||||||
x = head.x + game.vars.dir_x,
|
x = (head.x + game.vars.dir_x) % GRID_W,
|
||||||
y = head.y + game.vars.dir_y
|
y = (head.y + game.vars.dir_y) % GRID_H
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Check wall collision
|
-- Wrap around negative values (Lua modulo behavior)
|
||||||
if new_head.x < 0 or new_head.x >= GRID_W or new_head.y < 0 or new_head.y >= GRID_H then
|
if new_head.x < 0 then new_head.x = new_head.x + GRID_W end
|
||||||
game_over()
|
if new_head.y < 0 then new_head.y = new_head.y + GRID_H end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check self collision
|
-- Check self collision
|
||||||
for i = 1, #game.vars.snake do
|
for i = 1, #game.vars.snake do
|
||||||
@@ -190,8 +191,8 @@ function move_snake()
|
|||||||
game.vars.score = game.vars.score + 10
|
game.vars.score = game.vars.score + 10
|
||||||
spawn_food()
|
spawn_food()
|
||||||
|
|
||||||
-- Increase speed slightly
|
-- Increase speed slightly (but keep it slower overall)
|
||||||
if game.vars.move_speed > 3 then
|
if game.vars.move_speed > 5 then
|
||||||
game.vars.move_speed = game.vars.move_speed - 1
|
game.vars.move_speed = game.vars.move_speed - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,314 @@
|
|||||||
|
-- NAME: Tetris
|
||||||
|
-- DESC: Stack falling blocks, clear lines
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local GRID_WIDTH = 10
|
||||||
|
local GRID_HEIGHT = 20
|
||||||
|
local CELL_SIZE = 8
|
||||||
|
local SPAWN_RATE = 30 -- Frames before piece drops
|
||||||
|
|
||||||
|
-- Tetromino shapes (4 orientations each)
|
||||||
|
local TETROMINOS = {
|
||||||
|
-- I piece
|
||||||
|
{
|
||||||
|
{{0, 0}, {1, 0}, {2, 0}, {3, 0}},
|
||||||
|
{{0, 0}, {0, 1}, {0, 2}, {0, 3}},
|
||||||
|
{{0, 0}, {1, 0}, {2, 0}, {3, 0}},
|
||||||
|
{{0, 0}, {0, 1}, {0, 2}, {0, 3}}
|
||||||
|
},
|
||||||
|
-- O piece
|
||||||
|
{
|
||||||
|
{{0, 0}, {1, 0}, {0, 1}, {1, 1}},
|
||||||
|
{{0, 0}, {1, 0}, {0, 1}, {1, 1}},
|
||||||
|
{{0, 0}, {1, 0}, {0, 1}, {1, 1}},
|
||||||
|
{{0, 0}, {1, 0}, {0, 1}, {1, 1}}
|
||||||
|
},
|
||||||
|
-- T piece
|
||||||
|
{
|
||||||
|
{{1, 0}, {0, 1}, {1, 1}, {2, 1}},
|
||||||
|
{{1, 0}, {0, 1}, {1, 1}, {1, 2}},
|
||||||
|
{{0, 1}, {1, 1}, {2, 1}, {1, 2}},
|
||||||
|
{{1, 0}, {1, 1}, {2, 1}, {1, 2}}
|
||||||
|
},
|
||||||
|
-- S piece
|
||||||
|
{
|
||||||
|
{{1, 0}, {2, 0}, {0, 1}, {1, 1}},
|
||||||
|
{{0, 0}, {0, 1}, {1, 1}, {1, 2}},
|
||||||
|
{{1, 0}, {2, 0}, {0, 1}, {1, 1}},
|
||||||
|
{{0, 0}, {0, 1}, {1, 1}, {1, 2}}
|
||||||
|
},
|
||||||
|
-- Z piece
|
||||||
|
{
|
||||||
|
{{0, 0}, {1, 0}, {1, 1}, {2, 1}},
|
||||||
|
{{1, 0}, {0, 1}, {1, 1}, {0, 2}},
|
||||||
|
{{0, 0}, {1, 0}, {1, 1}, {2, 1}},
|
||||||
|
{{1, 0}, {0, 1}, {1, 1}, {0, 2}}
|
||||||
|
},
|
||||||
|
-- J piece
|
||||||
|
{
|
||||||
|
{{0, 0}, {0, 1}, {1, 1}, {2, 1}},
|
||||||
|
{{1, 0}, {2, 0}, {1, 1}, {1, 2}},
|
||||||
|
{{0, 1}, {1, 1}, {2, 1}, {2, 0}},
|
||||||
|
{{1, 0}, {1, 1}, {0, 2}, {1, 2}}
|
||||||
|
},
|
||||||
|
-- L piece
|
||||||
|
{
|
||||||
|
{{2, 0}, {0, 1}, {1, 1}, {2, 1}},
|
||||||
|
{{1, 0}, {1, 1}, {1, 2}, {2, 2}},
|
||||||
|
{{0, 1}, {1, 1}, {2, 1}, {0, 0}},
|
||||||
|
{{0, 0}, {1, 0}, {1, 1}, {1, 2}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.level = 1
|
||||||
|
game.vars.lines = 0
|
||||||
|
|
||||||
|
-- Grid (0 = empty, 1 = filled)
|
||||||
|
game.vars.grid = {}
|
||||||
|
for y = 1, GRID_HEIGHT do
|
||||||
|
game.vars.grid[y] = {}
|
||||||
|
for x = 1, GRID_WIDTH do
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Current piece
|
||||||
|
game.vars.piece = nil
|
||||||
|
game.vars.piece_x = 0
|
||||||
|
game.vars.piece_y = 0
|
||||||
|
game.vars.piece_type = 0
|
||||||
|
game.vars.piece_rotation = 0
|
||||||
|
|
||||||
|
-- Animation
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
game.vars.clear_rows = {}
|
||||||
|
game.vars.clearing = false
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Tetris initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
-- Handle input
|
||||||
|
if event.type == INPUT.TOUCH_DOWN then
|
||||||
|
if event.x < game.width() / 2 then
|
||||||
|
-- Left side: move left
|
||||||
|
if can_move(game.vars.piece, game.vars.piece_x - 1, game.vars.piece_y, game.vars.piece_rotation) then
|
||||||
|
game.vars.piece_x = game.vars.piece_x - 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Right side: move right
|
||||||
|
if can_move(game.vars.piece, game.vars.piece_x + 1, game.vars.piece_y, game.vars.piece_rotation) then
|
||||||
|
game.vars.piece_x = game.vars.piece_x + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update physics on frame tick
|
||||||
|
if event.type == INPUT.FRAME_TICK then
|
||||||
|
game.vars.frame_count = game.vars.frame_count + 1
|
||||||
|
|
||||||
|
if game.vars.clearing then
|
||||||
|
-- Clear animation
|
||||||
|
if game.vars.frame_count % 10 == 0 then
|
||||||
|
clear_lines()
|
||||||
|
game.vars.clearing = false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Drop piece
|
||||||
|
if game.vars.frame_count >= SPAWN_RATE then
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
|
||||||
|
if can_move(game.vars.piece, game.vars.piece_x, game.vars.piece_y + 1, game.vars.piece_rotation) then
|
||||||
|
game.vars.piece_y = game.vars.piece_y + 1
|
||||||
|
else
|
||||||
|
-- Lock piece
|
||||||
|
lock_piece()
|
||||||
|
|
||||||
|
-- Check for complete lines
|
||||||
|
local complete = check_complete_lines()
|
||||||
|
if #complete > 0 then
|
||||||
|
game.vars.clear_rows = complete
|
||||||
|
game.vars.clearing = true
|
||||||
|
else
|
||||||
|
spawn_piece()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false) -- Black background
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 20, game.height() / 2 - 30, "TETRIS", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING or state == STATE_GAME_OVER then
|
||||||
|
-- Draw grid
|
||||||
|
local start_x = 20
|
||||||
|
local start_y = 15
|
||||||
|
|
||||||
|
for y = 1, GRID_HEIGHT do
|
||||||
|
for x = 1, GRID_WIDTH do
|
||||||
|
if game.vars.grid[y][x] == 1 then
|
||||||
|
renderer.rect(start_x + (x - 1) * CELL_SIZE, start_y + (y - 1) * CELL_SIZE, CELL_SIZE, CELL_SIZE, true, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw current piece
|
||||||
|
if game.vars.piece then
|
||||||
|
for _, block in ipairs(game.vars.piece) do
|
||||||
|
local x = start_x + (game.vars.piece_x + block[1]) * CELL_SIZE
|
||||||
|
local y = start_y + (game.vars.piece_y + block[2]) * CELL_SIZE
|
||||||
|
renderer.rect(x, y, CELL_SIZE, CELL_SIZE, true, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw score
|
||||||
|
renderer.text_scaled(game.width(, 2) - 50, 10, "Score: " .. tostring(game.vars.score), true)
|
||||||
|
renderer.text_scaled(game.width(, 2) - 50, 20, "Lines: " .. tostring(game.vars.lines), true)
|
||||||
|
|
||||||
|
if state == STATE_GAME_OVER then
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2, "GAME OVER", true)
|
||||||
|
renderer.text_scaled(game.width(, 2) / 2 - 50, game.height() / 2 + 20, "Tap to Menu", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function spawn_piece()
|
||||||
|
game.vars.piece_type = math.random(1, #TETROMINOS)
|
||||||
|
game.vars.piece_rotation = 1
|
||||||
|
game.vars.piece = TETROMINOS[game.vars.piece_type][game.vars.piece_rotation]
|
||||||
|
game.vars.piece_x = 3
|
||||||
|
game.vars.piece_y = 0
|
||||||
|
|
||||||
|
-- Check if game over
|
||||||
|
if not can_move(game.vars.piece, game.vars.piece_x, game.vars.piece_y, game.vars.piece_rotation) then
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function lock_piece()
|
||||||
|
if not game.vars.piece then return end
|
||||||
|
|
||||||
|
for _, block in ipairs(game.vars.piece) do
|
||||||
|
local x = game.vars.piece_x + block[1] + 1
|
||||||
|
local y = game.vars.piece_y + block[2] + 1
|
||||||
|
|
||||||
|
if y >= 1 and y <= GRID_HEIGHT and x >= 1 and x <= GRID_WIDTH then
|
||||||
|
game.vars.grid[y][x] = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function can_move(piece, x, y, rotation)
|
||||||
|
if not piece then return false end
|
||||||
|
|
||||||
|
for _, block in ipairs(piece) do
|
||||||
|
local grid_x = x + block[1] + 1
|
||||||
|
local grid_y = y + block[2] + 1
|
||||||
|
|
||||||
|
if grid_x < 1 or grid_x > GRID_WIDTH or grid_y < 1 or grid_y > GRID_HEIGHT then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if game.vars.grid[grid_y][grid_x] == 1 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_complete_lines()
|
||||||
|
local complete = {}
|
||||||
|
|
||||||
|
for y = 1, GRID_HEIGHT do
|
||||||
|
local full = true
|
||||||
|
for x = 1, GRID_WIDTH do
|
||||||
|
if game.vars.grid[y][x] == 0 then
|
||||||
|
full = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if full then
|
||||||
|
table.insert(complete, y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return complete
|
||||||
|
end
|
||||||
|
|
||||||
|
function clear_lines()
|
||||||
|
for _, y in ipairs(game.vars.clear_rows) do
|
||||||
|
-- Remove line
|
||||||
|
table.remove(game.vars.grid, y)
|
||||||
|
-- Add empty line at top
|
||||||
|
table.insert(game.vars.grid, 1, {})
|
||||||
|
for x = 1, GRID_WIDTH do
|
||||||
|
game.vars.grid[1][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
game.vars.score = game.vars.score + (#game.vars.clear_rows * 100)
|
||||||
|
game.vars.lines = game.vars.lines + #game.vars.clear_rows
|
||||||
|
game.vars.clear_rows = {}
|
||||||
|
|
||||||
|
spawn_piece()
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.score = 0
|
||||||
|
game.vars.level = 1
|
||||||
|
game.vars.lines = 0
|
||||||
|
game.vars.frame_count = 0
|
||||||
|
game.vars.clearing = false
|
||||||
|
|
||||||
|
-- Clear grid
|
||||||
|
for y = 1, GRID_HEIGHT do
|
||||||
|
for x = 1, GRID_WIDTH do
|
||||||
|
game.vars.grid[y][x] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
spawn_piece()
|
||||||
|
end
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
-- NAME: Tic-Tac-Toe
|
||||||
|
-- DESC: Play vs AI opponent
|
||||||
|
|
||||||
|
-- Game states
|
||||||
|
local STATE_MENU = 0
|
||||||
|
local STATE_PLAYING = 1
|
||||||
|
local STATE_GAME_OVER = 2
|
||||||
|
|
||||||
|
-- Game constants
|
||||||
|
local GRID_SIZE = 3
|
||||||
|
|
||||||
|
-- Calculate cell size based on screen size
|
||||||
|
local function get_cell_size()
|
||||||
|
-- Use smallest dimension to maintain square grid
|
||||||
|
local min_dim = math.min(game.width(), game.height())
|
||||||
|
local padding = math.floor(min_dim / 8)
|
||||||
|
local available = min_dim - (padding * 2)
|
||||||
|
local cell_size = math.floor(available / GRID_SIZE)
|
||||||
|
return cell_size
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_grid_start_y()
|
||||||
|
-- Center grid vertically
|
||||||
|
local cell_size = get_cell_size()
|
||||||
|
local grid_height = cell_size * GRID_SIZE
|
||||||
|
return math.floor((game.height() - grid_height) / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_grid_start_x()
|
||||||
|
-- Center grid horizontally
|
||||||
|
local cell_size = get_cell_size()
|
||||||
|
local grid_width = cell_size * GRID_SIZE
|
||||||
|
return math.floor((game.width() - grid_width) / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function init()
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
game.vars.grid = {}
|
||||||
|
game.vars.player = 1 -- 1 = X, 2 = O
|
||||||
|
game.vars.ai = 2
|
||||||
|
game.vars.game_over = false
|
||||||
|
game.vars.winner = 0 -- 0 = ongoing, 1 = player wins, 2 = ai wins, 3 = draw
|
||||||
|
|
||||||
|
-- Enable continuous updates
|
||||||
|
game.set_frame_updates(true)
|
||||||
|
|
||||||
|
print("Tic-Tac-Toe initialized")
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(event)
|
||||||
|
local state = game.vars.state
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
reset_game()
|
||||||
|
game.vars.state = STATE_PLAYING
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN then
|
||||||
|
local cell = get_cell_at(event.x, event.y)
|
||||||
|
if cell and game.vars.grid[cell] == 0 then
|
||||||
|
-- Player move
|
||||||
|
game.vars.grid[cell] = game.vars.player
|
||||||
|
|
||||||
|
-- Check win
|
||||||
|
local winner = check_winner()
|
||||||
|
if winner ~= 0 then
|
||||||
|
if winner == game.vars.player then
|
||||||
|
game.vars.winner = 1
|
||||||
|
else
|
||||||
|
game.vars.winner = 2
|
||||||
|
end
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check draw
|
||||||
|
if is_board_full() then
|
||||||
|
game.vars.winner = 3
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- AI move
|
||||||
|
local ai_move = find_best_move()
|
||||||
|
game.vars.grid[ai_move] = game.vars.ai
|
||||||
|
|
||||||
|
-- Check win
|
||||||
|
winner = check_winner()
|
||||||
|
if winner ~= 0 then
|
||||||
|
if winner == game.vars.ai then
|
||||||
|
game.vars.winner = 2
|
||||||
|
end
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check draw
|
||||||
|
if is_board_full() then
|
||||||
|
game.vars.winner = 3
|
||||||
|
game.vars.state = STATE_GAME_OVER
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif state == STATE_GAME_OVER then
|
||||||
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||||
|
game.vars.state = STATE_MENU
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
renderer.clear(false)
|
||||||
|
|
||||||
|
local state = game.vars.state
|
||||||
|
local cell_size = get_cell_size()
|
||||||
|
local start_x = get_grid_start_x()
|
||||||
|
local start_y = get_grid_start_y()
|
||||||
|
local cell_spacing = 2
|
||||||
|
|
||||||
|
if state == STATE_MENU then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 - 30, "TIC-TAC-TOE", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2, "Play vs AI", true, 2)
|
||||||
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 + 20, "Tap to Start", true, 2)
|
||||||
|
|
||||||
|
elseif state == STATE_PLAYING or state == STATE_GAME_OVER then
|
||||||
|
-- Draw grid
|
||||||
|
for row = 0, GRID_SIZE - 1 do
|
||||||
|
for col = 0, GRID_SIZE - 1 do
|
||||||
|
local cell_idx = row * GRID_SIZE + col + 1
|
||||||
|
local cell_x = start_x + col * (cell_size + cell_spacing)
|
||||||
|
local cell_y = start_y + row * (cell_size + cell_spacing)
|
||||||
|
|
||||||
|
-- Draw cell
|
||||||
|
renderer.rect(cell_x, cell_y, cell_size, cell_size, true, false)
|
||||||
|
|
||||||
|
-- Draw X or O
|
||||||
|
local value = game.vars.grid[cell_idx]
|
||||||
|
if value == 1 then
|
||||||
|
-- Draw X
|
||||||
|
renderer.line(cell_x + 2, cell_y + 2, cell_x + cell_size - 2, cell_y + cell_size - 2, true, 1)
|
||||||
|
renderer.line(cell_x + cell_size - 2, cell_y + 2, cell_x + 2, cell_y + cell_size - 2, true, 1)
|
||||||
|
elseif value == 2 then
|
||||||
|
-- Draw O (convert center to integers)
|
||||||
|
renderer.circle(math.floor(cell_x + cell_size / 2 + 0.5), math.floor(cell_y + cell_size / 2 + 0.5), math.floor(cell_size / 2 - 2 + 0.5), true, false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE_GAME_OVER then
|
||||||
|
if game.vars.winner == 1 then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 30, 10, "YOU WIN!", true, 2)
|
||||||
|
elseif game.vars.winner == 2 then
|
||||||
|
renderer.text_scaled(game.width() / 2 - 30, 10, "AI WINS!", true, 2)
|
||||||
|
else
|
||||||
|
renderer.text_scaled(game.width() / 2 - 20, 10, "DRAW!", true, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
renderer.text_scaled(game.width() / 2 - 60, game.height() - 15, "Tap to Menu", true, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_cell_at(x, y)
|
||||||
|
local cell_size = get_cell_size()
|
||||||
|
local start_x = get_grid_start_x()
|
||||||
|
local start_y = get_grid_start_y()
|
||||||
|
local cell_spacing = 2
|
||||||
|
|
||||||
|
for row = 0, GRID_SIZE - 1 do
|
||||||
|
for col = 0, GRID_SIZE - 1 do
|
||||||
|
local cell_x = start_x + col * (cell_size + cell_spacing)
|
||||||
|
local cell_y = start_y + row * (cell_size + cell_spacing)
|
||||||
|
|
||||||
|
if x >= cell_x and x < cell_x + cell_size and
|
||||||
|
y >= cell_y and y < cell_y + cell_size then
|
||||||
|
return row * GRID_SIZE + col + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_winner()
|
||||||
|
-- Check rows
|
||||||
|
for row = 0, GRID_SIZE - 1 do
|
||||||
|
local val = game.vars.grid[row * GRID_SIZE + 1]
|
||||||
|
if val ~= 0 then
|
||||||
|
local match = true
|
||||||
|
for col = 1, GRID_SIZE - 1 do
|
||||||
|
if game.vars.grid[row * GRID_SIZE + col + 1] ~= val then
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if match then return val end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check columns
|
||||||
|
for col = 0, GRID_SIZE - 1 do
|
||||||
|
local val = game.vars.grid[col + 1]
|
||||||
|
if val ~= 0 then
|
||||||
|
local match = true
|
||||||
|
for row = 1, GRID_SIZE - 1 do
|
||||||
|
if game.vars.grid[row * GRID_SIZE + col + 1] ~= val then
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if match then return val end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check diagonals
|
||||||
|
local val = game.vars.grid[1]
|
||||||
|
if val ~= 0 then
|
||||||
|
if game.vars.grid[5] == val and game.vars.grid[9] == val then
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
val = game.vars.grid[3]
|
||||||
|
if val ~= 0 then
|
||||||
|
if game.vars.grid[5] == val and game.vars.grid[7] == val then
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function is_board_full()
|
||||||
|
for i = 1, 9 do
|
||||||
|
if game.vars.grid[i] == 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function find_best_move()
|
||||||
|
local best_score = -1000
|
||||||
|
local best_move = nil
|
||||||
|
|
||||||
|
for i = 1, 9 do
|
||||||
|
if game.vars.grid[i] == 0 then
|
||||||
|
game.vars.grid[i] = game.vars.ai
|
||||||
|
local score = minimax(0, false)
|
||||||
|
game.vars.grid[i] = 0
|
||||||
|
|
||||||
|
if score > best_score then
|
||||||
|
best_score = score
|
||||||
|
best_move = i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return best_move or 5 -- Fallback to center
|
||||||
|
end
|
||||||
|
|
||||||
|
function minimax(depth, is_maximizing)
|
||||||
|
local winner = check_winner()
|
||||||
|
|
||||||
|
if winner == game.vars.ai then return 10 - depth end
|
||||||
|
if winner == game.vars.player then return depth - 10 end
|
||||||
|
if is_board_full() then return 0 end
|
||||||
|
|
||||||
|
if is_maximizing then
|
||||||
|
local best_score = -1000
|
||||||
|
for i = 1, 9 do
|
||||||
|
if game.vars.grid[i] == 0 then
|
||||||
|
game.vars.grid[i] = game.vars.ai
|
||||||
|
local score = minimax(depth + 1, false)
|
||||||
|
game.vars.grid[i] = 0
|
||||||
|
best_score = math.max(best_score, score)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return best_score
|
||||||
|
else
|
||||||
|
local best_score = 1000
|
||||||
|
for i = 1, 9 do
|
||||||
|
if game.vars.grid[i] == 0 then
|
||||||
|
game.vars.grid[i] = game.vars.player
|
||||||
|
local score = minimax(depth + 1, true)
|
||||||
|
game.vars.grid[i] = 0
|
||||||
|
best_score = math.min(best_score, score)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return best_score
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function reset_game()
|
||||||
|
game.vars.grid = {}
|
||||||
|
for i = 1, 9 do
|
||||||
|
game.vars.grid[i] = 0
|
||||||
|
end
|
||||||
|
game.vars.winner = 0
|
||||||
|
end
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "lua_game.h"
|
#include "lua_game.h"
|
||||||
#include "lua_bindings.h"
|
#include "lua_bindings.h"
|
||||||
|
#include "sd_card.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -54,9 +55,13 @@ bool LuaGame::load_script() {
|
|||||||
FIL fil;
|
FIL fil;
|
||||||
FRESULT fr;
|
FRESULT fr;
|
||||||
|
|
||||||
|
// Set SPI speed for SD card operations
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
|
||||||
// Open Lua script from SD card
|
// Open Lua script from SD card
|
||||||
fr = f_open(&fil, script_path.c_str(), FA_READ);
|
fr = f_open(&fil, script_path.c_str(), FA_READ);
|
||||||
if (fr != FR_OK) {
|
if (fr != FR_OK) {
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
error_message = "Failed to open file (FatFS error: ";
|
error_message = "Failed to open file (FatFS error: ";
|
||||||
error_message += std::to_string((int)fr);
|
error_message += std::to_string((int)fr);
|
||||||
error_message += ")";
|
error_message += ")";
|
||||||
@@ -67,6 +72,7 @@ bool LuaGame::load_script() {
|
|||||||
FSIZE_t file_size = f_size(&fil);
|
FSIZE_t file_size = f_size(&fil);
|
||||||
if (file_size == 0 || file_size > 64 * 1024) { // Limit to 64KB
|
if (file_size == 0 || file_size > 64 * 1024) { // Limit to 64KB
|
||||||
f_close(&fil);
|
f_close(&fil);
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
error_message = "Script file size invalid (0 or > 64KB)";
|
error_message = "Script file size invalid (0 or > 64KB)";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -75,6 +81,7 @@ bool LuaGame::load_script() {
|
|||||||
char* script_buffer = new char[file_size + 1];
|
char* script_buffer = new char[file_size + 1];
|
||||||
if (!script_buffer) {
|
if (!script_buffer) {
|
||||||
f_close(&fil);
|
f_close(&fil);
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
error_message = "Failed to allocate memory for script";
|
error_message = "Failed to allocate memory for script";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -84,6 +91,9 @@ bool LuaGame::load_script() {
|
|||||||
fr = f_read(&fil, script_buffer, file_size, &bytes_read);
|
fr = f_read(&fil, script_buffer, file_size, &bytes_read);
|
||||||
f_close(&fil);
|
f_close(&fil);
|
||||||
|
|
||||||
|
// Restore SPI speed immediately after file operations
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
|
||||||
if (fr != FR_OK || bytes_read != file_size) {
|
if (fr != FR_OK || bytes_read != file_size) {
|
||||||
delete[] script_buffer;
|
delete[] script_buffer;
|
||||||
error_message = "Failed to read script file";
|
error_message = "Failed to read script file";
|
||||||
@@ -193,6 +203,18 @@ bool LuaGame::wants_to_exit() const {
|
|||||||
return exit;
|
return exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LuaGame::wants_frame_updates() const {
|
||||||
|
if (!L) return false;
|
||||||
|
|
||||||
|
// Check if Lua script wants continuous frame updates
|
||||||
|
lua_pushstring(L, "__wants_frame_updates");
|
||||||
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||||
|
bool wants_updates = lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return wants_updates;
|
||||||
|
}
|
||||||
|
|
||||||
bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) {
|
bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) {
|
||||||
int result = lua_pcall(L, nargs, nresults, 0);
|
int result = lua_pcall(L, nargs, nresults, 0);
|
||||||
if (result != LUA_OK) {
|
if (result != LUA_OK) {
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool wants_to_exit() const override;
|
bool wants_to_exit() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if game wants continuous frame updates
|
||||||
|
* @return true if Lua script set __wants_frame_updates flag
|
||||||
|
*/
|
||||||
|
bool wants_frame_updates() const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get Lua state for bindings access
|
* @brief Get Lua state for bindings access
|
||||||
*/
|
*/
|
||||||
|
|||||||
+69
-13
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "lua_game_loader.h"
|
#include "lua_game_loader.h"
|
||||||
#include "lua_game.h"
|
#include "lua_game.h"
|
||||||
|
#include "sd_card.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -16,10 +17,21 @@ extern "C" {
|
|||||||
// Structure to hold script path for factory closure
|
// Structure to hold script path for factory closure
|
||||||
struct LuaGameFactoryData {
|
struct LuaGameFactoryData {
|
||||||
char script_path[256];
|
char script_path[256];
|
||||||
|
char name[64];
|
||||||
|
char description[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<LuaGameFactoryData*> factory_data_list;
|
static std::vector<LuaGameFactoryData*> factory_data_list;
|
||||||
|
|
||||||
|
void LuaGameLoader::clear_factory_data() {
|
||||||
|
// Delete all factory data and clear the vector
|
||||||
|
for (LuaGameFactoryData* data : factory_data_list) {
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
factory_data_list.clear();
|
||||||
|
printf("LuaGameLoader: Cleared all factory data\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Factory wrapper that captures script path
|
// Factory wrapper that captures script path
|
||||||
static Game* lua_game_factory_wrapper(uint16_t width, uint16_t height,
|
static Game* lua_game_factory_wrapper(uint16_t width, uint16_t height,
|
||||||
LowLevelRenderer* renderer, LowLevelGUI* gui,
|
LowLevelRenderer* renderer, LowLevelGUI* gui,
|
||||||
@@ -49,10 +61,14 @@ bool LuaGameLoader::parse_metadata(const char* script_path, char* name, char* de
|
|||||||
// Default empty description
|
// Default empty description
|
||||||
description[0] = '\0';
|
description[0] = '\0';
|
||||||
|
|
||||||
|
// Set SPI speed for SD card operations
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
|
||||||
// Try to open file and parse metadata comments
|
// Try to open file and parse metadata comments
|
||||||
fr = f_open(&fil, script_path, FA_READ);
|
fr = f_open(&fil, script_path, FA_READ);
|
||||||
if (fr != FR_OK) {
|
if (fr != FR_OK) {
|
||||||
printf("LuaGameLoader: Warning - could not open %s for metadata\n", script_path);
|
printf("LuaGameLoader: Warning - could not open %s for metadata\n", script_path);
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +78,9 @@ bool LuaGameLoader::parse_metadata(const char* script_path, char* name, char* de
|
|||||||
fr = f_read(&fil, buffer, sizeof(buffer) - 1, &bytes_read);
|
fr = f_read(&fil, buffer, sizeof(buffer) - 1, &bytes_read);
|
||||||
f_close(&fil);
|
f_close(&fil);
|
||||||
|
|
||||||
|
// Restore SPI speed
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
|
||||||
if (fr != FR_OK) {
|
if (fr != FR_OK) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -107,47 +126,81 @@ int LuaGameLoader::register_all_games(GameLauncher* launcher) {
|
|||||||
|
|
||||||
printf("LuaGameLoader: Scanning /games directory for .lua scripts...\n");
|
printf("LuaGameLoader: Scanning /games directory for .lua scripts...\n");
|
||||||
|
|
||||||
|
// Set SPI speed for SD card operations
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
|
||||||
// Open /games directory
|
// Open /games directory
|
||||||
fr = f_opendir(&dir, "/games");
|
fr = f_opendir(&dir, "/games");
|
||||||
if (fr != FR_OK) {
|
if (fr != FR_OK) {
|
||||||
printf("LuaGameLoader: Could not open /games directory (error %d)\n", fr);
|
printf("LuaGameLoader: Could not open /games directory (error %d)\n", fr);
|
||||||
printf("LuaGameLoader: Make sure SD card is mounted and /games exists\n");
|
printf("LuaGameLoader: Make sure SD card is mounted and /games exists\n");
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan for .lua files
|
// Scan for .lua files
|
||||||
while (true) {
|
while (true) {
|
||||||
fr = f_readdir(&dir, &fno);
|
fr = f_readdir(&dir, &fno);
|
||||||
if (fr != FR_OK || fno.fname[0] == 0) break; // End of directory
|
if (fr != FR_OK) {
|
||||||
|
printf("LuaGameLoader: Error reading directory (error %d)\n", fr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fno.fname[0] == 0) {
|
||||||
|
printf("LuaGameLoader: End of directory reached\n");
|
||||||
|
break; // End of directory
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("LuaGameLoader: Found entry: %s (attrib=0x%02X)\n", fno.fname, fno.fattrib);
|
||||||
|
|
||||||
// Skip directories
|
// Skip directories
|
||||||
if (fno.fattrib & AM_DIR) continue;
|
if (fno.fattrib & AM_DIR) {
|
||||||
|
printf("LuaGameLoader: Skipping directory\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for .lua extension
|
// Skip hidden files (these are short 8.3 filename entries)
|
||||||
|
if (fno.fattrib & AM_HID) {
|
||||||
|
printf("LuaGameLoader: Skipping hidden file (short filename entry)\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for .lua extension (case-insensitive)
|
||||||
size_t len = strlen(fno.fname);
|
size_t len = strlen(fno.fname);
|
||||||
if (len < 5 || strcmp(fno.fname + len - 4, ".lua") != 0) continue;
|
printf("LuaGameLoader: Filename length: %d\n", len);
|
||||||
|
if (len < 5) {
|
||||||
|
printf("LuaGameLoader: Filename too short\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case-insensitive check for .lua or .LUA
|
||||||
|
const char* ext = fno.fname + len - 4;
|
||||||
|
if (strcasecmp(ext, ".lua") != 0) {
|
||||||
|
printf("LuaGameLoader: Not a .lua file (ext=%s)\n", ext);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("LuaGameLoader: Valid .lua file!\n");
|
||||||
|
|
||||||
// Build full path
|
// Build full path
|
||||||
char script_path[256];
|
char script_path[256];
|
||||||
snprintf(script_path, sizeof(script_path), "/games/%s", fno.fname);
|
snprintf(script_path, sizeof(script_path), "/games/%s", fno.fname);
|
||||||
|
|
||||||
// Parse metadata
|
|
||||||
char name[64];
|
|
||||||
char description[128];
|
|
||||||
parse_metadata(script_path, name, description);
|
|
||||||
|
|
||||||
printf("LuaGameLoader: Found %s - '%s'\n", fno.fname, name);
|
|
||||||
|
|
||||||
// Create factory data (persistent for game lifetime)
|
// Create factory data (persistent for game lifetime)
|
||||||
LuaGameFactoryData* data = new LuaGameFactoryData();
|
LuaGameFactoryData* data = new LuaGameFactoryData();
|
||||||
strncpy(data->script_path, script_path, sizeof(data->script_path) - 1);
|
strncpy(data->script_path, script_path, sizeof(data->script_path) - 1);
|
||||||
data->script_path[sizeof(data->script_path) - 1] = '\0';
|
data->script_path[sizeof(data->script_path) - 1] = '\0';
|
||||||
|
|
||||||
|
// Parse metadata directly into persistent storage
|
||||||
|
parse_metadata(script_path, data->name, data->description);
|
||||||
|
|
||||||
|
printf("LuaGameLoader: Found %s - '%s'\n", fno.fname, data->name);
|
||||||
|
|
||||||
factory_data_list.push_back(data);
|
factory_data_list.push_back(data);
|
||||||
|
|
||||||
// Register with launcher - using lambda factory pattern
|
// Register with launcher - using lambda factory pattern
|
||||||
launcher->register_game(
|
launcher->register_game(
|
||||||
name,
|
data->name,
|
||||||
description[0] ? description : "Lua Script",
|
data->description[0] ? data->description : "Lua Script",
|
||||||
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
|
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
|
||||||
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
|
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
|
||||||
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);
|
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);
|
||||||
@@ -159,6 +212,9 @@ int LuaGameLoader::register_all_games(GameLauncher* launcher) {
|
|||||||
|
|
||||||
f_closedir(&dir);
|
f_closedir(&dir);
|
||||||
|
|
||||||
|
// Restore SPI speed
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
|
||||||
printf("LuaGameLoader: Registered %d Lua games\n", count);
|
printf("LuaGameLoader: Registered %d Lua games\n", count);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
static int register_all_games(GameLauncher* launcher);
|
static int register_all_games(GameLauncher* launcher);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear all factory data (useful before re-scanning)
|
||||||
|
*/
|
||||||
|
static void clear_factory_data();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Parse metadata from Lua script comments
|
* @brief Parse metadata from Lua script comments
|
||||||
|
|||||||
+11
@@ -94,6 +94,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool wants_to_exit() const { return false; }
|
virtual bool wants_to_exit() const { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if game wants continuous frame updates
|
||||||
|
*
|
||||||
|
* Games that need animation or continuous updates (like physics simulations)
|
||||||
|
* can override this to return true. They will receive INPUT_FRAME_TICK events
|
||||||
|
* every frame, even without user input.
|
||||||
|
*
|
||||||
|
* @return true if game needs frame updates, false for event-driven only
|
||||||
|
*/
|
||||||
|
virtual bool wants_frame_updates() const { return false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the type of game for safe downcasting without RTTI
|
* @brief Get the type of game for safe downcasting without RTTI
|
||||||
*/
|
*/
|
||||||
|
|||||||
+203
-22
@@ -8,11 +8,13 @@
|
|||||||
#include "display/low_level_render.h"
|
#include "display/low_level_render.h"
|
||||||
#include "display/low_level_gui.h"
|
#include "display/low_level_gui.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
GameLauncher::GameLauncher(uint16_t width, uint16_t height,
|
GameLauncher::GameLauncher(uint16_t width, uint16_t height,
|
||||||
LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
|
LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
|
||||||
: width(width), height(height), renderer(renderer), gui(gui), input_manager(input_manager),
|
: width(width), height(height), renderer(renderer), gui(gui), input_manager(input_manager),
|
||||||
selected_index(0), selected_game(nullptr) {
|
selected_index(0), selected_game(nullptr), current_page(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameLauncher::register_game(const char* name, const char* description,
|
void GameLauncher::register_game(const char* name, const char* description,
|
||||||
@@ -32,15 +34,23 @@ void GameLauncher::draw() {
|
|||||||
|
|
||||||
renderer->set_font(&font_5x5_obj);
|
renderer->set_font(&font_5x5_obj);
|
||||||
|
|
||||||
// Draw title
|
// Draw title with page indicator
|
||||||
renderer->draw_string_scaled(30, 40, "Select a Game:", 2);
|
int total_pages = get_total_pages();
|
||||||
|
char title[64];
|
||||||
|
snprintf(title, sizeof(title), "Select a Game: (Page %d/%d)", current_page + 1, total_pages);
|
||||||
|
renderer->draw_string_scaled(30, 40, title, 2);
|
||||||
|
|
||||||
// Draw game list with GUI buttons
|
// Get games for current page
|
||||||
for (size_t i = 0; i < games.size(); i++) {
|
int page_start = get_page_start_index();
|
||||||
int y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
|
int page_end = get_page_end_index();
|
||||||
|
|
||||||
|
// Draw game list with GUI buttons for current page only
|
||||||
|
for (int i = page_start; i < page_end && i < (int)games.size(); i++) {
|
||||||
|
int item_index = i - page_start;
|
||||||
|
int y = MENU_Y_START + (item_index * MENU_ITEM_HEIGHT);
|
||||||
|
|
||||||
// Draw button (pressed/highlighted if selected)
|
// Draw button (pressed/highlighted if selected)
|
||||||
bool is_selected = ((int)i == selected_index);
|
bool is_selected = (i == selected_index);
|
||||||
gui->draw_button(window, 20, y, games[i].name, is_selected, true);
|
gui->draw_button(window, 20, y, games[i].name, is_selected, true);
|
||||||
|
|
||||||
// Draw description below button
|
// Draw description below button
|
||||||
@@ -49,27 +59,71 @@ void GameLauncher::draw() {
|
|||||||
renderer->draw_string_scaled(50, y + 36, games[i].description, 1);
|
renderer->draw_string_scaled(50, y + 36, games[i].description, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw instructions at bottom
|
// Draw navigation buttons at bottom (only if multiple pages)
|
||||||
const char* instructions;
|
if (total_pages > 1) {
|
||||||
if (input_manager->has_buttons()) {
|
int button_y = height - 65;
|
||||||
instructions = "Touch game or use KEY0/KEY1";
|
|
||||||
|
// Previous page button
|
||||||
|
bool has_prev = (current_page > 0);
|
||||||
|
gui->draw_button(window, 30, button_y, "< PREV", false, true);
|
||||||
|
|
||||||
|
// Next page button
|
||||||
|
bool has_next = (current_page + 1 < total_pages);
|
||||||
|
gui->draw_button(window, 200, button_y, "NEXT >", false, true);
|
||||||
|
|
||||||
|
// Draw instructions
|
||||||
|
renderer->set_font(&font_5x5_obj);
|
||||||
|
renderer->draw_string_scaled(30, height - 25, "Touch buttons or KEY0/KEY1", 1);
|
||||||
} else {
|
} else {
|
||||||
instructions = "Touch game to play";
|
// Single page - just show select instruction
|
||||||
|
renderer->set_font(&font_5x5_obj);
|
||||||
|
renderer->draw_string_scaled(30, height - 35, "KEY0: Navigate | KEY1: Select | Touch to play", 1);
|
||||||
}
|
}
|
||||||
renderer->set_font(&font_5x5_obj);
|
|
||||||
renderer->draw_string_scaled(30, height - 35, instructions, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool GameLauncher::update(const InputEvent& event) {
|
bool GameLauncher::update(const InputEvent& event) {
|
||||||
bool needs_refresh = false;
|
bool needs_refresh = false;
|
||||||
|
int total_pages = get_total_pages();
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case INPUT_TOUCH_DOWN: {
|
case INPUT_TOUCH_DOWN: {
|
||||||
printf("Touch at (%d,%d) in launcher\n", event.x, event.y);
|
printf("Touch at (%d,%d) in launcher\n", event.x, event.y);
|
||||||
|
|
||||||
// Check if touch is on a game entry
|
// Check if touch is on navigation buttons (if multiple pages)
|
||||||
for (size_t i = 0; i < games.size(); i++) {
|
if (total_pages > 1) {
|
||||||
int y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
|
// Previous button: x [30-180], y [235-275]
|
||||||
|
if (event.x >= PREV_BUTTON_X && event.x < PREV_BUTTON_X + BUTTON_WIDTH &&
|
||||||
|
event.y >= NAV_BUTTON_Y && event.y < NAV_BUTTON_Y + BUTTON_HEIGHT) {
|
||||||
|
if (current_page > 0) {
|
||||||
|
current_page--;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
needs_refresh = true;
|
||||||
|
printf("Navigated to previous page: %d\n", current_page);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next button: x [200-350], y [235-275]
|
||||||
|
if (event.x >= NEXT_BUTTON_X && event.x < NEXT_BUTTON_X + BUTTON_WIDTH &&
|
||||||
|
event.y >= NAV_BUTTON_Y && event.y < NAV_BUTTON_Y + BUTTON_HEIGHT) {
|
||||||
|
if (current_page + 1 < total_pages) {
|
||||||
|
current_page++;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
needs_refresh = true;
|
||||||
|
printf("Navigated to next page: %d\n", current_page);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if touch is on a game entry for current page
|
||||||
|
int page_start = get_page_start_index();
|
||||||
|
int page_end = get_page_end_index();
|
||||||
|
|
||||||
|
for (int i = page_start; i < page_end && i < (int)games.size(); i++) {
|
||||||
|
int item_index = i - page_start;
|
||||||
|
int y = MENU_Y_START + (item_index * MENU_ITEM_HEIGHT);
|
||||||
|
|
||||||
// Touch area is the entire menu item
|
// Touch area is the entire menu item
|
||||||
if (event.y >= y - 5 && event.y < y + MENU_ITEM_HEIGHT - 5) {
|
if (event.y >= y - 5 && event.y < y + MENU_ITEM_HEIGHT - 5) {
|
||||||
@@ -87,12 +141,41 @@ bool GameLauncher::update(const InputEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case INPUT_BUTTON_0: {
|
case INPUT_BUTTON_0: {
|
||||||
// Navigate menu
|
// Navigate within current page or switch pages
|
||||||
if (games.size() > 0) {
|
int page_start = get_page_start_index();
|
||||||
selected_index = (selected_index + 1) % games.size();
|
int page_end = get_page_end_index();
|
||||||
needs_refresh = true;
|
|
||||||
printf("Menu selection: %d (%s)\n", selected_index, games[selected_index].name);
|
// If multiple games on current page, navigate within page first
|
||||||
|
if (page_end - page_start > 1) {
|
||||||
|
// Move within current page
|
||||||
|
int old_index = selected_index;
|
||||||
|
selected_index++;
|
||||||
|
if (selected_index >= page_end) {
|
||||||
|
// Moving to next page
|
||||||
|
if (current_page + 1 < total_pages) {
|
||||||
|
current_page++;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
} else {
|
||||||
|
// Wrap to first page
|
||||||
|
current_page = 0;
|
||||||
|
selected_index = 0;
|
||||||
|
}
|
||||||
|
} else if (selected_index < page_start) {
|
||||||
|
selected_index = page_start;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single game on page, move to next page
|
||||||
|
if (current_page + 1 < total_pages) {
|
||||||
|
current_page++;
|
||||||
|
selected_index = get_page_start_index();
|
||||||
|
} else {
|
||||||
|
// Wrap to first page
|
||||||
|
current_page = 0;
|
||||||
|
selected_index = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
needs_refresh = true;
|
||||||
|
printf("Menu selection: %d (%s), Page: %d\n", selected_index, games[selected_index].name, current_page);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,5 +210,103 @@ void GameLauncher::reset() {
|
|||||||
selected_game = nullptr;
|
selected_game = nullptr;
|
||||||
}
|
}
|
||||||
selected_index = 0;
|
selected_index = 0;
|
||||||
|
current_page = 0;
|
||||||
printf("Launcher reset - returning to menu\n");
|
printf("Launcher reset - returning to menu\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameLauncher::clear_games() {
|
||||||
|
// Clean up currently selected game first
|
||||||
|
if (selected_game) {
|
||||||
|
delete selected_game;
|
||||||
|
selected_game = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the games vector
|
||||||
|
games.clear();
|
||||||
|
selected_index = 0;
|
||||||
|
current_page = 0;
|
||||||
|
printf("Launcher: Cleared all registered games\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameLauncher::select_game_by_name(const char* name) {
|
||||||
|
// Clean up old game first if one exists
|
||||||
|
if (selected_game) {
|
||||||
|
printf("Cleaning up previous game...\n");
|
||||||
|
delete selected_game;
|
||||||
|
selected_game = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First pass: search for exact match (case-insensitive)
|
||||||
|
for (size_t i = 0; i < games.size(); i++) {
|
||||||
|
if (strcasecmp(games[i].name, name) == 0) {
|
||||||
|
printf("Found exact match: %s\n", games[i].name);
|
||||||
|
|
||||||
|
// Create and initialize the game
|
||||||
|
selected_game = games[i].factory(width, height, renderer, gui, input_manager);
|
||||||
|
if (selected_game) {
|
||||||
|
printf("Game instance created, initializing...\n");
|
||||||
|
selected_game->init();
|
||||||
|
selected_index = i;
|
||||||
|
current_page = i / GAMES_PER_PAGE;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
printf("Failed to create game instance\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: search in reverse order for partial match (case-insensitive, newest files first)
|
||||||
|
for (int i = games.size() - 1; i >= 0; i--) {
|
||||||
|
// Convert both strings to lowercase for comparison
|
||||||
|
char game_name_lower[256];
|
||||||
|
char search_name_lower[256];
|
||||||
|
|
||||||
|
strncpy(game_name_lower, games[i].name, sizeof(game_name_lower) - 1);
|
||||||
|
game_name_lower[sizeof(game_name_lower) - 1] = '\0';
|
||||||
|
|
||||||
|
strncpy(search_name_lower, name, sizeof(search_name_lower) - 1);
|
||||||
|
search_name_lower[sizeof(search_name_lower) - 1] = '\0';
|
||||||
|
|
||||||
|
// Convert to lowercase
|
||||||
|
for (size_t j = 0; game_name_lower[j]; j++) {
|
||||||
|
game_name_lower[j] = tolower(game_name_lower[j]);
|
||||||
|
}
|
||||||
|
for (size_t j = 0; search_name_lower[j]; j++) {
|
||||||
|
search_name_lower[j] = tolower(search_name_lower[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strstr(game_name_lower, search_name_lower) != nullptr) {
|
||||||
|
printf("Found partial match: %s (searching for: %s)\n", games[i].name, name);
|
||||||
|
|
||||||
|
// Create and initialize the game
|
||||||
|
selected_game = games[i].factory(width, height, renderer, gui, input_manager);
|
||||||
|
if (selected_game) {
|
||||||
|
printf("Game instance created, initializing...\n");
|
||||||
|
selected_game->init();
|
||||||
|
selected_index = i;
|
||||||
|
current_page = i / GAMES_PER_PAGE;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
printf("Failed to create game instance\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Game not found: %s\n", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GameLauncher::get_total_pages() const {
|
||||||
|
if (games.empty()) return 1;
|
||||||
|
return (games.size() + GAMES_PER_PAGE - 1) / GAMES_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GameLauncher::get_page_start_index() const {
|
||||||
|
return current_page * GAMES_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GameLauncher::get_page_end_index() const {
|
||||||
|
return (current_page + 1) * GAMES_PER_PAGE;
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,12 +79,24 @@ public:
|
|||||||
*/
|
*/
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear all registered games (useful before re-scanning)
|
||||||
|
*/
|
||||||
|
void clear_games();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if a game is currently selected
|
* @brief Check if a game is currently selected
|
||||||
* @return true if game selected, false if in menu
|
* @return true if game selected, false if in menu
|
||||||
*/
|
*/
|
||||||
bool is_game_selected() const { return selected_game != nullptr; }
|
bool is_game_selected() const { return selected_game != nullptr; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Select a game by name (for programmatic launching)
|
||||||
|
* @param name Game name to select (partial match supported)
|
||||||
|
* @return true if game was found and launched, false otherwise
|
||||||
|
*/
|
||||||
|
bool select_game_by_name(const char* name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t width;
|
uint16_t width;
|
||||||
uint16_t height;
|
uint16_t height;
|
||||||
@@ -95,11 +107,23 @@ private:
|
|||||||
std::vector<GameEntry> games;
|
std::vector<GameEntry> games;
|
||||||
int selected_index; // Currently highlighted game
|
int selected_index; // Currently highlighted game
|
||||||
Game* selected_game; // Currently running game (nullptr = in menu)
|
Game* selected_game; // Currently running game (nullptr = in menu)
|
||||||
|
int current_page; // Current page in pagination
|
||||||
|
|
||||||
// Menu layout constants
|
// Menu layout constants
|
||||||
static const int MENU_Y_START = 60;
|
static const int MENU_Y_START = 60;
|
||||||
static const int MENU_ITEM_HEIGHT = 40;
|
static const int MENU_ITEM_HEIGHT = 40;
|
||||||
static const int MENU_PADDING = 10;
|
static const int MENU_PADDING = 10;
|
||||||
|
static const int GAMES_PER_PAGE = 4;
|
||||||
|
static const int NAV_BUTTON_Y = 235; // Bottom navigation buttons
|
||||||
|
static const int PREV_BUTTON_X = 30;
|
||||||
|
static const int NEXT_BUTTON_X = 200;
|
||||||
|
static const int BUTTON_WIDTH = 150;
|
||||||
|
static const int BUTTON_HEIGHT = 40;
|
||||||
|
|
||||||
|
// Helper functions for pagination
|
||||||
|
int get_total_pages() const;
|
||||||
|
int get_page_start_index() const;
|
||||||
|
int get_page_end_index() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GAME_LAUNCHER_H
|
#endif // GAME_LAUNCHER_H
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,8 @@ enum InputType {
|
|||||||
INPUT_TOUCH_UP,
|
INPUT_TOUCH_UP,
|
||||||
INPUT_BUTTON_0,
|
INPUT_BUTTON_0,
|
||||||
INPUT_BUTTON_1,
|
INPUT_BUTTON_1,
|
||||||
INPUT_GESTURE
|
INPUT_GESTURE,
|
||||||
|
INPUT_FRAME_TICK // Sent every frame for animation/continuous updates
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unified input event structure
|
// Unified input event structure
|
||||||
|
|||||||
+49
-7
@@ -105,7 +105,7 @@ bool sd_card_init(const sd_card_config_t *config) {
|
|||||||
// Configure MISO pin for SPI (MUST be done for SD card reads to work)
|
// Configure MISO pin for SPI (MUST be done for SD card reads to work)
|
||||||
gpio_set_function(config->gpio_miso, GPIO_FUNC_SPI);
|
gpio_set_function(config->gpio_miso, GPIO_FUNC_SPI);
|
||||||
|
|
||||||
// Save current SPI speed
|
// Save current SPI speed to restore on failure
|
||||||
uint baudrate = spi_get_baudrate(config->spi);
|
uint baudrate = spi_get_baudrate(config->spi);
|
||||||
|
|
||||||
// Slow down SPI for SD card initialization (100-400 kHz recommended)
|
// Slow down SPI for SD card initialization (100-400 kHz recommended)
|
||||||
@@ -128,6 +128,7 @@ bool sd_card_init(const sd_card_config_t *config) {
|
|||||||
|
|
||||||
if (r1 != SD_R1_IDLE_STATE) {
|
if (r1 != SD_R1_IDLE_STATE) {
|
||||||
printf("[SD] Card not responding to CMD0\n");
|
printf("[SD] Card not responding to CMD0\n");
|
||||||
|
spi_set_baudrate(config->spi, baudrate); // Restore original speed
|
||||||
return false; // Card not responding
|
return false; // Card not responding
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +152,7 @@ bool sd_card_init(const sd_card_config_t *config) {
|
|||||||
// Check if voltage range is supported
|
// Check if voltage range is supported
|
||||||
if (ocr[2] != 0x01 || ocr[3] != 0xAA) {
|
if (ocr[2] != 0x01 || ocr[3] != 0xAA) {
|
||||||
printf("[SD] Voltage range not supported\n");
|
printf("[SD] Voltage range not supported\n");
|
||||||
|
spi_set_baudrate(config->spi, baudrate); // Restore original speed
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +175,7 @@ bool sd_card_init(const sd_card_config_t *config) {
|
|||||||
|
|
||||||
if (r1 != SD_R1_READY) {
|
if (r1 != SD_R1_READY) {
|
||||||
printf("[SD] ACMD41 initialization timeout\n");
|
printf("[SD] ACMD41 initialization timeout\n");
|
||||||
|
spi_set_baudrate(config->spi, baudrate); // Restore original speed
|
||||||
return false; // Initialization failed
|
return false; // Initialization failed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,10 +225,12 @@ bool sd_card_init(const sd_card_config_t *config) {
|
|||||||
|
|
||||||
g_card_info.type = SD_CARD_TYPE_SD1;
|
g_card_info.type = SD_CARD_TYPE_SD1;
|
||||||
} else {
|
} else {
|
||||||
|
spi_set_baudrate(config->spi, baudrate); // Restore original speed
|
||||||
return false; // Not supported
|
return false; // Not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r1 != SD_R1_READY) {
|
if (r1 != SD_R1_READY) {
|
||||||
|
spi_set_baudrate(config->spi, baudrate); // Restore original speed
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,6 +242,7 @@ bool sd_card_init(const sd_card_config_t *config) {
|
|||||||
sd_card_deselect();
|
sd_card_deselect();
|
||||||
|
|
||||||
if (r1 != SD_R1_READY) {
|
if (r1 != SD_R1_READY) {
|
||||||
|
spi_set_baudrate(config->spi, baudrate); // Restore original speed
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,7 +323,9 @@ bool sd_card_read_blocks(uint32_t block_addr, uint32_t num_blocks, uint8_t *buff
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool sd_card_write_block(uint32_t block_addr, const uint8_t *buffer) {
|
bool sd_card_write_block(uint32_t block_addr, const uint8_t *buffer) {
|
||||||
if (!g_card_info.initialized || buffer == NULL) return false;
|
if (!g_card_info.initialized || buffer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// For non-SDHC cards, convert block address to byte address
|
// For non-SDHC cards, convert block address to byte address
|
||||||
if (g_card_info.type != SD_CARD_TYPE_SDHC) {
|
if (g_card_info.type != SD_CARD_TYPE_SDHC) {
|
||||||
@@ -328,11 +336,24 @@ bool sd_card_write_block(uint32_t block_addr, const uint8_t *buffer) {
|
|||||||
|
|
||||||
// Send write command
|
// Send write command
|
||||||
uint8_t r1 = sd_card_send_command(SD_CMD24, block_addr);
|
uint8_t r1 = sd_card_send_command(SD_CMD24, block_addr);
|
||||||
|
|
||||||
if (r1 != SD_R1_READY) {
|
if (r1 != SD_R1_READY) {
|
||||||
sd_card_deselect();
|
sd_card_deselect();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for card to be ready to receive data
|
||||||
|
uint32_t timeout_count = 0;
|
||||||
|
uint8_t ready_byte;
|
||||||
|
do {
|
||||||
|
ready_byte = sd_card_transfer(0xFF);
|
||||||
|
timeout_count++;
|
||||||
|
if (timeout_count > 1000) {
|
||||||
|
sd_card_deselect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (ready_byte != 0xFF);
|
||||||
|
|
||||||
// Send start token
|
// Send start token
|
||||||
sd_card_transfer(SD_START_TOKEN);
|
sd_card_transfer(SD_START_TOKEN);
|
||||||
|
|
||||||
@@ -345,8 +366,15 @@ bool sd_card_write_block(uint32_t block_addr, const uint8_t *buffer) {
|
|||||||
sd_card_transfer(0xFF);
|
sd_card_transfer(0xFF);
|
||||||
sd_card_transfer(0xFF);
|
sd_card_transfer(0xFF);
|
||||||
|
|
||||||
// Check data response
|
// Check data response - may need to read several bytes before getting response
|
||||||
uint8_t response = sd_card_transfer(0xFF);
|
uint8_t response = 0xFF;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
response = sd_card_transfer(0xFF);
|
||||||
|
if (response != 0xFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((response & 0x1F) != SD_DATA_ACCEPTED) {
|
if ((response & 0x1F) != SD_DATA_ACCEPTED) {
|
||||||
sd_card_deselect();
|
sd_card_deselect();
|
||||||
return false;
|
return false;
|
||||||
@@ -392,14 +420,28 @@ bool sd_card_init_with_board_config(void) {
|
|||||||
static const sd_card_config_t config = {
|
static const sd_card_config_t config = {
|
||||||
.spi = SD_SPI_PORT,
|
.spi = SD_SPI_PORT,
|
||||||
.gpio_cs = SD_CS_PIN,
|
.gpio_cs = SD_CS_PIN,
|
||||||
.gpio_miso = DISPLAY_MISO_PIN,
|
.gpio_miso = SD_MISO_PIN,
|
||||||
.gpio_mosi = DISPLAY_MOSI_PIN,
|
.gpio_mosi = SD_MOSI_PIN,
|
||||||
.gpio_sck = DISPLAY_SCK_PIN
|
.gpio_sck = SD_SCK_PIN
|
||||||
};
|
};
|
||||||
|
|
||||||
return sd_card_init(&config);
|
return sd_card_init(&config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint sd_card_set_spi_speed(void) {
|
||||||
|
if (!g_config) return 0;
|
||||||
|
|
||||||
|
// Save current speed and set to SD card speed
|
||||||
|
uint current_speed = spi_get_baudrate(g_config->spi);
|
||||||
|
spi_set_baudrate(g_config->spi, 12500 * 1000); // 12.5 MHz for SD card
|
||||||
|
return current_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sd_card_restore_spi_speed(uint baudrate) {
|
||||||
|
if (!g_config || baudrate == 0) return;
|
||||||
|
spi_set_baudrate(g_config->spi, baudrate);
|
||||||
|
}
|
||||||
|
|
||||||
bool sd_card_test_fatfs(void) {
|
bool sd_card_test_fatfs(void) {
|
||||||
if (!g_card_info.initialized) {
|
if (!g_card_info.initialized) {
|
||||||
printf("SD Card not initialized\n");
|
printf("SD Card not initialized\n");
|
||||||
|
|||||||
@@ -75,6 +75,19 @@ bool sd_card_init(const sd_card_config_t *config);
|
|||||||
*/
|
*/
|
||||||
bool sd_card_init_with_board_config(void);
|
bool sd_card_init_with_board_config(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set SPI speed to SD card speed (12.5 MHz) before SD operations
|
||||||
|
* Call this before any FatFS file operations if SPI is shared with display
|
||||||
|
* @return Previous baudrate to restore later
|
||||||
|
*/
|
||||||
|
uint sd_card_set_spi_speed(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore SPI speed after SD card operations
|
||||||
|
* @param baudrate Previous baudrate to restore
|
||||||
|
*/
|
||||||
|
void sd_card_restore_spi_speed(uint baudrate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get card information
|
* Get card information
|
||||||
* @param info Pointer to info structure to fill
|
* @param info Pointer to info structure to fill
|
||||||
|
|||||||
@@ -0,0 +1,330 @@
|
|||||||
|
#include "serial_uploader.h"
|
||||||
|
#include "game_launcher.h"
|
||||||
|
#include "lua_game_loader.h"
|
||||||
|
#include "sd_card.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
// Maximum file size: 64KB (should be plenty for Lua games)
|
||||||
|
#define MAX_FILE_SIZE (64 * 1024)
|
||||||
|
|
||||||
|
SerialUploader::SerialUploader(GameLauncher* launcher)
|
||||||
|
: state(IDLE)
|
||||||
|
, game_launcher(launcher)
|
||||||
|
, file_size(0)
|
||||||
|
, bytes_received(0)
|
||||||
|
, file_buffer(nullptr)
|
||||||
|
, base64_index(0)
|
||||||
|
{
|
||||||
|
filename[0] = '\0';
|
||||||
|
last_uploaded_name[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialUploader::reset() {
|
||||||
|
state = IDLE;
|
||||||
|
file_size = 0;
|
||||||
|
bytes_received = 0;
|
||||||
|
base64_index = 0;
|
||||||
|
filename[0] = '\0';
|
||||||
|
|
||||||
|
if (file_buffer) {
|
||||||
|
delete[] file_buffer;
|
||||||
|
file_buffer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SerialUploader::decode_base64_char(char c) {
|
||||||
|
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||||
|
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
||||||
|
if (c >= '0' && c <= '9') return c - '0' + 52;
|
||||||
|
if (c == '+') return 62;
|
||||||
|
if (c == '/') return 63;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialUploader::decode_base64_block(const char* input, uint8_t* output) {
|
||||||
|
uint32_t combined = (decode_base64_char(input[0]) << 18) |
|
||||||
|
(decode_base64_char(input[1]) << 12) |
|
||||||
|
(decode_base64_char(input[2]) << 6) |
|
||||||
|
decode_base64_char(input[3]);
|
||||||
|
|
||||||
|
output[0] = (combined >> 16) & 0xFF;
|
||||||
|
output[1] = (combined >> 8) & 0xFF;
|
||||||
|
output[2] = combined & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerialUploader::write_file_to_sd() {
|
||||||
|
if (bytes_received == 0) {
|
||||||
|
printf("ERROR No data received!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_buffer) {
|
||||||
|
printf("ERROR File buffer is NULL!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set SPI speed to SD card speed (12.5 MHz) and save previous speed
|
||||||
|
uint prev_speed = sd_card_set_spi_speed();
|
||||||
|
|
||||||
|
// Ensure /games directory exists
|
||||||
|
FRESULT fr = f_mkdir("/games");
|
||||||
|
if (fr != FR_OK && fr != FR_EXIST) {
|
||||||
|
printf("ERROR Failed to create /games directory: %d\n", fr);
|
||||||
|
// Try to continue anyway in case it already exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build file path - overwrite if file exists
|
||||||
|
char filepath[128];
|
||||||
|
snprintf(filepath, sizeof(filepath), "/games/%s", filename);
|
||||||
|
|
||||||
|
FIL fil;
|
||||||
|
fr = f_open(&fil, filepath, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
|
|
||||||
|
if (fr != FR_OK) {
|
||||||
|
printf("ERROR Failed to open file for writing: %d\n", fr);
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data in chunks (more reliable for large files)
|
||||||
|
const uint32_t CHUNK_SIZE = 512;
|
||||||
|
uint32_t total_written = 0;
|
||||||
|
|
||||||
|
while (total_written < bytes_received) {
|
||||||
|
uint32_t chunk = (bytes_received - total_written > CHUNK_SIZE) ? CHUNK_SIZE : (bytes_received - total_written);
|
||||||
|
|
||||||
|
UINT bytes_written = 0;
|
||||||
|
fr = f_write(&fil, file_buffer + total_written, chunk, &bytes_written);
|
||||||
|
|
||||||
|
if (fr != FR_OK) {
|
||||||
|
printf("ERROR f_write failed at byte %u: error %d\n", total_written, fr);
|
||||||
|
f_close(&fil);
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_written != chunk) {
|
||||||
|
printf("ERROR Partial write: wrote %u/%u bytes at offset %u\n", bytes_written, chunk, total_written);
|
||||||
|
f_close(&fil);
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_written += bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync to ensure data is written to SD card
|
||||||
|
fr = f_sync(&fil);
|
||||||
|
if (fr != FR_OK) {
|
||||||
|
printf("ERROR f_sync failed: %d\n", fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
f_close(&fil);
|
||||||
|
|
||||||
|
printf("✓ Wrote %u bytes to %s\n", total_written, filepath);
|
||||||
|
|
||||||
|
// Restore display SPI speed
|
||||||
|
sd_card_restore_spi_speed(prev_speed);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialUploader::launch_game() {
|
||||||
|
// Extract base game name (remove .lua extension)
|
||||||
|
strncpy(last_uploaded_name, filename, sizeof(last_uploaded_name) - 1);
|
||||||
|
last_uploaded_name[sizeof(last_uploaded_name) - 1] = '\0';
|
||||||
|
|
||||||
|
// Remove .lua extension
|
||||||
|
char* ext = strstr(last_uploaded_name, ".lua");
|
||||||
|
if (ext) {
|
||||||
|
*ext = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Re-scanning Lua games to pick up new file...\n");
|
||||||
|
|
||||||
|
// Clear existing games before re-scanning (prevents duplicates and memory leaks)
|
||||||
|
game_launcher->clear_games();
|
||||||
|
LuaGameLoader::clear_factory_data();
|
||||||
|
|
||||||
|
// Re-scan Lua games to pick up the newly uploaded file
|
||||||
|
// Note: LuaGameLoader::register_all_games handles SPI speed internally
|
||||||
|
int new_games = LuaGameLoader::register_all_games(game_launcher);
|
||||||
|
printf("Found %d Lua games after re-scan\n", new_games);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerialUploader::complete_launch() {
|
||||||
|
// This should only be called when it's safe (no display refresh in progress)
|
||||||
|
// Now try to launch the newly uploaded game by name
|
||||||
|
printf("Attempting to launch game: %s\n", last_uploaded_name);
|
||||||
|
bool launched = game_launcher->select_game_by_name(last_uploaded_name);
|
||||||
|
|
||||||
|
if (launched) {
|
||||||
|
printf("LAUNCHED %s\n", last_uploaded_name);
|
||||||
|
} else {
|
||||||
|
printf("ERROR Failed to launch game: %s\n", last_uploaded_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset state back to IDLE
|
||||||
|
reset();
|
||||||
|
|
||||||
|
return launched;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerialUploader::process() {
|
||||||
|
if (state == IDLE) {
|
||||||
|
// Check for "UPLOAD" command
|
||||||
|
int c = getchar_timeout_us(0);
|
||||||
|
if (c == PICO_ERROR_TIMEOUT) return false;
|
||||||
|
|
||||||
|
if (c == 'U') {
|
||||||
|
// Check for "UPLOAD "
|
||||||
|
static char cmd_buffer[8];
|
||||||
|
cmd_buffer[0] = c;
|
||||||
|
int idx = 1;
|
||||||
|
|
||||||
|
// Read "PLOAD "
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
c = getchar_timeout_us(100000); // 100ms timeout
|
||||||
|
if (c == PICO_ERROR_TIMEOUT) return false;
|
||||||
|
cmd_buffer[idx++] = c;
|
||||||
|
}
|
||||||
|
cmd_buffer[idx] = '\0';
|
||||||
|
|
||||||
|
if (strcmp(cmd_buffer, "UPLOAD ") == 0) {
|
||||||
|
state = RECEIVING_FILENAME;
|
||||||
|
filename[0] = '\0';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == RECEIVING_FILENAME) {
|
||||||
|
// Read filename until space
|
||||||
|
int idx = strlen(filename);
|
||||||
|
while (idx < sizeof(filename) - 1) {
|
||||||
|
int c = getchar_timeout_us(100000);
|
||||||
|
if (c == PICO_ERROR_TIMEOUT) break;
|
||||||
|
|
||||||
|
if (c == ' ') {
|
||||||
|
filename[idx] = '\0';
|
||||||
|
state = RECEIVING_SIZE;
|
||||||
|
file_size = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename[idx++] = (char)c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == RECEIVING_SIZE) {
|
||||||
|
// Read file size until newline
|
||||||
|
char size_buffer[16];
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
while (idx < sizeof(size_buffer) - 1) {
|
||||||
|
int c = getchar_timeout_us(100000);
|
||||||
|
if (c == PICO_ERROR_TIMEOUT) break;
|
||||||
|
|
||||||
|
if (c == '\n' || c == '\r') {
|
||||||
|
size_buffer[idx] = '\0';
|
||||||
|
file_size = atoi(size_buffer);
|
||||||
|
|
||||||
|
if (file_size == 0 || file_size > MAX_FILE_SIZE) {
|
||||||
|
printf("ERROR Invalid file size: %u\n", file_size);
|
||||||
|
reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate buffer for decoded data
|
||||||
|
// file_size is the ORIGINAL (decoded) size, so allocate exactly that
|
||||||
|
file_buffer = new uint8_t[file_size];
|
||||||
|
|
||||||
|
if (!file_buffer) {
|
||||||
|
printf("ERROR Failed to allocate %u bytes for file buffer\n", file_size);
|
||||||
|
reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_received = 0;
|
||||||
|
base64_index = 0;
|
||||||
|
|
||||||
|
state = RECEIVING_DATA;
|
||||||
|
printf("Receiving %u bytes for %s...\n", file_size, filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_buffer[idx++] = (char)c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == RECEIVING_DATA) {
|
||||||
|
// Read base64 data until "END"
|
||||||
|
while (true) {
|
||||||
|
int c = getchar_timeout_us(1000);
|
||||||
|
if (c == PICO_ERROR_TIMEOUT) break;
|
||||||
|
|
||||||
|
// Check for "END" marker
|
||||||
|
static char end_check[4] = {0, 0, 0, 0};
|
||||||
|
end_check[0] = end_check[1];
|
||||||
|
end_check[1] = end_check[2];
|
||||||
|
end_check[2] = end_check[3];
|
||||||
|
end_check[3] = (char)c;
|
||||||
|
|
||||||
|
if (end_check[0] == '\n' && end_check[1] == 'E' && end_check[2] == 'N' && end_check[3] == 'D') {
|
||||||
|
state = WRITING_FILE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip whitespace
|
||||||
|
if (isspace(c)) continue;
|
||||||
|
|
||||||
|
// Accumulate base64 characters
|
||||||
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||||
|
(c >= '0' && c <= '9') || c == '+' || c == '/' || c == '=') {
|
||||||
|
|
||||||
|
base64_buffer[base64_index++] = (char)c;
|
||||||
|
|
||||||
|
// Decode every 4 characters
|
||||||
|
if (base64_index == 4) {
|
||||||
|
uint8_t decoded[3];
|
||||||
|
decode_base64_block(base64_buffer, decoded);
|
||||||
|
|
||||||
|
// Handle padding
|
||||||
|
int decoded_count = 3;
|
||||||
|
if (base64_buffer[2] == '=') decoded_count = 1;
|
||||||
|
else if (base64_buffer[3] == '=') decoded_count = 2;
|
||||||
|
|
||||||
|
// Copy decoded bytes
|
||||||
|
for (int i = 0; i < decoded_count; i++) {
|
||||||
|
if (bytes_received < file_size) {
|
||||||
|
file_buffer[bytes_received++] = decoded[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base64_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == WRITING_FILE) {
|
||||||
|
if (write_file_to_sd()) {
|
||||||
|
state = LAUNCHING_GAME;
|
||||||
|
// Prepare for launch by scanning games, but don't actually launch yet
|
||||||
|
launch_game();
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == LAUNCHING_GAME) {
|
||||||
|
// Stay in this state until main loop calls complete_launch() when safe
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef SERIAL_UPLOADER_H
|
||||||
|
#define SERIAL_UPLOADER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "ff.h"
|
||||||
|
|
||||||
|
class GameLauncher;
|
||||||
|
|
||||||
|
class SerialUploader {
|
||||||
|
public:
|
||||||
|
SerialUploader(GameLauncher* launcher);
|
||||||
|
|
||||||
|
// Process incoming serial data (call this frequently in main loop)
|
||||||
|
// Returns true if a game was launched
|
||||||
|
bool process();
|
||||||
|
|
||||||
|
// Check if uploader wants to launch a game (after upload complete)
|
||||||
|
bool wants_to_launch_game() const { return state == LAUNCHING_GAME; }
|
||||||
|
|
||||||
|
// Complete the game launch (call only when safe - no display refresh in progress)
|
||||||
|
// Returns true if launch succeeded
|
||||||
|
bool complete_launch();
|
||||||
|
|
||||||
|
// Get the filename of the last uploaded game (without .lua extension)
|
||||||
|
const char* get_last_uploaded_filename() const { return last_uploaded_name; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum State {
|
||||||
|
IDLE,
|
||||||
|
RECEIVING_FILENAME,
|
||||||
|
RECEIVING_SIZE,
|
||||||
|
RECEIVING_DATA,
|
||||||
|
WRITING_FILE,
|
||||||
|
LAUNCHING_GAME
|
||||||
|
};
|
||||||
|
|
||||||
|
State state;
|
||||||
|
GameLauncher* game_launcher;
|
||||||
|
|
||||||
|
// Upload state
|
||||||
|
char filename[64];
|
||||||
|
char last_uploaded_name[64]; // Game name without .lua extension
|
||||||
|
uint32_t file_size;
|
||||||
|
uint32_t bytes_received;
|
||||||
|
uint8_t* file_buffer;
|
||||||
|
|
||||||
|
// Base64 decoding buffer
|
||||||
|
char base64_buffer[4];
|
||||||
|
int base64_index;
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
void reset();
|
||||||
|
bool write_file_to_sd();
|
||||||
|
void launch_game();
|
||||||
|
uint8_t decode_base64_char(char c);
|
||||||
|
void decode_base64_block(const char* input, uint8_t* output);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERIAL_UPLOADER_H
|
||||||
+65
-4
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "hardware/gpio.h"
|
#include "hardware/gpio.h"
|
||||||
#include "hardware/spi.h"
|
#include "hardware/spi.h"
|
||||||
|
#include "hardware/pwm.h"
|
||||||
#include "pico/binary_info.h"
|
#include "pico/binary_info.h"
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
|
|
||||||
@@ -60,6 +61,12 @@ static uint16_t height;
|
|||||||
static uint16_t x_offset;
|
static uint16_t x_offset;
|
||||||
static uint16_t y_offset;
|
static uint16_t y_offset;
|
||||||
|
|
||||||
|
// Backlight control
|
||||||
|
static uint pwm_slice;
|
||||||
|
static uint pwm_channel;
|
||||||
|
static uint8_t current_brightness = 100;
|
||||||
|
static bool pwm_initialized = false;
|
||||||
|
|
||||||
static inline void cs_select() {
|
static inline void cs_select() {
|
||||||
if (config->gpio_cs >= 0) {
|
if (config->gpio_cs >= 0) {
|
||||||
asm volatile("nop \n nop \n nop");
|
asm volatile("nop \n nop \n nop");
|
||||||
@@ -173,10 +180,19 @@ void st7789_init(const struct st7789_config *c, uint16_t w, uint16_t h) {
|
|||||||
gpio_init(config->gpio_rst);
|
gpio_init(config->gpio_rst);
|
||||||
gpio_set_dir(config->gpio_rst, GPIO_OUT);
|
gpio_set_dir(config->gpio_rst, GPIO_OUT);
|
||||||
|
|
||||||
// Initialize backlight pin
|
// Initialize backlight pin with PWM
|
||||||
gpio_init(config->gpio_bl);
|
if (config->gpio_bl >= 0) {
|
||||||
gpio_set_dir(config->gpio_bl, GPIO_OUT);
|
gpio_set_function(config->gpio_bl, GPIO_FUNC_PWM);
|
||||||
gpio_put(config->gpio_bl, 1); // Turn on backlight
|
pwm_slice = pwm_gpio_to_slice_num(config->gpio_bl);
|
||||||
|
pwm_channel = pwm_gpio_to_channel(config->gpio_bl);
|
||||||
|
|
||||||
|
// Set PWM frequency to ~1kHz
|
||||||
|
pwm_set_wrap(pwm_slice, 65535);
|
||||||
|
pwm_set_chan_level(pwm_slice, pwm_channel, 65535); // 100% duty cycle
|
||||||
|
pwm_set_enabled(pwm_slice, true);
|
||||||
|
|
||||||
|
pwm_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset display
|
// Reset display
|
||||||
reset_pulse();
|
reset_pulse();
|
||||||
@@ -403,3 +419,48 @@ void st7789_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void st7789_set_brightness(uint8_t brightness) {
|
||||||
|
if (!pwm_initialized || config->gpio_bl < 0) return;
|
||||||
|
|
||||||
|
// Clamp brightness
|
||||||
|
if (brightness > 100) brightness = 100;
|
||||||
|
|
||||||
|
current_brightness = brightness;
|
||||||
|
|
||||||
|
// Convert 0-100 to 0-65535
|
||||||
|
uint16_t level = (uint16_t)((brightness * 65535) / 100);
|
||||||
|
pwm_set_chan_level(pwm_slice, pwm_channel, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t st7789_get_brightness(void) {
|
||||||
|
return current_brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
void st7789_sleep(void) {
|
||||||
|
// Turn off backlight
|
||||||
|
if (pwm_initialized && config->gpio_bl >= 0) {
|
||||||
|
pwm_set_chan_level(pwm_slice, pwm_channel, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display off
|
||||||
|
write_command(ST7789_DISPOFF);
|
||||||
|
sleep_ms(10);
|
||||||
|
|
||||||
|
// Sleep in
|
||||||
|
write_command(ST7789_SLPIN);
|
||||||
|
sleep_ms(120);
|
||||||
|
}
|
||||||
|
|
||||||
|
void st7789_wake(void) {
|
||||||
|
// Sleep out
|
||||||
|
write_command(ST7789_SLPOUT);
|
||||||
|
sleep_ms(120);
|
||||||
|
|
||||||
|
// Display on
|
||||||
|
write_command(ST7789_DISPON);
|
||||||
|
sleep_ms(10);
|
||||||
|
|
||||||
|
// Restore brightness
|
||||||
|
st7789_set_brightness(current_brightness);
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ void st7789_draw_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
|
|||||||
void st7789_fill_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
|
void st7789_fill_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
|
||||||
void st7789_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color);
|
void st7789_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color);
|
||||||
|
|
||||||
|
void st7789_sleep(void);
|
||||||
|
void st7789_wake(void);
|
||||||
|
void st7789_set_brightness(uint8_t brightness);
|
||||||
|
uint8_t st7789_get_brightness(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+181
-9
@@ -58,6 +58,8 @@
|
|||||||
|
|
||||||
#include "hardware/gpio.h"
|
#include "hardware/gpio.h"
|
||||||
#include "hardware/spi.h"
|
#include "hardware/spi.h"
|
||||||
|
#include "hardware/dma.h"
|
||||||
|
#include "hardware/pwm.h"
|
||||||
#include "pico/binary_info.h"
|
#include "pico/binary_info.h"
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
|
|
||||||
@@ -118,6 +120,13 @@ static uint16_t width; // Display width in pixels (e.g., 480)
|
|||||||
static uint16_t height; // Display height in pixels (e.g., 320)
|
static uint16_t height; // Display height in pixels (e.g., 320)
|
||||||
static uint16_t x_offset; // X offset for display alignment (currently 0)
|
static uint16_t x_offset; // X offset for display alignment (currently 0)
|
||||||
static uint16_t y_offset; // Y offset for display alignment (currently 0)
|
static uint16_t y_offset; // Y offset for display alignment (currently 0)
|
||||||
|
static uint8_t current_brightness = 100; // Current brightness level (0-100)
|
||||||
|
static uint pwm_slice; // PWM slice number for backlight control
|
||||||
|
static uint pwm_channel; // PWM channel number for backlight control
|
||||||
|
|
||||||
|
// DMA Channel for fast transfers
|
||||||
|
static int dma_tx_channel = -1;
|
||||||
|
static dma_channel_config dma_tx_config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Activate chip select (pull CS LOW)
|
* @brief Activate chip select (pull CS LOW)
|
||||||
@@ -259,7 +268,7 @@ static void write_command_with_data(uint8_t cmd, const uint8_t *data, size_t len
|
|||||||
* This compensates for displays where the physical screen doesn't align
|
* This compensates for displays where the physical screen doesn't align
|
||||||
* with the controller's framebuffer (common with ST7789/ST7796).
|
* with the controller's framebuffer (common with ST7789/ST7796).
|
||||||
*/
|
*/
|
||||||
static void set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
void st7796_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
||||||
uint8_t data[4];
|
uint8_t data[4];
|
||||||
|
|
||||||
// Add offsets for display positioning
|
// Add offsets for display positioning
|
||||||
@@ -334,6 +343,12 @@ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) {
|
|||||||
x_offset = 0;
|
x_offset = 0;
|
||||||
y_offset = 0;
|
y_offset = 0;
|
||||||
|
|
||||||
|
// Initialize DMA for SPI
|
||||||
|
dma_tx_channel = dma_claim_unused_channel(true);
|
||||||
|
dma_tx_config = dma_channel_get_default_config(dma_tx_channel);
|
||||||
|
channel_config_set_transfer_data_size(&dma_tx_config, DMA_SIZE_8); // 8-bit transfers
|
||||||
|
channel_config_set_dreq(&dma_tx_config, spi_get_dreq(config->spi, true)); // Sync with SPI TX
|
||||||
|
|
||||||
// Initialize SPI at maximum stable speed for ST7796
|
// Initialize SPI at maximum stable speed for ST7796
|
||||||
// Datasheet says max 15.15 MHz, but modern displays work much faster
|
// Datasheet says max 15.15 MHz, but modern displays work much faster
|
||||||
// Successfully tested at 80 MHz, now trying 100 MHz
|
// Successfully tested at 80 MHz, now trying 100 MHz
|
||||||
@@ -359,12 +374,27 @@ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) {
|
|||||||
gpio_init(config->gpio_rst);
|
gpio_init(config->gpio_rst);
|
||||||
gpio_set_dir(config->gpio_rst, GPIO_OUT);
|
gpio_set_dir(config->gpio_rst, GPIO_OUT);
|
||||||
|
|
||||||
// Initialize backlight pin
|
// Initialize backlight pin with PWM for brightness control
|
||||||
// Most TFT displays have LED backlights that need power
|
// Most TFT displays have LED backlights that need power
|
||||||
if (config->gpio_bl >= 0) {
|
if (config->gpio_bl >= 0) {
|
||||||
gpio_init(config->gpio_bl);
|
// Configure GPIO for PWM function
|
||||||
gpio_set_dir(config->gpio_bl, GPIO_OUT);
|
gpio_set_function(config->gpio_bl, GPIO_FUNC_PWM);
|
||||||
gpio_put(config->gpio_bl, 1); // Turn on backlight immediately
|
|
||||||
|
// Find PWM slice and channel for this GPIO
|
||||||
|
pwm_slice = pwm_gpio_to_slice_num(config->gpio_bl);
|
||||||
|
pwm_channel = pwm_gpio_to_channel(config->gpio_bl);
|
||||||
|
|
||||||
|
// Configure PWM
|
||||||
|
// PWM frequency = clock_freq / (wrap + 1)
|
||||||
|
// We want ~1 kHz to avoid flicker: 125 MHz / 125000 = 1000 Hz
|
||||||
|
pwm_set_wrap(pwm_slice, 65535); // 16-bit resolution
|
||||||
|
pwm_set_clkdiv(pwm_slice, 1.907f); // 125 MHz / 1.907 / 65536 ≈ 1 kHz
|
||||||
|
|
||||||
|
// Start at full brightness
|
||||||
|
pwm_set_chan_level(pwm_slice, pwm_channel, 65535);
|
||||||
|
pwm_set_enabled(pwm_slice, true);
|
||||||
|
|
||||||
|
current_brightness = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hardware reset sequence
|
// Hardware reset sequence
|
||||||
@@ -445,7 +475,7 @@ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) {
|
|||||||
* @param color RGB565 color value (0x0000=black, 0xFFFF=white)
|
* @param color RGB565 color value (0x0000=black, 0xFFFF=white)
|
||||||
*/
|
*/
|
||||||
void st7796_fill(uint16_t color) {
|
void st7796_fill(uint16_t color) {
|
||||||
set_window(0, 0, width - 1, height - 1);
|
st7796_set_window(0, 0, width - 1, height - 1);
|
||||||
|
|
||||||
dc_data();
|
dc_data();
|
||||||
cs_select();
|
cs_select();
|
||||||
@@ -505,7 +535,7 @@ void st7796_put(uint16_t color) {
|
|||||||
* @param y Starting Y coordinate
|
* @param y Starting Y coordinate
|
||||||
*/
|
*/
|
||||||
void st7796_set_cursor(uint16_t x, uint16_t y) {
|
void st7796_set_cursor(uint16_t x, uint16_t y) {
|
||||||
set_window(x, y, width - 1, height - 1);
|
st7796_set_window(x, y, width - 1, height - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -532,6 +562,39 @@ void st7796_write(const uint16_t *data, size_t len) {
|
|||||||
cs_deselect();
|
cs_deselect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write raw buffer data directly to display
|
||||||
|
*/
|
||||||
|
void st7796_write_raw(const uint8_t *data, size_t len) {
|
||||||
|
dc_data();
|
||||||
|
cs_select();
|
||||||
|
|
||||||
|
// DMA Implementation (Blocking)
|
||||||
|
// 1. Configure DMA channel to write from buffer to SPI TX register
|
||||||
|
dma_channel_configure(
|
||||||
|
dma_tx_channel,
|
||||||
|
&dma_tx_config,
|
||||||
|
&spi_get_hw(config->spi)->dr, // Write to SPI TX register
|
||||||
|
data, // Read from buffer
|
||||||
|
len, // Element count (bytes)
|
||||||
|
true // Start immediately
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Wait for DMA to finish
|
||||||
|
dma_channel_wait_for_finish_blocking(dma_tx_channel);
|
||||||
|
|
||||||
|
// 3. Wait for SPI Transfer to complete (DMA only fills the FIFO)
|
||||||
|
// We need to wait for the FIFO to drain and the bus to be idle before raising CS
|
||||||
|
while (spi_is_busy(config->spi)) {
|
||||||
|
// tight_loop_contents() is empty on some platforms,
|
||||||
|
// using a simple volatile read ensures the compiler doesn't optimize this away
|
||||||
|
// efficiently.
|
||||||
|
__asm volatile ("nop");
|
||||||
|
}
|
||||||
|
|
||||||
|
cs_deselect();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draw single pixel at specific coordinates
|
* @brief Draw single pixel at specific coordinates
|
||||||
*
|
*
|
||||||
@@ -552,7 +615,7 @@ void st7796_write(const uint16_t *data, size_t len) {
|
|||||||
void st7796_draw_pixel(uint16_t x, uint16_t y, uint16_t color) {
|
void st7796_draw_pixel(uint16_t x, uint16_t y, uint16_t color) {
|
||||||
if (x >= width || y >= height) return; // Bounds check
|
if (x >= width || y >= height) return; // Bounds check
|
||||||
|
|
||||||
set_window(x, y, x, y); // 1x1 window
|
st7796_set_window(x, y, x, y); // 1x1 window
|
||||||
|
|
||||||
uint8_t data[2] = {(color >> 8) & 0xFF, color & 0xFF};
|
uint8_t data[2] = {(color >> 8) & 0xFF, color & 0xFF};
|
||||||
dc_data();
|
dc_data();
|
||||||
@@ -614,7 +677,7 @@ void st7796_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t c
|
|||||||
if (x + w > width) w = width - x;
|
if (x + w > width) w = width - x;
|
||||||
if (y + h > height) h = height - y;
|
if (y + h > height) h = height - y;
|
||||||
|
|
||||||
set_window(x, y, x + w - 1, y + h - 1);
|
st7796_set_window(x, y, x + w - 1, y + h - 1);
|
||||||
|
|
||||||
dc_data();
|
dc_data();
|
||||||
cs_select();
|
cs_select();
|
||||||
@@ -934,3 +997,112 @@ void st7796_fill_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
|||||||
st7796_fill_rect(a, y, b - a + 1, 1, color);
|
st7796_fill_rect(a, y, b - a + 1, 1, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set display brightness using PWM on backlight pin
|
||||||
|
*
|
||||||
|
* Controls the backlight LED brightness by adjusting the PWM duty cycle.
|
||||||
|
* The brightness parameter is a percentage that's converted to the
|
||||||
|
* appropriate PWM level (0-65535 for 16-bit PWM).
|
||||||
|
*
|
||||||
|
* Implementation:
|
||||||
|
* - brightness = 0: PWM level = 0 (backlight off)
|
||||||
|
* - brightness = 100: PWM level = 65535 (backlight fully on)
|
||||||
|
* - brightness = 50: PWM level = 32767 (50% duty cycle)
|
||||||
|
*
|
||||||
|
* The conversion formula: PWM_level = (brightness * 65535) / 100
|
||||||
|
*
|
||||||
|
* Human perception of brightness is logarithmic, but we use linear
|
||||||
|
* PWM for simplicity. For perceived linear brightness, a gamma
|
||||||
|
* correction curve could be applied.
|
||||||
|
*
|
||||||
|
* @param brightness Brightness level (0-100 percent)
|
||||||
|
*/
|
||||||
|
void st7796_set_brightness(uint8_t brightness) {
|
||||||
|
// Clamp brightness to valid range
|
||||||
|
if (brightness > 100) {
|
||||||
|
brightness = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store current brightness
|
||||||
|
current_brightness = brightness;
|
||||||
|
|
||||||
|
// Convert percentage to 16-bit PWM level (0-65535)
|
||||||
|
// Using 32-bit intermediate to avoid overflow
|
||||||
|
uint32_t pwm_level = ((uint32_t)brightness * 65535) / 100;
|
||||||
|
|
||||||
|
// Set PWM duty cycle
|
||||||
|
if (config->gpio_bl >= 0) {
|
||||||
|
pwm_set_chan_level(pwm_slice, pwm_channel, (uint16_t)pwm_level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current display brightness level
|
||||||
|
*
|
||||||
|
* Returns the brightness level that was last set via st7796_set_brightness()
|
||||||
|
* or the default value (100) if brightness was never changed.
|
||||||
|
*
|
||||||
|
* @return Current brightness level (0-100 percent)
|
||||||
|
*/
|
||||||
|
uint8_t st7796_get_brightness(void) {
|
||||||
|
return current_brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Put display into sleep mode (low power)
|
||||||
|
*
|
||||||
|
* Enters deep sleep mode to minimize power consumption while maintaining
|
||||||
|
* initialization state. The framebuffer contents and all settings are
|
||||||
|
* preserved, so waking up is fast.
|
||||||
|
*
|
||||||
|
* Sleep sequence:
|
||||||
|
* 1. Turn off display (DISPOFF) - stops showing framebuffer
|
||||||
|
* 2. Enter sleep mode (SLPIN) - stops oscillator, minimal power
|
||||||
|
* 3. Turn off backlight - set PWM to 0
|
||||||
|
*
|
||||||
|
* Total power savings: Display draws ~10μA in sleep vs ~150mA active
|
||||||
|
* Touch controller on separate I2C bus remains fully functional
|
||||||
|
*/
|
||||||
|
void st7796_sleep(void) {
|
||||||
|
// Turn off display output first
|
||||||
|
write_command(ST7796_DISPOFF);
|
||||||
|
sleep_ms(10);
|
||||||
|
|
||||||
|
// Enter sleep mode (stops internal oscillator)
|
||||||
|
write_command(ST7796_SLPIN);
|
||||||
|
sleep_ms(120); // Wait for sleep mode to take effect (spec: 120ms)
|
||||||
|
|
||||||
|
// Turn off backlight to save power
|
||||||
|
if (config->gpio_bl >= 0) {
|
||||||
|
pwm_set_chan_level(pwm_slice, pwm_channel, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wake display from sleep mode
|
||||||
|
*
|
||||||
|
* Exits sleep mode and restores display to full operation.
|
||||||
|
* All framebuffer contents and settings are preserved.
|
||||||
|
*
|
||||||
|
* Wake sequence:
|
||||||
|
* 1. Exit sleep mode (SLPOUT) - restarts oscillator
|
||||||
|
* 2. Wait for oscillator to stabilize (120ms)
|
||||||
|
* 3. Turn on display (DISPON) - starts showing framebuffer
|
||||||
|
* 4. Restore backlight to previous brightness
|
||||||
|
*/
|
||||||
|
void st7796_wake(void) {
|
||||||
|
// Exit sleep mode (restart oscillator)
|
||||||
|
write_command(ST7796_SLPOUT);
|
||||||
|
sleep_ms(120); // Wait for oscillator to stabilize (spec: 120ms)
|
||||||
|
|
||||||
|
// Turn on display output
|
||||||
|
write_command(ST7796_DISPON);
|
||||||
|
sleep_ms(10);
|
||||||
|
|
||||||
|
// Restore backlight to previous brightness level
|
||||||
|
if (config->gpio_bl >= 0) {
|
||||||
|
uint32_t pwm_level = ((uint32_t)current_brightness * 65535) / 100;
|
||||||
|
pwm_set_chan_level(pwm_slice, pwm_channel, (uint16_t)pwm_level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -219,6 +219,28 @@ void st7796_put(uint16_t color);
|
|||||||
*/
|
*/
|
||||||
void st7796_set_cursor(uint16_t x, uint16_t y);
|
void st7796_set_cursor(uint16_t x, uint16_t y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set drawing window to a specific rectangle
|
||||||
|
*
|
||||||
|
* Sets the drawing window to a rectangular region defined by (x0, y0)
|
||||||
|
* as the top-left corner and (x1, y1) as the bottom-right corner.
|
||||||
|
* Subsequent write operations will only affect this region.
|
||||||
|
*
|
||||||
|
* This is useful for partial screen updates (dirty rectangle optimization)
|
||||||
|
* where only a portion of the screen needs to be redrawn, significantly
|
||||||
|
* improving performance by reducing SPI data transfer.
|
||||||
|
*
|
||||||
|
* @param x0 Top-left X coordinate (0 to width-1)
|
||||||
|
* @param y0 Top-left Y coordinate (0 to height-1)
|
||||||
|
* @param x1 Bottom-right X coordinate (x0 to width-1)
|
||||||
|
* @param y1 Bottom-right Y coordinate (y0 to height-1)
|
||||||
|
*
|
||||||
|
* Example: Update only a 100x50 region starting at (50, 50):
|
||||||
|
* st7796_set_window(50, 50, 149, 99);
|
||||||
|
* st7796_write_raw(pixel_data, 100 * 50 * 2);
|
||||||
|
*/
|
||||||
|
void st7796_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write multiple pixels at current cursor position
|
* @brief Write multiple pixels at current cursor position
|
||||||
*
|
*
|
||||||
@@ -233,6 +255,21 @@ void st7796_set_cursor(uint16_t x, uint16_t y);
|
|||||||
*/
|
*/
|
||||||
void st7796_write(const uint16_t *data, size_t len);
|
void st7796_write(const uint16_t *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write raw buffer data directly to display
|
||||||
|
*
|
||||||
|
* Writes a buffer of bytes directly to the display without any
|
||||||
|
* conversion or byte swapping. This function assume the data is
|
||||||
|
* already in the correct format (Big-Endian RGB565) for the display.
|
||||||
|
*
|
||||||
|
* This is significantly faster for large block transfers than
|
||||||
|
* st7796_write() as it uses a single SPI transaction.
|
||||||
|
*
|
||||||
|
* @param data Pointer to raw byte buffer
|
||||||
|
* @param len Number of bytes to send
|
||||||
|
*/
|
||||||
|
void st7796_write_raw(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draw a single pixel at specified coordinates
|
* @brief Draw a single pixel at specified coordinates
|
||||||
*
|
*
|
||||||
@@ -357,6 +394,93 @@ void st7796_draw_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, ui
|
|||||||
*/
|
*/
|
||||||
void st7796_fill_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
|
void st7796_fill_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set display brightness using PWM on backlight pin
|
||||||
|
*
|
||||||
|
* Controls the backlight LED brightness using hardware PWM. The brightness
|
||||||
|
* is specified as a percentage (0-100), where:
|
||||||
|
* - 0 = Backlight completely off (minimum brightness)
|
||||||
|
* - 100 = Backlight fully on (maximum brightness)
|
||||||
|
* - Values in between = Proportional PWM duty cycle
|
||||||
|
*
|
||||||
|
* PWM Configuration:
|
||||||
|
* - Frequency: 1000 Hz (1 kHz) - high enough to avoid flicker
|
||||||
|
* - Resolution: 16-bit (0-65535 range internally)
|
||||||
|
* - Smooth transitions without visible flickering
|
||||||
|
*
|
||||||
|
* Use cases:
|
||||||
|
* - Power saving: Reduce brightness when idle
|
||||||
|
* - Auto-dimming: Lower brightness after timeout
|
||||||
|
* - Manual control: User brightness preferences
|
||||||
|
* - Night mode: Very low brightness for dark environments
|
||||||
|
*
|
||||||
|
* @param brightness Brightness level (0-100 percent)
|
||||||
|
* 0 = off, 100 = full brightness
|
||||||
|
*
|
||||||
|
* Performance: Instant - PWM is handled by hardware
|
||||||
|
*
|
||||||
|
* Note: Must be called after st7796_init() which configures the backlight pin
|
||||||
|
*/
|
||||||
|
void st7796_set_brightness(uint8_t brightness);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current display brightness level
|
||||||
|
*
|
||||||
|
* Returns the currently configured brightness level as a percentage (0-100).
|
||||||
|
* This reflects the last value set by st7796_set_brightness() or the
|
||||||
|
* default value (100) if brightness was never explicitly set.
|
||||||
|
*
|
||||||
|
* @return Current brightness level (0-100 percent)
|
||||||
|
*/
|
||||||
|
uint8_t st7796_get_brightness(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Put display into sleep mode (low power)
|
||||||
|
*
|
||||||
|
* Enters sleep mode to save power while keeping the controller initialized.
|
||||||
|
* In sleep mode:
|
||||||
|
* - Display is turned off (blank screen)
|
||||||
|
* - Internal oscillator is stopped
|
||||||
|
* - Framebuffer contents are preserved
|
||||||
|
* - Backlight is turned off (PWM set to 0)
|
||||||
|
* - Power consumption is minimized (~10μA typical)
|
||||||
|
*
|
||||||
|
* Touch controller remains active and functional since it's on a separate
|
||||||
|
* I2C bus. Touch interrupts can wake the system from sleep.
|
||||||
|
*
|
||||||
|
* Use st7796_wake() to exit sleep mode and restore display.
|
||||||
|
*
|
||||||
|
* Commands sent:
|
||||||
|
* - DISPOFF (0x28): Turn off display
|
||||||
|
* - SLPIN (0x10): Enter sleep mode
|
||||||
|
* - Backlight PWM set to 0
|
||||||
|
*
|
||||||
|
* Performance: ~120ms to enter sleep mode
|
||||||
|
*/
|
||||||
|
void st7796_sleep(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wake display from sleep mode
|
||||||
|
*
|
||||||
|
* Exits sleep mode and restores display to normal operation.
|
||||||
|
* After waking:
|
||||||
|
* - Internal oscillator restarts
|
||||||
|
* - Display controller becomes active
|
||||||
|
* - Framebuffer contents are preserved
|
||||||
|
* - Display is turned on
|
||||||
|
* - Backlight is restored to previous brightness
|
||||||
|
*
|
||||||
|
* Commands sent:
|
||||||
|
* - SLPOUT (0x11): Exit sleep mode
|
||||||
|
* - DISPON (0x29): Turn on display
|
||||||
|
* - Backlight PWM restored to saved level
|
||||||
|
*
|
||||||
|
* Performance: ~120ms to fully wake up
|
||||||
|
*
|
||||||
|
* Note: Must call st7796_init() before first use of sleep/wake
|
||||||
|
*/
|
||||||
|
void st7796_wake(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+528
@@ -0,0 +1,528 @@
|
|||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (215,122)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (203,137)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (312,131)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (355,140)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (376,273)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (357,277)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (323,258)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (321,261)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (89,291)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (327,264)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (315,250)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (262,151)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (319,150)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
Long press detected - returning to launcher
|
||||||
|
Launcher reset - returning to menu
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (358,157)
|
||||||
|
Touch at (358,157) in launcher
|
||||||
|
Selected game: Snake Game
|
||||||
|
Lua bindings registered
|
||||||
|
LuaGame Error [load script]: [string "/games/SNAKE.LUA"]:113: unexpected symbol near ','
|
||||||
|
LuaGame: Failed to load /games/SNAKE.LUA: load script: [string "/games/SNAKE.LUA"]:113: unexpected symbol near ','
|
||||||
|
Game launched successfully
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (164,97)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (194,215)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (288,289)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (285,169)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (255,117)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (168,100)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
Long press detected - returning to launcher
|
||||||
|
Launcher reset - returning to menu
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (424,264)
|
||||||
|
Touch at (424,264) in launcher
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (389,237)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (392,247)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (399,249)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (138,49)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (125,111)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (80,146)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (125,37)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (100,73)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (82,154)
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (120,255)
|
||||||
|
Touch at (120,255) in launcher
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (191,125)
|
||||||
|
Touch at (191,125) in launcher
|
||||||
|
Selected game: Touch Counter
|
||||||
|
Lua bindings registered
|
||||||
|
LuaGame Error [load script]: [string "/games/COUNTER.LUA"]:36: unexpected symbol near ','
|
||||||
|
LuaGame: Failed to load /games/COUNTER.LUA: load script: [string "/games/COUNTER.LUA"]:36: unexpected symbol near ','
|
||||||
|
Game launched successfully
|
||||||
|
INT: RISE
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (165,106)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (121,224)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (253,160)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (258,158)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (256,156)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (255,154)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (295,161)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (296,157)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
INT: RISE
|
||||||
|
Touch DOWN at (477,202)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (236,221)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (338,182)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
TFT: Dimmed to 5%
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (306,162)
|
||||||
|
TFT: Restored brightness
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (338,116)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (208,258)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (232,55)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (37,160)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (224,208)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (53,150)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (225,239)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (247,231)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (152,187)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (426,157)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (198,272)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (384,191)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (44,195)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (283,266)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (50,215)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (287,265)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (189,275)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (421,142)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (43,192)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (390,155)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (48,204)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (258,58)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (275,284)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (23,202)
|
||||||
|
LuaGame Error [draw]: [string "/games/2048.LUA"]:145: bad argument #1 to 'rect' (number has no integer representation)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (173,173)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (121,151)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (71,119)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (220,109)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (313,124)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (235,140)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (131,185)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (269,54)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
|
INT: FALL
|
||||||
|
Processing touch: flag=1, event_down=1
|
||||||
|
Touch DOWN at (227,165)
|
||||||
|
INT: RISE
|
||||||
|
Processing touch: flag=1, event_down=0
|
||||||
|
Touch UP
|
||||||
Executable
+149
@@ -0,0 +1,149 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Lua Game Uploader for RP2350
|
||||||
|
Rapidly upload and execute Lua games via USB serial for quick iteration.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python upload_game.py <lua_file> [serial_port]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python upload_game.py my_game.lua /dev/ttyACM0
|
||||||
|
python upload_game.py my_game.lua COM3
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
import serial
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def find_serial_port():
|
||||||
|
"""Auto-detect the RP2350 serial port"""
|
||||||
|
import serial.tools.list_ports
|
||||||
|
|
||||||
|
# Look for Pico/RP2350 devices
|
||||||
|
ports = list(serial.tools.list_ports.comports())
|
||||||
|
for port in ports:
|
||||||
|
# Common VID/PID for Raspberry Pi Pico
|
||||||
|
if 'Pico' in port.description or \
|
||||||
|
'RP2350' in port.description or \
|
||||||
|
'RP2040' in port.description or \
|
||||||
|
(port.vid == 0x2E8A): # Raspberry Pi VID
|
||||||
|
return port.device
|
||||||
|
|
||||||
|
# Fallback: return first available port
|
||||||
|
if ports:
|
||||||
|
print(f"Warning: Could not find RP2350, using first available port: {ports[0].device}")
|
||||||
|
return ports[0].device
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def upload_game(lua_file, serial_port=None):
|
||||||
|
"""Upload a Lua game file to the RP2350 and execute it"""
|
||||||
|
|
||||||
|
# Check if file exists
|
||||||
|
if not os.path.exists(lua_file):
|
||||||
|
print(f"Error: File '{lua_file}' not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Read the file
|
||||||
|
with open(lua_file, 'rb') as f:
|
||||||
|
file_data = f.read()
|
||||||
|
|
||||||
|
file_size = len(file_data)
|
||||||
|
filename = os.path.basename(lua_file)
|
||||||
|
|
||||||
|
# Ensure filename has .lua extension
|
||||||
|
if not filename.endswith('.lua'):
|
||||||
|
filename += '.lua'
|
||||||
|
|
||||||
|
print(f"Uploading {filename} ({file_size} bytes)...")
|
||||||
|
|
||||||
|
# Auto-detect serial port if not provided
|
||||||
|
if serial_port is None:
|
||||||
|
serial_port = find_serial_port()
|
||||||
|
if serial_port is None:
|
||||||
|
print("Error: No serial port found. Please specify manually.")
|
||||||
|
return False
|
||||||
|
print(f"Using serial port: {serial_port}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Open serial connection
|
||||||
|
ser = serial.Serial(serial_port, 115200, timeout=5)
|
||||||
|
time.sleep(0.1) # Wait for connection to stabilize
|
||||||
|
|
||||||
|
# Encode file data as base64
|
||||||
|
base64_data = base64.b64encode(file_data).decode('ascii')
|
||||||
|
|
||||||
|
# Send upload command
|
||||||
|
command = f"UPLOAD {filename} {file_size}\n"
|
||||||
|
print(f"Sending command: {command.strip()}")
|
||||||
|
ser.write(command.encode('ascii'))
|
||||||
|
|
||||||
|
# Send base64-encoded file data
|
||||||
|
print("Sending file data...")
|
||||||
|
ser.write(base64_data.encode('ascii'))
|
||||||
|
ser.write(b'\n')
|
||||||
|
|
||||||
|
# Send END marker
|
||||||
|
ser.write(b'END\n')
|
||||||
|
print("Upload complete, waiting for confirmation...")
|
||||||
|
|
||||||
|
# Read response from device
|
||||||
|
start_time = time.time()
|
||||||
|
response_lines = []
|
||||||
|
while time.time() - start_time < 10: # 10 second timeout
|
||||||
|
if ser.in_waiting > 0:
|
||||||
|
line = ser.readline().decode('utf-8', errors='ignore').strip()
|
||||||
|
if line:
|
||||||
|
print(f" << {line}")
|
||||||
|
response_lines.append(line)
|
||||||
|
|
||||||
|
# Check for success
|
||||||
|
if line.startswith('OK'):
|
||||||
|
print(f"✓ File written successfully!")
|
||||||
|
|
||||||
|
if line.startswith('LAUNCHED'):
|
||||||
|
game_name = line.split(' ', 1)[1] if ' ' in line else filename
|
||||||
|
print(f"✓ Game '{game_name}' launched!")
|
||||||
|
ser.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check for error
|
||||||
|
if line.startswith('ERROR'):
|
||||||
|
print(f"✗ Upload failed: {line}")
|
||||||
|
ser.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("Warning: No response received from device")
|
||||||
|
ser.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
except serial.SerialException as e:
|
||||||
|
print(f"Serial error: {e}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Unexpected error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(__doc__)
|
||||||
|
print("\nAvailable serial ports:")
|
||||||
|
try:
|
||||||
|
import serial.tools.list_ports
|
||||||
|
for port in serial.tools.list_ports.comports():
|
||||||
|
print(f" {port.device}: {port.description}")
|
||||||
|
except ImportError:
|
||||||
|
print(" (install pyserial to see available ports)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
lua_file = sys.argv[1]
|
||||||
|
serial_port = sys.argv[2] if len(sys.argv) > 2 else None
|
||||||
|
|
||||||
|
success = upload_game(lua_file, serial_port)
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user