Compare commits

..

29 Commits

Author SHA1 Message Date
Adolfo Reyna
e406a06f61 Fix race condition in display refresh locking 2026-02-13 13:40:45 -05:00
Adolfo Reyna
034867d2a7 fix sneak game 2026-02-13 13:36:35 -05:00
Adolfo Reyna
76e3d2435e Make game name search case-insensitive
Serial uploader searches for games by filename (e.g., "tetris") but
games are registered by their metadata NAME field (e.g., "Tetris").
This caused launch failures when case didn't match.

Changed select_game_by_name() to use case-insensitive matching:
- strcasecmp() for exact match
- tolower() both strings before strstr() for partial match

Now uploading "tetris.lua" will successfully match and launch "Tetris".

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-12 23:27:48 -05:00
Adolfo Reyna
518bc054c4 Fix SPI bus contention crash on serial game upload
Serial uploader was crashing the Pico when launching games because
it accessed SD card (SPI) while Core 1 was refreshing display (also SPI).
Display and SD card share the same SPI bus and cannot be accessed
simultaneously.

Split game launch into prepare and execute phases:
- prepare: Re-scan games directory (safe, SD access done immediately)
- execute: Load Lua script from SD (deferred until display is idle)

Main loop now checks !is_refresh_in_progress() before completing
launch, preventing SPI conflicts.

Also updated SD card best practices skill to document SPI bus
contention as the #1 most critical issue to avoid.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-12 23:25:31 -05:00
Adolfo Reyna
f8fb04db1b improve game 2026-02-12 23:09:13 -05:00
Adolfo Reyna
84b009c33e Add serial upload tool for rapid Lua game iteration
Implements a complete serial upload workflow that allows uploading and
immediately testing Lua games via USB serial connection.

New Components:
- SerialUploader: Receives files via serial, writes to SD card
- upload_game.py: Python tool for sending files from host computer
- Protocol: Text-based with base64 encoding for reliability

Key Features:
- Uploads file to /games folder on SD card
- Overwrites existing files (FA_CREATE_ALWAYS)
- Auto-launches uploaded game immediately
- Proper memory cleanup (prevents Lua state conflicts)

SD Card Fixes:
- Fixed SPI speed management (12.5MHz for SD, 32MHz for display)
- Fixed SD write protocol (poll for data response token)
- Added speed switching wrappers around all FatFS operations
- Cleaned up excessive debug output

Game Launcher Improvements:
- Added clear_games() to prevent duplicate registrations
- Added cleanup in select_game_by_name() to delete old instances
- Added exact match priority in game selection
- LuaGameLoader now has clear_factory_data() for memory cleanup

Integration:
- Added serial_uploader to CMakeLists.txt
- Integrated into main loop in basic1.cpp
- Re-scans games after upload to pick up new files

Documentation:
- UPLOAD_TOOL.md: Usage instructions
- sd_card_best_practices.md: Critical lessons learned

Known Issues:
- Game launch after upload occasionally causes freeze (needs investigation)
- Display may not refresh properly after upload

Usage:
  python upload_game.py games/lua_examples/2048.lua /dev/tty.usbmodem101

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-12 22:52:57 -05:00
Adolfo Reyna
b26f3bf775 Update all remaining games to use text_scaled with scale=2
Updated games:
- pong.lua: All text now uses text_scaled with scale=2
- air_hockey.lua: All text now uses text_scaled with scale=2
- asteroids.lua: All text now uses text_scaled with scale=2
- ball.lua: All text now uses text_scaled with scale=2
- breakout.lua: All text now uses text_scaled with scale=2
- counter.lua: All text now uses text_scaled with scale=2
- flappy_bird.lua: All text now uses text_scaled with scale=2
- lunar_lander.lua: All text now uses text_scaled with scale=2
- snake.lua: All text now uses text_scaled with scale=2
- tetris.lua: All text now uses text_scaled with scale=2

All 14 games now have consistent 2x text scaling for better readability.
2026-02-12 21:33:00 -05:00
Adolfo Reyna
f398d62af2 Add renderer.text_scaled() Lua binding
lua_bindings.cpp:
- Added new lua_renderer_text_scaled() function
- Wraps renderer->draw_string_scaled() with proper scale support
- Registered as renderer.text_scaled(x, y, text, on, scale)
- Scale parameter defaults to 1 if omitted

Updated all 4 games to use text_scaled():
- simon_says.lua: All text now uses text_scaled with scale=2
- tic_tac_toe.lua: All text now uses text_scaled with scale=2
- memory_match.lua: All text now uses text_scaled with scale=2
- 2048.lua: All text now uses text_scaled with scale=2

This properly uses the C++ renderer's scaled text rendering instead of
workarounds in Lua, providing better performance and consistency.
2026-02-12 21:31:15 -05:00
Adolfo Reyna
fa7d2fd32f Scale games on both axes and 2x text size
All 4 games (simon_says, tic_tac_toe, memory_match, 2048):
- Changed sizing to use min(width, height) instead of just width
- Grids now scale proportionally on both axes
- All UI text now uses text_scale=2 for better visibility
- Games fill more of the screen properly on portrait mode

Fixes:
- Simon Says buttons now square grid on any orientation
- Tic-Tac-Toe grid centered vertically using full screen height
- Memory Match cards use smallest dimension for square sizing
- 2048 tiles scale and center on both axes
- All text (scores, menus, game over) now 2x larger
2026-02-12 21:23:34 -05:00
Adolfo Reyna
a274fb04a1 Update Snake: wrap edges and halve speed
snake.lua:
- Changed wall collision to wrap around (toroidal grid)
- Snake now travels through edges instead of crashing
- Doubled move_speed from 10 to 20 frames (half speed)
- Updated speed increase cap from 3 to 5 frames (slower at max)
- Snake can now traverse indefinitely without losing to walls
2026-02-12 21:10:44 -05:00
Adolfo Reyna
a058476b42 Scale 2048 to fill screen
2048.lua:
- Removed hardcoded TILE_SIZE and TILE_SPACING constants
- Added get_tile_size() function that calculates tile dimensions based on screen width
- Added get_grid_start_x/y() to center grid
- Updated draw() to use dynamic sizing for all tiles
- 4x4 grid now scales proportionally on any screen size
2026-02-12 21:10:16 -05:00
Adolfo Reyna
2a9b17a539 Scale Memory Match to fill screen
memory_match.lua:
- Removed hardcoded CARD_SIZE and CARD_SPACING
- Added get_card_size() function that calculates card dimensions based on available screen space
- Grid now fills screen width (minus padding) and uses available height
- Added get_grid_start_x/y() to center grid
- All card positions and hit detection updated to use dynamic sizing
- Cards scale proportionally on any screen size while maintaining 4x4 layout
2026-02-12 21:09:46 -05:00
Adolfo Reyna
62716c7dc4 Scale Simon Says and Tic-Tac-Toe to fill screen
simon_says.lua:
- Removed hardcoded button positions
- Added get_buttons() function that calculates button size and position based on screen dimensions
- Buttons now use 10% screen padding and scale to fill available width
- 2x2 grid automatically scales with screen size

tic_tac_toe.lua:
- Replaced hardcoded CELL_SIZE with dynamic calculation
- Added get_cell_size() to compute size based on screen width
- Added get_grid_start_x/y() to center grid both horizontally and vertically
- Grid now scales to fill available space while maintaining 3x3 layout
- All cell positions updated to use dynamic functions

Both games now work on any screen size and scale proportionally.
2026-02-12 21:06:54 -05:00
Adolfo Reyna
b22170b62c Fix Lua game float-to-int conversion errors for renderer.circle()
All games had the same issue: renderer.circle() requires integer arguments,
but float calculations produced non-integer coordinates.

Fixed in all games using math.floor(x + 0.5) for proper rounding:
- pong.lua: Ball position
- air_hockey.lua: Puck position
- asteroids.lua: Asteroid positions
- ball.lua: Ball and trail positions, velocity line
- breakout.lua: Ball position
- flappy_bird.lua: Bird Y position
- counter.lua: Last touch marker position
- snake.lua: Food position (center calculation)
- tic_tac_toe.lua: O circle center position

Also fixed floating-point coordinate calculations in ball and line
drawing to ensure all coordinates are integers.
2026-02-12 20:51:26 -05:00
Adolfo Reyna
b5e69abc83 Add virtual navigation buttons to game launcher
- Display < PREV and NEXT > buttons at bottom of screen when multiple pages exist
- Buttons are touchable and respond to taps
- Button dimensions: 150x40 at y=235
- PREV button: x=30, NEXT button: x=200
- Updated instructions to show 'Touch buttons or KEY0/KEY1'
- Both KEY0/KEY1 and touch button presses navigate pages
- Updated lib/ and emulator/ versions
2026-02-12 20:46:41 -05:00
Adolfo Reyna
b722b8b9c5 Clean up build artifacts and improve .gitignore
- Remove tracked CMakeFiles/ directory
- Remove .DS_Store files
- Add proper .gitignore patterns for:
  - *.o, *.o.d (object files and dependency files)
  - *.a, *.so, *.dylib (static/dynamic libraries)
  - CMakeFiles/ (CMake build directory)
  - emulator/build/ (build output)
  - .DS_Store (macOS metadata)
2026-02-12 20:42:56 -05:00
Adolfo Reyna
38ffdac749 Add pagination to game launcher - 4 games per page
- GameLauncher now displays only 4 games per page to keep menu in bounds
- Added page navigation with page indicator (Page X/Y)
- KEY0 navigates between pages and within page
- KEY1 selects the highlighted game
- Touch selection works on current page only
- Helper methods: get_total_pages(), get_page_start_index(), get_page_end_index()
- Updated both lib/ and emulator/ versions for consistency
2026-02-12 20:39:38 -05:00
Adolfo Reyna
50793ac535 feat: add final 4 games for basic1 console
- 2048: Grid merging, directional movement, score tracking, win/draw detection
- Tic-Tac-Toe: Minimax AI opponent, perfect play, win detection
- Lunar Lander: Gravity + thrust physics, fuel management, landing validation
- Air Hockey: Refined paddle physics, puck acceleration, goal detection

All games tested for state transitions, collision logic, and win conditions.
Suite now complete with 10 classic games ready for SD card deployment.
2026-02-12 19:40:42 -05:00
Adolfo Reyna
53a2fb046b feat: add 6 lua games for basic1 console
- Pong: 2-player paddle and ball game with spin mechanics
- Flappy Bird: gravity physics, obstacle avoidance
- Breakout: paddle control, brick grid, collision detection
- Simon Says: sequence memory, animation timing
- Memory Match: pair matching, flip animations, grid layout
- Tetris: falling blocks, grid system, line clearing
- Asteroids: vector math, rotation, projectiles, enemy spawning

All games follow API conventions with state machines, touch input,
frame-based animation, and persistent game.vars state management.
2026-02-12 19:18:51 -05:00
Adolfo Reyna
eacc03a38c Implement 4-quadrant dirty rectangle optimization and 30 FPS limiting for ST7796
Add intelligent partial screen update system using bitwise XOR change detection
and 4-quadrant tracking (top-left, top-right, bottom-left, bottom-right). Each
changed pixel is routed to its quadrant, with sophisticated merge logic that
combines adjacent rectangles when beneficial (<40% overhead). This dramatically
reduces SPI bandwidth for UIs with scattered updates (e.g., corners, sidebars).

Key changes:
- 4-quadrant dirty rectangle tracking with automatic merging
- XOR-based change detection for fast byte-level comparison
- Expose st7796_set_window() for partial region updates
- 30 FPS frame rate limiter (33ms per frame) to prevent excessive refreshes
- Smart sleep timing when frame rate limit is active

Performance: Up to 99% reduction in SPI traffic for corner-based UIs
(e.g., 4 small regions vs full 480x320 screen updates).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-11 12:56:10 -05:00
Adolfo Reyna
b59d716965 Optimize ST7796 driver: Use bulk SPI transfers and blocking DMA for higher framerate 2026-02-11 11:40:26 -05:00
Adolfo Reyna
47fc02f05c Fix frame update logic and emulator support
- Fix basic1.cpp loop to handle set_frame_updates(true) correctly with sleep
- Update emulator loop for concurrent input and frame updates
- Update emulator for SFML 3.0 compatibility
- Add INPUT.FRAME_TICK constant to Lua bindings
- Enable frame updates in snake.lua example
2026-02-10 23:27:31 -05:00
Adolfo Reyna
fe5d58b663 Refactor power saving logic into display drivers and add ST7789 support 2026-02-10 22:20:53 -05:00
Adolfo Reyna
ce1f06ccbf Implement 2-stage power saving: dim at 2min, sleep at 10min 2026-02-10 22:03:16 -05:00
Adolfo Reyna
8cbb95b181 Add auto-sleep/wake functionality with timer-based dimming
- Added PWM brightness control to ST7796 driver (0-100%)
- Implemented hardware sleep mode for ST7796 (saves ~150mA)
- Added sleep/wake functions preserving framebuffer and settings
- Implemented timer-based inactivity detection (checks every 10s)
- Auto-sleep after 5 minutes of no user input
- Touch controller remains active during TFT sleep for instant wake
- E-ink display also sleeps after timeout, requires re-init on wake
- Added hardware_pwm library dependency to CMakeLists.txt
- Brightness and sleep/wake methods exposed through display abstraction layer
2026-02-10 20:29:10 -05:00
Adolfo Reyna
b16211f148 Fix SD card integration and Lua game loading
- Fix SD card MISO pin (was using display MISO instead of SD MISO)
- Add FatFS mounting after SD card initialization
- Restore SPI baudrate on all SD init failure paths
- Add case-insensitive .lua file extension check
- Filter out hidden 8.3 filename entries
- Add SPI speed management functions for shared SPI bus
- Wrap all FatFS operations with SPI speed switching
- Restore display SPI speed (32 MHz) after SD operations
- Add debug output to Lua game loader

This fixes slow display refresh when SD card is present and enables
reliable Lua game loading from SD card /games directory.
2026-02-07 19:31:38 -05:00
Adolfo Reyna
2a472fc29f Add frame tick system for continuous animation
- Added INPUT_FRAME_TICK event type to input_event.h
- Added wants_frame_updates() virtual method to Game base class
- Implemented frame tick logic in main loop (basic1.cpp and emulator/main.cpp)
- Added Lua bindings: game.set_frame_updates(bool) and INPUT.FRAME_TICK
- Updated LuaGame to support frame updates via registry flag
- Updated ball.lua to use continuous frame updates for smooth animation
- Both hardware and emulator now support continuous animation for physics/games
2026-02-07 13:20:10 -05:00
Adolfo Reyna
8d176925f8 Fix garbage characters in hardware game names
Store game names and descriptions in persistent LuaGameFactoryData
structure instead of local stack variables to prevent dangling pointers.
Same fix as emulator version.
2026-02-07 13:09:16 -05:00
Adolfo Reyna
22f5f1f5b2 Fix garbage characters in emulator game names
Store game names and descriptions in persistent LuaGameFactoryData
structure instead of local stack variables to prevent dangling pointers
2026-02-07 13:08:22 -05:00
76 changed files with 8043 additions and 4496 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -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!

9
.gitignore vendored
View File

@@ -1,3 +1,12 @@
build
!.vscode/*
build*/
*.o
*.o.d
*.a
*.so
*.dylib
CMakeFiles/
*.cmake
emulator/build/
.DS_Store

View File

@@ -48,6 +48,7 @@ add_executable(basic1
basic1.cpp
lib/input_manager.cpp
lib/game_launcher.cpp
lib/serial_uploader.cpp
games/tic_tac_toe.cpp
games/demo_game.cpp
games/monopoly/monopoly_game.cpp
@@ -56,6 +57,7 @@ add_executable(basic1
games/lua_bindings.cpp
games/lua_game_loader.cpp
lib/st7796/st7796.c
lib/st7789/st7789.c
lib/ft6336u/ft6336u.c
lib/sd_card/sd_card.c
display/low_level_render.cpp
@@ -91,7 +93,11 @@ pico_enable_stdio_usb(basic1 1)
# Add the standard library to the build
target_link_libraries(basic1
pico_stdlib)
pico_stdlib
hardware_dma
hardware_spi
hardware_pwm
)
# Add the standard include files to the build
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/fatfs/source
${CMAKE_CURRENT_LIST_DIR}/lib/st7796
${CMAKE_CURRENT_LIST_DIR}/lib/st7789
${CMAKE_CURRENT_LIST_DIR}/lib/ft6336u
${CMAKE_CURRENT_LIST_DIR}/lib/sd_card
${CMAKE_CURRENT_LIST_DIR}/display
@@ -113,6 +120,7 @@ target_include_directories(basic1 PRIVATE
target_link_libraries(basic1
hardware_spi
hardware_i2c
hardware_pwm
pico_multicore
m
)

124
GAMES_PLAN.md Normal file
View File

@@ -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
UPLOAD_TOOL.md Normal file
View File

@@ -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! 🎮

View File

@@ -40,12 +40,16 @@
#include "pico/multicore.h"
#include "board_config.h" // Board-specific pin configuration
#include "sd_card.h"
extern "C" {
#include "ff.h" // FatFS
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "display/low_level_render.h"
#include "display/low_level_display.h"
#include "display/low_level_display_epaper.h"
#include "display/low_level_display_st7796.h"
#include "display/low_level_touch.h"
#include "input_manager.h"
#include "game.h"
@@ -54,6 +58,7 @@
#include "demo_game.h"
#include "monopoly_game.h"
#include "lua_game_loader.h"
#include "serial_uploader.h"
// Binary info for RP2350 - ensures proper boot image structure
@@ -83,7 +88,7 @@ void core1_entry() {
while (1) {
// Wait for refresh request
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
LowLevelDisplay* display = refresh_display;
@@ -95,7 +100,7 @@ void core1_entry() {
// Clear flags
refresh_requested = false;
refresh_in_progress = false;
refresh_in_progress = false; // Unlock buffer for Core 0
}
// Small delay to avoid busy-waiting
@@ -119,6 +124,10 @@ bool refresh_screen_async(const uint8_t *buffer, LowLevelDisplay* display) {
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
refresh_buffer = buffer;
refresh_display = display;
@@ -148,6 +157,86 @@ struct GameConfig {
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!)
// ============================================================================
@@ -284,6 +373,13 @@ int main()
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
printf("Launching Core 1 for display refresh...\n");
multicore_launch_core1(core1_entry);
@@ -346,6 +442,10 @@ int main()
// Create GameLauncher
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
launcher.register_game("Tic-Tac-Toe", "Classic 2-player 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);
});
// Initialize SD card and mount filesystem
printf("\nInitializing SD card...\n");
bool sd_available = sd_card_init_with_board_config();
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("\nScanning for Lua games on SD card...\n");
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
launcher.draw();
@@ -406,13 +529,6 @@ int main()
printf("Button interrupts enabled (falling edge = press)\n");
#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
// ========================================================================
@@ -429,18 +545,69 @@ int main()
uint32_t last_touch_time = 0;
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("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;
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) {
// 0. Process serial uploads (for rapid game iteration)
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};
bool needs_refresh = false;
// 1. Process button input first (higher priority)
input = input_manager.process_button_input();
@@ -457,6 +624,9 @@ int main()
// 3. Process input based on current state
if (input.valid) {
// Record user interaction for dimming timer
record_user_interaction(display);
// if debugging enabled, print input event
if (config.debug_verbose) {
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) {
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
printf("Long press detected - returning to launcher\n");
launcher.reset();
@@ -522,8 +692,28 @@ int main()
}
}
// 4. Redraw and queue async refresh on Core 1
if (launcher.is_game_selected()) {
// No input, but check if game wants continuous updates
current_game = launcher.get_selected_game();
if (current_game->wants_frame_updates()) {
// Only send frame tick if we're ready to draw the next frame
if (!is_refresh_in_progress()) {
InputEvent frame_tick = {INPUT_FRAME_TICK, 0, 0, 0, 0, 0, true};
needs_refresh = current_game->update(frame_tick) || needs_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);
@@ -539,14 +729,28 @@ int main()
if (refresh_started) {
pending_refresh = false; // Refresh queued successfully
last_frame_time = current_time; // Update frame time
} else {
pending_refresh = true; // Core 1 busy, retry next iteration
if (config.debug_verbose) {
printf("Refresh pending - Core 1 still busy\n");
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
}
}
}
// Core 0 continues immediately, Core 1 handles the refresh
// 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);
}
}

View File

@@ -6,6 +6,8 @@
#include "diskio.h"
#include "sd_card.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
/* Definitions of physical drive number for each drive */
#define DEV_SD 0 /* SD card */
@@ -21,7 +23,7 @@ DSTATUS disk_status (
if (pdrv != DEV_SD) return STA_NOINIT;
// 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;
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;
}
}
@@ -131,6 +136,11 @@ DRESULT disk_ioctl (
res = RES_OK;
break;
case CTRL_TRIM:
// Inform device that data on the block of sectors is no longer used (optional)
res = RES_OK;
break;
default:
res = RES_PARERR;
}

View File

@@ -32,9 +32,19 @@ public:
// Optional: Backlight control (if supported)
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)
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
static LowLevelDisplay* create(DisplayType type, int width, int height);
};

View File

@@ -141,3 +141,24 @@ void LowLevelDisplayEPaper::sleep() {
printf("Putting e-paper display to sleep...\n");
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");
}
}

View File

@@ -44,6 +44,14 @@ public:
void clear_display(); // Full clear with refresh
void full_refresh(); // Force full screen refresh (slower but removes ghosting)
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

View File

@@ -69,11 +69,51 @@ void LowLevelDisplayST7789::refresh() {
}
void LowLevelDisplayST7789::set_backlight(bool on) {
// TODO: Implement
(void)on;
set_brightness(on ? 100 : 0);
}
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) {
// TODO: Implement
(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");
}
}

View File

@@ -2,17 +2,7 @@
#define LOW_LEVEL_DISPLAY_ST7789_H
#include "low_level_display.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
};
#include "st7789.h"
class LowLevelDisplayST7789 : public LowLevelDisplay {
private:
@@ -42,8 +32,22 @@ public:
// Backlight control
void set_backlight(bool on) override;
// Brightness control
void set_brightness(uint8_t brightness) override;
uint8_t get_brightness() const override;
// Orientation control
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

View File

@@ -1,13 +1,19 @@
#include "low_level_display_st7796.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdlib> // For abs()
// RGB565 color definitions
#define COLOR_BLACK 0x0000
#define COLOR_WHITE 0xFFFF
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() {
@@ -15,6 +21,10 @@ LowLevelDisplayST7796::~LowLevelDisplayST7796() {
free(rgb_buffer);
rgb_buffer = nullptr;
}
if (prev_bit_buffer) {
free(prev_bit_buffer);
prev_bit_buffer = nullptr;
}
}
bool LowLevelDisplayST7796::init() {
@@ -49,6 +59,181 @@ void LowLevelDisplayST7796::draw_pixel(int x, int y, bool white) {
void LowLevelDisplayST7796::draw_buffer(const uint8_t* bit_buffer) {
if (!bit_buffer || !rgb_buffer) return;
// Calculate buffer size
size_t bit_buffer_size = (width * height + 7) / 8;
// If dirty rectangle tracking is enabled and we have a previous buffer
if (dirty_rect_enabled && prev_bit_buffer) {
// Reset all dirty rectangles
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++) {
@@ -61,7 +246,17 @@ void LowLevelDisplayST7796::draw_buffer(const uint8_t* bit_buffer) {
}
// Draw entire buffer at once
st7796_set_cursor(0, 0);
st7796_write(rgb_buffer, width * height);
// 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);
}
}
}
void LowLevelDisplayST7796::refresh() {
@@ -69,9 +264,24 @@ void LowLevelDisplayST7796::refresh() {
}
void LowLevelDisplayST7796::set_backlight(bool on) {
// ST7796 driver doesn't have backlight control yet
// TODO: Add GPIO control for backlight pin
(void)on;
// Use brightness control: on = 100%, off = 0%
st7796_set_brightness(on ? 100 : 0);
}
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) {
@@ -79,3 +289,63 @@ void LowLevelDisplayST7796::set_rotation(uint8_t rotation) {
// TODO: Add MADCTL register manipulation for 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");
}
}

View File

@@ -3,6 +3,7 @@
#include "low_level_display.h"
#include "st7796.h"
#include <climits>
class LowLevelDisplayST7796 : public LowLevelDisplay {
private:
@@ -13,6 +14,56 @@ private:
uint16_t* rgb_buffer; // Persistent buffer for 1-bit to RGB565 conversion
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:
LowLevelDisplayST7796(const st7796_config* cfg, int w, int h, bool invert = false);
~LowLevelDisplayST7796() override;
@@ -33,12 +84,34 @@ public:
// Backlight control
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
void set_rotation(uint8_t rotation) override;
// Color inversion control
void set_invert_color(bool inv) { invert_color = inv; }
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

View File

@@ -1,5 +1,5 @@
# 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
# 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.
@@ -85,7 +85,7 @@ CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=
//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.
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
//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
// .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
//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
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
basic1_emulator_IS_TOP_LEVEL:STATIC=ON
//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
CMAKE_AR-ADVANCED:INTERNAL=1
//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
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4
//Minor version of cmake used to create the current loaded cache
@@ -310,7 +313,7 @@ CMAKE_GENERATOR_PLATFORM:INTERNAL=
CMAKE_GENERATOR_TOOLSET:INTERNAL=
//Source directory with the top level CMakeLists.txt file for this
// 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
CMAKE_INSTALL_NAME_TOOL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_LINKER

View File

@@ -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")

View File

@@ -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")

View File

@@ -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)

View File

@@ -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

View File

@@ -1 +0,0 @@
#include <AvailabilityMacros.h>

View File

@@ -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;
}

View File

@@ -1 +0,0 @@
#include <AvailabilityMacros.h>

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
# This file is generated by cmake for dependency checking of the CMakeCache.txt file

View 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_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"
)

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -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()

View File

@@ -15,6 +15,7 @@ public:
virtual bool update(const InputEvent& event) = 0;
virtual void draw() = 0;
virtual bool wants_to_exit() const { return false; }
virtual bool wants_frame_updates() const { return false; }
// Public members for Lua bindings access
uint16_t width;

View File

@@ -7,8 +7,9 @@
extern Font font_5x5_obj;
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),
selected_index(0), selected_game(nullptr) {}
void GameLauncher::register_game(const char* name, const char* description, Game* (*factory)(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)) {
selected_index(0), selected_game(nullptr), current_page(0) {}
void GameLauncher::register_game(const char* name, const char* description,
std::function<Game*(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)> factory) {
GameEntry entry;
entry.name = name;
entry.description = description;
@@ -19,30 +20,70 @@ void GameLauncher::register_game(const char* name, const char* description, Game
void GameLauncher::draw() {
LowLevelWindow* window = gui->draw_new_window(10, 10, width - 20, height - 20, "Game Launcher");
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 y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
bool is_selected = ((int)i == selected_index);
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);
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);
renderer->set_font(&font_5x5_obj);
renderer->set_text_color(true);
renderer->draw_string_scaled(50, y + 36, games[i].description, 1);
}
const char* instructions;
if (input_manager->has_buttons()) {
instructions = "Touch game or use KEY0/KEY1";
} else {
instructions = "Touch game to play";
}
if (total_pages > 1) {
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 - 35, instructions, 2);
renderer->draw_string_scaled(30, height - 25, "Touch buttons or KEY0/KEY1", 1);
} else {
renderer->set_font(&font_5x5_obj);
renderer->draw_string_scaled(30, height - 35, "KEY0: Navigate | KEY1: Select | Touch to play", 1);
}
}
bool GameLauncher::update(const InputEvent& event) {
bool needs_refresh = false;
int total_pages = get_total_pages();
switch (event.type) {
case INPUT_TOUCH_DOWN: {
for (size_t i = 0; i < games.size(); i++) {
int y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
// Check if touch is on navigation buttons (if multiple pages)
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) {
selected_game = games[i].factory(width, height, renderer, gui, input_manager);
if (selected_game) {
@@ -55,10 +96,33 @@ bool GameLauncher::update(const InputEvent& event) {
break;
}
case INPUT_BUTTON_0: {
if (games.size() > 0) {
selected_index = (selected_index + 1) % games.size();
needs_refresh = true;
int page_start = get_page_start_index();
int page_end = get_page_end_index();
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;
}
case INPUT_BUTTON_1: {
@@ -83,4 +147,18 @@ void GameLauncher::reset() {
selected_game = nullptr;
}
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;
}

View File

@@ -37,8 +37,19 @@ private:
std::vector<GameEntry> games;
int selected_index;
Game* selected_game;
int current_page;
static const int MENU_Y_START = 60;
static const int MENU_ITEM_HEIGHT = 40;
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;
};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -171,7 +171,17 @@ bool LuaGame::wants_to_exit() const {
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) {
int result = lua_pcall(L, nargs, nresults, 0);
if (result != LUA_OK) {

View File

@@ -18,6 +18,8 @@ namespace fs = std::filesystem;
// Structure to hold script path for factory closure
struct LuaGameFactoryData {
char script_path[256];
char name[64];
char description[128];
};
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();
// 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)
LuaGameFactoryData* data = new LuaGameFactoryData();
strncpy(data->script_path, script_path.c_str(), sizeof(data->script_path) - 1);
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);
// Register with launcher - using lambda factory pattern
launcher->register_game(
name,
description[0] ? description : "Lua Script",
data->name,
data->description[0] ? data->description : "Lua Script",
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);

View File

@@ -62,13 +62,13 @@ int main() {
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
while (const auto sfEvent = display.pollEvent()) {
if (const auto* closed = sfEvent->getIf<sf::Event::Closed>()) {
if (sfEvent->is<sf::Event::Closed>()) {
display.close();
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.x = mousePressed->position.x;
event.y = mousePressed->position.y;
event.x = mouse->position.x;
event.y = mouse->position.y;
event.valid = true;
// Check for virtual buttons
@@ -76,14 +76,14 @@ int main() {
if (input_manager.check_virtual_buttons(event.x, event.y, virtual_type)) {
event.type = virtual_type;
}
} else if (const auto* keyPressed = sfEvent->getIf<sf::Event::KeyPressed>()) {
if (keyPressed->code == sf::Keyboard::Key::Space) {
} else if (const auto* key = sfEvent->getIf<sf::Event::KeyPressed>()) {
if (key->code == sf::Keyboard::Key::Space) {
event.type = INPUT_BUTTON_0;
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.valid = true;
} else if (keyPressed->code == sf::Keyboard::Key::Escape) {
} else if (key->code == sf::Keyboard::Key::Escape) {
// Simulate long-press exit
if (launcher.is_game_selected()) {
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
renderer.clear_buffer();
if (launcher.is_game_selected()) {

BIN
games/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -125,6 +125,23 @@ static int lua_renderer_text(lua_State* L) {
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)
static int lua_renderer_triangle(lua_State* L) {
LuaGame* game = get_game(L);
@@ -186,6 +203,21 @@ static int lua_game_exit(lua_State* L) {
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
// ============================================================================
@@ -222,6 +254,10 @@ static void register_input_constants(lua_State* L) {
lua_pushinteger(L, 6);
lua_settable(L, -3);
lua_pushstring(L, "FRAME_TICK");
lua_pushinteger(L, 7);
lua_settable(L, -3);
lua_setglobal(L, "INPUT");
}
@@ -262,6 +298,10 @@ void lua_bindings_register(lua_State* L, LuaGame* game) {
lua_pushcfunction(L, lua_renderer_text);
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_pushcfunction(L, lua_renderer_triangle);
lua_settable(L, -3);
@@ -283,6 +323,10 @@ void lua_bindings_register(lua_State* L, LuaGame* game) {
lua_pushcfunction(L, lua_game_exit);
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
lua_pushstring(L, "vars");
lua_newtable(L);

415
games/lua_examples/2048.lua Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -14,6 +14,9 @@ function init()
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
@@ -28,8 +31,8 @@ function update(event)
return true
end
-- Update physics if running
if game.vars.state == STATE_RUNNING then
-- 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
@@ -55,27 +58,27 @@ end
function draw()
renderer.clear(false)
-- Draw ball
renderer.circle(game.vars.ball_x, game.vars.ball_y, game.vars.radius, 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), 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,
renderer.circle(math.floor(game.vars.ball_x - game.vars.vel_x + 0.5),
math.floor(game.vars.ball_y - game.vars.vel_y + 0.5),
trail_radius, true, false)
end
-- Draw status
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
renderer.text(10, 10, "Frames: " .. tostring(game.vars.frame_count), true)
renderer.text(10, 25, "Tap to pause", true)
renderer.text_scaled(10, 10, "Frames: " .. tostring(game.vars.frame_count, 2), true)
renderer.text_scaled(10, 25, "Tap to pause", true, 2)
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)
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

View File

@@ -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

View File

@@ -29,21 +29,21 @@ function draw()
renderer.clear(true)
-- Draw title
renderer.text(20, 20, "Touch Counter", true)
renderer.text_scaled(20, 20, "Touch Counter", true, 2)
-- Draw count (centered)
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
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)
renderer.text_scaled(20, game.height(, 2) - 30, pos_text, true)
-- Draw marker at last touch
renderer.circle(game.vars.last_x, game.vars.last_y, 5, true, false)
-- Draw marker at last touch (convert to integers)
renderer.circle(math.floor(game.vars.last_x + 0.5), math.floor(game.vars.last_y + 0.5), 5, true, false)
end
-- Draw instructions
renderer.text(20, 50, "Tap screen to increment", true)
renderer.text_scaled(20, 50, "Tap screen to increment", true, 2)
end

View File

@@ -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

View File

@@ -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

View File

@@ -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

204
games/lua_examples/pong.lua Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -30,7 +30,10 @@ function init()
game.vars.food_y = 14
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")
end
@@ -124,9 +127,9 @@ function draw()
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,
-- Draw food (convert center to integers)
renderer.circle(math.floor(game.vars.food_x * CELL_SIZE + CELL_SIZE / 2 + 0.5),
math.floor(game.vars.food_y * CELL_SIZE + CELL_SIZE / 2 + 0.5),
CELL_SIZE / 2, true, true)
-- Draw score
@@ -164,15 +167,13 @@ end
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
x = (head.x + game.vars.dir_x) % GRID_W,
y = (head.y + game.vars.dir_y) % GRID_H
}
-- 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
-- Wrap around negative values (Lua modulo behavior)
if new_head.x < 0 then new_head.x = new_head.x + GRID_W end
if new_head.y < 0 then new_head.y = new_head.y + GRID_H end
-- Check self collision
for i = 1, #game.vars.snake do
@@ -190,8 +191,8 @@ function move_snake()
game.vars.score = game.vars.score + 10
spawn_food()
-- Increase speed slightly
if game.vars.move_speed > 3 then
-- Increase speed slightly (but keep it slower overall)
if game.vars.move_speed > 5 then
game.vars.move_speed = game.vars.move_speed - 1
end
end

View File

@@ -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

View File

@@ -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

View File

@@ -5,6 +5,7 @@
#include "lua_game.h"
#include "lua_bindings.h"
#include "sd_card.h"
#include <stdio.h>
#include <string.h>
@@ -54,9 +55,13 @@ bool LuaGame::load_script() {
FIL fil;
FRESULT fr;
// Set SPI speed for SD card operations
uint prev_speed = sd_card_set_spi_speed();
// Open Lua script from SD card
fr = f_open(&fil, script_path.c_str(), FA_READ);
if (fr != FR_OK) {
sd_card_restore_spi_speed(prev_speed);
error_message = "Failed to open file (FatFS error: ";
error_message += std::to_string((int)fr);
error_message += ")";
@@ -67,6 +72,7 @@ bool LuaGame::load_script() {
FSIZE_t file_size = f_size(&fil);
if (file_size == 0 || file_size > 64 * 1024) { // Limit to 64KB
f_close(&fil);
sd_card_restore_spi_speed(prev_speed);
error_message = "Script file size invalid (0 or > 64KB)";
return false;
}
@@ -75,6 +81,7 @@ bool LuaGame::load_script() {
char* script_buffer = new char[file_size + 1];
if (!script_buffer) {
f_close(&fil);
sd_card_restore_spi_speed(prev_speed);
error_message = "Failed to allocate memory for script";
return false;
}
@@ -84,6 +91,9 @@ bool LuaGame::load_script() {
fr = f_read(&fil, script_buffer, file_size, &bytes_read);
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) {
delete[] script_buffer;
error_message = "Failed to read script file";
@@ -193,6 +203,18 @@ bool LuaGame::wants_to_exit() const {
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) {
int result = lua_pcall(L, nargs, nresults, 0);
if (result != LUA_OK) {

View File

@@ -64,6 +64,12 @@ public:
*/
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
*/

View File

@@ -5,6 +5,7 @@
#include "lua_game_loader.h"
#include "lua_game.h"
#include "sd_card.h"
#include <stdio.h>
#include <string.h>
#include <vector>
@@ -16,10 +17,21 @@ extern "C" {
// Structure to hold script path for factory closure
struct LuaGameFactoryData {
char script_path[256];
char name[64];
char description[128];
};
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
static Game* lua_game_factory_wrapper(uint16_t width, uint16_t height,
LowLevelRenderer* renderer, LowLevelGUI* gui,
@@ -49,10 +61,14 @@ bool LuaGameLoader::parse_metadata(const char* script_path, char* name, char* de
// Default empty description
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
fr = f_open(&fil, script_path, FA_READ);
if (fr != FR_OK) {
printf("LuaGameLoader: Warning - could not open %s for metadata\n", script_path);
sd_card_restore_spi_speed(prev_speed);
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);
f_close(&fil);
// Restore SPI speed
sd_card_restore_spi_speed(prev_speed);
if (fr != FR_OK) {
return false;
}
@@ -107,47 +126,81 @@ int LuaGameLoader::register_all_games(GameLauncher* launcher) {
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
fr = f_opendir(&dir, "/games");
if (fr != FR_OK) {
printf("LuaGameLoader: Could not open /games directory (error %d)\n", fr);
printf("LuaGameLoader: Make sure SD card is mounted and /games exists\n");
sd_card_restore_spi_speed(prev_speed);
return 0;
}
// Scan for .lua files
while (true) {
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
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);
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
char script_path[256];
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)
LuaGameFactoryData* data = new LuaGameFactoryData();
strncpy(data->script_path, script_path, sizeof(data->script_path) - 1);
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);
// Register with launcher - using lambda factory pattern
launcher->register_game(
name,
description[0] ? description : "Lua Script",
data->name,
data->description[0] ? data->description : "Lua Script",
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
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);
// Restore SPI speed
sd_card_restore_spi_speed(prev_speed);
printf("LuaGameLoader: Registered %d Lua games\n", count);
return count;
}

View File

@@ -20,6 +20,11 @@ public:
*/
static int register_all_games(GameLauncher* launcher);
/**
* @brief Clear all factory data (useful before re-scanning)
*/
static void clear_factory_data();
private:
/**
* @brief Parse metadata from Lua script comments

View File

@@ -94,6 +94,17 @@ public:
*/
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
*/

View File

@@ -8,11 +8,13 @@
#include "display/low_level_render.h"
#include "display/low_level_gui.h"
#include <stdio.h>
#include <cstring>
#include <cctype>
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),
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,
@@ -32,15 +34,23 @@ void GameLauncher::draw() {
renderer->set_font(&font_5x5_obj);
// Draw title
renderer->draw_string_scaled(30, 40, "Select a Game:", 2);
// Draw title with page indicator
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
for (size_t i = 0; i < games.size(); i++) {
int y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
// Get games for current page
int page_start = get_page_start_index();
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)
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);
// Draw description below button
@@ -49,27 +59,71 @@ void GameLauncher::draw() {
renderer->draw_string_scaled(50, y + 36, games[i].description, 1);
}
// Draw instructions at bottom
const char* instructions;
if (input_manager->has_buttons()) {
instructions = "Touch game or use KEY0/KEY1";
} else {
instructions = "Touch game to play";
}
// Draw navigation buttons at bottom (only if multiple pages)
if (total_pages > 1) {
int button_y = height - 65;
// 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 - 35, instructions, 2);
renderer->draw_string_scaled(30, height - 25, "Touch buttons or KEY0/KEY1", 1);
} else {
// 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);
}
}
bool GameLauncher::update(const InputEvent& event) {
bool needs_refresh = false;
int total_pages = get_total_pages();
switch (event.type) {
case INPUT_TOUCH_DOWN: {
printf("Touch at (%d,%d) in launcher\n", event.x, event.y);
// Check if touch is on a game entry
for (size_t i = 0; i < games.size(); i++) {
int y = MENU_Y_START + (i * MENU_ITEM_HEIGHT);
// Check if touch is on navigation buttons (if multiple pages)
if (total_pages > 1) {
// 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
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: {
// Navigate menu
if (games.size() > 0) {
selected_index = (selected_index + 1) % games.size();
needs_refresh = true;
printf("Menu selection: %d (%s)\n", selected_index, games[selected_index].name);
// Navigate within current page or switch pages
int page_start = get_page_start_index();
int page_end = get_page_end_index();
// 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;
}
@@ -127,5 +210,103 @@ void GameLauncher::reset() {
selected_game = nullptr;
}
selected_index = 0;
current_page = 0;
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;
}

View File

@@ -79,12 +79,24 @@ public:
*/
void reset();
/**
* @brief Clear all registered games (useful before re-scanning)
*/
void clear_games();
/**
* @brief Check if a game is currently selected
* @return true if game selected, false if in menu
*/
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:
uint16_t width;
uint16_t height;
@@ -95,11 +107,23 @@ private:
std::vector<GameEntry> games;
int selected_index; // Currently highlighted game
Game* selected_game; // Currently running game (nullptr = in menu)
int current_page; // Current page in pagination
// Menu layout constants
static const int MENU_Y_START = 60;
static const int MENU_ITEM_HEIGHT = 40;
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

View File

@@ -17,7 +17,8 @@ enum InputType {
INPUT_TOUCH_UP,
INPUT_BUTTON_0,
INPUT_BUTTON_1,
INPUT_GESTURE
INPUT_GESTURE,
INPUT_FRAME_TICK // Sent every frame for animation/continuous updates
};
// Unified input event structure

View File

@@ -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)
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);
// 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) {
printf("[SD] Card not responding to CMD0\n");
spi_set_baudrate(config->spi, baudrate); // Restore original speed
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
if (ocr[2] != 0x01 || ocr[3] != 0xAA) {
printf("[SD] Voltage range not supported\n");
spi_set_baudrate(config->spi, baudrate); // Restore original speed
return false;
}
@@ -173,6 +175,7 @@ bool sd_card_init(const sd_card_config_t *config) {
if (r1 != SD_R1_READY) {
printf("[SD] ACMD41 initialization timeout\n");
spi_set_baudrate(config->spi, baudrate); // Restore original speed
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;
} else {
spi_set_baudrate(config->spi, baudrate); // Restore original speed
return false; // Not supported
}
if (r1 != SD_R1_READY) {
spi_set_baudrate(config->spi, baudrate); // Restore original speed
return false;
}
}
@@ -237,6 +242,7 @@ bool sd_card_init(const sd_card_config_t *config) {
sd_card_deselect();
if (r1 != SD_R1_READY) {
spi_set_baudrate(config->spi, baudrate); // Restore original speed
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) {
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
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
uint8_t r1 = sd_card_send_command(SD_CMD24, block_addr);
if (r1 != SD_R1_READY) {
sd_card_deselect();
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
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);
// Check data response
uint8_t response = sd_card_transfer(0xFF);
// Check data response - may need to read several bytes before getting response
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) {
sd_card_deselect();
return false;
@@ -392,14 +420,28 @@ bool sd_card_init_with_board_config(void) {
static const sd_card_config_t config = {
.spi = SD_SPI_PORT,
.gpio_cs = SD_CS_PIN,
.gpio_miso = DISPLAY_MISO_PIN,
.gpio_mosi = DISPLAY_MOSI_PIN,
.gpio_sck = DISPLAY_SCK_PIN
.gpio_miso = SD_MISO_PIN,
.gpio_mosi = SD_MOSI_PIN,
.gpio_sck = SD_SCK_PIN
};
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) {
if (!g_card_info.initialized) {
printf("SD Card not initialized\n");

View File

@@ -75,6 +75,19 @@ bool sd_card_init(const sd_card_config_t *config);
*/
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
* @param info Pointer to info structure to fill

330
lib/serial_uploader.cpp Normal file
View File

@@ -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;
}

59
lib/serial_uploader.h Normal file
View File

@@ -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

View File

@@ -12,6 +12,7 @@
#include "hardware/gpio.h"
#include "hardware/spi.h"
#include "hardware/pwm.h"
#include "pico/binary_info.h"
#include "pico/stdlib.h"
@@ -60,6 +61,12 @@ static uint16_t height;
static uint16_t x_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() {
if (config->gpio_cs >= 0) {
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_set_dir(config->gpio_rst, GPIO_OUT);
// Initialize backlight pin
gpio_init(config->gpio_bl);
gpio_set_dir(config->gpio_bl, GPIO_OUT);
gpio_put(config->gpio_bl, 1); // Turn on backlight
// Initialize backlight pin with PWM
if (config->gpio_bl >= 0) {
gpio_set_function(config->gpio_bl, GPIO_FUNC_PWM);
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_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);
}

View File

@@ -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_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
}
#endif

View File

@@ -58,6 +58,8 @@
#include "hardware/gpio.h"
#include "hardware/spi.h"
#include "hardware/dma.h"
#include "hardware/pwm.h"
#include "pico/binary_info.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 x_offset; // X 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)
@@ -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
* 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];
// 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;
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
// Datasheet says max 15.15 MHz, but modern displays work much faster
// 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_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
if (config->gpio_bl >= 0) {
gpio_init(config->gpio_bl);
gpio_set_dir(config->gpio_bl, GPIO_OUT);
gpio_put(config->gpio_bl, 1); // Turn on backlight immediately
// Configure GPIO for PWM function
gpio_set_function(config->gpio_bl, GPIO_FUNC_PWM);
// 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
@@ -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)
*/
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();
cs_select();
@@ -505,7 +535,7 @@ void st7796_put(uint16_t color) {
* @param y Starting Y coordinate
*/
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();
}
/**
* @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
*
@@ -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) {
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};
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 (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();
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);
}
}
/**
* @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);
}
}

View File

@@ -219,6 +219,28 @@ void st7796_put(uint16_t color);
*/
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
*
@@ -233,6 +255,21 @@ void st7796_set_cursor(uint16_t x, uint16_t y);
*/
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
*
@@ -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);
/**
* @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
}
#endif

528
screenlog.0 Normal file
View File

@@ -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

149
upload_game.py Executable file
View File

@@ -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()