├── patches ├── .gitignore ├── mod_marker │ ├── mod_marker.bin │ ├── .gitignore │ ├── README.md │ └── Makefile ├── file_loader │ ├── README.md │ ├── .gitignore │ ├── Makefile │ └── file_loader.s ├── menu_texts │ ├── imaginary_unit.bin │ ├── .gitignore │ ├── README.md │ └── Makefile ├── backslash │ └── mod.part.txt └── Makefile ├── launcher ├── .gitignore ├── linker.ld ├── init.s ├── apps.hpp ├── bins.hpp ├── main_bootstrap.cpp ├── execs.hpp ├── Makefile ├── bins.cpp ├── apps.cpp └── main.cpp ├── patcher ├── .gitignore ├── make.bat ├── Makefile └── README.md ├── demos ├── snake │ ├── .gitignore │ ├── linker.ld │ ├── Makefile │ └── main.cpp ├── tetris │ ├── .gitignore │ ├── linker.ld │ ├── Makefile │ └── main.cpp ├── demo_gui │ ├── .gitignore │ ├── linker.ld │ ├── Makefile │ └── main.cpp ├── input_key │ ├── .gitignore │ ├── linker.ld │ ├── Makefile │ └── main.cpp ├── input_test │ ├── .gitignore │ ├── linker.ld │ ├── Makefile │ └── main.cpp ├── breakpoint_util │ ├── .gitignore │ ├── linker.ld │ ├── Makefile │ ├── breakpoint_handler_stub.s │ └── main.cpp ├── random_circles │ ├── .gitignore │ ├── linker.ld │ ├── main.cpp │ └── Makefile └── README.md ├── sdk ├── .gitignore ├── os │ ├── functions │ │ ├── _util.inc │ │ ├── input.s │ │ ├── mem.s │ │ ├── string.s │ │ ├── mcs.s │ │ ├── lcd.s │ │ ├── gui.s │ │ ├── debug.s │ │ ├── file.s │ │ └── serial.s │ └── gui │ │ ├── radioButton.cpp │ │ ├── longLabel.cpp │ │ ├── dropDown.cpp │ │ ├── button.cpp │ │ ├── label.cpp │ │ ├── textBox.cpp │ │ └── dialog.cpp ├── Doxyfile ├── include │ ├── stdlib.h │ ├── sdk │ │ ├── os │ │ │ ├── string.hpp │ │ │ ├── gui │ │ │ │ ├── radioButton.hpp │ │ │ │ ├── button.hpp │ │ │ │ ├── longLabel.hpp │ │ │ │ ├── label.hpp │ │ │ │ ├── textBox.hpp │ │ │ │ ├── dropDown.hpp │ │ │ │ ├── util.hpp │ │ │ │ └── dialog.hpp │ │ │ ├── mem.hpp │ │ │ ├── serial.hpp │ │ │ ├── gui.hpp │ │ │ ├── debug.hpp │ │ │ ├── lcd.hpp │ │ │ ├── mcs.hpp │ │ │ └── input.hpp │ │ ├── cpu │ │ │ └── ubc.hpp │ │ └── calc │ │ │ └── calc.hpp │ └── appdef.hpp ├── cxx.cpp ├── Makefile └── calc │ ├── getKey.s │ └── calc.cpp ├── app_template ├── linker_hhk.ld ├── .gitignore ├── start.s ├── linker_bin.ld ├── main.cpp └── Makefile ├── doc └── user │ ├── using_launcher.png │ ├── using.md │ ├── functions.md │ ├── developing.md │ └── patching.md ├── .gitmodules └── README.md /patches/.gitignore: -------------------------------------------------------------------------------- 1 | mod.txt 2 | *.log -------------------------------------------------------------------------------- /patches/mod_marker/mod_marker.bin: -------------------------------------------------------------------------------- 1 | hollyhock-2 -------------------------------------------------------------------------------- /patches/file_loader/README.md: -------------------------------------------------------------------------------- 1 | # `file_loader` 2 | -------------------------------------------------------------------------------- /launcher/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.bin 4 | -------------------------------------------------------------------------------- /patcher/.gitignore: -------------------------------------------------------------------------------- 1 | Snail2021mod.c 2 | Snail2021.exe 3 | -------------------------------------------------------------------------------- /patches/menu_texts/imaginary_unit.bin: -------------------------------------------------------------------------------- 1 | Hollyhock-2 Launcher -------------------------------------------------------------------------------- /demos/snake/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | -------------------------------------------------------------------------------- /demos/tetris/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | -------------------------------------------------------------------------------- /demos/demo_gui/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | -------------------------------------------------------------------------------- /demos/input_key/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | -------------------------------------------------------------------------------- /demos/input_test/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | -------------------------------------------------------------------------------- /patches/menu_texts/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | mod.part.txt 3 | -------------------------------------------------------------------------------- /patches/mod_marker/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | mod.part.txt 3 | -------------------------------------------------------------------------------- /demos/breakpoint_util/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | -------------------------------------------------------------------------------- /demos/random_circles/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | -------------------------------------------------------------------------------- /demos/snake/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /demos/tetris/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /patches/file_loader/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.bin 4 | mod.part.txt 5 | -------------------------------------------------------------------------------- /sdk/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.bin 4 | 5 | # Doxygen output 6 | doc/ 7 | -------------------------------------------------------------------------------- /app_template/linker_hhk.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /demos/demo_gui/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /demos/input_key/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /demos/input_test/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /demos/breakpoint_util/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /demos/random_circles/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | . = 0x8CFF0000; 5 | } 6 | -------------------------------------------------------------------------------- /doc/user/using_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailMath/hollyhock-2/HEAD/doc/user/using_launcher.png -------------------------------------------------------------------------------- /sdk/os/functions/_util.inc: -------------------------------------------------------------------------------- 1 | .macro DEFINE_OS_FUNC name, addr 2 | .globl _\name 3 | .set _\name, \addr 4 | .endm 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "patcher/Snail202x"] 2 | path = patcher/Snail202x 3 | url = https://github.com/SnailMath/Snail202x 4 | -------------------------------------------------------------------------------- /app_template/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.hhk 4 | *.bin 5 | 6 | # some temporary files under wsl 7 | *~ 8 | .*~ 9 | -------------------------------------------------------------------------------- /patches/mod_marker/README.md: -------------------------------------------------------------------------------- 1 | # `mod_marker` 2 | Replaces the "ClassPad II" string in the Version dialog with the string "hollyhock" to signify that the operating system has been patched. 3 | -------------------------------------------------------------------------------- /sdk/os/functions/input.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC Input_GetKeyState 0x8006FE58 4 | DEFINE_OS_FUNC Input_IsAnyKeyDown 0x8006FF00 5 | DEFINE_OS_FUNC GetInput 0x8007CE2C 6 | -------------------------------------------------------------------------------- /launcher/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("binary"); 2 | 3 | SECTIONS { 4 | start_addr = 0x8CFE0000; 5 | 6 | . = start_addr; 7 | 8 | .init start_addr : AT(start_addr) { 9 | *(.init) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sdk/os/functions/mem.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC free 0x800A76FC 4 | DEFINE_OS_FUNC malloc 0x800CFB00 5 | DEFINE_OS_FUNC memcpy 0x800A78AC 6 | DEFINE_OS_FUNC memset 0x800A7FC0 7 | -------------------------------------------------------------------------------- /app_template/start.s: -------------------------------------------------------------------------------- 1 | .section .init 2 | the_loading_address: 3 | 4 | mov.l main_addr, r0 5 | jmp @r0 6 | nop 7 | 8 | .align 2 9 | main_addr: 10 | .long _main 11 | load_addr: 12 | .long the_loading_address 13 | -------------------------------------------------------------------------------- /sdk/Doxyfile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = "hollyhock" 2 | 3 | INPUT = . 4 | FILE_PATTERNS = *.c *.cpp *.h *.hpp *.s 5 | RECURSIVE = YES 6 | EXTRACT_PRIVATE = NO 7 | 8 | GENERATE_HTML = YES 9 | HTML_OUTPUT = doc 10 | 11 | GENERATE_LATEX = NO 12 | -------------------------------------------------------------------------------- /sdk/os/functions/string.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC strcat 0x800A80D0 4 | DEFINE_OS_FUNC strchr 0x800A80F8 5 | DEFINE_OS_FUNC strcmp 0x800AB802 6 | DEFINE_OS_FUNC strcpy 0x800A811C 7 | DEFINE_OS_FUNC strlen 0x800A8128 8 | -------------------------------------------------------------------------------- /patches/menu_texts/README.md: -------------------------------------------------------------------------------- 1 | # `mod_marker` 2 | Replaces the "Imaginary Unit" string in the Settings dialog with the string "Hollyhock-2 Laucncher" to show that this function was replaced. (For all languages) ; Replaces the last line from the exam mode window with Hollyhock. 3 | -------------------------------------------------------------------------------- /sdk/os/functions/mcs.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC MCS_CreateFolder 0x8007689C 4 | DEFINE_OS_FUNC MCS_GetVariable 0x80076A82 5 | DEFINE_OS_FUNC MCS_List_Create 0x8012AD62 6 | DEFINE_OS_FUNC MCS_List_Set 0x8012B0C4 7 | DEFINE_OS_FUNC MCS_SetVariable 0x80076B20 8 | -------------------------------------------------------------------------------- /patches/mod_marker/Makefile: -------------------------------------------------------------------------------- 1 | ADDR:=808FCC10 2 | BIN_FILE:=mod_marker 3 | 4 | mod.part.txt: Makefile ${BIN_FILE}.bin README.md 5 | echo -n "${ADDR}: '" > mod.part.txt 6 | head -2 README.md| tail -1 >> mod.part.txt 7 | echo "" >> mod.part.txt 8 | xxd -ps ${BIN_FILE}.bin >> mod.part.txt 9 | echo "" >> mod.part.txt 10 | clean: 11 | rm mod.part.txt 12 | 13 | -------------------------------------------------------------------------------- /demos/README.md: -------------------------------------------------------------------------------- 1 | This folder contains the original demos by The6P4C. 2 | I wrote a few programs on my own for the hollyhock laucher. These are located somewhere on my github. 3 | You can download them as the final hhk files. 4 | 5 | My own version of __Tetris__: 6 | https://github.com/SnailMath/CPoldTetris 7 | 8 | My __HexEditor__: 9 | https://github.com/SnailMath/CPhexEditor 10 | -------------------------------------------------------------------------------- /launcher/init.s: -------------------------------------------------------------------------------- 1 | .section .init 2 | 3 | sts.l pr, @-r15 4 | 5 | ! Can't use the bsr instruction, because we get errors about the displacement not fitting in the 12-bit field. 6 | ! Gotta use jsr instead 7 | mov.l main_bootstrap_addr, r0 8 | jsr @r0 9 | nop 10 | 11 | lds.l @r15+, pr 12 | rts 13 | nop 14 | 15 | .align 4 16 | main_bootstrap_addr: 17 | .long _main_bootstrap 18 | -------------------------------------------------------------------------------- /sdk/include/stdlib.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | #define _GLIBCXX_INCLUDE_NEXT_C_HEADERS 3 | #include_next 4 | #undef _GLIBCXX_INCLUDE_NEXT_C_HEADERS 5 | 6 | #undef setenv 7 | #undef itoa 8 | 9 | extern "C++" 10 | { 11 | namespace std 12 | { 13 | using ::setenv; 14 | using ::itoa; 15 | } // namespace std 16 | 17 | using std::setenv; 18 | using std::itoa; 19 | } // extern C++ 20 | 21 | #endif // __cplusplus 22 | #include_next 23 | -------------------------------------------------------------------------------- /sdk/os/functions/lcd.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC LCD_ClearScreen 0x800394C0 4 | DEFINE_OS_FUNC LCD_GetPixel 0x80039328 5 | DEFINE_OS_FUNC LCD_GetSize 0x8002E140 6 | DEFINE_OS_FUNC LCD_GetVRAMAddress 0x8002E154 7 | DEFINE_OS_FUNC LCD_Refresh 0x8003733E 8 | DEFINE_OS_FUNC LCD_SetPixel 0x80039302 9 | DEFINE_OS_FUNC LCD_SetPixelFromPalette 0x8003931A 10 | DEFINE_OS_FUNC LCD_VRAMBackup 0x8002D3FA 11 | DEFINE_OS_FUNC LCD_VRAMRestore 0x8002D41A 12 | -------------------------------------------------------------------------------- /patcher/make.bat: -------------------------------------------------------------------------------- 1 | 2 | C: 3 | cd C:\MinGW\bin 4 | mkdir C:\Users\IEUser\Desktop\Snail2021\ 5 | rem gcc -o F:\Snail202x\Snail2021.exe F:\Snail202x\Snail2021.c -I \MinGW\msys\1.0\include -L \MinGW\msys\1.0\lib -lz -static 6 | gcc -o C:\Users\IEUser\Desktop\Snail2021\Snail2021.exe F:\hollyhock-2\patcher\Snail2021mod.c -lz -static 7 | del C:\Users\IEUser\Desktop\Snail2021\mod.txt 8 | 9 | pause 10 | 11 | copy C:\Users\IEUser\Desktop\Snail2021\Snail2021.exe F:\hollyhock-2\patcher\Snail2021.exe 12 | 13 | pause 14 | 15 | -------------------------------------------------------------------------------- /launcher/apps.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Apps { 4 | struct AppInfo { 5 | char fileName[100]; 6 | char path[200]; 7 | char name[100]; 8 | char description[100]; 9 | char author[100]; 10 | char version[100]; 11 | }; 12 | 13 | typedef void (*EntryPoint)(); 14 | 15 | const int MAX_APPS = 64; 16 | 17 | extern struct AppInfo g_apps[MAX_APPS]; 18 | extern int g_numApps; 19 | 20 | void LoadAppInfo(); 21 | EntryPoint RunApp(int i); 22 | }; 23 | -------------------------------------------------------------------------------- /launcher/bins.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Bins { 4 | struct AppInfo { 5 | char fileName[100]; 6 | char path[200]; 7 | char name[100]; 8 | char description[100]; 9 | char author[100]; 10 | char version[100]; 11 | }; 12 | 13 | typedef void (*EntryPoint)(); 14 | 15 | const int MAX_APPS = 64; 16 | 17 | extern struct AppInfo g_apps[MAX_APPS]; 18 | extern int g_numApps; 19 | 20 | void LoadAppInfo(); 21 | EntryPoint RunApp(int i); 22 | }; 23 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/string.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief String manipulation functions. 4 | * 5 | * For documentation, see the C standard library. 6 | */ 7 | #pragma once 8 | 9 | extern "C" 10 | char *strcat(char *dest, const char *src); 11 | 12 | extern "C" 13 | const char *strchr(const char *str, char c); 14 | 15 | extern "C" 16 | int strcmp(const char *str1, const char *str2); 17 | 18 | extern "C" 19 | char *strcpy(char *destination, const char *source); 20 | 21 | extern "C" 22 | int strlen(const char *str); 23 | -------------------------------------------------------------------------------- /app_template/linker_bin.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_main); 2 | 3 | SECTIONS { 4 | start_address = 0x8CFF0000; 5 | .init start_address : AT(start_address) { 6 | *(.init) 7 | } 8 | info_address = 0x8CFF0010; 9 | . = info_address; 10 | .hollyhock_name : { 11 | *(.hollyhock_name) 12 | } 13 | .hollyhock_description : { 14 | *(.hollyhock_description) 15 | } 16 | .hollyhock_author : { 17 | *(.hollyhock_author) 18 | } 19 | .hollyhock_version : { 20 | *(.hollyhock_version) 21 | } 22 | .text : { 23 | *(.text) 24 | } 25 | } 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /sdk/cxx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void *operator new(size_t size) { 5 | return malloc(size); 6 | } 7 | 8 | void *operator new[](size_t size) { 9 | return malloc(size); 10 | } 11 | 12 | void operator delete(void *p) { 13 | free(p); 14 | } 15 | 16 | void operator delete(void *p, size_t size [[maybe_unused]]) { 17 | free(p); 18 | } 19 | 20 | void operator delete[](void *p) { 21 | free(p); 22 | } 23 | 24 | void operator delete[](void *p, size_t size [[maybe_unused]]) { 25 | free(p); 26 | } 27 | -------------------------------------------------------------------------------- /sdk/os/functions/gui.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC GUIButton_ctor 0x800F84F6 4 | DEFINE_OS_FUNC GUIDropDownMenu_ctor 0x800FBEAA 5 | DEFINE_OS_FUNC GUIDropDownMenuItem_ctor 0x800FAB38 6 | DEFINE_OS_FUNC GUIDialog_ctor 0x800D57F6 7 | DEFINE_OS_FUNC GUILabel_ctor 0x80109980 8 | DEFINE_OS_FUNC GUILongLabel_ctor 0x8011C078 9 | DEFINE_OS_FUNC GUIRadioButton_ctor 0x800F9964 10 | DEFINE_OS_FUNC GUITextBox_ctor 0x80117544 11 | DEFINE_OS_FUNC GUI_DisplayMessageBox 0x80146A38 12 | DEFINE_OS_FUNC GUI_DisplayMessageBox_Internal 0x800B7914 13 | -------------------------------------------------------------------------------- /sdk/os/functions/debug.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC Debug_GetCursorPosition 0x8002E448 4 | DEFINE_OS_FUNC Debug_Printf 0x8002DBC8 5 | DEFINE_OS_FUNC Debug_PrintNumberHex_Byte 0x800944A0 6 | DEFINE_OS_FUNC Debug_PrintNumberHex_Dword 0x80094514 7 | DEFINE_OS_FUNC Debug_PrintNumberHex_Nibble 0x80094466 8 | DEFINE_OS_FUNC Debug_PrintNumberHex_Word 0x800944C8 9 | DEFINE_OS_FUNC Debug_PrintString 0x8002DA0C 10 | DEFINE_OS_FUNC Debug_PrintString 0x8002DA0C 11 | DEFINE_OS_FUNC Debug_SetCursorPosition 0x8002E430 12 | DEFINE_OS_FUNC Debug_WaitKey 0x80094380 13 | -------------------------------------------------------------------------------- /sdk/os/functions/file.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | DEFINE_OS_FUNC close 0x80057912 4 | DEFINE_OS_FUNC findClose 0x8005A8BA 5 | DEFINE_OS_FUNC findNext 0x8005A5F0 6 | DEFINE_OS_FUNC findFirst 0x8005A2AC 7 | DEFINE_OS_FUNC fstat 0x8005798E 8 | DEFINE_OS_FUNC getAddr 0x80057C6A 9 | DEFINE_OS_FUNC lseek 0x80057A96 10 | DEFINE_OS_FUNC mkdir 0x80057814 11 | DEFINE_OS_FUNC open 0x80057854 12 | DEFINE_OS_FUNC read 0x800578A2 13 | DEFINE_OS_FUNC remove 0x8005794E 14 | DEFINE_OS_FUNC rename 0x80057A06 15 | DEFINE_OS_FUNC stat 0x800579C2 16 | DEFINE_OS_FUNC write 0x800578DA 17 | -------------------------------------------------------------------------------- /doc/user/using.md: -------------------------------------------------------------------------------- 1 | # Using add-on software with your fx-CP400 2 | 3 | After you've [patched](patching.md) your calculator's firmware (and copied the launcher), it's easy to run add-on software. 4 | 5 | Simply take the `.hhk` file of the add-on, and place it into the root directory of your calculator's flash memory. Open the launcher (currently in the System app, accessed through the System > Imaginary Unit menu) and select the application you wish to launch from the list. 6 | 7 | Tap 'Run' to launch the application. 8 | 9 | ![The Hollyhock Launcher opened, with the app "Tetris" selected from the drop down menu.](using_launcher.png) 10 | -------------------------------------------------------------------------------- /sdk/include/appdef.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // volatile so the compiler doesn't optimise the section out 4 | #define HOLLYHOCK_SECTION_STRING(name, str) \ 5 | __attribute__ ((section(".hollyhock_" #name))) \ 6 | volatile char hollyhock_ ## name [] = str; 7 | 8 | #define APP_NAME(app_name) \ 9 | HOLLYHOCK_SECTION_STRING(name, app_name) 10 | #define APP_DESCRIPTION(app_description) \ 11 | HOLLYHOCK_SECTION_STRING(description, app_description) 12 | #define APP_AUTHOR(app_author) \ 13 | HOLLYHOCK_SECTION_STRING(author, app_author) 14 | #define APP_VERSION(app_version) \ 15 | HOLLYHOCK_SECTION_STRING(version, app_version) 16 | -------------------------------------------------------------------------------- /sdk/os/gui/radioButton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Creates a radio button. 5 | * 6 | * @param x,y The position to place the radio button at, in pixels and relative 7 | * to the top left of the display. 8 | * @param[in] text The text to display to the right-hand side of the radio 9 | * button. 10 | * @param flags A bitfield of flags specified by bitwise-ORing members of 11 | * the @ref Flag enum. 12 | */ 13 | GUIRadioButton::GUIRadioButton( 14 | int x, int y, 15 | const char *text, 16 | int flags 17 | ) { 18 | m_wrapped = GUIRadioButton_ctor( 19 | 0, 20 | x, y, 21 | text, 22 | 0, 23 | flags, 24 | 0, 0 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /launcher/main_bootstrap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void main(); 4 | 5 | extern "C" 6 | void *main_bootstrap() { 7 | main(); 8 | 9 | // Returning the output of this function is necessary to allow the 10 | // OS to resume execution once our code has finished running. 11 | // Removing this line (i.e. not returning the output of this function) will 12 | // cause the GC to lock up when the program's execution finishes. 13 | // This is only because of the current location of the file execution stub. 14 | return GUI_DisplayMessageBox_Internal( 15 | 0, 16 | "Program", 17 | "run.bin", "The program has finished execution.", 18 | 0, false 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/radioButton.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "util.hpp" 3 | 4 | class GUIRadioButton : public GUIElement { 5 | public: 6 | enum Flag : int { 7 | /// Causes the radio button to be selected by default. 8 | FlagSelected = 1 << 2, 9 | 10 | /// Makes the radio button interactive. 11 | FlagEnabled = 1 << 15 12 | }; 13 | 14 | GUIRadioButton( 15 | int x, int y, 16 | const char *text, 17 | int flags 18 | ); 19 | }; 20 | 21 | /// @cond INTERNAL 22 | extern "C" 23 | void *GUIRadioButton_ctor( 24 | void *radioButton, 25 | int x, int y, 26 | const char *text, 27 | int unknown0, 28 | int flags, 29 | void *font, 30 | int unknown2 31 | ); 32 | /// @endcond 33 | -------------------------------------------------------------------------------- /patches/backslash/mod.part.txt: -------------------------------------------------------------------------------- 1 | 806321C8: ' Replace the yen sign with the '\' so that file path are displayed correctly using Debug_PrintString() and Debug_Printf() 2 | 3 | FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 4 | FFFF 0000 FFFF FFFF FFFF FFFF FFFF FFFF 5 | FFFF 0000 FFFF FFFF FFFF FFFF FFFF FFFF 6 | FFFF FFFF 0000 FFFF FFFF FFFF FFFF FFFF 7 | FFFF FFFF 0000 FFFF FFFF FFFF FFFF FFFF 8 | FFFF FFFF FFFF 0000 FFFF FFFF FFFF FFFF 9 | FFFF FFFF FFFF 0000 FFFF FFFF FFFF FFFF 10 | FFFF FFFF FFFF FFFF 0000 FFFF FFFF FFFF 11 | FFFF FFFF FFFF FFFF 0000 FFFF FFFF FFFF 12 | FFFF FFFF FFFF FFFF FFFF 0000 FFFF FFFF 13 | FFFF FFFF FFFF FFFF FFFF 0000 FFFF FFFF 14 | FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 15 | 16 | -------------------------------------------------------------------------------- /patches/file_loader/Makefile: -------------------------------------------------------------------------------- 1 | ADDR:=80128018 2 | 3 | AS:=sh4-elf-as 4 | AS_FLAGS:= 5 | 6 | OBJCOPY:=sh4-elf-objcopy 7 | OBJCOPY_FLAGS:=-O binary 8 | 9 | all: mod.part.txt Makefile 10 | 11 | clean: 12 | rm -f file_loader.o file_loader.bin mod.part.txt 13 | 14 | mod.part.txt: file_loader.bin Makefile 15 | echo -n "${ADDR}: '" > mod.part.txt 16 | head -2 README.md| tail -1 >> mod.part.txt 17 | echo "" >> mod.part.txt 18 | xxd -ps file_loader.bin >> mod.part.txt 19 | echo "" >> mod.part.txt 20 | 21 | file_loader.bin: file_loader.o 22 | $(OBJCOPY) $(OBJCOPY_FLAGS) file_loader.o file_loader.bin 23 | 24 | file_loader.o: file_loader.s 25 | $(AS) $< -o $@ $(AS_FLAGS) 26 | 27 | .PHONY: all clean 28 | -------------------------------------------------------------------------------- /patcher/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: Snail2021mod.c 3 | # 4 | # Use make.bat under windows to compile the windows executable. 5 | # 6 | 7 | Snail2021mod.c: Snail202x/Snail2021.c ../patches/mod.txt Makefile 8 | #this will output the file Snail2021mod.c which can be compiled under 9 | #Windows using MinGW. 10 | echo -n "#define MOD_TXT \"" > Snail2021mod.c 11 | cat ../patches/mod.txt | tr '\\\n' '/\\' | sed "s/\\\/\\\r\\\n/g" | sed "s/\"/\\\\\"/g" >> Snail2021mod.c 12 | echo "\"" >> Snail2021mod.c 13 | cat Snail202x/Snail2021.c >> Snail2021mod.c 14 | 15 | ../patches/mod.txt: 16 | cd ../patches; make; cd ../patcher 17 | 18 | clean: 19 | rm Snail2021mod.c Snail2021.exe 20 | cd ../patches; make clean; cd ../patcher 21 | -------------------------------------------------------------------------------- /launcher/execs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Execs { 4 | struct ExecInfo { 5 | char fileName[9]; 6 | //char path[200]; 7 | char name[100]; 8 | char description[100]; 9 | char author[100]; 10 | char version[100]; 11 | //char* fp; //this throws linker error: undefined reference to `___movmem_i4_even' 12 | unsigned char fp[4]; //this is the address of the first byte. A pointer throws an assembler error... This has to work. 13 | }; 14 | 15 | typedef void (*EntryPoint)(); 16 | 17 | const int MAX_EXECS = 64; 18 | 19 | extern struct ExecInfo g_execs[MAX_EXECS]; 20 | extern int g_numExecs; 21 | 22 | void LoadExecInfo(); 23 | EntryPoint RunExec(int i); 24 | }; 25 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/button.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "util.hpp" 4 | 5 | class GUIButton : public GUIElement { 6 | public: 7 | enum Flag : int { 8 | /// Allows the button to be pressed. 9 | FlagEnabled = 1 << 15 10 | }; 11 | 12 | GUIButton( 13 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 14 | const char *text, 15 | uint16_t eventID 16 | ); 17 | GUIButton( 18 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 19 | const char *text, 20 | uint16_t eventID, int flags 21 | ); 22 | }; 23 | 24 | /// @cond INTERNAL 25 | extern "C" 26 | void *GUIButton_ctor( 27 | void *button, 28 | uint16_t bounds[4], 29 | const char *text, 30 | uint16_t eventID, int unk0, int unk1 31 | ); 32 | /// @endcond 33 | -------------------------------------------------------------------------------- /sdk/os/functions/serial.s: -------------------------------------------------------------------------------- 1 | .include "os/functions/_util.inc" 2 | 3 | 4 | !Syscall index and address on the cg50 5 | DEFINE_OS_FUNC Serial_Open 0x8008533e !1bb7 8030da30 6 | DEFINE_OS_FUNC Serial_Close 0x800853e6 !1bb8 8030dace 7 | DEFINE_OS_FUNC Serial_ReadSingle 0x8008547a !1bb9 8030db82 8 | DEFINE_OS_FUNC Serial_Read 0x800854b8 !1bba 8030dbc6 9 | DEFINE_OS_FUNC Serial_Peek 0x80084f22 !1bbb 8030dc86 10 | DEFINE_OS_FUNC Serial_WriteSingle 0x800855d0 !1bbc 8030dcce 11 | DEFINE_OS_FUNC Serial_WriteUnbuffered 0x80085616 !1bbd 8030dd14 12 | DEFINE_OS_FUNC Serial_Write 0x80085652 !1bbe 8030dd50 13 | DEFINE_OS_FUNC Serial_PollRX 0x800856b0 !1bbf 8030ddae 14 | DEFINE_OS_FUNC Serial_PollTX 0x800856c4 !1bc0 8030ddc2 15 | DEFINE_OS_FUNC Serial_ClearRX 0x800856d8 !1bc1 8030ddd6 16 | DEFINE_OS_FUNC Serial_ClearTX 0x800856fe !1bc2 8030ddfc 17 | DEFINE_OS_FUNC Serial_IsOpen 0x8008587c !1bc6 8030df76 18 | 19 | -------------------------------------------------------------------------------- /sdk/Makefile: -------------------------------------------------------------------------------- 1 | DOXYGEN=doxygen 2 | 3 | AS:=sh4-elf-as 4 | AS_FLAGS:= 5 | 6 | CC:=sh4-elf-g++ 7 | CC_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -m4-nofpu -Wall -Wextra -O2 -I include/ 8 | 9 | # -r flag so the sdk.o file can be linked with the user's application object 10 | # files: generates a relocatable object file 11 | LD:=sh4-elf-ld 12 | LD_FLAGS:=-nostdlib -r 13 | 14 | AS_SOURCES:=$(wildcard *.s) $(wildcard **/*.s) $(wildcard **/**/*.s) 15 | CC_SOURCES:=$(wildcard *.cpp) $(wildcard **/*.cpp) $(wildcard **/**/*.cpp) 16 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.cpp=.o) 17 | 18 | all: sdk.o Makefile 19 | 20 | docs: 21 | $(DOXYGEN) 22 | 23 | clean: 24 | rm -f $(OBJECTS) sdk.o 25 | 26 | clean_docs: 27 | rm -rf doc/ 28 | 29 | sdk.o: $(OBJECTS) 30 | $(LD) -o $@ $(LD_FLAGS) $(OBJECTS) 31 | 32 | %.o: %.s 33 | $(AS) $< -o $@ $(AS_FLAGS) 34 | 35 | %.o: %.cpp 36 | $(CC) -c $< -o $@ $(CC_FLAGS) 37 | 38 | .PHONY: all docs clean clean_docs 39 | -------------------------------------------------------------------------------- /sdk/calc/getKey.s: -------------------------------------------------------------------------------- 1 | .align 2 2 | .global _getKey 3 | .type _getKey, @function 4 | 5 | _getKey: 6 | mov.l GetKey_keyAddr, r2 !Load the keyboard addr 7 | mov.l GetKey_and, r3 8 | mov.w @r2, r1 !Load first word in r1 9 | shll16 r1 10 | add #2, r2 11 | mov.w @r2, r0 !Load 2nd word in r0 12 | and r3, r0 !Just use the lower 16 bits 13 | add r0, r1 !Add both in r1 14 | mov.l r1, @r4 !Store in first argument 15 | add #2, r2 16 | mov.w @r2, r1 !Load 3rd word in r1 17 | shll16 r1 18 | add #2, r2 19 | mov.w @r2, r0 !Load 4th word in r0 20 | and r3, r0 !Just use the lower 16 bits 21 | add r0, r1 !Add both in r1 22 | mov.l r1, @r5 !Store in 2nd argument 23 | add #2, r2 24 | mov.w @r2, r1 !Load 3rd because otherwise the hardware doesn't work 25 | add #2, r2 26 | rts 27 | mov.w @r2, r1 !Load 4th because of the hardware. 28 | 29 | .align 2 30 | GetKey_keyAddr: 31 | .long 0xa44B0000 32 | GetKey_and: 33 | .long 0x0000FFFF 34 | 35 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/longLabel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "util.hpp" 3 | 4 | /// @private 5 | struct GUILongLabel_Wrapped_VTable { 6 | VTABLE_FAKE_ENTRY(9, 0); 7 | 8 | VTableFunction Refresh; 9 | 10 | VTABLE_FAKE_ENTRY(33, 1); 11 | 12 | // Args: text 13 | VTableFunction SetText; 14 | }; 15 | 16 | /// @private 17 | struct GUILongLabel_Wrapped { 18 | uint8_t unknown0[0x4C]; 19 | 20 | struct GUILongLabel_Wrapped_VTable *vtable; 21 | 22 | uint8_t unknown1[0x5C]; 23 | }; 24 | static_assert(sizeof(struct GUILongLabel_Wrapped) == 0xAC); 25 | 26 | class GUILongLabel : public GUIElement { 27 | public: 28 | GUILongLabel( 29 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 30 | const char *text 31 | ); 32 | 33 | void SetText(const char *text); 34 | void Refresh(); 35 | }; 36 | 37 | /// @cond INTERNAL 38 | extern "C" 39 | void *GUILongLabel_ctor(void *longLabel, uint16_t bounds[4], int unk0, int unk1, const char *text, int unk2); 40 | /// @endcond 41 | -------------------------------------------------------------------------------- /sdk/os/gui/longLabel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Creates a long label, capable of displaying multiple lines of text. 5 | * 6 | * @param leftX,topY,rightX,bottomY The coordinates of the bounding box of the 7 | * long label, in screen pixels relative to the top left of the display. 8 | * @param[in] text The text for the long label to display. 9 | */ 10 | GUILongLabel::GUILongLabel( 11 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 12 | const char *text 13 | ) { 14 | uint16_t bounds[] = {leftX, topY, rightX, bottomY}; 15 | m_wrapped = GUILongLabel_ctor(0, bounds, 0, 0x441, text, 0x3E8); 16 | } 17 | 18 | /** 19 | * Sets the long label's text. 20 | * 21 | * @param[in] text The new string for the long label. 22 | */ 23 | void GUILongLabel::SetText(const char *text) { 24 | GetWrapped()->vtable->SetText( 25 | m_wrapped, 26 | text 27 | ); 28 | } 29 | 30 | /** 31 | * Refreshes the long label, redrawing it. 32 | */ 33 | void GUILongLabel::Refresh() { 34 | GetWrapped()->vtable->Refresh(m_wrapped); 35 | } 36 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/label.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "util.hpp" 4 | 5 | class GUILabel : public GUIElement { 6 | public: 7 | enum Flag : int { 8 | /// Enables displaying the background color of the label. 9 | FlagBackground = 1 << 0, 10 | 11 | /** 12 | * Allows the label to be selected/brought into focus. 13 | * 14 | * When the label is selected, the text and background colors switch. 15 | */ 16 | FlagSelectable = 1 << 15 17 | }; 18 | 19 | GUILabel(int x, int y, const char *text); 20 | GUILabel( 21 | int x, int y, 22 | const char *text, 23 | int flags, 24 | uint16_t *textColor, uint16_t *backgroundColor 25 | ); 26 | GUILabel( 27 | int x, int y, 28 | const char *text, 29 | int flags, 30 | uint16_t *textColor, uint16_t *backgroundColor, 31 | bool showShadow, uint16_t shadowColor 32 | ); 33 | }; 34 | 35 | /// @cond INTERNAL 36 | extern "C" 37 | void *GUILabel_ctor( 38 | void *label, 39 | int x, int y, 40 | const char *text, 41 | int unknown0, 42 | int flags, 43 | void *font, 44 | uint16_t *textColor, uint16_t *backgroundColor, 45 | bool showShadow, uint16_t shadowColor, 46 | int unknown1 47 | ); 48 | /// @endcond 49 | -------------------------------------------------------------------------------- /patches/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | cd mod_marker; make; cd .. 3 | #don't build the backslash files, because I made that mod.part.txt by hand... 4 | cd menu_texts; make; cd .. 5 | cd file_loader; make; cd .. 6 | # 7 | # You have to make sure to cat the files in the right order by hand. 8 | # The patcher works its way from the start of the file, so the mod.txt 9 | # file needs the modifications in the correct order. 10 | echo "'ver02.01.2000\n" > mod.txt #add the version of the os update we 11 | #want to modify. The user gets notified by Snail2021.exe if they try 12 | #to use the wrong version of the firmware. 13 | cat file_loader/mod.part.txt >> mod.txt #this is first 80128018 14 | echo "*\n" >> mod.txt 15 | cat backslash/mod.part.txt >> mod.txt #this is second 806321C8 16 | echo "*\n" >> mod.txt 17 | cat menu_texts/mod.part.txt >> mod.txt #this is second 808B5CD8 18 | echo "*\n" >> mod.txt 19 | cat mod_marker/mod.part.txt >> mod.txt #this is second 808FCC10 20 | 21 | 22 | clean: 23 | cd mod_marker; make clean; cd .. 24 | #don't remove the backslash files, because I made that mod.part.txt by hand... 25 | cd menu_texts; make clean; cd .. 26 | cd file_loader; make clean; cd .. 27 | rm mod.txt 28 | 29 | .PHONY: all clean 30 | -------------------------------------------------------------------------------- /sdk/include/sdk/cpu/ubc.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief UBC (User Break Controller). 4 | */ 5 | #pragma once 6 | #include 7 | 8 | /// Match condition setting register 0. 9 | volatile uint32_t &UBC_REG_CBR0 = *reinterpret_cast(0xFF200000); 10 | /// Match operation setting register 0. 11 | volatile uint32_t &UBC_REG_CRR0 = *reinterpret_cast(0xFF200004); 12 | /// Match address setting register 0. 13 | volatile uint32_t &UBC_REG_CAR0 = *reinterpret_cast(0xFF200008); 14 | /// Match address mask setting register 0. 15 | volatile uint32_t &UBC_REG_CAMR0 = *reinterpret_cast(0xFF20000C); 16 | /// Break control register. 17 | volatile uint32_t &UBC_REG_CBCR = *reinterpret_cast(0xFF200620); 18 | 19 | /// CBR.ID offset (bits). 20 | const uint32_t UBC_CBR_ID = 4; 21 | /// CBR.RW offset (bits). 22 | const uint32_t UBC_CBR_RW = 1; 23 | /// CBR.CE offset (bits). 24 | const uint32_t UBC_CBR_CE = 0; 25 | 26 | /// CRR.RESERVED offset (bits). 27 | const uint32_t UBC_CRR_RESERVED = 13; 28 | /// CRR.PCB offset (bits). 29 | const uint32_t UBC_CRR_PCB = 1; 30 | /// CRR.BIE offset (bits). 31 | const uint32_t UBC_CRR_BIE = 0; 32 | 33 | /// CBCR.UBDE offset (bits). 34 | const uint32_t UBC_CBCR_UBDE = 0; 35 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/textBox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "util.hpp" 4 | 5 | /// @private 6 | struct GUITextBox_Wrapped_VTable { 7 | VTABLE_FAKE_ENTRY(32, 0); 8 | 9 | // Args: text 10 | VTableFunction SetText; 11 | }; 12 | 13 | /// @private 14 | struct GUITextBox_Wrapped { 15 | uint8_t unknown0[0x4C]; 16 | 17 | struct GUITextBox_Wrapped_VTable *vtable; 18 | 19 | uint8_t unknown1[0x4]; 20 | 21 | const char *text; 22 | 23 | uint8_t unknown2[0x48]; 24 | }; 25 | static_assert(sizeof(struct GUITextBox_Wrapped) == 0xA0); 26 | 27 | class GUITextBox : public GUIElement { 28 | public: 29 | enum Flag { 30 | /// Enables drawing the text box's outline and background. 31 | FlagDrawBox = 1 << 3, 32 | 33 | /// Allows the contents of the text box to be modified. 34 | FlagEditable = 1 << 8 35 | }; 36 | 37 | GUITextBox( 38 | int x, int y, int width, 39 | int maxLength, bool countLengthByBytes 40 | ); 41 | GUITextBox( 42 | int x, int y, int width, 43 | const char *text, int maxLength, bool countLengthByBytes 44 | ); 45 | 46 | const char *GetText(); 47 | void SetText(const char *text); 48 | }; 49 | 50 | /// @cond INTERNAL 51 | extern "C" 52 | struct GUITextBox_Wrapped *GUITextBox_ctor( 53 | void *textBox, 54 | int x, int y, int width, 55 | const char *text, 56 | int unknown0, 57 | int flags, 58 | int maxLength, bool countLengthByBytes 59 | ); 60 | /// @endcond 61 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/mem.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions used for modifying and allocating memory. 4 | * 5 | * Similar to the memory functions provided by the C standard library. 6 | */ 7 | 8 | #pragma once 9 | #include 10 | 11 | /** 12 | * Frees memory allocated by @ref malloc, allowing it to be reused. 13 | * 14 | * @param ptr The pointer to the allocated region of memory to free. 15 | */ 16 | extern "C" 17 | void free(void *ptr); 18 | 19 | /** 20 | * Allocates @p size bytes of memory. 21 | * 22 | * The current application crashes if the memory cannot be allocated. 23 | * 24 | * @param size The number of bytes of memory to allocate. 25 | * @return A pointer to the allocated memory region. 26 | */ 27 | extern "C" 28 | void *malloc(uint32_t size); 29 | 30 | /** 31 | * Copies one region of memory to another. Equivalent to the C standard library 32 | * function with the same name. 33 | * 34 | * Copies @p num bytes from @p source to @p destination. 35 | * 36 | * @param[out] destination A pointer to the destination of the copy. 37 | * @param[in] source A pointer to the source for the copy. 38 | * @param num The number of bytes to copy. 39 | * @return @p destination 40 | */ 41 | extern "C" 42 | void *memcpy(void *destination, const void *source, int num); 43 | 44 | /** 45 | * Sets a region of memory to a specific value. Equivalent to the C standard 46 | * library function with the same name. 47 | * 48 | * Fills the region pointed to by @p ptr with @p num bytes of value @p value 49 | * (zero-extended to a byte). 50 | * 51 | * @param[out] ptr A pointer to the region of memory to fill. 52 | * @param value The value to fill the memory region with. 53 | * @param num The number of bytes to fill. 54 | * @return @p ptr 55 | */ 56 | extern "C" 57 | void *memset(void *ptr, int value, int num); 58 | -------------------------------------------------------------------------------- /app_template/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | * Fill this section in with some information about your app. 8 | * All fields are optional - so if you don't need one, take it out. 9 | */ 10 | APP_NAME("My app name") 11 | APP_DESCRIPTION("A short description of my app") 12 | APP_AUTHOR("My name") 13 | APP_VERSION("1.0.2") 14 | 15 | extern "C" 16 | void main() { 17 | calcInit(); //backup screen and init some variables 18 | 19 | // Put your app's code here! 20 | 21 | //Example for fillScreen(color); 22 | fillScreen(color(0,64,0)); 23 | 24 | //Example for Debug_Printf(x,y,invert_color,0,format_string) //(small text) 25 | Debug_Printf(10,1,false,0,"HelloWorld%d",42); 26 | 27 | //Example for Debug_PrintString(string, invert_color) //(big text) 28 | Debug_SetCursorPosition(13,1); 29 | Debug_PrintString("HelloWorld",0); 30 | 31 | //use this command to actually update the screen 32 | LCD_Refresh(); 33 | 34 | //Example for setPixel(x,y,color) 35 | for (int x=0; x<256;x++){ 36 | for (int y=0; y<256; y++){ 37 | setPixel(50+x,250+y, color(x,y,0) ); 38 | } 39 | } 40 | 41 | //Example for triangle(x0,y0,x1,y1,x2,y2,colorFill,colorLine); 42 | triangle(10,20,40,250,300,100,color(0,255,0),color(0,0,255)); 43 | 44 | //Example for line(x1,y1,x2,y2,color); 45 | line(100,30,290,500,color(255,0,0)); //Use RGB color 46 | line(110,30,300,500,0b1111100000000000); //Or use 565 color 47 | 48 | //Don't forget to do LCD_Refresh after setPixel(); line(); and triangle(); 49 | LCD_Refresh(); 50 | 51 | //Example for getKey 52 | while(true){ 53 | uint32_t key1, key2; //First create variables 54 | getKey(&key1, &key2); //then read the keys 55 | if(testKey(key1, key2, KEY_CLEAR)){ //Use testKey() to test if a specific key is pressed 56 | break; 57 | } 58 | } 59 | 60 | calcEnd(); //restore screen and do stuff 61 | } 62 | -------------------------------------------------------------------------------- /launcher/Makefile: -------------------------------------------------------------------------------- 1 | ifndef SDK_DIR 2 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 3 | endif 4 | 5 | AS:=sh4-elf-as 6 | AS_FLAGS:= 7 | 8 | CC:=sh4-elf0-gcc 9 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 10 | 11 | CXX:=sh4-elf-g++ 12 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 13 | 14 | LD:=sh4-elf-ld 15 | LD_FLAGS:=-nostdlib --no-undefined 16 | 17 | READELF:=sh4-elf-readelf 18 | OBJCOPY:=sh4-elf-objcopy 19 | 20 | AS_SOURCES:=$(wildcard *.s) 21 | CC_SOURCES:=$(wildcard *.c) 22 | CXX_SOURCES:=$(wildcard *.cpp) 23 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 24 | 25 | all: run.bin Makefile 26 | 27 | clean: 28 | rm -f $(OBJECTS) run.bin 29 | 30 | run.bin: $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 31 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 32 | 33 | # We're not actually building sdk.o, just telling the user they need to do it 34 | # themselves. Just using the target to trigger an error when the file is 35 | # required but does not exist. 36 | $(SDK_DIR)/sdk.o: 37 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 38 | 39 | %.o: %.s 40 | $(AS) $< -o $@ $(AS_FLAGS) 41 | 42 | %.o: %.c 43 | $(CC) $< -o $@ $(CC_FLAGS) 44 | 45 | # Break the build if global constructors are present: 46 | # Read the sections from the object file (with readelf -S) and look for any 47 | # called .ctors - if they exist, give the user an error message, delete the 48 | # object file (so that on subsequent runs of make the build will still fail) 49 | # and exit with an error code to halt the build. 50 | %.o: %.cpp 51 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 52 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 53 | 54 | .PHONY: all clean 55 | -------------------------------------------------------------------------------- /sdk/os/gui/dropDown.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Creates a drop down menu. 5 | * 6 | * The bounds do not effect the height of the normally visible part of the menu, 7 | * but are used to set the height of the menu which appears when the drop down 8 | * menu is selected. 9 | * 10 | * @param leftX,topY,rightX,bottomY The coordinates of the bounding box of the 11 | * drop down menu, in screen pixels relative to the top left of the display. 12 | * @param eventID The ID to use for events relating to this drop down menu. 13 | */ 14 | GUIDropDownMenu::GUIDropDownMenu( 15 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 16 | uint16_t eventID 17 | ) { 18 | uint16_t bounds[] = {leftX, topY, rightX, bottomY}; 19 | m_wrapped = GUIDropDownMenu_ctor( 20 | nullptr, bounds, eventID, 8 21 | ); 22 | } 23 | 24 | /** 25 | * Sets the visibility of the scroll bar. 26 | * 27 | * @param visibility The visibility of the scroll bar. 28 | */ 29 | void GUIDropDownMenu::SetScrollBarVisibility(ScrollBarVisibility visibility) { 30 | GetWrapped()->vtable->SetScrollBarVisibility( 31 | m_wrapped, 32 | visibility 33 | ); 34 | } 35 | 36 | /** 37 | * Adds an item to the drop down menu. 38 | * 39 | * @param The item to add. 40 | */ 41 | void GUIDropDownMenu::AddMenuItem(GUIDropDownMenuItem &dropDownMenuItem) { 42 | GetWrapped()->vtable->AddMenuItem( 43 | m_wrapped, 44 | dropDownMenuItem.GetWrapped(), 1 45 | ); 46 | } 47 | 48 | /** 49 | * Creates a item for a drop down menu (@ref GUIDropDownMenu). 50 | * 51 | * @param text The item's text. 52 | * @param index A unique 1-based index for the item. 53 | * @param flags A bitfield of flags specified by bitwise-ORing members of the 54 | * @ref Flag enum. 55 | */ 56 | GUIDropDownMenuItem::GUIDropDownMenuItem(const char *text, int index, int flags) { 57 | m_wrapped = GUIDropDownMenuItem_ctor( 58 | 0, 0, 0, text, index, flags, 0 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/serial.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions used for sending and receiving serial data. 4 | */ 5 | 6 | // This works exactly the same as the calls on the cg50. Read more here: 7 | // https://prizm.cemetech.net/index.php?title=Category:Syscalls:Serial 8 | // These implementations are similar to: 9 | // https://github.com/Jonimoose/libfxcg/blob/master/include/fxcg/serial.h 10 | 11 | #pragma once 12 | #include 13 | 14 | /** 15 | * Opens the 3-pin serial port with the specified parameters. 16 | * see https://prizm.cemetech.net/index.php?title=Serial_Open for more info. 17 | * mode: 18 | * 0 always 0 19 | * 1 bit rate 0=300, 5=9600, 9=115200 20 | * 2 parity 0=none, 1=odd, 2=even 21 | * 3 data length 0=8bit, 1=7bit 22 | * 4 stop bits 0=1bit, 1=2bit 23 | * 5 always 0 24 | * Returns: 25 | * 0 success 26 | * 3 already open 27 | * 28 | * @param mode The mode of the serial port. 29 | */ 30 | extern "C" 31 | int Serial_Open(unsigned char *mode); 32 | 33 | extern "C" 34 | int Serial_IsOpen(void); 35 | 36 | extern "C" 37 | int Serial_Close(int mode); 38 | 39 | extern "C" 40 | int Serial_Read(unsigned char *out, int sz, short *count); 41 | 42 | extern "C" 43 | int Serial_ReadSingle(unsigned char *out); 44 | 45 | extern "C" 46 | int Serial_Peek(int idx, unsigned char *out); 47 | 48 | extern "C" 49 | int Serial_PollRX(void); 50 | 51 | extern "C" 52 | int Serial_ClearRX(void); 53 | 54 | /** 55 | * Sends a number of bytes through the 3-pin serial port, by putting them in the transmit buffer. 56 | * see https://prizm.cemetech.net/index.php?title=Serial_Write 57 | * 58 | * @param buf pointer to buffer containing the bytes to transmit 59 | * @count amount of bytes to transmit from buf 60 | */ 61 | extern "C" 62 | int Serial_Write(const unsigned char *buf, int count); 63 | 64 | extern "C" 65 | int Serial_WriteSingle(unsigned char x); 66 | 67 | extern "C" 68 | int Serial_WriteUnbuffered(unsigned char x); 69 | 70 | extern "C" 71 | int Serial_PollTX(void); 72 | 73 | extern "C" 74 | int Serial_ClearTX(void); 75 | 76 | 77 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/dropDown.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "util.hpp" 4 | 5 | class GUIDropDownMenuItem; 6 | 7 | /// @private 8 | struct GUIDropDownMenu_Wrapped_VTable { 9 | VTABLE_FAKE_ENTRY(5, 0); 10 | 11 | // Args: dropDownMenuItem, unknown0 12 | // unknown0 - always pass 1 13 | VTableFunction AddMenuItem; 14 | 15 | VTABLE_FAKE_ENTRY(28, 1); 16 | 17 | // Args: visibility 18 | VTableFunction SetScrollBarVisibility; 19 | }; 20 | 21 | /// @private 22 | struct GUIDropDownMenu_Wrapped { 23 | uint8_t unknown0[0x4C]; 24 | 25 | struct GUIDropDownMenu_Wrapped_VTable *vtable; 26 | }; 27 | 28 | class GUIDropDownMenu : public GUIElement { 29 | public: 30 | enum ScrollBarVisibility : uint32_t { 31 | /// Always hide the scroll bar. 32 | ScrollBarHidden = 0, 33 | /// Always show the scroll bar. 34 | ScrollBarAlwaysVisible = 1, 35 | /// Only show the scroll bar when required. 36 | ScrollBarVisibleWhenRequired = 2 37 | }; 38 | 39 | GUIDropDownMenu( 40 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 41 | uint16_t eventID 42 | ); 43 | 44 | void SetScrollBarVisibility(ScrollBarVisibility visibility); 45 | void AddMenuItem(GUIDropDownMenuItem &dropDownMenuItem); 46 | }; 47 | 48 | class GUIDropDownMenuItem : public GUIElement { 49 | public: 50 | enum Flag : int { 51 | /// Align the text of the item within the drop down to the right. 52 | FlagTextAlignRight = 1 << 5, 53 | /// Align the text of the item within the drop down to the left. 54 | FlagTextAlignLeft = 1 << 6, 55 | /// Allow the item to be selected. 56 | FlagEnabled = 1 << 15 57 | }; 58 | 59 | GUIDropDownMenuItem(const char *s, int idx, int flags); 60 | }; 61 | 62 | /// @cond INTERNAL 63 | extern "C" 64 | void *GUIDropDownMenu_ctor( 65 | void *dropDownMenu, 66 | uint16_t bounds[4], 67 | uint16_t eventID, int flags1 68 | ); 69 | 70 | extern "C" 71 | void *GUIDropDownMenuItem_ctor( 72 | void *dropDownMenuItem, 73 | int unk0, int unk1, 74 | const char *text, 75 | int index, int flags, 76 | int unk2 77 | ); 78 | /// @endcond 79 | -------------------------------------------------------------------------------- /sdk/os/gui/button.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Creates a button. 5 | * 6 | * The @c event.type value which arises when the button is pressed is not the 7 | * value passed in the @p eventType parameter. You must use the 8 | * @ref GetEventType method to convert the @p eventType parameter passed in here 9 | * to the @c event.type value. 0 is not a valid value for the @p eventType 10 | * parameter. 11 | * 12 | * @param leftX,topY,rightX,bottomY The coordinates of the bounding box of the 13 | * button, in screen pixels relative to the top left of the display. 14 | * @param[in] text The text to display on the button. 15 | * @param eventID The ID to use for events relating to this button. 16 | */ 17 | GUIButton::GUIButton( 18 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 19 | const char *text, 20 | uint16_t eventID 21 | ) : GUIButton( 22 | leftX, topY, rightX, bottomY, text, eventID, 23 | Flag::FlagEnabled 24 | ) { 25 | 26 | } 27 | 28 | /** 29 | * Creates a button. 30 | * 31 | * The @c event.type value which arises when the button is pressed is not the 32 | * value passed in the @p eventType parameter. You must use the 33 | * @ref GetEventType method to convert the @p eventType parameter passed in here 34 | * to the @c event.type value. 0 is not a valid value for the @p eventType 35 | * parameter. 36 | * 37 | * @param leftX,topY,rightX,bottomY The coordinates of the bounding box of the 38 | * button, in screen pixels relative to the top left of the display. 39 | * @param[in] text The text to display on the button. 40 | * @param eventID The ID to use for events relating to this button. 41 | * @param flags A bitfield of flags specified by bitwise-ORing members of 42 | * the @ref Flag enum. 43 | */ 44 | GUIButton::GUIButton( 45 | uint16_t leftX, uint16_t topY, uint16_t rightX, uint16_t bottomY, 46 | const char *text, 47 | uint16_t eventID, int flags 48 | ) { 49 | uint16_t bounds[4] = { 50 | leftX, topY, 51 | rightX, bottomY 52 | }; 53 | 54 | m_wrapped = GUIButton_ctor( 55 | 0, 56 | bounds, text, eventID, 57 | flags, 0 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /demos/random_circles/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | APP_NAME("Random Circles (VRAM Test)") 7 | APP_DESCRIPTION("Draw random circles at tapped locations on the display.") 8 | APP_AUTHOR("The6P4C") 9 | APP_VERSION("1.0.0") 10 | 11 | #define PIXEL(x, y) (vram[(x) + (y) * width]) 12 | 13 | uint16_t *vram; 14 | int width, height; 15 | uint16_t lfsr; 16 | 17 | void drawCircle(int32_t x0, int32_t y0, int radius) { 18 | for (int32_t dx = -radius; dx < radius; ++dx) { 19 | for (int32_t dy = -radius; dy < radius; ++dy) { 20 | if (dx * dx + dy * dy < radius * radius) { 21 | int32_t x = x0 + dx; 22 | int32_t y = y0 + dy; 23 | 24 | if (x < 0 || x > width || y < 0 || y > height) { 25 | continue; 26 | } 27 | 28 | PIXEL(x, y) = lfsr; 29 | } 30 | } 31 | } 32 | } 33 | 34 | void main() { 35 | LCD_VRAMBackup(); 36 | 37 | vram = LCD_GetVRAMAddress(); 38 | LCD_GetSize(&width, &height); 39 | 40 | lfsr = 0x453A; 41 | 42 | struct InputEvent event; 43 | 44 | LCD_ClearScreen(); 45 | LCD_Refresh(); 46 | 47 | bool running = true; 48 | while (running) { 49 | memset(&event, 0, sizeof(event)); 50 | GetInput(&event, 0xFFFFFFFF, 0x10); 51 | 52 | switch (event.type) { 53 | case EVENT_KEY: 54 | switch (event.data.key.keyCode) { 55 | case KEYCODE_POWER_CLEAR: 56 | running = false; 57 | break; 58 | case KEYCODE_0: 59 | LCD_ClearScreen(); 60 | LCD_Refresh(); 61 | break; 62 | } 63 | break; 64 | case EVENT_TOUCH: { 65 | int32_t x = event.data.touch_single.p1_x; 66 | int32_t y = event.data.touch_single.p1_y; 67 | 68 | if (x < 0 || x > width || y < 0 || y > height) { 69 | break; 70 | } 71 | 72 | uint16_t radius = (lfsr ^ (lfsr >> 4) ^ (lfsr >> 8) ^ (lfsr >> 12)) & 0xF; 73 | drawCircle(x, y, radius + 10); 74 | 75 | uint16_t bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5)) & 1; 76 | lfsr = (lfsr >> 1) | (bit << 15); 77 | 78 | LCD_Refresh(); 79 | break; 80 | } 81 | } 82 | } 83 | 84 | LCD_VRAMRestore(); 85 | LCD_Refresh(); 86 | } 87 | -------------------------------------------------------------------------------- /doc/user/functions.md: -------------------------------------------------------------------------------- 1 | These are the most useful functions: 2 | 3 | Exec: Abbreviation used in [exec files](exec.md) 4 | 5 | C++: name of the function in C++ 6 | 7 | Addr./Val.: Address in ROM (OS version 2.1.2) (or value if it is not a function) 8 | 9 | | Exec | C++ | Addr./Val. | Explanation | 10 | | :------------- | :----------: | :----------: | -----------: | 11 | | #C | void LCD_ClearScreen(); | 0x800394C0 | Clears the VRAM | 12 | | #R | void LCD_Refresh(); | 0x8003733E | Copys VRAM content to the screen | 13 | | | uint16_t *LCD_GetVRAMAddress(); | 0x8002E154 | Function, that returns the VRAM address | 14 | | #V | | 0x8C000000 | The actual VRAM address (in OS version 2.1.2)| 15 | | | void LCD_GetSize(int *width, int *height); | 0x8002E140 | Stores the size of the screen in the variables pointed to| 16 | | #X | | 320 | The actual width of the screen | 17 | | #Y | | 528 | The actual height of the screen | 18 | | #S | int Debug_SetCursorPosition(int x, int y); | 0x8002E430 | Sets the cursor for PrintString | 19 | | #G | void Debug_GetCursorPosition(int *x, int *y); | 0x8002E448 | Gets the cursor position | 20 | | #W | int Debug_WaitKey(); | 0x80094380 | Waits until clear is pressed (some other keys also work)| 21 | | #PS | bool Debug_PrintString(const char *string, bool invert); | 0x8002DA0C | Prints the string at the cursor position| 22 | | #PN | void Debug_PrintNumberHex_Nibble(uint8_t value, int x, int y); | 0x80094466 | prints a hex nibble | 23 | | #PB | void Debug_PrintNumberHex_Byte(uint8_t value, int x, int y); | 0x800944A0 | prints a hex byte | 24 | | #PW | void Debug_PrintNumberHex_Word(uint16_t value, int x, int y); | 0x800944C8 | prints a hex word | 25 | | #PL / #PD | void Debug_PrintNumberHex_Dword(uint32_t value, int x, int y); | 0x80094514 | prints a hex long/dword | 26 | | #PF | void Debug_Printf(int x, int y, bool invert, int zero, const char *format, ...); | 0x8002DBC8 | prints the format string | 27 | | | void LCD_VRAMBackup(); | 0x8002D3FA | Backs up the VRAM | 28 | | | void LCD_VRAMRestore(); | 0x8002D41A | Restores the VRAM | 29 | 30 | You can find all functions in the .hpp files in `/sdk/include/`. 31 | Their adresses are in `/sdk/os/` 32 | -------------------------------------------------------------------------------- /sdk/os/gui/label.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Creates a label. 5 | * 6 | * @param x,y The position to place the label at, in pixels and relative to the 7 | * top left of the display. 8 | * @param[in] text The text to display as the label. 9 | */ 10 | GUILabel::GUILabel(int x, int y, const char *text) : GUILabel( 11 | x, y, text, 0, 0, 0, false, 0 12 | ) { 13 | 14 | } 15 | 16 | /** 17 | * Creates a label. 18 | * 19 | * @param x,y The position to place the label at, in pixels and relative to the 20 | * top left of the display. 21 | * @param[in] text The text to display as the label. 22 | * @param flags A bitfield of flags specified by bitwise-ORing members of 23 | * the @ref Flag enum. 24 | * @param[in] textColor A pointer to an RGB565 color to use for the text, or 0 25 | * for the default color. 26 | * @param[in] backgroundColor A pointer to an RGB565 color to use for the 27 | * background, or 0 for the default color. 28 | */ 29 | GUILabel::GUILabel( 30 | int x, int y, 31 | const char *text, 32 | int flags, 33 | uint16_t *textColor, uint16_t *backgroundColor 34 | ) : GUILabel(x, y, text, flags, textColor, backgroundColor, false, 0) { 35 | 36 | } 37 | 38 | /** 39 | * Creates a label. 40 | * 41 | * @param x,y The position to place the label at, in pixels and relative to the 42 | * top left of the display. 43 | * @param[in] text The text to display as the label. 44 | * @param flags A bitfield of flags specified by bitwise-ORing members of 45 | * the @ref Flag enum. 46 | * @param[in] textColor A pointer to an RGB565 color to use for the text, or 0 47 | * for the default color. 48 | * @param[in] backgroundColor A pointer to an RGB565 color to use for the 49 | * background, or 0 for the default color. 50 | * @param showShadow True if the text shadow should be shown. 51 | * @param shadowColor An RGB565 color to use for the text shadow. 52 | */ 53 | GUILabel::GUILabel( 54 | int x, int y, 55 | const char *text, 56 | int flags, 57 | uint16_t *textColor, uint16_t *backgroundColor, 58 | bool showShadow, uint16_t shadowColor 59 | ) { 60 | m_wrapped = GUILabel_ctor( 61 | 0, 62 | x, y, text, 63 | 0, 64 | flags, 65 | 0, 66 | textColor, backgroundColor, showShadow, shadowColor, 67 | 0 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /demos/snake/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME:=snake 2 | 3 | ifndef SDK_DIR 4 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 5 | endif 6 | 7 | AS:=sh4-elf-as 8 | AS_FLAGS:= 9 | 10 | CC:=sh4-elf-gcc 11 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 12 | 13 | CXX:=sh4-elf-g++ 14 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 15 | 16 | LD:=sh4-elf-ld 17 | LD_FLAGS:=-nostdlib --no-undefined 18 | 19 | READELF:=sh4-elf-readelf 20 | OBJCOPY:=sh4-elf-objcopy 21 | 22 | AS_SOURCES:=$(wildcard *.s) 23 | CC_SOURCES:=$(wildcard *.c) 24 | CXX_SOURCES:=$(wildcard *.cpp) 25 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 26 | 27 | APP_ELF:=$(APP_NAME).hhk 28 | 29 | all: $(APP_ELF) Makefile 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(APP_ELF) 33 | 34 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 35 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 36 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 37 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 38 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 39 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 40 | 41 | # We're not actually building sdk.o, just telling the user they need to do it 42 | # themselves. Just using the target to trigger an error when the file is 43 | # required but does not exist. 44 | $(SDK_DIR)/sdk.o: 45 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 46 | 47 | %.o: %.s 48 | $(AS) $< -o $@ $(AS_FLAGS) 49 | 50 | %.o: %.c 51 | $(CC) $< -o $@ $(CC_FLAGS) 52 | 53 | # Break the build if global constructors are present: 54 | # Read the sections from the object file (with readelf -S) and look for any 55 | # called .ctors - if they exist, give the user an error message, delete the 56 | # object file (so that on subsequent runs of make the build will still fail) 57 | # and exit with an error code to halt the build. 58 | %.o: %.cpp 59 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 60 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 61 | 62 | .PHONY: all clean 63 | -------------------------------------------------------------------------------- /demos/tetris/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME:=tetris 2 | 3 | ifndef SDK_DIR 4 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 5 | endif 6 | 7 | AS:=sh4-elf-as 8 | AS_FLAGS:= 9 | 10 | CC:=sh4-elf-gcc 11 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 12 | 13 | CXX:=sh4-elf-g++ 14 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 15 | 16 | LD:=sh4-elf-ld 17 | LD_FLAGS:=-nostdlib --no-undefined 18 | 19 | READELF:=sh4-elf-readelf 20 | OBJCOPY:=sh4-elf-objcopy 21 | 22 | AS_SOURCES:=$(wildcard *.s) 23 | CC_SOURCES:=$(wildcard *.c) 24 | CXX_SOURCES:=$(wildcard *.cpp) 25 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 26 | 27 | APP_ELF:=$(APP_NAME).hhk 28 | 29 | all: $(APP_ELF) Makefile 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(APP_ELF) 33 | 34 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 35 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 36 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 37 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 38 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 39 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 40 | 41 | # We're not actually building sdk.o, just telling the user they need to do it 42 | # themselves. Just using the target to trigger an error when the file is 43 | # required but does not exist. 44 | $(SDK_DIR)/sdk.o: 45 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 46 | 47 | %.o: %.s 48 | $(AS) $< -o $@ $(AS_FLAGS) 49 | 50 | %.o: %.c 51 | $(CC) $< -o $@ $(CC_FLAGS) 52 | 53 | # Break the build if global constructors are present: 54 | # Read the sections from the object file (with readelf -S) and look for any 55 | # called .ctors - if they exist, give the user an error message, delete the 56 | # object file (so that on subsequent runs of make the build will still fail) 57 | # and exit with an error code to halt the build. 58 | %.o: %.cpp 59 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 60 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 61 | 62 | .PHONY: all clean 63 | -------------------------------------------------------------------------------- /demos/demo_gui/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME:=demo_gui 2 | 3 | ifndef SDK_DIR 4 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 5 | endif 6 | 7 | AS:=sh4-elf-as 8 | AS_FLAGS:= 9 | 10 | CC:=sh4-elf-gcc 11 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 12 | 13 | CXX:=sh4-elf-g++ 14 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 15 | 16 | LD:=sh4-elf-ld 17 | LD_FLAGS:=-nostdlib --no-undefined 18 | 19 | READELF:=sh4-elf-readelf 20 | OBJCOPY:=sh4-elf-objcopy 21 | 22 | AS_SOURCES:=$(wildcard *.s) 23 | CC_SOURCES:=$(wildcard *.c) 24 | CXX_SOURCES:=$(wildcard *.cpp) 25 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 26 | 27 | APP_ELF:=$(APP_NAME).hhk 28 | 29 | all: $(APP_ELF) Makefile 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(APP_ELF) 33 | 34 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 35 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 36 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 37 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 38 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 39 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 40 | 41 | # We're not actually building sdk.o, just telling the user they need to do it 42 | # themselves. Just using the target to trigger an error when the file is 43 | # required but does not exist. 44 | $(SDK_DIR)/sdk.o: 45 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 46 | 47 | %.o: %.s 48 | $(AS) $< -o $@ $(AS_FLAGS) 49 | 50 | %.o: %.c 51 | $(CC) $< -o $@ $(CC_FLAGS) 52 | 53 | # Break the build if global constructors are present: 54 | # Read the sections from the object file (with readelf -S) and look for any 55 | # called .ctors - if they exist, give the user an error message, delete the 56 | # object file (so that on subsequent runs of make the build will still fail) 57 | # and exit with an error code to halt the build. 58 | %.o: %.cpp 59 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 60 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 61 | 62 | .PHONY: all clean 63 | -------------------------------------------------------------------------------- /demos/input_key/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME:=input_key_demo 2 | 3 | ifndef SDK_DIR 4 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 5 | endif 6 | 7 | AS:=sh4-elf-as 8 | AS_FLAGS:= 9 | 10 | CC:=sh4-elf-gcc 11 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 12 | 13 | CXX:=sh4-elf-g++ 14 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 15 | 16 | LD:=sh4-elf-ld 17 | LD_FLAGS:=-nostdlib --no-undefined 18 | 19 | READELF:=sh4-elf-readelf 20 | OBJCOPY:=sh4-elf-objcopy 21 | 22 | AS_SOURCES:=$(wildcard *.s) 23 | CC_SOURCES:=$(wildcard *.c) 24 | CXX_SOURCES:=$(wildcard *.cpp) 25 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 26 | 27 | APP_ELF:=$(APP_NAME).hhk 28 | 29 | all: $(APP_ELF) Makefile 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(APP_ELF) 33 | 34 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 35 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 36 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 37 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 38 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 39 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 40 | 41 | # We're not actually building sdk.o, just telling the user they need to do it 42 | # themselves. Just using the target to trigger an error when the file is 43 | # required but does not exist. 44 | $(SDK_DIR)/sdk.o: 45 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 46 | 47 | %.o: %.s 48 | $(AS) $< -o $@ $(AS_FLAGS) 49 | 50 | %.o: %.c 51 | $(CC) $< -o $@ $(CC_FLAGS) 52 | 53 | # Break the build if global constructors are present: 54 | # Read the sections from the object file (with readelf -S) and look for any 55 | # called .ctors - if they exist, give the user an error message, delete the 56 | # object file (so that on subsequent runs of make the build will still fail) 57 | # and exit with an error code to halt the build. 58 | %.o: %.cpp 59 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 60 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 61 | 62 | .PHONY: all clean 63 | -------------------------------------------------------------------------------- /demos/input_test/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME:=input_test 2 | 3 | ifndef SDK_DIR 4 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 5 | endif 6 | 7 | AS:=sh4-elf-as 8 | AS_FLAGS:= 9 | 10 | CC:=sh4-elf-gcc 11 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 12 | 13 | CXX:=sh4-elf-g++ 14 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 15 | 16 | LD:=sh4-elf-ld 17 | LD_FLAGS:=-nostdlib --no-undefined 18 | 19 | READELF:=sh4-elf-readelf 20 | OBJCOPY:=sh4-elf-objcopy 21 | 22 | AS_SOURCES:=$(wildcard *.s) 23 | CC_SOURCES:=$(wildcard *.c) 24 | CXX_SOURCES:=$(wildcard *.cpp) 25 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 26 | 27 | APP_ELF:=$(APP_NAME).hhk 28 | 29 | all: $(APP_ELF) Makefile 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(APP_ELF) 33 | 34 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 35 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 36 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 37 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 38 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 39 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 40 | 41 | # We're not actually building sdk.o, just telling the user they need to do it 42 | # themselves. Just using the target to trigger an error when the file is 43 | # required but does not exist. 44 | $(SDK_DIR)/sdk.o: 45 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 46 | 47 | %.o: %.s 48 | $(AS) $< -o $@ $(AS_FLAGS) 49 | 50 | %.o: %.c 51 | $(CC) $< -o $@ $(CC_FLAGS) 52 | 53 | # Break the build if global constructors are present: 54 | # Read the sections from the object file (with readelf -S) and look for any 55 | # called .ctors - if they exist, give the user an error message, delete the 56 | # object file (so that on subsequent runs of make the build will still fail) 57 | # and exit with an error code to halt the build. 58 | %.o: %.cpp 59 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 60 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 61 | 62 | .PHONY: all clean 63 | -------------------------------------------------------------------------------- /demos/random_circles/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME:=random_circles 2 | 3 | ifndef SDK_DIR 4 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 5 | endif 6 | 7 | AS:=sh4-elf-as 8 | AS_FLAGS:= 9 | 10 | CC:=sh4-elf-gcc 11 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 12 | 13 | CXX:=sh4-elf-g++ 14 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 15 | 16 | LD:=sh4-elf-ld 17 | LD_FLAGS:=-nostdlib --no-undefined 18 | 19 | READELF:=sh4-elf-readelf 20 | OBJCOPY:=sh4-elf-objcopy 21 | 22 | AS_SOURCES:=$(wildcard *.s) 23 | CC_SOURCES:=$(wildcard *.c) 24 | CXX_SOURCES:=$(wildcard *.cpp) 25 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 26 | 27 | APP_ELF:=$(APP_NAME).hhk 28 | 29 | all: $(APP_ELF) Makefile 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(APP_ELF) 33 | 34 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 35 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 36 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 37 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 38 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 39 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 40 | 41 | # We're not actually building sdk.o, just telling the user they need to do it 42 | # themselves. Just using the target to trigger an error when the file is 43 | # required but does not exist. 44 | $(SDK_DIR)/sdk.o: 45 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 46 | 47 | %.o: %.s 48 | $(AS) $< -o $@ $(AS_FLAGS) 49 | 50 | %.o: %.c 51 | $(CC) $< -o $@ $(CC_FLAGS) 52 | 53 | # Break the build if global constructors are present: 54 | # Read the sections from the object file (with readelf -S) and look for any 55 | # called .ctors - if they exist, give the user an error message, delete the 56 | # object file (so that on subsequent runs of make the build will still fail) 57 | # and exit with an error code to halt the build. 58 | %.o: %.cpp 59 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 60 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 61 | 62 | .PHONY: all clean 63 | -------------------------------------------------------------------------------- /demos/breakpoint_util/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME:=breakpoint_util 2 | 3 | ifndef SDK_DIR 4 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 5 | endif 6 | 7 | AS:=sh4-elf-as 8 | AS_FLAGS:= 9 | 10 | CC:=sh4-elf-gcc 11 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 12 | 13 | CXX:=sh4-elf-g++ 14 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 15 | 16 | LD:=sh4-elf-ld 17 | LD_FLAGS:=-nostdlib --no-undefined 18 | 19 | READELF:=sh4-elf-readelf 20 | OBJCOPY:=sh4-elf-objcopy 21 | 22 | AS_SOURCES:=$(wildcard *.s) 23 | CC_SOURCES:=$(wildcard *.c) 24 | CXX_SOURCES:=$(wildcard *.cpp) 25 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 26 | 27 | APP_ELF:=$(APP_NAME).hhk 28 | 29 | all: $(APP_ELF) Makefile 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(APP_ELF) 33 | 34 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld 35 | $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 36 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 37 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 38 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 39 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 40 | 41 | # We're not actually building sdk.o, just telling the user they need to do it 42 | # themselves. Just using the target to trigger an error when the file is 43 | # required but does not exist. 44 | $(SDK_DIR)/sdk.o: 45 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 46 | 47 | %.o: %.s 48 | $(AS) $< -o $@ $(AS_FLAGS) 49 | 50 | %.o: %.c 51 | $(CC) $< -o $@ $(CC_FLAGS) 52 | 53 | # Break the build if global constructors are present: 54 | # Read the sections from the object file (with readelf -S) and look for any 55 | # called .ctors - if they exist, give the user an error message, delete the 56 | # object file (so that on subsequent runs of make the build will still fail) 57 | # and exit with an error code to halt the build. 58 | %.o: %.cpp 59 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 60 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 61 | 62 | .PHONY: all clean 63 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // No point in documenting these macros. If people are trying to use them, 5 | // they're doing something weird and I don't want to help them :) 6 | // If you need to know what they do, look at how they're used. 7 | /// @cond INTERNAL 8 | /** 9 | * Create @p n fake vtable function entries. 10 | * 11 | * The parameter @p x is required as the fake entry needs a name. It is 12 | * recommended to simply increment from 0 as fake entries are required. 13 | * 14 | * @param n The number of fake vtable entries to create. 15 | * @param x An integer, unique within the vtable. 16 | */ 17 | #define VTABLE_FAKE_ENTRY(n, x) uint32_t fakeentry##x[n * 3] 18 | 19 | /** 20 | * Represents an entry in a vtable. 21 | * 22 | * The function can be called with the () operator. The first argument must be 23 | * the object to call the function on, followed by the arguments to the 24 | * function. 25 | * 26 | * @tparam TReturn The return type of the function. 27 | * @tparam ...TArgs The types of the function's arguments. 28 | */ 29 | template 30 | struct VTableFunction { 31 | int32_t offset; 32 | uint32_t unused; 33 | TReturn (*func)(void *self, TArgs...); 34 | 35 | TReturn operator()(void *self, TArgs... args) { 36 | void *self2 = reinterpret_cast( 37 | reinterpret_cast(self) + offset 38 | ); 39 | 40 | return func(self2, args...); 41 | } 42 | }; 43 | /// @endcond 44 | 45 | /** 46 | * Wraps an internal class from the fx-CP400. 47 | */ 48 | class Wrapped { 49 | public: 50 | /** 51 | * Returns a pointer to the internal class. 52 | * 53 | * @tparam T The type of the pointer to return. Can be @c void. 54 | * @return A pointer to the internal class. 55 | */ 56 | template 57 | T *GetWrapped() { 58 | return static_cast(m_wrapped); 59 | } 60 | 61 | protected: 62 | Wrapped() = default; 63 | ~Wrapped() = default; 64 | 65 | // Since we use the trick with the vtable to overide functions (@c .me), we 66 | // can't let the object's address change. Prevent that by deleting the copy 67 | // constructor and assignment operator. 68 | Wrapped(Wrapped const &) = delete; 69 | void operator=(Wrapped const &) = delete; 70 | 71 | /// A pointer to the wrapped class. 72 | void *m_wrapped; 73 | }; 74 | 75 | /** 76 | * Utility class which is the parent of all GUI element classes. 77 | */ 78 | class GUIElement : public Wrapped {}; 79 | -------------------------------------------------------------------------------- /patcher/README.md: -------------------------------------------------------------------------------- 1 | # `patcher aka Snail2021` 2 | This folder contains the program to modify the firmware updater with the modifications from the folder patches. This program is called __Snail2021.exe__, it war first developed for an old project of mine (LoaderII and LoaderIIb). 3 | 4 | __Snail202x is a submodule!__ (https://github.com/SnailMath/Snail202x) 5 | 6 | Because the firmware updater is only available for windows, this needs to be compiled and executed unter windows. 7 | 8 | The executable is called Snail2021.exe because this is derived from my previous attempts to modify the firmware, where the program was called Snail2020.exe. I don't want to change this name, so the experience for users who just want to do this modification see the similarity of this programs when looking at just the file name. 9 | 10 | If you want to compile Snail2021 from source, you need minGW under windows. 11 | (You can always download the precompiled file Snail2021.zip which contains Snail2021.exe and the modifications precompiled, so you don't need Linux to compile the patches [in the folder /patches] ) 12 | 13 | #colpile# 14 | I split the compilation in two parts. 15 | 16 | - The first part is running `make all` under Linux. This will make all the patches, create the mod.txt for modifying the firmware and create Snail2021mod.c, 17 | which is a copy of Snail2020.c which contains the mod.txt. 18 | I did this, so the user does not need to download mod.txt seperately. 19 | When they execute the executable (which will be compiled in step two) the file 20 | mod.txt will be created, because this step includes it in the code. 21 | 22 | - The second step is running `make.bat` (the one in this folder, not the one from Snail202x) under windows. 23 | You need to install minGW (and zlib) beforehand. See the README file in the Snail202x repository for more info. 24 | The batch file make.bat will use gcc to compile Snail2021mod.c (and the required libaries) in a single file called `Snail2021-hollyhock2.exe`. 25 | 26 | `Snail2021-hollyhock2.exe` is the only file you need from all this to modify the calculator. 27 | It includes the tools to extract the frirmware, it contains the mod.txt and it contains the programs to start the updater. 28 | Copy this file to a real Computer (If you compiled it in a VM) and execute it. 29 | It will tell the user to download the last two missing parts: the Updater itself and Resourcehacker. 30 | 31 | 32 | 33 | ##Note## 34 | I am not affiliated with the program ResourceHacker and I am not publishing any part of it. 35 | -------------------------------------------------------------------------------- /patches/menu_texts/Makefile: -------------------------------------------------------------------------------- 1 | BIN_FILE:=imaginary_unit 2 | ADDRengIM:=808B5CD8# Imaginary Unit 3 | ADDRengEX:=808BB211# 808BB221 -User name editing (19 chars including 0x00) 4 | ADDRfraIM:=808C5460# Imaginaire i ou j 5 | ADDRfraEX:=808CB1E4# -Édition Nom d'Utilisateur (28 inc 0x00) 6 | ADDRdeuIM:=808D65C0# Imaginäre Einheit 7 | ADDRdeuEX:=808DC202# -Änderung des Benutzernamens (30 inc 0x00) 8 | ADDRespIM:=808E7054# Unidad imaginaria 9 | ADDRespEX:=808ECC6B# -Edición del nombre de usuario (33 inc 0x00 0x00) 10 | ADDRsuoIM:=808F5F90# Imaginaariyks. 11 | ADDRsuoEX:=808FB57A# -Käyttäjätunnuksen muokkaaminen (38 inc 4x 0x00) 12 | 13 | mod.part.txt: Makefile ${BIN_FILE}.bin README.md 14 | #replaces the text Imaginary Unit in the settings with the text from 15 | #imaginary_unit.bin and replaces the last line from the exam mode 16 | #start windows with the info that hollyhock is disabled during exam. 17 | #in english the original line "-User name editing" is too short, so we 18 | #have to shorten the line before that to get space... 19 | echo -n "'" > mod.part.txt 20 | head -2 README.md| tail -1 >> mod.part.txt 21 | # 22 | echo "${ADDRengIM}: 'eng" >> mod.part.txt 23 | xxd -ps ${BIN_FILE}.bin >> mod.part.txt 24 | echo "*" >> mod.part.txt 25 | echo "${ADDRengEX}:" >> mod.part.txt 26 | #also teplace "with 3pin cable" with "(3pin cable)" (2 less characters) 27 | echo -n "(3pin cable)\r-Hollyhock Launcher\000" | xxd -ps -c 64 >> mod.part.txt 28 | echo "*" >> mod.part.txt 29 | # 30 | echo "${ADDRfraIM}: 'fra" >> mod.part.txt 31 | xxd -ps ${BIN_FILE}.bin >> mod.part.txt 32 | echo "*" >> mod.part.txt 33 | echo "${ADDRfraEX}:" >> mod.part.txt 34 | echo -n "-Hollyhock Launcher\000" | xxd -ps -c 64 >> mod.part.txt #28 35 | echo "*" >> mod.part.txt 36 | # 37 | echo "${ADDRdeuIM}: 'deu" >> mod.part.txt 38 | xxd -ps ${BIN_FILE}.bin >> mod.part.txt 39 | echo "*" >> mod.part.txt 40 | echo "${ADDRdeuEX}:" >> mod.part.txt 41 | echo -n "-Hollyhock Launcher\000" | xxd -ps -c 64 >> mod.part.txt #30 42 | echo "*" >> mod.part.txt 43 | # 44 | echo "${ADDRespIM}: 'esp" >> mod.part.txt 45 | xxd -ps ${BIN_FILE}.bin >> mod.part.txt 46 | echo "*" >> mod.part.txt 47 | echo "${ADDRespEX}:" >> mod.part.txt 48 | echo -n "-Hollyhock Launcher\000" | xxd -ps -c 64 >> mod.part.txt #33 49 | echo "*" >> mod.part.txt 50 | # 51 | echo "${ADDRsuoIM}: 'suo" >> mod.part.txt 52 | xxd -ps ${BIN_FILE}.bin >> mod.part.txt 53 | echo "*" >> mod.part.txt 54 | echo "${ADDRsuoEX}:" >> mod.part.txt 55 | echo -n "-Hollyhock Launcher\000" | xxd -ps -c 64 >> mod.part.txt #38 56 | echo "" >> mod.part.txt 57 | clean: 58 | rm mod.part.txt 59 | -------------------------------------------------------------------------------- /sdk/include/sdk/calc/calc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include //needed for getVramAddress and 5 | 6 | 7 | //Graphics stuff 8 | 9 | extern uint16_t *vram; //The vram pointer (this is used by routines like setPixel and has to be initialized by getVramAddress() or calc_init(); 10 | extern int width; //width of the screen 11 | extern int height; //height of the screen 12 | 13 | void line(int x1, int y1, int x2, int y2, uint16_t color); 14 | void triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint16_t colorFill, uint16_t colorLine); 15 | void fillScreen(uint16_t color); 16 | 17 | inline uint16_t color(uint8_t R, uint8_t G, uint8_t B){ 18 | return (((R<<8) & 0b1111100000000000) | 19 | ((G<<3) & 0b0000011111100000) | 20 | ((B>>3) & 0b0000000000011111)); 21 | } 22 | 23 | inline void setPixel(int x,int y, uint32_t color) { 24 | if(x>=0 && x < width && y>=0 && y < height) 25 | vram[width*y + x] = color; 26 | } 27 | 28 | //Stuff for Initialisation and stuff 29 | 30 | inline void calcInit(){ 31 | vram = LCD_GetVRAMAddress(); 32 | LCD_GetSize(&width, &height); 33 | LCD_VRAMBackup(); 34 | } 35 | inline void calcEnd(){ 36 | LCD_VRAMRestore(); 37 | LCD_Refresh(); 38 | } 39 | 40 | //Stuff for the keyboard 41 | 42 | extern "C" void getKey(uint32_t *key1, uint32_t *key2); 43 | 44 | enum Keys1 { 45 | KEY_SHIFT = 0x80000000, 46 | KEY_CLEAR = 0x00020000, //The Power key 47 | KEY_BACKSPACE = 0x00000080, 48 | KEY_LEFT = 0x00004000, 49 | KEY_RIGHT = 0x00008000, 50 | KEY_Z = 0x00002000, 51 | KEY_POWER = 0x00000040, //The exponent key 52 | KEY_DIVIDE = 0x40000000, 53 | KEY_MULTIPLY = 0x20000000, 54 | KEY_SUBTRACT = 0x10000000, 55 | KEY_ADD = 0x08000000, 56 | KEY_EXE = 0x04000000, 57 | KEY_EXP = 0x00000004, 58 | KEY_3 = 0x00000008, 59 | KEY_6 = 0x00000010, 60 | KEY_9 = 0x00000020, 61 | }; 62 | 63 | enum Keys2 { 64 | KEY_KEYBOARD = 0x80000000, 65 | KEY_UP = 0x00800000, 66 | KEY_DOWN = 0x00400000, 67 | KEY_EQUALS = 0x00000080, 68 | KEY_X = 0x00000040, 69 | KEY_Y = 0x40000000, 70 | KEY_LEFT_BRACKET = 0x00000020, 71 | KEY_RIGHT_BRACKET = 0x00000010, 72 | KEY_COMMA = 0x00000008, 73 | KEY_NEGATIVE = 0x00000004, 74 | KEY_0 = 0x04000000, 75 | KEY_DOT = 0x00040000, 76 | KEY_1 = 0x08000000, 77 | KEY_2 = 0x00080000, 78 | KEY_4 = 0x10000000, 79 | KEY_5 = 0x00100000, 80 | KEY_7 = 0x20000000, 81 | KEY_8 = 0x00200000, 82 | }; 83 | 84 | inline bool testKey(uint32_t key1, uint32_t key2, Keys1 key){ 85 | (void) key2; 86 | if (key1 & key) return true; 87 | else return false; 88 | } 89 | 90 | inline bool testKey(uint32_t key1, uint32_t key2, Keys2 key){ 91 | (void) key1; 92 | if (key2 & key) return true; 93 | else return false; 94 | } 95 | -------------------------------------------------------------------------------- /app_template/Makefile: -------------------------------------------------------------------------------- 1 | # run `make all` to compile the .hhk and .bin file, use `make` to compile only the .bin file. 2 | # The .hhk file is the original format, the bin file is a newer format. 3 | APP_NAME:=app_template 4 | 5 | ifndef SDK_DIR 6 | $(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) 7 | endif 8 | 9 | AS:=sh4-elf-as 10 | AS_FLAGS:= 11 | 12 | CC:=sh4-elf-gcc 13 | CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ 14 | 15 | CXX:=sh4-elf-g++ 16 | CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ -m4a-nofpu 17 | 18 | LD:=sh4-elf-ld 19 | LD_FLAGS:=-nostdlib --no-undefined 20 | 21 | READELF:=sh4-elf-readelf 22 | OBJCOPY:=sh4-elf-objcopy 23 | 24 | AS_SOURCES:=$(wildcard *.s) 25 | CC_SOURCES:=$(wildcard *.c) 26 | CXX_SOURCES:=$(wildcard *.cpp) 27 | OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) 28 | 29 | APP_ELF:=$(APP_NAME).hhk 30 | APP_BIN:=$(APP_NAME).bin 31 | 32 | bin: $(APP_BIN) Makefile 33 | 34 | hhk: $(APP_ELF) Makefile 35 | 36 | all: $(APP_ELF) $(APP_BIN) Makefile 37 | 38 | clean: 39 | rm -f $(OBJECTS) $(APP_ELF) $(APP_BIN) 40 | 41 | $(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker_hhk.ld 42 | $(LD) -T linker_hhk.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 43 | $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) 44 | $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) 45 | $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) 46 | $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) 47 | 48 | $(APP_BIN): $(OBJECTS) $(SDK_DIR)/sdk.o linker_bin.ld 49 | $(LD) --oformat binary -T linker_bin.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 50 | 51 | # We're not actually building sdk.o, just telling the user they need to do it 52 | # themselves. Just using the target to trigger an error when the file is 53 | # required but does not exist. 54 | $(SDK_DIR)/sdk.o: 55 | $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) 56 | 57 | %.o: %.s 58 | $(AS) $< -o $@ $(AS_FLAGS) 59 | 60 | %.o: %.c 61 | $(CC) -c $< -o $@ $(CC_FLAGS) 62 | 63 | # Break the build if global constructors are present: 64 | # Read the sections from the object file (with readelf -S) and look for any 65 | # called .ctors - if they exist, give the user an error message, delete the 66 | # object file (so that on subsequent runs of make the build will still fail) 67 | # and exit with an error code to halt the build. 68 | %.o: %.cpp 69 | $(CXX) -c $< -o $@ $(CXX_FLAGS) 70 | @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 71 | 72 | .PHONY: bin hhk all clean 73 | -------------------------------------------------------------------------------- /demos/breakpoint_util/breakpoint_handler_stub.s: -------------------------------------------------------------------------------- 1 | .global _BreakpointHandlerStub 2 | .type _BreakpointHandlerStub, @function 3 | _BreakpointHandlerStub: 4 | /* 5 | I'm not sure why we have to push r0 to r7. When we switch into the 6 | exception handler, we should get our own bank so our changes to r0 to r7 7 | shouldn't have any effect. They seem to though, and I don't know why. 8 | */ 9 | sts.l pr, @-r15 10 | mov.l r0, @-r15 11 | mov.l r1, @-r15 12 | mov.l r2, @-r15 13 | mov.l r3, @-r15 14 | mov.l r4, @-r15 15 | mov.l r5, @-r15 16 | mov.l r6, @-r15 17 | mov.l r7, @-r15 18 | mov.l r8, @-r15 19 | mov.l r9, @-r15 20 | mov.l r10, @-r15 21 | mov.l r11, @-r15 22 | mov.l r12, @-r15 23 | mov.l r13, @-r15 24 | mov.l r14, @-r15 25 | 26 | mov.l cpuState, r4 27 | 28 | /* 29 | Since we've moved to privileged mode, the registers r0 to r7 from where 30 | the breakpoint was triggered are now stored in r0_bank to r7_bank 31 | */ 32 | stc r0_bank, r5 33 | mov.l r5, @(4*0, r4) 34 | stc r1_bank, r5 35 | mov.l r5, @(4*1, r4) 36 | stc r2_bank, r5 37 | mov.l r5, @(4*2, r4) 38 | stc r3_bank, r5 39 | mov.l r5, @(4*3, r4) 40 | stc r4_bank, r5 41 | mov.l r5, @(4*4, r4) 42 | stc r5_bank, r5 43 | mov.l r5, @(4*5, r4) 44 | stc r6_bank, r5 45 | mov.l r5, @(4*6, r4) 46 | stc r7_bank, r5 47 | mov.l r5, @(4*7, r4) 48 | 49 | /* 50 | Since we haven't modified r8 to r14, so they've retained the values they 51 | had when the breakpoint was triggered. 52 | */ 53 | mov.l r8, @(4*8, r4) 54 | mov.l r9, @(4*9, r4) 55 | mov.l r10, @(4*10, r4) 56 | mov.l r11, @(4*11, r4) 57 | mov.l r12, @(4*12, r4) 58 | mov.l r13, @(4*13, r4) 59 | mov.l r14, @(4*14, r4) 60 | 61 | /* Fix r15 so it points to the right place before we pushed stuff */ 62 | mov r15, r6 63 | mov #0x40, r7 64 | sub r7, r6 65 | mov.l r6, @(4*15, r4) 66 | 67 | /* 68 | We're out of range for doing @(4*?, r4) so use r7 as the pointer into 69 | the CPUState struct 70 | */ 71 | mov r4, r7 72 | add #(4*16), r7 73 | 74 | stc gbr, r6 75 | mov.l r6, @r7 76 | 77 | sts pr, r6 78 | add #4, r7 79 | mov.l r6, @r7 80 | 81 | sts mach, r6 82 | add #4, r7 83 | mov.l r6, @r7 84 | 85 | sts macl, r6 86 | add #4, r7 87 | mov.l r6, @r7 88 | 89 | mov.l BreakpointHandler_Addr, r0 90 | jsr @r0 91 | nop 92 | 93 | mov.l @r15+, r14 94 | mov.l @r15+, r13 95 | mov.l @r15+, r12 96 | mov.l @r15+, r11 97 | mov.l @r15+, r10 98 | mov.l @r15+, r9 99 | mov.l @r15+, r8 100 | mov.l @r15+, r7 101 | mov.l @r15+, r6 102 | mov.l @r15+, r5 103 | mov.l @r15+, r4 104 | mov.l @r15+, r3 105 | mov.l @r15+, r2 106 | mov.l @r15+, r1 107 | mov.l @r15+, r0 108 | lds.l @r15+, pr 109 | 110 | stc sgr, r15 111 | rte 112 | nop 113 | 114 | .align 4 115 | BreakpointHandler_Addr: 116 | .long _BreakpointHandler 117 | cpuState: 118 | /* 20 x 32-bit (4-byte) registers */ 119 | .space 20 * 4 120 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions and classes to create/display GUI elements. 4 | * 5 | * Example: display a simple dialog with a label 6 | * @code{cpp} 7 | * GUIDialog dialog( 8 | * GUIDialog::Height25, GUIDialog::AlignTop, 9 | * "Dialog Title", 10 | * GUIDialog::KeyboardStateABC 11 | * ); 12 | * 13 | * GUILabel label( 14 | * dialog.GetLeftX() + 10, 15 | * dialog.GetTopY() + 10, 16 | * "Label Text" 17 | * ); 18 | * dialog.AddElement(label); 19 | * 20 | * dialog.ShowDialog(); 21 | * @endcode 22 | */ 23 | 24 | #pragma once 25 | #include "gui/button.hpp" 26 | #include "gui/dialog.hpp" 27 | #include "gui/dropDown.hpp" 28 | #include "gui/label.hpp" 29 | #include "gui/longLabel.hpp" 30 | #include "gui/radioButton.hpp" 31 | #include "gui/textBox.hpp" 32 | 33 | const int BUTTON_OK = 1 << 5; 34 | const int BUTTON_YES = 1 << 6; 35 | const int BUTTON_NO = 1 << 7; 36 | const int BUTTON_ABORT = 1 << 8; 37 | const int BUTTON_RETRY = 1 << 9; 38 | const int BUTTON_CANCEL = 1 << 10; 39 | 40 | /** 41 | * Displays a message box with the specified title and content, retrieved 42 | * through their ID in the string table. 43 | * 44 | * @param unknown An unknown parameter. 45 | * @param titleStringID The ID of the string to use for the message box's title. 46 | * @param contentStringID The ID of the string to use for the message box's 47 | * content. 48 | */ 49 | extern "C" 50 | void GUI_DisplayMessageBox(int unknown, int titleStringID, int contentStringID); 51 | 52 | /** 53 | * Displays a message box with the specified title and content. A prefix to the 54 | * content may be specified, which is displayed before the main content. 55 | * 56 | * The buttons to be displayed can be set using the @p buttons bitfield. 57 | * Possible values are defined by macros beginning with @c BUTTON_, which can be 58 | * bitwise OR'd together to display multiple buttons. A maximum of 3 buttons can 59 | * be specified. 60 | * 61 | * The true usage of the 4th bit of the @p buttons bitfield is unknown, however 62 | * it has been observed to shrink the black box which forms the title bar of the 63 | * message box. 64 | * 65 | * Note: if no buttons are specified, and the close button is disabled, it is 66 | * impossible to exit the message box. 67 | * 68 | * @param unknown An unknown parameter. 69 | * @param[in] titleString A string to use for the message box's title. 70 | * @param[in] contentPrefix A string to prefix the content with. 71 | * @param[in] contentString A string to use for the message box's content. 72 | * @param buttons A bitfield specifying which buttons to show. 73 | * @param disableCloseButton Set to @c true to disable the close button. 74 | * @return An unknown GUI struct. 75 | */ 76 | extern "C" 77 | void *GUI_DisplayMessageBox_Internal( 78 | int unknown, 79 | const char *titleString, 80 | const char *contentPrefix, const char *contentString, 81 | int buttons, bool disableCloseButton 82 | ); 83 | -------------------------------------------------------------------------------- /demos/input_key/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | APP_NAME("Input_IsAnyKeyDown/Input_GetKeyState Demo") 7 | 8 | struct LUTEntry { 9 | const char *str; 10 | InputScancode scancode; 11 | }; 12 | 13 | #define LUT_ENTRY(name) { #name, name } 14 | 15 | // A simple lookup table, mapping scancodes to their readable names. 16 | struct LUTEntry lut[] = { 17 | LUT_ENTRY(ScancodeKeyboard), 18 | LUT_ENTRY(ScancodeShift), 19 | LUT_ENTRY(ScancodeBackspace), 20 | LUT_ENTRY(ScancodeClear), 21 | LUT_ENTRY(ScancodeUp), 22 | LUT_ENTRY(ScancodeDown), 23 | LUT_ENTRY(ScancodeLeft), 24 | LUT_ENTRY(ScancodeRight), 25 | LUT_ENTRY(ScancodeEquals), 26 | LUT_ENTRY(ScancodeX), 27 | LUT_ENTRY(ScancodeY), 28 | LUT_ENTRY(ScancodeZ), 29 | LUT_ENTRY(ScancodePower), 30 | LUT_ENTRY(ScancodeDivide), 31 | LUT_ENTRY(ScancodeOpenParenthesis), 32 | LUT_ENTRY(Scancode7), 33 | LUT_ENTRY(Scancode8), 34 | LUT_ENTRY(Scancode9), 35 | LUT_ENTRY(ScancodeTimes), 36 | LUT_ENTRY(ScancodeCloseParenthesis), 37 | LUT_ENTRY(Scancode4), 38 | LUT_ENTRY(Scancode5), 39 | LUT_ENTRY(Scancode6), 40 | LUT_ENTRY(ScancodeMinus), 41 | LUT_ENTRY(ScancodeComma), 42 | LUT_ENTRY(Scancode1), 43 | LUT_ENTRY(Scancode2), 44 | LUT_ENTRY(Scancode3), 45 | LUT_ENTRY(ScancodePlus), 46 | LUT_ENTRY(ScancodeNegative), 47 | LUT_ENTRY(Scancode0), 48 | LUT_ENTRY(ScancodeDot), 49 | LUT_ENTRY(ScancodeEXP), 50 | LUT_ENTRY(ScancodeEXE) 51 | }; 52 | constexpr int numScancodes = sizeof(lut) / sizeof(lut[0]); 53 | 54 | extern "C" 55 | void main() { 56 | bool running = true; 57 | while (running) { 58 | LCD_ClearScreen(); 59 | 60 | bool isKeyDown = Input_IsAnyKeyDown(); 61 | Debug_Printf( 62 | 0, 0, false, 0, 63 | "IsAnyKeyDown = %s", isKeyDown ? "true" : "false" 64 | ); 65 | 66 | // The Y position to start printing the names of the currently pressed 67 | // keys at 68 | int printY = 2; 69 | 70 | if (isKeyDown) { 71 | for (int i = 0; i < numScancodes; ++i) { 72 | struct LUTEntry lutEntry = lut[i]; 73 | 74 | // Pass a pointer to the scancode to check into 75 | // Input_GetKeyState 76 | if (Input_GetKeyState(&lutEntry.scancode)) { 77 | // Print the name of the key that was pressed 78 | Debug_Printf( 79 | 0, printY++, false, 0, 80 | "%s", lutEntry.str 81 | ); 82 | 83 | // If the scancode we're currently checking is the Clear key 84 | // and it's pressed (we can't be here if Input_GetKeyState 85 | // was false), exit the loop. 86 | if (lutEntry.scancode == ScancodeClear) { 87 | running = false; 88 | } 89 | } 90 | } 91 | } 92 | 93 | LCD_Refresh(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /sdk/os/gui/textBox.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Creates a text box. 5 | * 6 | * With an example value for the parameter @p maxLength of 4, the behaviour 7 | * of @p countLengthByBytes is as follows: 8 | * - If true, 4 bytes worth of text can be written in the text box. This may 9 | * be two 2-byte characters, one 2-byte character and two 1-byte characters, 10 | * etc. 11 | * - If false, 4 characters worth of text can be written in the text box. 12 | * This may be four 2-byte characters, one 2-byte character and three 1-byte 13 | * characters, etc. 14 | * 15 | * @param x,y The position to place the text box at, in pixels and relative to 16 | * the top left of the display. 17 | * @param width The width of the textbox, in pixels. 18 | * @param maxLength The maximum number of characters/bytes the text box 19 | * should hold. 20 | * @param countLengthByBytes True if @p maxLength specifies the maximum 21 | * number of bytes, and false if it specifies the maximum number of 22 | * characters. 23 | */ 24 | GUITextBox::GUITextBox( 25 | int x, int y, int width, 26 | int maxLength, bool countLengthByBytes 27 | ) : GUITextBox( 28 | x, y, width, "", maxLength, countLengthByBytes 29 | ) { 30 | 31 | } 32 | 33 | /** 34 | * Creates a text box. 35 | * 36 | * With an example value for the parameter @p maxLength of 4, the behaviour 37 | * of @p countLengthByBytes is as follows: 38 | * - If true, 4 bytes worth of text can be written in the text box. This may 39 | * be two 2-byte characters, one 2-byte character and two 1-byte characters, 40 | * etc. 41 | * - If false, 4 characters worth of text can be written in the text box. 42 | * This may be four 2-byte characters, one 2-byte character and three 1-byte 43 | * characters, etc. 44 | * 45 | * @param x,y The position to place the text box at, in pixels and relative to 46 | * the top left of the display. 47 | * @param width The width of the textbox, in pixels. 48 | * @param[in] text A string to pre-populate the text box with, or 0 if the 49 | * textbox should be initially empty. 50 | * @param maxLength The maximum number of characters/bytes the text box 51 | * should hold. 52 | * @param countLengthByBytes True if @p maxLength specifies the maximum 53 | * number of bytes, and false if it specifies the maximum number of 54 | * characters. 55 | */ 56 | GUITextBox::GUITextBox( 57 | int x, int y, int width, 58 | const char *text, int maxLength, bool countLengthByBytes 59 | ) { 60 | m_wrapped = GUITextBox_ctor( 61 | 0, 62 | x, y, width, 63 | text, 64 | 0, 65 | Flag::FlagDrawBox | Flag::FlagEditable, 66 | maxLength, countLengthByBytes 67 | ); 68 | } 69 | 70 | /** 71 | * Retrieves the text box's current text content. 72 | * 73 | * @return The text box's text, or 0 if the text box is empty. 74 | */ 75 | const char *GUITextBox::GetText() { 76 | return GetWrapped()->text; 77 | } 78 | 79 | /** 80 | * Sets the text box's text. 81 | * 82 | * If the text specified is longer than the maximum string length permitted by 83 | * the textbox, it is truncated to fit. 84 | * 85 | * @param[in] text The new string for the textbox. 86 | */ 87 | void GUITextBox::SetText(const char *text) { 88 | GetWrapped()->vtable->SetText(m_wrapped, text); 89 | } 90 | -------------------------------------------------------------------------------- /patches/file_loader/file_loader.s: -------------------------------------------------------------------------------- 1 | .macro print_error error_string 2 | mov r0, r4 3 | mova \error_string, r0 4 | mov r0, r5 5 | 6 | mov.l PrintStandardErrorString, r0 7 | jsr @r0 8 | nop 9 | .endm 10 | 11 | ! r8 - file descriptor 12 | ! all delay slots are nops cause i can't be bothered 13 | 14 | mov.l r8, @-r15 15 | sts.l pr, @-r15 16 | 17 | .macro print_str x_val y_val string 18 | !Set the cursor 19 | mov.l setCursor, r2 20 | mov #\x_val, r4 21 | jsr @r2 22 | mov #\y_val, r5 !Delay slot (gets executed before the jsr) 23 | !Print the string 24 | mov.l print, r2 25 | mova \string, r0 26 | mov r0, r4 27 | jsr @r2 28 | mov #0, r5 !Delay slot (gets executed before the jsr) 29 | .endm 30 | 31 | mov.l xpa, r1 32 | mov.w xc, r2 33 | mov.w @r1, r1 34 | cmp/eq r1, r2 35 | bt naxp 36 | 37 | print_str 1,1,line1 38 | print_str 1,2,line2 39 | print_str 1,3,line3 40 | print_str 1,5,line4 41 | mov.l LCD_Refresh, r2 42 | jsr @r2 43 | nop !delay slot 44 | 45 | !Wait until the user presses a key 46 | mov.l waitkey, r2 47 | jsr @r2 48 | nop !delay slot 49 | 50 | !Reboot the calculator 51 | mov.l reset, r2 52 | jmp @r2 53 | nop !delay slot 54 | 55 | 56 | naxp: 57 | 58 | print_str 1,5,file_path !just a test 59 | mov.l LCD_Refresh, r2 60 | 61 | ! Open the file and get a file descriptor 62 | mova file_path, r0 63 | mov r0, r4 64 | mov #5, r5 65 | 66 | mov.l open, r0 67 | jsr @r0 68 | nop 69 | 70 | ! Save our file descriptor so it's not overwritten 71 | mov r0, r8 72 | 73 | print_error open_str 74 | 75 | ! Run fstat to get the file size 76 | mov r8, r4 77 | mov.l buf, r5 78 | 79 | mov.l fstat, r0 80 | jsr @r0 81 | nop 82 | 83 | print_error fstat_str 84 | 85 | ! Read the file into our buffer 86 | mov r8, r4 87 | mov.l buf, r5 88 | mov.l @(4,r5), r6 ! 4 bytes into the fstat struct is the file size in bytes 89 | 90 | mov.l read, r0 91 | jsr @r0 92 | nop 93 | 94 | print_error read_str 95 | 96 | ! Close the file 97 | mov r8, r4 98 | 99 | mov.l close, r0 100 | jsr @r0 101 | nop 102 | 103 | print_error close_str 104 | 105 | ! Jump to the code we just loaded, as a subroutine/function 106 | mov.l buf, r0 107 | jsr @r0 108 | nop 109 | 110 | lds.l @r15+, pr 111 | rts 112 | mov.l @r15+, r8 113 | 114 | .align 4 115 | open: 116 | .long 0x80057854 117 | fstat: 118 | .long 0x8005798E 119 | read: 120 | .long 0x800578A2 121 | close: 122 | .long 0x80057912 123 | PrintStandardErrorString: 124 | .long 0x80065998 125 | 126 | buf: 127 | .long 0x8CFE0000 128 | 129 | num_bytes: 130 | .long 0x000000FF 131 | 132 | .align 2 !The6p4c wrote .align 4, but I think .align 2 works, too. 133 | open_str: 134 | .string "open" 135 | .align 2 136 | fstat_str: 137 | .string "fstat" 138 | .align 2 139 | read_str: 140 | .string "read" 141 | .align 2 142 | close_str: 143 | .string "close" 144 | 145 | .align 2 146 | file_path: 147 | .string "\\fls0\\run.bin" 148 | 149 | 150 | .align 2 151 | setCursor: 152 | .long 0x8002E430 153 | print: 154 | .long 0x8002DA0C 155 | LCD_Refresh: 156 | .long 0x8003733E 157 | waitkey: 158 | .long 0x80094380 159 | reset: 160 | .long 0x80000000 161 | xpa: 162 | .long 0x8c000000 + ((317 + (507*320))*2) 163 | xc: 164 | .word 0x5ACB 165 | .align 2 166 | line1: 167 | .string "Can not use the" 168 | .align 2 169 | line2: 170 | .string "hollyhock launcher" 171 | .align 2 172 | line3: 173 | .string "right now!" 174 | .align 2 175 | line4: 176 | .string "Press Clear to reboot." 177 | -------------------------------------------------------------------------------- /sdk/os/gui/dialog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * Creates a dialog. 6 | * 7 | * @param height The height of the dialog. 8 | * @param alignment The screen position to align the dialog with. 9 | * @param[in] title The string to display in the title bar of the dialog. 10 | * @param keyboard The keyboard to display when the dialog is shown. 11 | */ 12 | GUIDialog::GUIDialog( 13 | enum Height height, enum Alignment alignment, 14 | const char* title, 15 | enum KeyboardState keyboard 16 | ) : m_vtable({}) { 17 | m_wrapped = GUIDialog_ctor( 18 | 0, 19 | height, alignment, 20 | title, 21 | 0, 0, 22 | keyboard 23 | ); 24 | 25 | // Save the old vtable 26 | m_oldVTable = GetWrapped()->vtable; 27 | 28 | // Copy the pre-existing vtable 29 | memcpy(&m_vtable, GetWrapped()->vtable, sizeof(m_vtable)); 30 | 31 | m_vtable.me = this; 32 | m_vtable.OnEvent.func = reinterpret_cast(OnEvent_Wrap); 33 | 34 | GetWrapped()->vtable = &m_vtable; 35 | } 36 | 37 | int GUIDialog::OnEvent_Wrap(struct GUIDialog_Wrapped *dialog, struct GUIDialog_OnEvent_Data *event) { 38 | // Call the OnEvent function of our class (not of CASIO's one!) 39 | return dialog->vtable->me->OnEvent(dialog, event); 40 | } 41 | 42 | // TODO: remove the dialog param and update apps 43 | int GUIDialog::OnEvent(struct GUIDialog_Wrapped *dialog, struct GUIDialog_OnEvent_Data *event) { 44 | // We've overidden the entry for OnEvent in our vtable with one that 45 | // redirects to this method. Since we've done that, if we want to call the 46 | // actual base OnEvent method, we use the old vtable we backed up in the 47 | // constructor. 48 | return m_oldVTable->OnEvent(m_wrapped, event); 49 | } 50 | 51 | /** 52 | * Returns the X position of the left edge of the dialog body, in pixels. 53 | * 54 | * @return The X position of the left edge of the dialog body, in pixels. 55 | */ 56 | uint16_t GUIDialog::GetLeftX() { 57 | return GetWrapped()->leftX; 58 | } 59 | 60 | /** 61 | * Returns the Y position of the top edge of the dialog body, in pixels. 62 | * 63 | * @return The Y position of the top edge of the dialog body, in pixels. 64 | */ 65 | uint16_t GUIDialog::GetTopY() { 66 | return GetWrapped()->topY; 67 | } 68 | 69 | /** 70 | * Returns the X position of the right edge of the dialog body, in pixels. 71 | * 72 | * @return The X position of the right edge of the dialog body, in pixels. 73 | */ 74 | uint16_t GUIDialog::GetRightX() { 75 | return GetWrapped()->rightX; 76 | } 77 | 78 | /** 79 | * Returns the Y position of the bottom edge of the dialog body, in pixels. 80 | * 81 | * @return The Y position of the bottom edge of the dialog body, in pixels. 82 | */ 83 | uint16_t GUIDialog::GetBottomY() { 84 | return GetWrapped()->bottomY; 85 | } 86 | 87 | /** 88 | * Adds a GUI element to the dialog. 89 | * 90 | * @param element The GUI element to add. 91 | */ 92 | void GUIDialog::AddElement(GUIElement &element) { 93 | GetWrapped()->vtable->AddElement( 94 | m_wrapped, 95 | element.GetWrapped(), 1 96 | ); 97 | } 98 | 99 | /** 100 | * Refreshes the dialog, redrawing all components. 101 | */ 102 | void GUIDialog::Refresh() { 103 | GetWrapped()->vtable->Refresh(m_wrapped); 104 | } 105 | 106 | /** 107 | * Presents the dialog to the user. Blocks until the dialog is closed. 108 | * 109 | * @return The result of the dialog. 110 | */ 111 | GUIDialog::DialogResult GUIDialog::ShowDialog() { 112 | return static_cast( 113 | GetWrapped()->vtable->ShowDialog(m_wrapped) 114 | ); 115 | } 116 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/gui/dialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "util.hpp" 4 | 5 | // pre-declare the class, since a pointer to it is placed in the vtable 6 | class GUIDialog; 7 | 8 | /// @private 9 | struct GUIDialog_Wrapped_VTable { 10 | /** 11 | * Stores the address of the @ref GUIDialog object this vtable belongs to. 12 | * Should be set by the constructor of @ref GUIDialog. 13 | * 14 | * In the original vtable, the first dword is zero. Since it appears to 15 | * never be accessed, and the vtables in the firmware are stored in ROM (i.e 16 | * even if the firmware wanted to change the value, it couldn't) we're safe 17 | * to use it for our own purposes. 18 | * 19 | * That purpose is to link the vtable to an instance of the @ref GUIDialog. 20 | * Since we can't populate the vtable with instance function pointers, we 21 | * have to use a static function. Without this entry, the static function 22 | * would have no idea which @ref GUIDialog instance the function call was 23 | * associated with. 24 | */ 25 | GUIDialog *me; 26 | uint32_t fakeentrypadding[2]; 27 | 28 | VTABLE_FAKE_ENTRY(1, 0); 29 | 30 | // Args: event 31 | // Return: unknown? 32 | VTableFunction OnEvent; 33 | 34 | VTABLE_FAKE_ENTRY(1, 1); 35 | 36 | // Args: element, unknown0 37 | // unknown0 - always pass 0 38 | VTableFunction AddElement; 39 | 40 | VTABLE_FAKE_ENTRY(4, 2); 41 | 42 | VTableFunction Refresh; 43 | 44 | VTABLE_FAKE_ENTRY(23, 3); 45 | 46 | // Return: dialog result 47 | VTableFunction ShowDialog; 48 | 49 | VTABLE_FAKE_ENTRY(20, 4); 50 | }; 51 | 52 | /// @private 53 | struct GUIDialog_Wrapped { 54 | uint8_t unknown0[0x10]; 55 | 56 | // refer to accessors in GUIDialog class for documentation 57 | uint16_t leftX; 58 | uint16_t topY; 59 | uint16_t rightX; 60 | uint16_t bottomY; 61 | 62 | uint8_t unknown1[0x34]; 63 | 64 | struct GUIDialog_Wrapped_VTable *vtable; 65 | 66 | uint8_t unknown2[0x58]; 67 | }; 68 | static_assert(sizeof(struct GUIDialog_Wrapped) == 0xA8); 69 | 70 | struct GUIDialog_OnEvent_Data { 71 | uint16_t type; 72 | 73 | /// Arbitary data. Usage dependent on event and element type. 74 | uint16_t data; 75 | 76 | /// The pointer to the internal GUI element class the event refers to. 77 | void *element; 78 | 79 | /** 80 | * Returns the event ID encoded in this event's @c type. This is the value 81 | * which would have been passed in to the @c eventID parameter when the 82 | * GUI element was created. 83 | * 84 | * @returns The event ID of the event. 85 | */ 86 | constexpr uint16_t GetEventID() { 87 | return (type >> 4) - 8; 88 | } 89 | }; 90 | 91 | class GUIDialog : public Wrapped { 92 | public: 93 | enum Height : int { 94 | Height25 = 0, 95 | Height55 = 1, 96 | Height75 = 2, 97 | Height95 = 3, 98 | Height35 = 4, 99 | Height60 = 5 100 | }; 101 | 102 | enum Alignment : int { 103 | AlignTop = 0, 104 | AlignCenter = 1, 105 | AlignBottom = 2 106 | }; 107 | 108 | enum KeyboardState : int { 109 | KeyboardStateNone = 0, // 2 gives same effect 110 | KeyboardStateMath1 = 1, // 3 gives same effect 111 | KeyboardStateMath2 = 4, 112 | KeyboardStateMath3 = 5, 113 | KeyboardStateTrig = 6, 114 | KeyboardStateVar = 7, 115 | KeyboardStateABC = 8, 116 | KeyboardStateCatalog = 9, 117 | KeyboardStateAdvance = 10, 118 | KeyboardStateNumber = 11 119 | }; 120 | 121 | enum DialogResult : int { 122 | DialogResultOK = 0x3EA, 123 | DialogResultCancel = 0x3EB 124 | }; 125 | 126 | GUIDialog( 127 | enum Height height, enum Alignment alignment, 128 | const char* title, 129 | enum KeyboardState keyboard 130 | ); 131 | 132 | virtual int OnEvent(struct GUIDialog_Wrapped *dialog, struct GUIDialog_OnEvent_Data *event); 133 | 134 | uint16_t GetLeftX(); 135 | uint16_t GetTopY(); 136 | uint16_t GetRightX(); 137 | uint16_t GetBottomY(); 138 | 139 | void AddElement(GUIElement &element); 140 | void Refresh(); 141 | DialogResult ShowDialog(); 142 | 143 | private: 144 | struct GUIDialog_Wrapped_VTable *m_oldVTable; 145 | struct GUIDialog_Wrapped_VTable m_vtable; 146 | 147 | static int OnEvent_Wrap(struct GUIDialog_Wrapped *dialog, struct GUIDialog_OnEvent_Data *event); 148 | }; 149 | 150 | /// @cond INTERNAL 151 | extern "C" 152 | struct GUIDialog_Wrapped *GUIDialog_ctor( 153 | void *dialog, 154 | int height, int alignment, 155 | const char *title, 156 | int unknown2, int unknown3, 157 | int keyboard 158 | ); 159 | /// @endcond 160 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/debug.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions useful during debugging. 4 | * 5 | * It can be useful to print text or numbers to the screen whilst debugging, 6 | * without creating a full GUI, or pause execution and wait for a key press. 7 | * Thoroughly recommended only for debugging - prefer GUI elements for 8 | * user-facing input/output! 9 | * 10 | * Example: 11 | * @code{cpp} 12 | * // Print "Hello, world!" at 0, 0 in black on white 13 | * Debug_SetCursorPosition(0, 0); 14 | * Debug_PrintString("Hello, world!", false); 15 | * 16 | * // Print "Inverted text" at 1, 1 in white on black 17 | * Debug_SetCursorPosition(1, 1); 18 | * Debug_PrintString("Inverted text", true); 19 | * 20 | * // Print the number 0x1322 at 3, 7 21 | * Debug_PrintNumberHex_Word(0x1322, 3, 7); 22 | * 23 | * // Print small text with a format string 24 | * Debug_Printf(20, 20, false, 0, "Format strings are %s in %d!", "cool", 2018); 25 | * 26 | * // Draw the changes we made to VRAM onto the LCD 27 | * // Defined in sdk/os/lcd.hpp 28 | * LCD_Refresh(); 29 | * 30 | * // Block until the user presses a key 31 | * Debug_WaitKey(); 32 | * @endcode 33 | */ 34 | 35 | #pragma once 36 | #include 37 | 38 | /** 39 | * Returns the current position of the cursor in debug text mode. 40 | * 41 | * @param[out] x,y The position of the cursor. 42 | */ 43 | extern "C" 44 | void Debug_GetCursorPosition(int *x, int *y); 45 | 46 | /** 47 | * Print a formatted string in small debug text mode, either in normal 48 | * black-on-white or inverted white-on-black. Color inversion occurs if 49 | * @p invert is true. 50 | * 51 | * Supports most format specifiers. 52 | * 53 | * @param x,y The coordinates to print the formatted string at. 54 | * @param invert True if the colors used to print the text should be inverted. 55 | * @param zero Must be passed 0. 56 | * @param[in] format The format string to use. 57 | * @param ... The values to substitute into the format string. 58 | */ 59 | extern "C" 60 | void Debug_Printf(int x, int y, bool invert, int zero, const char *format, ...); 61 | 62 | /** 63 | * Prints the hex representation of a byte (8-bit number) at the specified 64 | * position in debug text mode. 65 | * 66 | * @param value The byte to print. 67 | * @param x,y The coordinates to print the number at. 68 | */ 69 | extern "C" 70 | void Debug_PrintNumberHex_Byte(uint8_t value, int x, int y); 71 | 72 | /** 73 | * Prints the hex representation of a dword (32-bit number) at the specified 74 | * position in debug text mode. 75 | * 76 | * @param value The dword to print. 77 | * @param x,y The coordinates to print the number at. 78 | */ 79 | extern "C" 80 | void Debug_PrintNumberHex_Dword(uint32_t value, int x, int y); 81 | 82 | /** 83 | * Prints the hex representation of a nibble (4-bit number) at the specified 84 | * position in debug text mode. 85 | * 86 | * @param value The nibble to print. High 4 bits are ignored. 87 | * @param x,y The coordinates to print the number at. 88 | */ 89 | extern "C" 90 | void Debug_PrintNumberHex_Nibble(uint8_t value, int x, int y); 91 | 92 | /** 93 | * Prints the hex representation of a word (16-bit number) at the specified 94 | * position in debug text mode. 95 | * 96 | * @param value The word to print. 97 | * @param x,y The coordinates to print the number at. 98 | */ 99 | extern "C" 100 | void Debug_PrintNumberHex_Word(uint16_t value, int x, int y); 101 | 102 | /** 103 | * Prints a string in debug text mode, either in normal black-on-white or 104 | * inverted white-on-black. Color inversion occurs if \c invert is true. 105 | * 106 | * Returns false if the string did not fit on the screen. 107 | * 108 | * @param[in] string The string to print. 109 | * @param invert True if the colors used to print the text should be inverted. 110 | * @return True if writing the string was successful, false otherwise. 111 | */ 112 | extern "C" 113 | bool Debug_PrintString(const char *string, bool invert); 114 | 115 | /** 116 | * Sets the position of the cursor in debug text mode. 117 | * 118 | * @param x,y The cursor's new position. 119 | * @return Always returns 0. 120 | */ 121 | extern "C" 122 | int Debug_SetCursorPosition(int x, int y); 123 | 124 | /** 125 | * Waits until a key is pressed, then returns a number representing the key. 126 | * Only appears to react to number keys and the Power/Clear key. Returns 0x30 127 | * to 0x39 for keys 0 to 9, and 0x98 for the Power/Clear key. 128 | * 129 | * @return A number representing the key that was pressed. 130 | */ 131 | extern "C" 132 | int Debug_WaitKey(); 133 | -------------------------------------------------------------------------------- /demos/demo_gui/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | APP_NAME("Demo GUI") 7 | 8 | class MyDialog : public GUIDialog { 9 | public: 10 | MyDialog() : GUIDialog( 11 | GUIDialog::Height55, GUIDialog::AlignTop, 12 | "My Demo Dialog", 13 | GUIDialog::KeyboardStateABC 14 | ), m_label( 15 | // The position of all GUI elements should be specified in relation to 16 | // the bounding box of the dialog. This means that if the height or 17 | // alignment of the dialog is changed, the GUI elements will move 18 | // accordingly. 19 | GetLeftX() + 10, GetTopY() + 10, 20 | "Label 0 Text" 21 | ), m_buttonOK( 22 | // Some controls take a bounding box, rather than an XY position. 23 | GetLeftX() + 10, GetTopY() + 40, 24 | // With these controls, it's a lot clearer to specify the right/bottom 25 | // coords by repeating the left/top and adding the width/height. 26 | // Here, a width of 100 and height of 35 is used. 27 | GetLeftX() + 10 + 120, GetTopY() + 40 + 35, 28 | "Close (OK)", 29 | BUTTON_OK_EVENT_ID 30 | ), m_buttonCancel( 31 | // This button is specified from the right edge of the dialog, rather 32 | // than the left edge like the OK button. 33 | GetRightX() - 10 - 120, GetTopY() + 40, 34 | GetRightX() - 10, GetTopY() + 40 + 35, 35 | "Close (Cancel)", 36 | BUTTON_CANCEL_EVENT_ID 37 | ), m_buttonTest( 38 | GetLeftX() + 10, GetTopY() + 85, 39 | GetRightX() - 10, GetTopY() + 85 + 35, 40 | "Test", 41 | BUTTON_TEST_EVENT_ID 42 | ), m_textBox( 43 | GetLeftX() + 10, GetTopY() + 130, GetRightX() - GetLeftX() - 10, 44 | "Initial text box text", 45 | // Specify the maximum length of the text box's content, and if that 46 | // length should be counted by bytes or by characters (there's no 47 | // distinction if using normal characters, but 1 wide character is 2 48 | // bytes). 49 | 100, false 50 | ), m_longLabel( 51 | GetLeftX() + 10, GetTopY() + 160, GetRightX() - 10, GetBottomY() - 10, 52 | "Long labels like these allow text wrapping, and support newlines. They're great for lots of text!\nHere's some text, after a newline." 53 | ) { 54 | // Every element of the dialog must be added by calling AddElement. 55 | // Calling the element's constructor isn't enough! 56 | AddElement(m_label); 57 | AddElement(m_buttonOK); 58 | AddElement(m_buttonCancel); 59 | AddElement(m_buttonTest); 60 | AddElement(m_textBox); 61 | AddElement(m_longLabel); 62 | } 63 | 64 | // This is the method that's called when an event occurs. 65 | // The most important information about the event is contained within the 66 | // 'event' parameter. 67 | virtual int OnEvent(struct GUIDialog_Wrapped *dialog, struct GUIDialog_OnEvent_Data *event) { 68 | // Check the ID of the event, to see if it matches an event assigned to 69 | // one of our controls. 70 | if (event->GetEventID() == BUTTON_TEST_EVENT_ID) { 71 | m_textBox.SetText("Test button pressed!"); 72 | 73 | // Refresh the dialog, to ensure the text box's updated content is 74 | // drawn. 75 | Refresh(); 76 | 77 | // Since we processed the event, return 0. 78 | return 0; 79 | } 80 | 81 | // If we don't process the event, let the base class process it. 82 | return GUIDialog::OnEvent(dialog, event); 83 | } 84 | 85 | private: 86 | // Each of the elements in our GUI is a property of our dialog's class. 87 | // They're instantiated in the constructor. 88 | GUILabel m_label; 89 | 90 | // To make a button close the dialog, we can use a DialogResult for the 91 | // event ID. When one of these buttons causes the dialog to close, the 92 | // DialogResult is the value returned from ShowDialog. 93 | const uint16_t BUTTON_OK_EVENT_ID = DialogResultOK; 94 | GUIButton m_buttonOK; 95 | 96 | const uint16_t BUTTON_CANCEL_EVENT_ID = DialogResultCancel; 97 | GUIButton m_buttonCancel; 98 | 99 | // When not using a DialogResult for an event ID, start incrementing from 1. 100 | const uint16_t BUTTON_TEST_EVENT_ID = 1; 101 | GUIButton m_buttonTest; 102 | 103 | GUITextBox m_textBox; 104 | 105 | GUILongLabel m_longLabel; 106 | }; 107 | 108 | void main() { 109 | // Create an instance of our dialog - but don't show it yet. 110 | MyDialog dialog; 111 | 112 | // Call ShowDialog on our dialog to display it to the user. 113 | // This call blocks until the user or system closes the dialog, which causes 114 | // the ShowDialog method to return a value specifying how the dialog was 115 | // closed. 116 | GUIDialog::DialogResult result = dialog.ShowDialog(); 117 | 118 | Debug_Printf( 119 | 0, 0, false, 0, 120 | "Dialog closed with result %X", result 121 | ); 122 | LCD_Refresh(); 123 | } 124 | -------------------------------------------------------------------------------- /sdk/calc/calc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint16_t *vram; 4 | int width; 5 | int height; 6 | 7 | //Draw a line (bresanham line algorithm) 8 | void line(int x1, int y1, int x2, int y2, uint16_t color){ 9 | int8_t ix, iy; 10 | 11 | int dx = (x2>x1 ? (ix=1, x2-x1) : (ix=-1, x1-x2) ); 12 | int dy = (y2>y1 ? (iy=1, y2-y1) : (iy=-1, y1-y2) ); 13 | 14 | setPixel(x1,y1,color); 15 | if(dx>=dy){ //the derivative is less than 1 (not so steep) 16 | //y1 is the whole number of the y value 17 | //error is the fractional part (times dx to make it a whole number) 18 | // y = y1 + (error/dx) 19 | //if error/dx is greater than 0.5 (error is greater than dx/2) we add 1 to y1 and subtract dx from error (so error/dx is now around -0.5) 20 | int error = 0; 21 | while (x1!=x2) { 22 | x1 += ix; //go one step in x direction 23 | error += dy;//add dy/dx to the y value. 24 | if (error>=(dx>>1)){ //If error is greater than dx/2 (error/dx is >=0.5) 25 | y1+=iy; 26 | error-=dx; 27 | } 28 | setPixel(x1,y1,color); 29 | } 30 | }else{ //the derivative is greater than 1 (very steep) 31 | int error = 0; 32 | while (y1!=y2) { //The same thing, just go up y and look at x 33 | y1 += iy; //go one step in y direction 34 | error += dx;//add dx/dy to the x value. 35 | if (error>=(dy>>1)){ //If error is greater than dx/2 (error/dx is >=0.5) 36 | x1+=ix; 37 | error-=dy; 38 | } 39 | setPixel(x1,y1,color); 40 | } 41 | } 42 | } 43 | 44 | void vline(int x, int y1, int y2, uint16_t color){ //vertical line needed for triangle() 45 | if (y1>y2) { int z=y2; y2=y1; y1=z;} 46 | for (int y=y1; y<=y2; y++) 47 | setPixel(x,y,color); 48 | } 49 | 50 | //Draw a filled triangle. 51 | void triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint16_t colorFill, uint16_t colorLine){ 52 | //Filled triangles are a lot of vertical lines. 53 | /* - 54 | a ___________----------P3 - 55 | P0 _________--------- ____--- - 56 | ---____ _____----- - 57 | b ----___ _----- c - 58 | P2 - 59 | The triangle has three points P0, P1 and P2 and three lines a, b and c. We go from left to right, calculating the point on a and the point on b or c and then we draw a vertical line connecting these two. 60 | */ 61 | 62 | //Sort the points by x coordinate 63 | { 64 | int z; 65 | if(x0>x2){ z=x2; x2=x0; x0=z; z=y2; y2=y0; y0=z; } 66 | if(x1>x2){ z=x2; x2=x1; x1=z; z=y2; y2=y1; y1=z; } 67 | if(x0>x1){ z=x1; x1=x0; x0=z; z=y1; y1=y0; y0=z; } 68 | } 69 | 70 | int x = x0; //x is the variable that counts from left to right 71 | 72 | //Values for line a 73 | int ay = y0; //The point y for the current x on the line a 74 | int aiy; //The direction of line a 75 | int adx = (x2>x0 ? ( x2-x0) : ( x0-x2) ); 76 | int ady = (y2>y0 ? (aiy=1, y2-y0) : (aiy=-1, y0-y2) ); 77 | int aerr = 0; //The y value of a (fractional part). y is actually ay+(aerr/adx) 78 | 79 | //Values for line b 80 | int by = y0; //The point y for the current x on the line b 81 | int biy; //The direction of line b 82 | int bdx = (x1>x0 ? ( x1-x0) : ( x0-x1) ); 83 | int bdy = (y1>y0 ? (biy=1, y1-y0) : (biy=-1, y0-y1) ); 84 | int berr = 0; 85 | 86 | //Values for line c 87 | int cy = y1; //The point y for the current x on the line y (starting at P1) 88 | int ciy; //The direction of line c 89 | int cdx = (x2>x1 ? ( x2-x1) : ( x1-x2) ); 90 | int cdy = (y2>y1 ? (ciy=1, y2-y1) : (ciy=-1, y1-y2) ); 91 | int cerr = 0; 92 | 93 | //First draw area between a and b 94 | while (x=adx >> 2){ //if aerr/adx >= 0.5 98 | aerr-=adx; 99 | ay+=aiy; 100 | } 101 | berr+=bdy; 102 | while(berr>=bdx >> 2){ //if berr/bdx >= 0.5 103 | berr-=bdx; 104 | by+=biy; 105 | } 106 | vline(x,ay,by,colorFill); 107 | } 108 | 109 | //Then draw area between a and c 110 | while (x=adx >> 2){ //if aerr/adx >= 0.5 114 | aerr-=adx; 115 | ay+=aiy; 116 | } 117 | cerr+=cdy; 118 | while(cerr>=cdx >> 2){ //if berr/bdx >= 0.5 119 | cerr-=cdx; 120 | cy+=ciy; 121 | } 122 | vline(x,ay,cy,colorFill); 123 | } 124 | 125 | line(x0,y0,x1,y1,colorLine); 126 | line(x1,y1,x2,y2,colorLine); 127 | line(x2,y2,x0,y0,colorLine); 128 | } 129 | 130 | 131 | void fillScreen(uint16_t color){ 132 | const uint32_t size = width * height; 133 | for(uint32_t i = 0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | APP_NAME("Input Test") 8 | APP_DESCRIPTION("Test of the GetInput function.") 9 | APP_AUTHOR("The6P4C") 10 | APP_VERSION("1.0.0") 11 | 12 | struct LUTEntry { 13 | const char *str; 14 | uint32_t keyCode; 15 | }; 16 | 17 | struct LUTEntry lut[] = { 18 | {"KEYBOARD", 0x00B3}, 19 | {"SHIFT", 0x00A0}, 20 | {"BACKSPACE", 0x0097}, 21 | {"POWER_CLEAR", 0x0080}, 22 | {"UP", 0x0090}, 23 | {"DOWN", 0x0091}, 24 | {"LEFT", 0x0092}, 25 | {"RIGHT", 0x0093}, 26 | {"EQUALS", 0x003D}, 27 | {"X", 0xEDB8}, 28 | {"Y", 0xEDB9}, 29 | {"Z", 0xEDBA}, 30 | {"POWER", 0x005E}, 31 | {"DIVIDE", 0x002F}, 32 | {"OPEN_PARENTHESIS", 0x0028}, 33 | {"7", 0x0037}, 34 | {"8", 0x0038}, 35 | {"9", 0x0039}, 36 | {"TIMES", 0xEE10}, 37 | {"CLOSE_PARENTHESIS", 0x0029}, 38 | {"4", 0x0034}, 39 | {"5", 0x0035}, 40 | {"6", 0x0036}, 41 | {"MINUS", 0x002D}, 42 | {"COMMA", 0x002C}, 43 | {"1", 0x0031}, 44 | {"2", 0x0032}, 45 | {"3", 0x0033}, 46 | {"PLUS", 0x002B}, 47 | {"NEGATIVE", 0x001F}, 48 | {"0", 0x0030}, 49 | {"DOT", 0x002E}, 50 | {"EXP", 0x001D}, 51 | {"EXE", 0x0094} 52 | }; 53 | 54 | void printHeader() { 55 | Debug_SetCursorPosition(0, 0); 56 | Debug_PrintString("GetInput demo (clear=quit)", false); 57 | } 58 | 59 | void main() { 60 | struct InputEvent event; 61 | 62 | bool running = true; 63 | 64 | LCD_ClearScreen(); 65 | printHeader(); 66 | LCD_Refresh(); 67 | 68 | while (running) { 69 | memset(&event, 0, sizeof(event)); 70 | GetInput(&event, 0xFFFFFFFF, 0x10); 71 | 72 | switch (event.type) { 73 | case EVENT_KEY: 74 | LCD_ClearScreen(); 75 | 76 | printHeader(); 77 | 78 | Debug_SetCursorPosition(0, 1); 79 | Debug_PrintString("Key Event", true); 80 | 81 | Debug_SetCursorPosition(0, 3); 82 | Debug_PrintString("Direction: ", false); 83 | 84 | switch (event.data.key.direction) { 85 | case KEY_PRESSED: 86 | Debug_PrintString("pressed", false); 87 | break; 88 | case KEY_HELD: 89 | Debug_PrintString("held", false); 90 | break; 91 | case KEY_RELEASED: 92 | Debug_PrintString("released", false); 93 | break; 94 | } 95 | 96 | Debug_SetCursorPosition(0, 4); 97 | Debug_PrintString("Key code: ", false); 98 | 99 | // Wrap in a block to avoid "jump to case label" errors due to the 100 | // definition of the variable i 101 | { 102 | int i = 0; 103 | while (true) { 104 | struct LUTEntry lutEntry = lut[i++]; 105 | 106 | if (event.data.key.keyCode == lutEntry.keyCode) { 107 | Debug_PrintString(lutEntry.str, false); 108 | break; 109 | } 110 | } 111 | } 112 | 113 | LCD_Refresh(); 114 | 115 | if (event.data.key.keyCode == KEYCODE_POWER_CLEAR) { 116 | running = false; 117 | } 118 | break; 119 | case EVENT_TOUCH: 120 | LCD_ClearScreen(); 121 | 122 | printHeader(); 123 | 124 | Debug_SetCursorPosition(0, 1); 125 | Debug_PrintString("Touch Event", true); 126 | 127 | Debug_SetCursorPosition(0, 3); 128 | Debug_PrintString("Direction: ", false); 129 | 130 | switch (event.data.touch_single.direction) { 131 | case TOUCH_DOWN: 132 | Debug_PrintString("down", false); 133 | break; 134 | case TOUCH_HOLD_DRAG: 135 | Debug_PrintString("hold/drag", false); 136 | break; 137 | case TOUCH_ACT_BAR: 138 | Debug_PrintString("act. bar", false); 139 | break; 140 | case TOUCH_UP: 141 | Debug_PrintString("up", false); 142 | break; 143 | } 144 | 145 | Debug_SetCursorPosition(0, 4); 146 | Debug_PrintString("x:", false); 147 | Debug_PrintNumberHex_Dword(event.data.touch_single.p1_x, 3, 4); 148 | 149 | Debug_SetCursorPosition(0, 5); 150 | Debug_PrintString("y:", false); 151 | Debug_PrintNumberHex_Dword(event.data.touch_single.p1_y, 3, 5); 152 | 153 | LCD_Refresh(); 154 | break; 155 | case EVENT_ACTBAR_RESIZE: 156 | case EVENT_ACTBAR_SWAP: 157 | case EVENT_ACTBAR_ROTATE: 158 | case EVENT_ACTBAR_ESC: 159 | case EVENT_ACTBAR_SETTINGS: 160 | LCD_ClearScreen(); 161 | 162 | printHeader(); 163 | 164 | Debug_SetCursorPosition(0, 1); 165 | Debug_PrintString("Act. Bar Event", true); 166 | 167 | Debug_SetCursorPosition(0, 3); 168 | Debug_PrintString("Location: ", false); 169 | 170 | switch (event.type) { 171 | case EVENT_ACTBAR_RESIZE: 172 | Debug_PrintString("Resize", false); 173 | break; 174 | case EVENT_ACTBAR_SWAP: 175 | Debug_PrintString("Swap", false); 176 | break; 177 | case EVENT_ACTBAR_ROTATE: 178 | Debug_PrintString("Rotate", false); 179 | break; 180 | case EVENT_ACTBAR_ESC: 181 | Debug_PrintString("Esc", false); 182 | break; 183 | case EVENT_ACTBAR_SETTINGS: 184 | Debug_PrintString("Settings", false); 185 | break; 186 | } 187 | 188 | LCD_Refresh(); 189 | break; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/lcd.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions for interacting with the LCD and VRAM. 4 | * 5 | * Different levels of indirection are availiable to write to VRAM (direct 6 | * memory access, getter/setters and palette-based drawing). The contents of 7 | * VRAM are not automatically drawn to the LCD, and must be rendered with 8 | * @ref LCD_Refresh. 9 | * 10 | * Example: drawing a 30x50 rectangle at 10, 20 in purple 11 | * @code{cpp} 12 | * uint16_t *vram = LCD_GetVRAMAddress(); 13 | * int width, height; 14 | * LCD_GetSize(&width, &height); 15 | * 16 | * for (int y = 0; y < 50; ++y) { 17 | * for (int x = 0; x < 30; ++x) { 18 | * vram[(x + 10) + (y + 20) * width] = RGB_TO_RGB565(0x1F, 0x3B, 0x08); 19 | * } 20 | * } 21 | * 22 | * // Put our changes on the display 23 | * LCD_Refresh(); 24 | * @endcode 25 | */ 26 | 27 | #pragma once 28 | #include 29 | 30 | /** 31 | * @defgroup palette_colors Palette Colors 32 | * Passed to @ref LCD_SetPixelFromPalette. 33 | * @{ 34 | */ 35 | const uint8_t PALETTE_BLACK = 0; 36 | const uint8_t PALETTE_BLUE = 1; 37 | const uint8_t PALETTE_GREEN = 2; 38 | const uint8_t PALETTE_CYAN = 3; 39 | const uint8_t PALETTE_RED = 4; 40 | const uint8_t PALETTE_MAGENTA = 5; 41 | const uint8_t PALETTE_YELLOW = 6; 42 | const uint8_t PALETTE_WHITE = 7; 43 | /// @} 44 | 45 | /** 46 | * Converts three RGB values into one RGB565 value. 47 | * 48 | * @param r The red component, between 0 and 31 (0x1F) inclusive. 49 | * @param g The green component, between 0 and 63 (0x3F) inclusive. 50 | * @param b The blue component, between 0 and 31 (0x1F) inclusive. 51 | * @return The specified color, in RGB565 format. 52 | */ 53 | #define RGB_TO_RGB565(r, g, b) ( \ 54 | ((r & 0x1F) << 11) | \ 55 | ((g & 0x3F) << 5) | \ 56 | (b & 0x1F) \ 57 | ) 58 | 59 | /** 60 | * Extracts the red component from an RGB565 value. 61 | * 62 | * @param rgb565 The RGB565 value. 63 | * @return The red component. 64 | */ 65 | #define RGB565_TO_R(rgb565) ((rgb565 >> 11) & 0x1F) 66 | 67 | /** 68 | * Extracts the green component from an RGB565 value. 69 | * 70 | * @param rgb565 The RGB565 value. 71 | * @return The green component. 72 | */ 73 | #define RGB565_TO_G(rgb565) ((rgb565 >> 5) & 0x3F) 74 | 75 | /** 76 | * Extracts the blue component from an RGB565 value. 77 | * 78 | * @param rgb565 The RGB565 value. 79 | * @return The blue component. 80 | */ 81 | #define RGB565_TO_B(rgb565) (rgb565 & 0x1F) 82 | 83 | /** 84 | * Clears the LCD. Fills VRAM with white, but does not refresh the LCD. 85 | */ 86 | extern "C" 87 | void LCD_ClearScreen(); 88 | 89 | /** 90 | * Returns the color of a pixel. This is not necessarily the color which is 91 | * being displayed on the LCD, but instead is the color specified for the pixel 92 | * in the VRAM buffer. Color is in RGB565 format. 93 | * 94 | * @param x,y The coordinates of the pixel. 95 | * @return The color of the pixel, in RGB565 format. 96 | */ 97 | extern "C" 98 | uint16_t LCD_GetPixel(int x, int y); 99 | 100 | /** 101 | * Retrieves the size, in pixels, of the LCD. 102 | * 103 | * @param[out] width,height The LCD's size. 104 | */ 105 | extern "C" 106 | void LCD_GetSize(int *width, int *height); 107 | 108 | /** 109 | * Returns a pointer to the video RAM. Video RAM is composed of 110 | * width * height 16-bit integers (in row-major order) representing the 111 | * color at each pixel, in RGB565 format. 112 | * 113 | * @return A pointer to the VRAM. 114 | */ 115 | extern "C" 116 | uint16_t *LCD_GetVRAMAddress(); 117 | 118 | /** 119 | * Pushes the content of the VRAM to the LCD. 120 | */ 121 | extern "C" 122 | void LCD_Refresh(); 123 | 124 | /** 125 | * Sets the color of a pixel. The result of this operation will not be visible 126 | * until @ref LCD_Refresh is called. Color is in RGB565 format. 127 | * 128 | * @param x,y The coordinate of the pixel. 129 | * @param color The color to set the pixel, in RGB565 format. 130 | */ 131 | extern "C" 132 | void LCD_SetPixel(int x, int y, uint16_t color); 133 | 134 | /** 135 | * Sets the color of a pixel, from a pre-defined palette. Result is not visible 136 | * until @ref LCD_Refresh is called. See @ref palette_colors. 137 | * 138 | * @param x,y The coordinate of the pixel. 139 | * @param index The index of the color in the palette to use. 140 | */ 141 | extern "C" 142 | void LCD_SetPixelFromPalette(int x, int y, uint8_t index); 143 | 144 | /** 145 | * Backs up the current contents of VRAM. 146 | * 147 | * Should be used to prevent display corruption when writing directly to VRAM. 148 | * 149 | * Used in conjunction with @ref LCD_VRAMRestore. 150 | */ 151 | extern "C" 152 | void LCD_VRAMBackup(); 153 | 154 | /** 155 | * Restores the backed up contents of VRAM. The restored content is not 156 | * displayed until @ref LCD_Refresh is called. 157 | * 158 | * Used in conjunction with @ref LCD_VRAMBackup. 159 | */ 160 | extern "C" 161 | void LCD_VRAMRestore(); 162 | -------------------------------------------------------------------------------- /launcher/bins.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bins.hpp" 5 | #include 6 | 7 | #define hex2asc(x) ((x)>9?((x)+'A'-10):((x)+'0')) 8 | 9 | #define Serial_print(s) Serial_Write((const unsigned char*)s,sizeof(s)-1) 10 | 11 | #define Serial_printhex(x) Serial_WriteSingle( hex2asc( ((x)>>28)&0xf ) );\ 12 | Serial_WriteSingle( hex2asc( ((x)>>24)&0xf ) );\ 13 | Serial_WriteSingle( hex2asc( ((x)>>20)&0xf ) );\ 14 | Serial_WriteSingle( hex2asc( ((x)>>16)&0xf ) );\ 15 | Serial_WriteSingle( hex2asc( ((x)>>12)&0xf ) );\ 16 | Serial_WriteSingle( hex2asc( ((x)>> 8)&0xf ) );\ 17 | Serial_WriteSingle( hex2asc( ((x)>> 4)&0xf ) );\ 18 | Serial_WriteSingle( hex2asc( ((x)>> 0)&0xf ) ); 19 | 20 | class File { 21 | public: 22 | File() : m_opened(false), m_fd(-1) { 23 | 24 | } 25 | 26 | ~File() { 27 | if (m_opened) { 28 | close(m_fd); 29 | } 30 | } 31 | 32 | int open(const char *path, int flags) { 33 | m_fd = ::open(path, flags); 34 | m_opened = true; 35 | return m_fd; 36 | } 37 | 38 | int getAddr(int offset, const void **addr) { 39 | return ::getAddr(m_fd, offset, addr); 40 | } 41 | 42 | int read(void *buf, int count) { 43 | return ::read(m_fd, buf, count); 44 | } 45 | 46 | private: 47 | bool m_opened; 48 | int m_fd; 49 | }; 50 | 51 | class Find { 52 | public: 53 | Find() : m_opened(false), m_findHandle(-1) { 54 | 55 | } 56 | 57 | ~Find() { 58 | if (m_opened) { 59 | findClose(m_findHandle); 60 | } 61 | } 62 | 63 | int findFirst(const wchar_t *path, wchar_t *name, struct findInfo *findInfoBuf) { 64 | int ret = ::findFirst(path, &m_findHandle, name, findInfoBuf); 65 | m_opened = true; 66 | return ret; 67 | } 68 | 69 | int findNext(wchar_t *name, struct findInfo *findInfoBuf) { 70 | return ::findNext(m_findHandle, name, findInfoBuf); 71 | } 72 | 73 | private: 74 | bool m_opened; 75 | int m_findHandle; 76 | }; 77 | 78 | namespace Bins { 79 | const char *BIN_FOLDER[] = { 80 | "\\fls0\\", 81 | "\\drv0\\" 82 | }; 83 | const char FILE_MASK[] = "*.bin"; 84 | 85 | struct AppInfo g_apps[MAX_APPS]; 86 | int g_numApps; 87 | 88 | void LoadApp(const char *folder, wchar_t *fileName) { 89 | struct AppInfo app; 90 | memset(&app, 0, sizeof(app)); 91 | 92 | // copy the file name (converting to a non-wide string in the 93 | // process) 94 | for (int i = 0; i < 100; ++i) { 95 | wchar_t c = fileName[i]; 96 | app.fileName[i] = c; 97 | if (c == 0x0000) { 98 | break; 99 | } 100 | } 101 | 102 | // build the path 103 | //strcat(app.path, "\\fls0\\"); 104 | strcat(app.path, folder); 105 | strcat(app.path, app.fileName); 106 | 107 | File f; 108 | int ret = f.open(app.path, OPEN_READ); 109 | if (ret < 0) { 110 | return; 111 | } 112 | char *binInfo; 113 | f.getAddr(0x10, (const void**)&binInfo); 114 | 115 | if(*binInfo>=32&&*binInfo<127){ 116 | for(int i=0;*binInfo!=0;i++) 117 | app.name[i] = *(binInfo++); 118 | while(*binInfo==0)binInfo++; 119 | for(int i=0;*binInfo!=0;i++) 120 | app.description[i] = *(binInfo++); 121 | while(*binInfo==0)binInfo++; 122 | for(int i=0;*binInfo!=0;i++) 123 | app.author[i] = *(binInfo++); 124 | while(*binInfo==0)binInfo++; 125 | for(int i=0;*binInfo!=0;i++) 126 | app.version[i] = *(binInfo++); 127 | } 128 | g_apps[g_numApps++] = app; 129 | } 130 | 131 | void LoadAppInfo() { 132 | g_numApps = 0; 133 | 134 | for (unsigned int dirNr=0; dirNr= 0) { 154 | if (findInfoBuf.type == findInfoBuf.EntryTypeFile) { 155 | const char* exclude = "run.bin"; 156 | char different = 0; 157 | for(unsigned int i=0;ipath, OPEN_READ); 172 | if (ret < 0) { 173 | return nullptr; 174 | } 175 | //get address where the program should be loaded 176 | unsigned char* address; 177 | f.getAddr(0x0c, (const void**)&address); 178 | EntryPoint entrypoint = (EntryPoint)0x8cff0000; 179 | if (address[0]==0x8c) 180 | entrypoint = (EntryPoint)((address[0]<<24) + (address[1]<<16) + (address[2]<<8) + (address[3])); 181 | 182 | int length = f.read((void*)entrypoint,0x20000); 183 | //unsigned char mode[] = {0,5,0,0,0,0}; 184 | //Serial_Open(mode); 185 | //Serial_print("The length is: 0x"); 186 | //Serial_printhex(length); 187 | //unsigned char c; 188 | //while(c!='\n') { 189 | // Serial_ReadSingle(&c); 190 | // if(c=='.'){ 191 | // Serial_Close(0); 192 | // return nullptr; 193 | // } 194 | //} 195 | //Serial_Close(0); 196 | return entrypoint; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /doc/user/developing.md: -------------------------------------------------------------------------------- 1 | # Developing for the fx-CP400 2 | ## I (SnailMath) have made a video, where I explained this. Just click [here: youtube.com/playlist...](https://www.youtube.com/playlist?list=PLrNL2wv7MwKqpz-BhOiwH_1DbS690ZDs4) 3 | 4 | You'll need your SH4 cross compiler to develop applications for the fx-CP400. If you patched your firmware, you'll already have done this. If not, check the instructions in [this](patching.md) document to build and install one. 5 | 6 | ## 1. Build the SDK 7 | This only needs to happen once to generate the required object files which will be linked with your application when it's compiled. It isn't necessary to rebuild the SDK itself whenever you change your application code. 8 | 9 | `cd` into the `sdk/` directory, and run the `make` command. Ensure the file `sdk.o` is generated - this is the boilerplate code required for your applications to work when compiled. 10 | 11 | If you'd like a local copy of the SDK documentation, run the `make docs` command. Open `sdk/doc/index.html` to view them. 12 | 13 | _I would recommend adding `export PATH="$PATH:$HOME/opt/cross/bin"` to the file .bashrc in the home directory to add the path automatically after every login._ 14 | _I would recommend adding `export SDK_DIR="PATH-TO/hollyhock-2/sdk"` to the file .bashrc in the home directory to export the sdk dir automatically after every login._ 15 | 16 | ## 2. Start your project 17 | Copy the contents of the `app_template/` directory to an empty folder. This will become your project's root directory. 18 | 19 | Open `main.cpp` in your favourite text editor, and edit the strings in the `APP_NAME`, `APP_DESCRIPTION`, `APP_AUTHOR` and `APP_VERSION` macro calls to match your application. These are the strings that will be displayed in the launcher when your application is selected. If you don't wish to provide one or more of these fields, simply delete the line (they're all optional). 20 | 21 | Similarly, open the `Makefile` and edit the first line (`APP_NAME:=app_template` by default), changing `app_template` to the filename you'd like your generated `.hhk` file to have. 22 | 23 | Edit `main.cpp` to your hearts content, and add other `.cpp`, `.c`, and `.s` files as you please. The `Makefile` will automatically detect new source files at compile time. 24 | 25 | ## 3. Get ready to build 26 | You'll need to point the Makefile to the SDK using the `SDK_DIR` environment variable. For example, if you cloned this repository to `~/hollyhock-2/`, set `SDK_DIR` to `~/hollyhock-2/sdk/`. You can do this either by exporting the variable before running any `make` commands, or setting the variable in the same command as you run make. It's recommended you make this an absolute path and not a relative one - otherwise you may run into issues if you attempt to call `make` while not in the same directory as the one you defined `SDK_DIR` relative to. 27 | 28 | ```sh 29 | # Either do this once, at the start of every session (recommended): 30 | export SDK_DIR=~/hollyhock-2/sdk/ 31 | 32 | # Or, run make like this: 33 | SDK_DIR=~/hollyhock-2/sdk/ make 34 | ``` 35 | 36 | ## 4. Build your app! 37 | Run `make` (remembering to set `SDK_DIR` inline if you didn't `export` it). 38 | 39 | A `.hhk` file with the name you specified in the `Makefile` will be generated, which you can then copy onto the root directory of the calculator's flash. 40 | 41 | Open the launcher and select your application to launch it. Have fun! 42 | 43 | ## Newlib 44 | If you want to use C standard libraries such as math.h, string.h and others when developing for the fx-CP400, you must have a standard library implementation such as Newlib installed. Newlib also includes division and other arithmetic subroutines for our SuperH CPU, so that you do not have to always add them to your project manualy when you need them. 45 | 46 | First, you will have to define a few environment variables. The `$PREFIX` variable can be changed to your prefered installation directory but `$TARGET` should be left untouched. 47 | 48 | ```sh 49 | # Directory to install newlib into 50 | # You most likely already have this defined if you have built a cross platform gcc 51 | # I recommend you to change it to the SDK's directory like this: 52 | export PREFIX="$SDK_DIR/newlib" 53 | 54 | # Our target architecture 55 | # Again, you might already have $TARGET defined as "sh4-elf" 56 | # Make sure to change it to "sh-elf" 57 | export TARGET="sh-elf" 58 | 59 | # The prefix of our cross compiler's binaries such as gcc, as, ld, etc ... 60 | export TARGET_BINS="sh4-elf" 61 | ``` 62 | 63 | After that, grab the latest stable version of the Newlib source code from [their website](https://sourceware.org/newlib/) and extract it. Then run the following commands. 64 | 65 | _Make sure to change `VERSION` to your newlib version._ 66 | 67 | ```sh 68 | mkdir build-newlib 69 | cd build-newlib 70 | ../newlib-VERSION/configure --target=$TARGET --prefix=$PREFIX CC_FOR_TARGET=${TARGET_BINS}-gcc AS_FOR_TARGET=${TARGET_BINS}-as LD_FOR_TARGET=${TARGET_BINS}-ld AR_FOR_TARGET=${TARGET_BINS}-ar RANLIB_FOR_TARGET=${TARGET_BINS}-ranlib 71 | make all 72 | make install 73 | ``` 74 | 75 | You will now have Newlib installed on your machine. To use it in your projects, add the following to your Makefile's `CC_FLAGS`, `CXX_FLAGS` and `LD_FLAGS`. 76 | If you have used a different installation directory you need to change them here. 77 | 78 | ```make 79 | CC_FLAGS:=-I $(SDK_DIR)/newlib/sh-elf/include 80 | 81 | CXX_FLAS:=-I $(SDK_DIR)/newlib/sh-elf/include 82 | 83 | LD_FLAGS:=-L$(SDK_DIR)/newlib/sh-elf/lib 84 | ``` 85 | 86 | Congratulations! You can now utilize standard C functions in your fx-CP400 project. 87 | 88 | If you also want newlib to create the arithmetic subroutines, you will need to modify a few more things in your Makefile. 89 | 90 | ```make 91 | LD:=sh4-elf-gcc # Change the linker to gcc instead of ld 92 | LD_FLAGS:=-nostartfiles -m4-nofpu -Wno-undef -L$(SDK_DIR)/newlib/sh-elf/lib # Change your LD_FLAGS to this (remove -nostdlib and --no-undefined) 93 | 94 | # For the APP_BIN target rule, change the line 95 | $(LD) --oformat=binary -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 96 | 97 | # To 98 | $(LD) -Wl,--oformat=binary -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o 99 | ``` 100 | 101 | Thats it. You can now build your app and enjoy Newlib! -------------------------------------------------------------------------------- /sdk/include/sdk/os/mcs.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions for retrieving and setting data in the MCS. 4 | */ 5 | 6 | #pragma once 7 | #include 8 | 9 | /** 10 | * Variable type: %OBCD (@c struct @ref OBCD). 11 | */ 12 | const uint8_t VARTYPE_OBCD = 0x01; 13 | 14 | /** 15 | * Variable type: %CBCD (@c struct @ref CBCD). 16 | */ 17 | const uint8_t VARTYPE_CBCD = 0x02; 18 | 19 | /** 20 | * Variable type: null-terminated string. 21 | */ 22 | const uint8_t VARTYPE_STR = 0x05; 23 | 24 | /** 25 | * Variable type: list. 26 | */ 27 | const uint8_t VARTYPE_LIST = 0x0A; 28 | 29 | /** 30 | * Variable type: matrix. 31 | */ 32 | const uint8_t VARTYPE_MAT = 0x0C; 33 | 34 | /** 35 | * Variable type: program. 36 | */ 37 | const uint8_t VARTYPE_PRGM = 0x47; 38 | 39 | /** 40 | * Variable type: function. 41 | */ 42 | const uint8_t VARTYPE_FUNC = 0x48; 43 | 44 | /** 45 | * Variable type: geometry data. 46 | */ 47 | const uint8_t VARTYPE_GEO = 0x54; 48 | 49 | /** 50 | * Error code signaling that the variable does not exist. 51 | */ 52 | const int MCS_NO_VARIABLE = 0x30; 53 | 54 | /** 55 | * Error code signaling that the folder does not exist. 56 | */ 57 | const int MCS_NO_FOLDER = 0x40; 58 | 59 | /** 60 | * Error code signaling that the folder already exists. 61 | */ 62 | const int MCS_FOLDER_EXISTS = 0x42; 63 | 64 | /** 65 | * Error code signaling that the specified size was not a power of 2. 66 | */ 67 | const int MCS_SIZE_NOT_PO2 = 0x61; 68 | 69 | /** 70 | * Error code signaling that the variable was not a list. 71 | */ 72 | const int MCS_NOT_LIST = 0x62; 73 | 74 | /** 75 | * Error code signaling that the index was out of bounds. 76 | */ 77 | const int MCS_INDEX_OOB = 0x63; 78 | 79 | /** 80 | * Retrieves the BCD digit at position @c i (0 representing the least 81 | * significant place) from a BCD encoded number @c n. 82 | * 83 | * @param n The number to retrieve a digit from. 84 | * @param i The index of the digit to retrieve. 85 | */ 86 | #define GET_BCD_DIGIT(n, i) (((n) >> ((i) * 4)) & 0xF) 87 | 88 | /** 89 | * Stores a decimal number. Both the mantissa and exponent are stored in BCD. 90 | * 91 | * See ClassPad 300 SDK documentation for more details. 92 | */ 93 | struct OBCD { 94 | uint8_t mantissa[10]; 95 | uint16_t exponent; 96 | }; 97 | 98 | /** 99 | * Stores a complex number, with real part @c re and imaginary part @c im. 100 | * 101 | * See ClassPad 300 SDK documentation for more details. 102 | */ 103 | struct CBCD { 104 | struct OBCD re; 105 | struct OBCD im; 106 | }; 107 | 108 | /** 109 | * Creates a folder in the MCS. 110 | * 111 | * If the folder name is greater than the 8 character max, it is truncated to a 112 | * valid length. 113 | * 114 | * The @p folderIndex parameter is still populated if the folder already exists. 115 | * However, its purpose is currently unknown. 116 | * 117 | * @param[in] folder The name of the folder to create. 118 | * @param[out] folderIndex An unknown index for the folder. 119 | * @return 0 if the folder was created successfully, or @c MCS_FOLDER_EXISTS if 120 | * the folder already exists. 121 | */ 122 | extern "C" 123 | int MCS_CreateFolder(const char *folder, uint8_t *folderIndex); 124 | 125 | /** 126 | * Retrieves information about a variable stored in the MCS. 127 | * 128 | * @param[in] folder The folder the requested MCS variable is located in. 129 | * @param[in] name The name of the requested MCS variable. 130 | * @param[out] variableType The variable's type. 131 | * @param[out] name2 The variable's name. 132 | * @param[out] data A pointer to the variable's data. 133 | * @param[out] size The length of the variable's data. 134 | * @return 0 if the variable exists, @c MCS_NO_VARIABLE if the variable does not 135 | * exist, or @c MCS_NO_FOLDER if the folder does not exist. 136 | */ 137 | extern "C" 138 | int MCS_GetVariable( 139 | const char *folder, const char *name, 140 | uint8_t *variableType, char **name2, void **data, uint32_t *size 141 | ); 142 | 143 | /** 144 | * Creates a list variable in the MCS. 145 | * 146 | * The list is initially populated with the default value of the variable type 147 | * specified. If the variable already exists, it is overwritten (even if it's 148 | * not a list). 149 | * 150 | * @param[in] folder The folder to create the list in. 151 | * @param[in] name The desired name of the variable. 152 | * @param size The size of the variable type specified. 153 | * @param length The number of entries in the list. 154 | * @param variableType The variable type to initialize the list with (macros 155 | * starting with @c VARTYPE). 156 | * @return 0 on success, @c MCS_NO_FOLDER if the requested folder does not 157 | * exist. 158 | */ 159 | extern "C" 160 | int MCS_List_Create( 161 | const char *folder, const char *name, 162 | uint32_t size, uint16_t length, uint8_t variableType 163 | ); 164 | 165 | /** 166 | * Sets an element of a list in the MCS. 167 | * 168 | * @param[in] folder The folder containing the list variable. 169 | * @param[in] name The name of the list variable. 170 | * @param size The size of the data to store in the element. 171 | * @param index The index into the list of the element to modify. 172 | * @param variableType The type of the data to be stored in the list element 173 | * (macros starting with @c VARTYPE). 174 | * @param[in] data The data to copy into the list element. 175 | * @return 0 on success, @c MCS_NO_FOLDER if the requested folder does not 176 | * exist, @c MCS_NO_VARIABLE if the variable does not exist, @c MCS_NOT_LIST 177 | * if the variable was not a list, or @c MCS_INDEX_OOB if the index was out of 178 | * bounds. 179 | */ 180 | extern "C" 181 | int MCS_List_Set( 182 | const char *folder, const char *name, 183 | uint32_t size, int index, uint8_t variableType, void *data 184 | ); 185 | 186 | /** 187 | * Sets the value of a variable stored in the MCS. Can be used to create a new 188 | * variable, or replace the value of an existing variable. 189 | * 190 | * This function does not create the folder if it does not exist. Use 191 | * @ref MCS_CreateFolder to create a folder if necessary. 192 | * 193 | * @param[in] folder The folder the variable to set it stored in. 194 | * @param[in] name The name of the desired MCS variable. 195 | * @param variableType The type of the variable (macros starting with 196 | * @c VARTYPE). 197 | * @param size The size of the variable's data. 198 | * @param[in] data A pointer to the data to copy into the variable. 199 | * @return 0 if the variable was set successfully, or @c MCS_NO_FOLDER if the 200 | * specified folder does not exist. 201 | */ 202 | extern "C" 203 | int MCS_SetVariable( 204 | const char *folder, const char *name, 205 | uint8_t variableType, uint32_t size, void *data 206 | ); 207 | -------------------------------------------------------------------------------- /demos/snake/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | APP_NAME("Snake") 7 | APP_DESCRIPTION("A simple implementation of the game Snake.") 8 | APP_AUTHOR("The6P4C, modified by SnailMath") 9 | APP_VERSION("1.0.0") 10 | 11 | #define COLOR_BACKGROUND RGB_TO_RGB565(0, 0, 0) 12 | #define COLOR_SNAKE RGB_TO_RGB565(0x1F, 0, 0) 13 | #define COLOR_FRUIT RGB_TO_RGB565(0, 0x3F, 0) 14 | 15 | #define BLOCK_SIZE 20 16 | #define MAX_SNAKE_LENGTH 50 17 | #define FRUIT_SQUARES_BONUS 3 18 | 19 | #define DIRECTION_NORTH 0 20 | #define DIRECTION_EAST 1 21 | #define DIRECTION_SOUTH 2 22 | #define DIRECTION_WEST 3 23 | 24 | uint16_t *vram; 25 | int lcdWidth, lcdHeight; 26 | int numBlocksX, numBlocksY; 27 | 28 | // the snake's head is at position 0 in our list 29 | int snakeLength; 30 | int snakeX[MAX_SNAKE_LENGTH]; 31 | int snakeY[MAX_SNAKE_LENGTH]; 32 | int numBlocksToAdd; 33 | 34 | int direction; 35 | 36 | uint32_t fruitXLFSR; 37 | uint32_t fruitYLFSR; 38 | 39 | int fruitX; 40 | int fruitY; 41 | 42 | void drawBlock(int blockX, int blockY, uint16_t color) { 43 | for (int x = blockX * BLOCK_SIZE; x < (blockX + 1) * BLOCK_SIZE; ++x) { 44 | for (int y = blockY * BLOCK_SIZE; y < (blockY + 1) * BLOCK_SIZE; ++y) { 45 | vram[x + (y + 24) * lcdWidth] = color; 46 | } 47 | } 48 | } 49 | 50 | void draw() { 51 | Debug_SetCursorPosition(0, 0); 52 | Debug_PrintString("Score: ", false); 53 | 54 | char *num = "0000"; 55 | num[0] = (snakeLength / 1000) % 10 + '0'; 56 | num[1] = (snakeLength / 100) % 10 + '0'; 57 | num[2] = (snakeLength / 10) % 10 + '0'; 58 | num[3] = snakeLength % 10 + '0'; 59 | Debug_PrintString(num, false); 60 | 61 | // Draw the background 62 | for (int blockY = 0; blockY < numBlocksY; ++blockY) { 63 | for (int blockX = 0; blockX < numBlocksX; ++blockX) { 64 | drawBlock(blockX, blockY, COLOR_BACKGROUND); 65 | } 66 | } 67 | 68 | // Draw the snake 69 | for (int i = 0; i < snakeLength; ++i) { 70 | drawBlock(snakeX[i], snakeY[i], COLOR_SNAKE); 71 | } 72 | 73 | // Draw the fruit 74 | drawBlock(fruitX, fruitY, COLOR_FRUIT); 75 | 76 | LCD_Refresh(); 77 | } 78 | 79 | bool moveSnake() { 80 | int deltaX = 0; 81 | int deltaY = 0; 82 | 83 | if (direction == DIRECTION_NORTH) { 84 | deltaY = -1; 85 | } else if (direction == DIRECTION_EAST) { 86 | deltaX = 1; 87 | } else if (direction == DIRECTION_SOUTH) { 88 | deltaY = 1; 89 | } else if (direction == DIRECTION_WEST) { 90 | deltaX = -1; 91 | } 92 | 93 | int newX = snakeX[0] + deltaX; 94 | int newY = snakeY[0] + deltaY; 95 | 96 | // if we've gone outside the grid 97 | if (newX < 0 || newX >= numBlocksX || newY < 0 || newY >= numBlocksY) { 98 | return false; 99 | } 100 | 101 | // Move the current blocks of the snake back one to make room for the head 102 | for (int i = snakeLength - 1; i >= 0; --i) { 103 | snakeX[i + 1] = snakeX[i]; 104 | snakeY[i + 1] = snakeY[i]; 105 | } 106 | 107 | snakeX[0] = newX; 108 | snakeY[0] = newY; 109 | 110 | if (numBlocksToAdd > 0) { 111 | --numBlocksToAdd; 112 | ++snakeLength; 113 | } 114 | 115 | return true; 116 | } 117 | 118 | void moveFruit() { 119 | bool foundPosition = false; 120 | 121 | while (!foundPosition) { 122 | // Emulate modulo - SH4 doesn't let us do mod by an arbitary value 123 | uint32_t xCopy = fruitXLFSR & 0xFF; 124 | while (xCopy >= numBlocksX) { 125 | xCopy -= numBlocksX; 126 | } 127 | fruitX = xCopy; 128 | 129 | uint32_t yCopy = fruitYLFSR & 0xFF; 130 | while (yCopy >= numBlocksY) { 131 | yCopy -= numBlocksY; 132 | } 133 | fruitY = yCopy; 134 | 135 | // calculate lfsr 136 | // poly: x^32 + x^22 + x^2 + x 137 | uint32_t fruitXLFSRBit = ((fruitXLFSR >> 0) ^ (fruitXLFSR >> 10) ^ (fruitXLFSR >> 30) ^ (fruitXLFSR >> 31)) & 1; 138 | fruitXLFSR = (fruitXLFSR >> 1) | (fruitXLFSRBit << 31); 139 | 140 | uint32_t fruitYLFSRBit = ((fruitYLFSR >> 0) ^ (fruitYLFSR >> 10) ^ (fruitYLFSR >> 30) ^ (fruitYLFSR >> 31)) & 1; 141 | fruitYLFSR = (fruitYLFSR >> 1) | (fruitYLFSRBit << 31); 142 | 143 | foundPosition = true; 144 | 145 | // check we haven't placed the fruit on the snake 146 | for (int i = 0; i < snakeLength; ++i) { 147 | if (fruitX == snakeX[i] && fruitY == snakeY[i]) { 148 | foundPosition = false; 149 | break; 150 | } 151 | } 152 | } 153 | } 154 | 155 | bool checkSnake() { 156 | bool collidedWithSelf = false; 157 | for (int i = 1; i < snakeLength; ++i) { 158 | if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]) { 159 | collidedWithSelf = true; 160 | break; 161 | } 162 | } 163 | 164 | if (snakeX[0] == fruitX && snakeY[0] == fruitY) { 165 | numBlocksToAdd += FRUIT_SQUARES_BONUS; 166 | moveFruit(); 167 | } 168 | 169 | return !collidedWithSelf; 170 | } 171 | 172 | void main() { 173 | LCD_VRAMBackup(); 174 | 175 | // Initialize our constants 176 | LCD_ClearScreen(); 177 | 178 | vram = LCD_GetVRAMAddress(); 179 | LCD_GetSize(&lcdWidth, &lcdHeight); 180 | 181 | numBlocksX = lcdWidth / BLOCK_SIZE; 182 | numBlocksY = (lcdHeight - 24) / BLOCK_SIZE; 183 | 184 | snakeLength = 3; 185 | for (int i = 0; i < snakeLength; ++i) { 186 | snakeX[i] = numBlocksX / 2 - i; 187 | snakeY[i] = numBlocksY / 2; 188 | } 189 | direction = DIRECTION_EAST; 190 | 191 | fruitXLFSR = 0x7312FAE1; 192 | fruitYLFSR = 0xAF05432A; 193 | moveFruit(); 194 | 195 | struct InputEvent event; 196 | 197 | bool lost = false; 198 | bool running = true; 199 | while (running) { 200 | draw(); 201 | 202 | GetInput(&event, 0xFFFFFFFF, 0x10); 203 | 204 | Debug_PrintNumberHex_Dword(event.type, 12, 0); 205 | switch (event.type) { 206 | case EVENT_TIMER: //Defined in sdk/os/input.hpp scince 03/07/2021 207 | 208 | if (!moveSnake()) { 209 | lost = true; 210 | running = false; 211 | } 212 | 213 | if (!checkSnake()) { 214 | lost = true; 215 | running = false; 216 | } 217 | 218 | case EVENT_KEY: 219 | switch (event.data.key.keyCode) { 220 | case KEYCODE_UP: 221 | if (direction != DIRECTION_SOUTH) { 222 | direction = DIRECTION_NORTH; 223 | } 224 | break; 225 | case KEYCODE_RIGHT: 226 | if (direction != DIRECTION_WEST) { 227 | direction = DIRECTION_EAST; 228 | } 229 | break; 230 | case KEYCODE_DOWN: 231 | if (direction != DIRECTION_NORTH) { 232 | direction = DIRECTION_SOUTH; 233 | } 234 | break; 235 | case KEYCODE_LEFT: 236 | if (direction != DIRECTION_EAST) { 237 | direction = DIRECTION_WEST; 238 | } 239 | break; 240 | case KEYCODE_POWER_CLEAR: 241 | running = false; 242 | break; 243 | } 244 | break; 245 | } 246 | } 247 | 248 | Debug_WaitKey(); 249 | 250 | LCD_VRAMRestore(); 251 | LCD_Refresh(); 252 | } 253 | -------------------------------------------------------------------------------- /sdk/include/sdk/os/input.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions used to retrieve user input via the touch screen or keypad. 4 | * 5 | * Provides the @ref GetInput method, a function which polls for input events 6 | * (screen touches and key presses). 7 | */ 8 | 9 | #pragma once 10 | #include 11 | 12 | const uint16_t EVENT_KEY = 0x0017; 13 | const uint16_t EVENT_ACTBAR_RESIZE = 0x1003; 14 | const uint16_t EVENT_ACTBAR_SWAP = 0x1004; 15 | const uint16_t EVENT_ACTBAR_ROTATE = 0x1005; 16 | const uint16_t EVENT_ACTBAR_ESC = 0x3009; 17 | const uint16_t EVENT_ACTBAR_SETTINGS = 0x300A; 18 | const uint16_t EVENT_TOUCH = 0x4000; 19 | const uint16_t EVENT_TIMER = 0x0005; 20 | 21 | const uint32_t KEY_PRESSED = 1; 22 | const uint32_t KEY_HELD = 0x100; 23 | const uint32_t KEY_RELEASED = 0x40; 24 | 25 | const uint16_t KEYCODE_KEYBOARD = 0x00B3; 26 | const uint16_t KEYCODE_SHIFT = 0x00A0; 27 | const uint16_t KEYCODE_BACKSPACE = 0x0097; 28 | const uint16_t KEYCODE_POWER_CLEAR = 0x0080; 29 | const uint16_t KEYCODE_UP = 0x0090; 30 | const uint16_t KEYCODE_DOWN = 0x0091; 31 | const uint16_t KEYCODE_LEFT = 0x0092; 32 | const uint16_t KEYCODE_RIGHT = 0x0093; 33 | const uint16_t KEYCODE_EQUALS = 0x003D; 34 | const uint16_t KEYCODE_X = 0xEDB8; 35 | const uint16_t KEYCODE_Y = 0xEDB9; 36 | const uint16_t KEYCODE_Z = 0xEDBA; 37 | const uint16_t KEYCODE_POWER = 0x005E; 38 | const uint16_t KEYCODE_DIVIDE = 0x002F; 39 | const uint16_t KEYCODE_OPEN_PARENTHESIS = 0x0028; 40 | const uint16_t KEYCODE_7 = 0x0037; 41 | const uint16_t KEYCODE_8 = 0x0038; 42 | const uint16_t KEYCODE_9 = 0x0039; 43 | const uint16_t KEYCODE_TIMES = 0xEE10; 44 | const uint16_t KEYCODE_CLOSE_PARENTHESIS = 0x0029; 45 | const uint16_t KEYCODE_4 = 0x0034; 46 | const uint16_t KEYCODE_5 = 0x0035; 47 | const uint16_t KEYCODE_6 = 0x0036; 48 | const uint16_t KEYCODE_MINUS = 0x002D; 49 | const uint16_t KEYCODE_COMMA = 0x002C; 50 | const uint16_t KEYCODE_1 = 0x0031; 51 | const uint16_t KEYCODE_2 = 0x0032; 52 | const uint16_t KEYCODE_3 = 0x0033; 53 | const uint16_t KEYCODE_PLUS = 0x002B; 54 | const uint16_t KEYCODE_NEGATIVE = 0x001F; 55 | const uint16_t KEYCODE_0 = 0x0030; 56 | const uint16_t KEYCODE_DOT = 0x002E; 57 | const uint16_t KEYCODE_EXP = 0x001D; 58 | const uint16_t KEYCODE_EXE = 0x0094; 59 | 60 | const uint32_t TOUCH_DOWN = 1; 61 | const uint32_t TOUCH_HOLD_DRAG = 2; 62 | const uint32_t TOUCH_ACT_BAR = 0x100; 63 | const uint32_t TOUCH_UP = 0x40; 64 | 65 | enum InputScancode : uint16_t { 66 | ScancodeKeyboard = (7 << 8) | 5, 67 | ScancodeShift = (7 << 8) | 1, 68 | ScancodeBackspace = (7 << 8) | 2, 69 | ScancodeClear = (1 << 8) | 0, 70 | ScancodeUp = (7 << 8) | 4, 71 | ScancodeDown = (6 << 8) | 4, 72 | ScancodeLeft = (6 << 8) | 3, 73 | ScancodeRight = (7 << 8) | 3, 74 | ScancodeEquals = (7 << 8) | 6, 75 | ScancodeX = (6 << 8) | 6, 76 | ScancodeY = (6 << 8) | 5, 77 | ScancodeZ = (5 << 8) | 3, 78 | ScancodePower = (6 << 8) | 2, 79 | ScancodeDivide = (6 << 8) | 1, 80 | ScancodeOpenParenthesis = (5 << 8) | 6, 81 | Scancode7 = (5 << 8) | 5, 82 | Scancode8 = (5 << 8) | 4, 83 | Scancode9 = (5 << 8) | 2, 84 | ScancodeTimes = (5 << 8) | 1, 85 | ScancodeCloseParenthesis = (4 << 8) | 6, 86 | Scancode4 = (4 << 8) | 5, 87 | Scancode5 = (4 << 8) | 4, 88 | Scancode6 = (4 << 8) | 2, 89 | ScancodeMinus = (4 << 8) | 1, 90 | ScancodeComma = (3 << 8) | 6, 91 | Scancode1 = (3 << 8) | 5, 92 | Scancode2 = (3 << 8) | 4, 93 | Scancode3 = (3 << 8) | 2, 94 | ScancodePlus = (3 << 8) | 1, 95 | ScancodeNegative = (2 << 8) | 6, 96 | Scancode0 = (2 << 8) | 5, 97 | ScancodeDot = (2 << 8) | 4, 98 | ScancodeEXP = (2 << 8) | 2, 99 | ScancodeEXE = (2 << 8) | 1 100 | }; 101 | 102 | /** 103 | * Information about an input event returned from @ref GetInput. See 104 | * documentation for individual members for more information. 105 | */ 106 | struct InputEvent { 107 | /** 108 | * Code representing which event occurred. Events with a value for this 109 | * field which do not correspond to a macro beginning with @c EVENT_ must 110 | * be ignored. 111 | */ 112 | uint16_t type; 113 | uint16_t zero; 114 | 115 | /** 116 | * The data associated with the event. Under no circumstances should the 117 | * sub-structure for an event type other than the one communicated by 118 | * @c eventType be accessed. 119 | * 120 | * Corresponding members: 121 | * - @c EVENT_KEY: @c key 122 | * - @c EVENT_TOUCH: @c touch_single 123 | * 124 | * The events @c EVENT_ACTBAR_RESIZE, @c EVENT_ACTBAR_SWAP, 125 | * @c EVENT_ACTBAR_ROTATE, @c EVENT_ACTBAR_ESC, and @c EVENT_ACTBAR_SETTINGS 126 | * do not report any data. 127 | */ 128 | union { 129 | struct { 130 | /** 131 | * The direction the key traveled. One of @c KEY_PRESSED, 132 | * @c KEY_HELD or @c KEY_RELEASED. 133 | */ 134 | uint32_t direction; 135 | 136 | /** 137 | * The key code for the key. See macros beginning with @c KEYCODE_. 138 | */ 139 | uint16_t keyCode; 140 | } key; 141 | 142 | struct { 143 | /** 144 | * The direction of the touch. One of @c TOUCH_DOWN, 145 | * @c TOUCH_HOLD_DRAG, @c TOUCH_ACT_BAR, or @c TOUCH_UP. 146 | */ 147 | uint32_t direction; 148 | 149 | /** 150 | * The X position of the cursor, in screen pixels. May be negative 151 | * or be greater than or equal to the width of the screen. 152 | */ 153 | int32_t p1_x; 154 | 155 | /** 156 | * The Y position of the cursor, in screen pixels. May be negative 157 | * or be greater than or equal to the height of the screen. 158 | */ 159 | int32_t p1_y; 160 | 161 | uint16_t adc_x1; 162 | uint16_t adc_y1; 163 | uint16_t adc_z1; 164 | uint16_t adc_z2; 165 | uint16_t adc_x2; 166 | uint16_t adc_y2; 167 | 168 | uint16_t adc_gh; 169 | uint16_t adc_dm; 170 | } touch_single; 171 | 172 | struct { 173 | uint32_t direction; 174 | 175 | int32_t p1_x; 176 | int32_t p1_y; 177 | int32_t p1_z; 178 | 179 | int32_t p2_x; 180 | int32_t p2_y; 181 | int32_t p2_z; 182 | 183 | uint32_t distance; 184 | 185 | uint16_t adc_x1; 186 | uint16_t adc_y1; 187 | uint16_t adc_z1; 188 | uint16_t adc_z2; 189 | uint16_t adc_x2; 190 | uint16_t adc_y2; 191 | 192 | uint16_t adc_gh; 193 | uint16_t adc_dm; 194 | } touch_multi; 195 | } data; 196 | }; 197 | 198 | /** 199 | * Polls for any input events. 200 | * 201 | * The @p event structure should be zeroed before calling @ref GetInput. 202 | * 203 | * @param[out] event The input event's data. 204 | * @param unknown1 An unknown value. The value @c 0xFFFFFFFF must be supplied. 205 | * @param unknown2 An unknown value. The value @c 0x10 must be supplied. 206 | * @return Always returns 0. 207 | */ 208 | extern "C" 209 | int GetInput(struct InputEvent *event, uint32_t unknown1, uint32_t unknown2); 210 | 211 | /** 212 | * Returns true if the specified key is currently down. 213 | * 214 | * @param[in] scanCode The scancode of the key to check. 215 | * @returns True if the key is down, false otherwise. 216 | */ 217 | extern "C" 218 | bool Input_GetKeyState(InputScancode *scancode); 219 | 220 | /** 221 | * Returns true if any key on the keyboard is currently down. 222 | * 223 | * @returns True if a key is pressed, false otherwise. 224 | */ 225 | extern "C" 226 | bool Input_IsAnyKeyDown(); 227 | -------------------------------------------------------------------------------- /doc/user/patching.md: -------------------------------------------------------------------------------- 1 | If you want to install gcc for sh4, look [here](https://github.com/TheRainbowPhoenix/sh4-devenv-gitpod/blob/master/Dockerfile)! 2 | 3 | --- 4 | 5 | # Patching the fx-CP400 firmware 6 | **You do not need to do this by hand if you just want to install programs on ypur calculator! Just use the program Snail2021.exe which is available in the releases.** 7 | 8 | Simple instructions if you just want to install it: https://github.com/SnailMath/hollyhock-2#installation-simple 9 | 10 | If you don't want to youse the precompiled program and you want to compile it by hand, 11 | you need both a Windows and Linux machine to complete this process (as there is no updater for Linux, and the extraction tool extracts the firmware from the Windows DLLs). I'd recommend a virtual machine (or Vagrant box for simplicity) rather than two physical machines. 12 | 13 | You'll also need a resource editor program - [Resource Hacker](http://www.angusj.com/resourcehacker/) has been tested to work ~~(Visual Studio can also be used to replace the RCDATA resource - just be careful to give the replaced resource an ID of 3070!).~~ (Snail2021 relies on 'Resource Hacker') 14 | 15 | ## 0. Clone this repository 16 | Clone this repository onto your machine. If you're using a virtual machine, this is really great place to use the shared folders feature your VM host probably provides. 17 | 18 | ## 1. Build an SH4 cross-compiler 19 | (This is also needed if you just want to develop programs for the Classpad II.) 20 | 21 | Hop onto your Linux machine. 22 | 23 | First, you'll have to define a few environment variables to ensure the process goes smoothly. Feel free to change `PREFIX` (the installation location), but don't change `TARGET`. 24 | 25 | ```sh 26 | # folder to install our cross-compiler into 27 | export PREFIX="$HOME/opt/cross" 28 | 29 | # the architecture and executable format we're targeting 30 | export TARGET=sh4-elf 31 | 32 | # add the cross-compiler to our PATH 33 | # you'll probably want to put this somewhere like .bash-profile or .bashrc 34 | export PATH="$PREFIX/bin:$PATH" 35 | ``` 36 | 37 | Now, download the most recent stable version of the Binutils source code from the [Binutils website](https://gnu.org/software/binutils/). Extract the compressed file containing the source code into a directory, and `cd` into the newly created directory. Then, run these commands inside that directory. (SnailMath is using version 2.35) 38 | 39 | ```sh 40 | mkdir build 41 | cd build 42 | ../configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror 43 | make 44 | sudo make install 45 | ``` 46 | 47 | You'll now have tools such as `sh4-elf-as` and `sh4-elf-objcopy` available. 48 | 49 | Now, download the most recent stable version of the GCC source code from the [GCC website](https://gnu.org/software/gcc/). Extract the compressed file containing the source code into a directory, and `cd` into the newly created directory. Then, run these commands inside that directory. Be warned - the `make` commands may take a long time, depending on your system, and the `contrib/download_prerequisites` script will download about 30 MB of archives. (SnailMath is using version 10.2.0) 50 | 51 | ```sh 52 | contrib/download_prerequisites 53 | mkdir build 54 | cd build 55 | ../configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers --with-multilib-list=m4-nofpu 56 | make all-gcc 57 | make all-target-libgcc 58 | sudo make install-gcc 59 | sudo make install-target-libgcc 60 | ``` 61 | (Note: The `--with-multilib-list=m4-nofpu` flag is needed because the cpu doesn't have an fpu) 62 | 63 | You’ll now have `sh4-elf-gcc` and `sh4-elf-g++` available for your usage. 64 | 65 | _I would recommend adding `export PATH="$PATH:$HOME/opt/cross/bin"` to the file .bashrc in the home directory to add the path automatically after every login._ 66 | _I would recommend adding `export SDK_DIR="PATH-TO/hollyhock-2/sdk"` to the file .bashrc in the home directory to export the sdk dir automatically after every login._ 67 | 68 | ## 2. Build the patches and the patcher 69 | Keep working on your Linux machine. 70 | 71 | `cd` into the `patcher/` directory. Run `make`. 72 | (This will run `make` in `patches/` and it will merge the file mod.txt with the program Snail2021 into a file called Snail2021mod.c) 73 | 74 | ## 3. Building Snail2021 75 | Snail2021 is the program, that will extract the firmware updater from the casio updater. To build this, go to your Windows machine. 76 | 77 | -Install the MinGW-Installer 78 | -Select `mingw32-base-bin` (from All Packages -> MinGW -> MinGW Base System) -> right click -> 'Mark for installation' 79 | -Select `mingw32-libz-dev` (from All Packages -> MinGW -> MinGW Libaries -> MinGW Standard Libaries) -> right click -> 'Mark for installation' 80 | -Click on 'Installation' -> 'Apply Changes' 81 | 82 | -Double click on `patcher/make.bat`. You should not see any errors. This will create the file Snail2021.exe in the folder Snail2021 on the desktop 83 | 84 | (If your name is not IEUser, you have to change the path in make.bat) 85 | 86 | ## 4. Run Snail2021 and modify the firmware 87 | Go to the desktop into the folder Snail2021. 88 | 89 | Doule click on Snai2021.exe 90 | 91 | Follow the instructions on screen: 92 | - Download and install Resource Hacker http://angusj.com/resourcehacker/ (scroll down and click on exe install) 93 | - Download the calculator updater version 02.01.2 https://tiplanet.org/forum/archives_voir.php?id=1044960 (Click on the green download 94 | - Run the updater, click ok on every question, but __do not__ connect the calculator, go back to the Snail2021.exe window when you are asked to connect the calculator. 95 | - Click Enter in the Snail2021.exe until it asks you to connect the calculator. 96 | - At this point, you can close the Official Updater again. 97 | - When you click enter in Snail2021.exe for the last time, the modified updater opens. 98 | 99 | ## 5. Update the firmware 100 | At this point the modified updater should be open. (it opens at the end of Snail2021.exe, but you can start it manually by clicking on the file `click_to_modify_the_calculator.bat`) 101 | 102 | When the updater opens, hold the `[EXP]`, `[^]` and `Clear` keys on your fx-CP400. With those keys depressed, momentarily press the `RESET` button on the back of the calculator (hint: use the stylus!). Keep the three front buttons depressed until the LCD displays the following: 103 | 104 | ``` 105 | ***************** 106 | * OS ERROR * 107 | * Please update * 108 | * OS * 109 | ***************** 110 | ``` 111 | 112 | Release the buttons and connect the calculator to your PC using a USB cable. Proceed through the OS updater, and disconnect your calculator **only** once the update has finished. 113 | 114 | ## 6. Check the patch 115 | Turn on your calculator and confirm the patch was applied correctly by opening the version dialog (either from the settings menu in the top left corner of Main > Version, or in System) and confirming the string "hollyhock" is displayed. 116 | 117 | ## 7. Build the launcher 118 | `cd` into the `launcher/` directory. Run `make`, then copy the file `run.bin` into the root directory of your GC's flash. 119 | 120 | ## 8. Celebrate! 121 | You've successfully patched your fx-CP400's firmware, and installed the Hollyhock Launcher. You're ready to go! 122 | 123 | You can now start [using](using.md) or [developing](developing.md) software for your GC. 124 | -------------------------------------------------------------------------------- /launcher/apps.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "apps.hpp" 5 | #include "elf.h" 6 | #include 7 | 8 | #define hex2asc(x) ((x)>9?((x)+'A'-10):((x)+'0')) 9 | 10 | class File { 11 | public: 12 | File() : m_opened(false), m_fd(-1) { 13 | 14 | } 15 | 16 | ~File() { 17 | if (m_opened) { 18 | close(m_fd); 19 | } 20 | } 21 | 22 | int open(const char *path, int flags) { 23 | m_fd = ::open(path, flags); 24 | m_opened = true; 25 | return m_fd; 26 | } 27 | 28 | int getAddr(int offset, const void **addr) { 29 | return ::getAddr(m_fd, offset, addr); 30 | } 31 | 32 | private: 33 | bool m_opened; 34 | int m_fd; 35 | }; 36 | 37 | class Find { 38 | public: 39 | Find() : m_opened(false), m_findHandle(-1) { 40 | 41 | } 42 | 43 | ~Find() { 44 | if (m_opened) { 45 | findClose(m_findHandle); 46 | } 47 | } 48 | 49 | int findFirst(const wchar_t *path, wchar_t *name, struct findInfo *findInfoBuf) { 50 | int ret = ::findFirst(path, &m_findHandle, name, findInfoBuf); 51 | m_opened = true; 52 | return ret; 53 | } 54 | 55 | int findNext(wchar_t *name, struct findInfo *findInfoBuf) { 56 | return ::findNext(m_findHandle, name, findInfoBuf); 57 | } 58 | 59 | private: 60 | bool m_opened; 61 | int m_findHandle; 62 | }; 63 | 64 | namespace Apps { 65 | const char *HHK_FOLDER[] = { 66 | "\\fls0\\", 67 | "\\drv0\\" 68 | }; 69 | const char FILE_MASK[] = "*.hhk"; 70 | 71 | struct AppInfo g_apps[MAX_APPS]; 72 | int g_numApps; 73 | 74 | const Elf32_Ehdr *LoadELF(File f, const Elf32_Shdr **sectionHeaders) { 75 | const Elf32_Ehdr *elf; 76 | int ret = f.getAddr(0, (const void **) &elf); 77 | if (ret < 0) { 78 | return nullptr; 79 | } 80 | 81 | // Check magic number 82 | if (!( 83 | elf->e_ident[EI_MAG0] == ELFMAG0 && 84 | elf->e_ident[EI_MAG1] == ELFMAG1 && 85 | elf->e_ident[EI_MAG2] == ELFMAG2 && 86 | elf->e_ident[EI_MAG3] == ELFMAG3 87 | )) { 88 | return nullptr; 89 | } 90 | 91 | // Check file class 92 | if (elf->e_ident[EI_CLASS] != ELFCLASS32) { 93 | return nullptr; 94 | } 95 | 96 | // Check data encoding 97 | if (elf->e_ident[EI_DATA] != ELFDATA2MSB) { 98 | return nullptr; 99 | } 100 | 101 | // Check ELF version 102 | if (elf->e_ident[EI_VERSION] != EV_CURRENT) { 103 | return nullptr; 104 | } 105 | 106 | // Check ABI (ignore ABI version EI_ABIVERSION) 107 | if (elf->e_ident[EI_OSABI] != ELFOSABI_SYSV) { 108 | return nullptr; 109 | } 110 | 111 | // Check ELF is an executable file 112 | if (elf->e_type != ET_EXEC) { 113 | return nullptr; 114 | } 115 | 116 | // Check machine 117 | if (elf->e_machine != EM_SH) { 118 | return nullptr; 119 | } 120 | 121 | // Check version 122 | if (elf->e_version != EV_CURRENT) { 123 | return nullptr; 124 | } 125 | 126 | *sectionHeaders = reinterpret_cast( 127 | reinterpret_cast(elf) + elf->e_shoff 128 | ); 129 | 130 | return elf; 131 | } 132 | 133 | void LoadApp(const char *folder, wchar_t *fileName) { 134 | struct AppInfo app; 135 | memset(&app, 0, sizeof(app)); 136 | 137 | // copy the file name (converting to a non-wide string in the 138 | // process) 139 | for (int i = 0; i < 100; ++i) { 140 | wchar_t c = fileName[i]; 141 | app.fileName[i] = c; 142 | if (c == 0x0000) { 143 | break; 144 | } 145 | } 146 | 147 | // build the path 148 | //strcat(app.path, "\\fls0\\"); 149 | strcat(app.path, folder); 150 | strcat(app.path, app.fileName); 151 | 152 | File f; 153 | int ret = f.open(app.path, OPEN_READ); 154 | if (ret < 0) { 155 | return; 156 | } 157 | 158 | const Elf32_Shdr *sectionHeaders; 159 | const Elf32_Ehdr *elf = LoadELF(f, §ionHeaders); 160 | 161 | if (elf == nullptr) { 162 | return; 163 | } 164 | 165 | const Elf32_Shdr *sectionHeaderStringTable = §ionHeaders[elf->e_shstrndx]; 166 | for (int i = 0; i < elf->e_shnum; ++i) { 167 | const Elf32_Shdr *sectionHeader = §ionHeaders[i]; 168 | 169 | // skip the first empty section header 170 | if (sectionHeader->sh_type == SHT_NULL) { 171 | continue; 172 | } 173 | 174 | const char *sectionName = reinterpret_cast( 175 | reinterpret_cast(elf) + 176 | sectionHeaderStringTable->sh_offset + 177 | sectionHeader->sh_name 178 | ); 179 | 180 | const char *sectionData = reinterpret_cast( 181 | reinterpret_cast(elf) + 182 | sectionHeader->sh_offset 183 | ); 184 | 185 | if (strcmp(sectionName, ".hollyhock_name") == 0) { 186 | strcat(app.name, sectionData); 187 | } else if (strcmp(sectionName, ".hollyhock_description") == 0) { 188 | strcat(app.description, sectionData); 189 | } else if (strcmp(sectionName, ".hollyhock_author") == 0) { 190 | strcat(app.author, sectionData); 191 | } else if (strcmp(sectionName, ".hollyhock_version") == 0) { 192 | strcat(app.version, sectionData); 193 | } 194 | } 195 | 196 | g_apps[g_numApps++] = app; 197 | } 198 | 199 | void LoadAppInfo() { 200 | g_numApps = 0; 201 | 202 | for (unsigned int dirNr=0; dirNr= 0) { 222 | if (findInfoBuf.type == findInfoBuf.EntryTypeFile) { 223 | LoadApp(HHK_FOLDER[dirNr],fileName); 224 | } 225 | ret = find.findNext(fileName, &findInfoBuf); 226 | } 227 | } 228 | } 229 | 230 | EntryPoint RunApp(int i) { 231 | struct AppInfo *app = &g_apps[i]; 232 | 233 | File f; 234 | int ret = f.open(app->path, OPEN_READ); 235 | if (ret < 0) { 236 | return nullptr; 237 | } 238 | 239 | const Elf32_Shdr *sectionHeaders; 240 | const Elf32_Ehdr *elf = LoadELF(f, §ionHeaders); 241 | 242 | if (elf == nullptr) { 243 | return nullptr; 244 | } 245 | 246 | for (int i = 0; i < elf->e_shnum; ++i) { 247 | const Elf32_Shdr *sectionHeader = §ionHeaders[i]; 248 | 249 | // skip the first empty section header 250 | if (sectionHeader->sh_type == SHT_NULL) { 251 | continue; 252 | } 253 | 254 | const void *sectionData = reinterpret_cast( 255 | reinterpret_cast(elf) + 256 | sectionHeader->sh_offset 257 | ); 258 | 259 | if ((sectionHeader->sh_flags & SHF_ALLOC) == SHF_ALLOC) { 260 | void *dest = reinterpret_cast(sectionHeader->sh_addr); 261 | 262 | if (sectionHeader->sh_type == SHT_PROGBITS) { 263 | memcpy(dest, sectionData, sectionHeader->sh_size); 264 | } else if (sectionHeader->sh_type == SHT_NOBITS) { 265 | memset(dest, 0, sectionHeader->sh_size); 266 | } 267 | } 268 | } 269 | 270 | return reinterpret_cast(elf->e_entry); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /demos/breakpoint_util/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | APP_NAME("Breakpoint Utility") 9 | APP_DESCRIPTION("Set breakpoints within apps or OS code to view CPU state.") 10 | APP_AUTHOR("The6P4C") 11 | APP_VERSION("1.0.0") 12 | 13 | struct CPUState { 14 | uint32_t r0; 15 | uint32_t r1; 16 | uint32_t r2; 17 | uint32_t r3; 18 | uint32_t r4; 19 | uint32_t r5; 20 | uint32_t r6; 21 | uint32_t r7; 22 | uint32_t r8; 23 | uint32_t r9; 24 | uint32_t r10; 25 | uint32_t r11; 26 | uint32_t r12; 27 | uint32_t r13; 28 | uint32_t r14; 29 | uint32_t r15; 30 | 31 | uint32_t gbr; 32 | uint32_t pr; 33 | 34 | uint32_t mach; 35 | uint32_t macl; 36 | }; 37 | 38 | // Defined in breakpoint_handler_stub.s 39 | extern "C" void BreakpointHandlerStub(); 40 | 41 | /** 42 | * Called by @c BreakpointHandlerStub when a breakpoint is triggered. 43 | * 44 | * @param cpuState The state of the CPU when the breakpoint was triggered. 45 | */ 46 | extern "C" 47 | void BreakpointHandler(struct CPUState *cpuState) { 48 | Debug_SetCursorPosition(0, 0); 49 | Debug_PrintString("Breakpoint triggered!", true); 50 | 51 | Debug_SetCursorPosition(0, 1); 52 | Debug_PrintString("r0 =xxxxxxxx r1 =xxxxxxxx", false); 53 | Debug_PrintNumberHex_Dword(cpuState->r0, 4, 1); 54 | Debug_PrintNumberHex_Dword(cpuState->r1, 17, 1); 55 | 56 | Debug_SetCursorPosition(0, 2); 57 | Debug_PrintString("r2 =xxxxxxxx r3 =xxxxxxxx", false); 58 | Debug_PrintNumberHex_Dword(cpuState->r2, 4, 2); 59 | Debug_PrintNumberHex_Dword(cpuState->r3, 17, 2); 60 | 61 | Debug_SetCursorPosition(0, 3); 62 | Debug_PrintString("r4 =xxxxxxxx r5 =xxxxxxxx", false); 63 | Debug_PrintNumberHex_Dword(cpuState->r4, 4, 3); 64 | Debug_PrintNumberHex_Dword(cpuState->r5, 17, 3); 65 | 66 | Debug_SetCursorPosition(0, 4); 67 | Debug_PrintString("r6 =xxxxxxxx r7 =xxxxxxxx", false); 68 | Debug_PrintNumberHex_Dword(cpuState->r6, 4, 4); 69 | Debug_PrintNumberHex_Dword(cpuState->r7, 17, 4); 70 | 71 | Debug_SetCursorPosition(0, 5); 72 | Debug_PrintString("r8 =xxxxxxxx r9 =xxxxxxxx", false); 73 | Debug_PrintNumberHex_Dword(cpuState->r8, 4, 5); 74 | Debug_PrintNumberHex_Dword(cpuState->r9, 17, 5); 75 | 76 | Debug_SetCursorPosition(0, 6); 77 | Debug_PrintString("r10=xxxxxxxx r11=xxxxxxxx", false); 78 | Debug_PrintNumberHex_Dword(cpuState->r10, 4, 6); 79 | Debug_PrintNumberHex_Dword(cpuState->r11, 17, 6); 80 | 81 | Debug_SetCursorPosition(0, 7); 82 | Debug_PrintString("r12=xxxxxxxx r13=xxxxxxxx", false); 83 | Debug_PrintNumberHex_Dword(cpuState->r12, 4, 7); 84 | Debug_PrintNumberHex_Dword(cpuState->r13, 17, 7); 85 | 86 | Debug_SetCursorPosition(0, 8); 87 | Debug_PrintString("r14=xxxxxxxx r15=xxxxxxxx", false); 88 | Debug_PrintNumberHex_Dword(cpuState->r14, 4, 8); 89 | Debug_PrintNumberHex_Dword(cpuState->r15, 17, 8); 90 | 91 | Debug_SetCursorPosition(0, 10); 92 | Debug_PrintString("gbr=xxxxxxxx pr =xxxxxxxx", false); 93 | Debug_PrintNumberHex_Dword(cpuState->gbr, 4, 10); 94 | Debug_PrintNumberHex_Dword(cpuState->pr, 17, 10); 95 | 96 | Debug_SetCursorPosition(0, 12); 97 | Debug_PrintString("mach=xxxxxxxx", false); 98 | Debug_PrintNumberHex_Dword(cpuState->mach, 5, 12); 99 | 100 | Debug_SetCursorPosition(0, 13); 101 | Debug_PrintString("macl=xxxxxxxx", false); 102 | Debug_PrintNumberHex_Dword(cpuState->macl, 5, 13); 103 | 104 | LCD_Refresh(); 105 | } 106 | 107 | /** 108 | * Sets a breakpoint at @p address, triggered after an instruction at the 109 | * address is executed. 110 | * 111 | * @param address The address to set the breakpoint at. 112 | */ 113 | void SetBreakpoint(uint32_t address) { 114 | // Set our breakpoint handler stub as the DBR (debug base register) 115 | __asm__ volatile( 116 | "ldc %0, dbr\n" 117 | : 118 | : "r"(&BreakpointHandlerStub) 119 | : 120 | ); 121 | 122 | // Tell the UBC to use the function pointed to by DBR 123 | UBC_REG_CBCR = 1 << UBC_CBCR_UBDE; 124 | 125 | // Set the breakpoint to occur on an instruction fetch cycle 126 | UBC_REG_CBR0 = 1 << UBC_CBR_ID; 127 | 128 | // Set the address of the function we want to debug 129 | UBC_REG_CAR0 = address; 130 | 131 | // Set address mask to 0 (only trigger on the exact address) 132 | UBC_REG_CAMR0 = 0; 133 | 134 | // Enable the breakpoint and set the breakpoint to trigger after instruction 135 | // execution 136 | UBC_REG_CRR0 = (1 << UBC_CRR_RESERVED) | (1 << UBC_CRR_PCB) | (1 << UBC_CRR_BIE); 137 | 138 | // Enable the channel 139 | UBC_REG_CBR0 |= 1 << UBC_CBR_CE; 140 | } 141 | 142 | /** 143 | * Disables any breakpoint which was set. 144 | */ 145 | void RemoveBreakpoint() { 146 | // Disable the channel 147 | UBC_REG_CBR0 = 0 << UBC_CBR_CE; 148 | } 149 | 150 | class BreakpointDialog : public GUIDialog { 151 | public: 152 | BreakpointDialog() : GUIDialog( 153 | Height55, AlignCenter, 154 | "Breakpoint Utility", 155 | KeyboardStateABC 156 | ), m_breakpointAddressLabel( 157 | GetLeftX() + 10, GetTopY() + 10, 158 | "Breakpoint address (hex)" 159 | ), m_breakpointAddress( 160 | GetLeftX() + 10, GetTopY() + 30, GetRightX() - GetLeftX() - 20, 161 | 8, true 162 | ), m_setBreakpoint( 163 | GetLeftX() + 10, GetTopY() + 60, GetRightX() - 10, GetTopY() + 90, 164 | "Set breakpoint", BUTTON_SET_BREAKPOINT_EVENT_ID 165 | ), m_removeBreakpoint( 166 | GetLeftX() + 10, GetTopY() + 95, GetRightX() - 10, GetTopY() + 125, 167 | "Remove breakpoint", BUTTON_REMOVE_BREAKPOINT_EVENT_ID 168 | ), m_close( 169 | GetLeftX() + 10, GetTopY() + 130, GetRightX() - 10, GetTopY() + 160, 170 | "Close", BUTTON_CLOSE_EVENT_ID 171 | ) { 172 | AddElement(m_breakpointAddressLabel); 173 | AddElement(m_breakpointAddress); 174 | AddElement(m_setBreakpoint); 175 | AddElement(m_removeBreakpoint); 176 | AddElement(m_close); 177 | } 178 | 179 | /** 180 | * Returns the integer representation of the address the user entered. 181 | * 182 | * Accepts both uppercase and lowercase hexidecimal. 183 | * 184 | * @return The integer representation of the address the user entered, or 0 185 | * if the address was invalid in some way. 186 | */ 187 | uint32_t GetBreakpointAddress() { 188 | const char *text = m_breakpointAddress.GetText(); 189 | 190 | if (text == 0) { 191 | return 0; 192 | } 193 | 194 | char c; 195 | uint32_t address = 0; 196 | while ((c = *text++) != '\0') { 197 | uint32_t v; 198 | 199 | // Since 0-9, a-f and A-F are consecutive in the ASCII space, this 200 | // is an easy way to cheat the conversion from hex string -> number 201 | if (c >= '0' && c <= '9') { 202 | v = c - '0'; 203 | } else if (c >= 'a' && c <= 'f') { 204 | v = c - 'a' + 0xA; 205 | } else if (c >= 'A' && c <= 'F') { 206 | v = c - 'A' + 0xA; 207 | } else { 208 | return 0; 209 | } 210 | 211 | address *= 0x10; 212 | address += v; 213 | } 214 | 215 | // Check if the address is within an actual region of memory 216 | // i.e 0x8000000-0x81500000 or 0x8C000000-0x8D000000 217 | if ( 218 | !(address >= 0x80000000 && address <= 0x81500000) && 219 | !(address >= 0x8C000000 && address <= 0x8D000000) 220 | ) { 221 | return 0; 222 | } 223 | 224 | return address; 225 | } 226 | 227 | int OnEvent(struct GUIDialog_Wrapped *dialog, struct GUIDialog_OnEvent_Data *event) { 228 | // Annoyingly, setting the text of the address textbox is the best 229 | // way I have at the moment of providing some feedback to the user. 230 | // Not elegant, but it gets the job done. 231 | // TODO: Add a label to indicate the status -> requires finding a 232 | // SetText function for labels. 233 | if (event->GetEventID() == BUTTON_SET_BREAKPOINT_EVENT_ID) { 234 | uint32_t address = GetBreakpointAddress(); 235 | 236 | if (address != 0) { 237 | SetBreakpoint(address); 238 | 239 | m_breakpointAddress.SetText("Set!"); 240 | } else { 241 | m_breakpointAddress.SetText("Invalid."); 242 | } 243 | 244 | Refresh(); 245 | return 0; 246 | } 247 | 248 | if (event->GetEventID() == BUTTON_REMOVE_BREAKPOINT_EVENT_ID) { 249 | RemoveBreakpoint(); 250 | 251 | m_breakpointAddress.SetText("Removed."); 252 | Refresh(); 253 | return 0; 254 | } 255 | 256 | return GUIDialog::OnEvent(dialog, event); 257 | } 258 | 259 | private: 260 | GUILabel m_breakpointAddressLabel; 261 | GUITextBox m_breakpointAddress; 262 | 263 | static const int BUTTON_SET_BREAKPOINT_EVENT_ID = 1; 264 | GUIButton m_setBreakpoint; 265 | 266 | static const int BUTTON_REMOVE_BREAKPOINT_EVENT_ID = 2; 267 | GUIButton m_removeBreakpoint; 268 | 269 | static const int BUTTON_CLOSE_EVENT_ID = GUIDialog::DialogResultCancel; 270 | GUIButton m_close; 271 | }; 272 | 273 | void main() { 274 | BreakpointDialog dialog; 275 | dialog.ShowDialog(); 276 | } 277 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a __fork__ of the original hollyhock launcher by The6P4C (https://github.com/Stellaris-code/hollyhock-fork/). 2 | My intention was to modify it a little. I changed the following things: 3 | - BIN: I added the ability to load prograrms from binary files instead of .hhk files, the extrypoint is the start of the file and the load location is somwhere in the file, 4 | look at the linker.ld file in the app_template folder. 5 | - EXEC003: I added the ability to run programs located in program variables. This was the function of the old "LoaderIIb", which is now obsolete. 6 | I didn't want to miss the feel of typing in programs byte per byte by hand (only if you want to) and sending them over the serial cable. 7 | - Exam mode: The Launcher is disabled in exam mode to prevent cheating. (Even though I would recommend going back to the original firmware for exams.) 8 | The loader checks the color of the battery logo to test, if the exam mode is on or off. 9 | - Strings: I changed a few texts in the OS, the Imaginary Unit button used to load the program was renamed to 'Hollyhock-Launcher'. The version text (usually "classpad II") 10 | Was changed to "hollyhock" by The6P4C, I changed it to "hollyhock-2". 11 | - Backslash: I will change the backslash character from the symbol `¥` to the symbol `\`. 12 | 13 | # DISCLAIMER 14 | - I AM NOT RESPONSIBLE FOR ANY DAMAGE YOU MAY DO TO YOUR CALCULATOR (fx-CP400, fx-CP400+E, fx-CG500 or whatever other model). 15 | - I AM NOT LIABLE IF YOU BRICK OR OTHERWISE DAMAGE YOUR CALCULATOR. 16 | - CASIO WILL NOT FIX YOUR CALCULATOR IF IT SHOWS EVIDENCE OF TAMPERING, I.E. FLASHING WITH MODIFIED FIRMWARE IMAGES OR MESSING WITH DEBUG MENUS. 17 | - I WILL NOT FIX YOUR CALCULATOR. 18 | - IF YOU CANNOT FIX YOUR CALCULATOR YOURSELF, DO NOT USE THIS TOOLKIT. 19 | - **BY USING THESE TOOLS, YOU AGREE TO TAKE FULL RESPONSIBILITY FOR ANY DAMAGE THAT MAY OCCUR.** 20 | - **YOU HAVE BEEN WARNED**. 21 | 22 | # Compatibility 23 | Hollyhock is compatible with the fx-CP400, fx-CP400+E and fx-CG500. 24 | As of 2024, new fx-CP400 hardware revisions (maybe fx-CP400+E and fx-CG500 too?) started to show up. These calculators do not boot on OSes older than v2.01.7002, such as 2.01.2000 (which is used by hollyhock). Please follow at least one of the following procedures to ensure you have a compatible model before installing. 25 | 26 | > **Note:** There is a private beta of hollyhock v3 available for testing on our Discord server. It should suport ABS2022 / OS 2.01.7002 / newer calculators. If you are interested in trying out new features or helping with development, please join [our Discord](https://discord.gg/y8Mr8tD) and ask for access to the beta. 27 | 28 | ### ABS_Date 29 | - With your calculator powered off, open the debug menu by pressing and holding [=] + [EXP] + [Clear/ON] until you see a "Factory use only" screen 30 | - Quickly press [z] + [left] before the screen disappears 31 | - At the bottom of the screen, you should see your `ABS_Date`listed 32 | - Quickly take note of it or take a picture, as this screen will disappear afteer a few seconds 33 | - Your `OS_Date` is not important here 34 | - Calculators with an `ABS_Date` of `2012/09/04 15:11` are known to work with older OS versions and Hollyhock 35 | - Calculators with an `ABS_Date` of `2022/10/28 13:16` are known to boot to a black screen on OS versions older than `2.01.7002`, and therefore will not work with Hollyhock for now 36 | - If your calculator has an ABS_Date different from those, it's something we haven't seen yet and would love to hear about! 37 | 38 | ### Serial Number 39 | After a bit of research with affected calculators, we've noticed a pattern in their serial numbers 40 | - Calculators with a new serial number format (starting with `L777A` or `C805A`, with an _Ex (x being a number) suffix in the end) should check their serial numbers second to last digit before the _Ex suffix 41 | - Calculators which have a `C` there seem to always have an ABS_Date of 2022, and they don't work on OSes older than 2.01.7002. That means no hollyhock on them for now 42 | - Calculators which have a `B` there seem to always have a 2012 ABS_Date, and should work with hollyhock 43 | - Calculators with an old serial number format (starting with `777A`, `805A` or `804A`, no suffix) seem to always have a 2012 ABS_Date, and should work with hollyhock 44 | - The serial number is located in a label near the batteries, you need to remove the battery cover to find it 45 | - If your serial number doesn't fit any of the above categories, it's something we haven't seen yet and would love to hear about! 46 | 47 | Examples: 48 | ```plaintext 49 | Known uncomopatible SNs: 50 | - L777A*******5CB_E4 51 | - L777A*******0CE_E0 52 | - L777A*******8CB_E3 53 | - L777A*******9CE_E0 54 | 55 | Known compatible SNs: 56 | - L777A*******3BA_E3 57 | - L777A*******9BA_E2 58 | - C805A*******3BA_E2 59 | - 777A**********2 60 | - 804A**********6 61 | ``` 62 | 63 | (if unsure, check the ABS_Date version) 64 | 65 | ### Notes 66 | - If you install an OS older than 2.01.7002, hollyhock or not, on a 2022 ABS_Date calculator, it'll show nothing on the screen - it'll remain black when you try to turn it on 67 | - At this state, update mode and the OS Error screen should still work normally. On the calculator hold down the `EXP`, `^` and `Clear` keys. With those keys pressed, momentarily press the RESET button on the back of the calculator. Keep the three front buttons pressed until the screen displays the `OS ERROR` message. 68 | - You may need to remove the batteries and put them back in in order for this to work 69 | - There have been reports that adding `Shift` to the above key combination may work to get to the OS Error screen if the normal key combo doesn't work 70 | - At this point, you can download the latest official updater from CASIO and use it to update your calculator. Updating to 2.01.7002 is confirmed to make the calculator work again. 71 | - No matter what, by installing hollyhock, upgrading or downgrading the OS of your calculator or even just following the above procedures to check the ABS_Date, you do so at your own risk. YOU AGREE TO TAKE FULL RESPONSIBILITY FOR ANY DAMAGE THAT MAY OCCUR. 72 | - It would be really helpful if you could submit your calculators serial number and ABS_Date (on the comments field) to [MyCalcs](https://my.calcs.quest/), specially if it's one of the 2022 ABS_Date models (but actually no matter your model), and let us know! 73 | 74 | 75 | # Installation (simple) 76 | This is how you install hollyhock-2 onto your calculator: 77 | - check the compatibility section above to ensure your calculator is compatible 78 | - download the newest prebuild release of hollyhock-2: https://github.com/SnailMath/hollyhock-2/releases 79 | - copy the folder Snail2021 from the .zip file in the release onto the desktop. 80 | - open the folder Snail2021 on the desktor and double click on Snail2021.exe 81 | - follow the instructions on screen: 82 | - Download and install ResourceHacker (scroll down, click on _EXE install_): http://angusj.com/resourcehacker/ 83 | - Download the official updater version 02.01.2 (click the green download button): https://tiplanet.org/forum/archives_voir.php?id=1044960 84 | - Follow the instructions in the updater, but don't connect the calculator when it asks you to. 85 | - at this point, return back to the first windown (The Snail2021.exe) 86 | - click enter until it asks you to connect the calculator. 87 | - You can close the updater window now. 88 | - When you click enter the last time in the Snail2021.exe window, the updater will open again, but this time it is the modified version. 89 | - Connect the calculator via USB. 90 | - On the calculator hold down the `EXP`, `^` and `Clear` keys. With those keys pressed, momentarily press the RESET button on the back of the calculator. Keep the three front buttons pressed until the screen displays the `OS ERROR` message. Don't worry, there is no actual error, this is just the update mode. 91 | - On the computer, Click on OK in the updater. 92 | - __DO NOT TOUCH__ the calculator until the update is finished. 93 | - When the updater is finished, unplug it and plug it in again 94 | - Select USB Storage and copy the file run.bin and all .hhk and .bin files (from the folder Snail2021) directly onto the calculator. 95 | - On the computer, click on eject (probably on the bottom right) and eject the calculator. 96 | 97 | To start a program, go to the Menu, go to the right, select System, click on the gear on the top left and click on `Imaginary Unit` (I will probably rename this to 'Start hollyhock' or 'Start program' soon.) 98 | 99 | Select the program and click OK. 100 | 101 | 102 | # hollyhock 103 | An SDK to write your own applications for the CASIO fx-CP400 graphics calculator. 104 | 105 | This is by no means a finished project. Like the disclaimer says, use at your own risk! Breaking changes are not out of the question, and should be 100% expected. 106 | 107 | I (@The6P4C) have burned out *hard* on this project, and won't be maintaining it any further. These tools, patches and SDK work only for OS v02.01.2000.0000. The tools are easily updated, but the patches and SDK are **much** more difficult (and half the reason I'm so burned out). 108 | 109 | ~~Please contact @ps100000 regarding this project.~~ If you have any questions, you can visit our discord https://discord.gg/y8Mr8tD or comment on YouTube. 110 | 111 | ## User Guides 112 | 1. [Compatibility](#Compatibility) 113 | 2. [Install hollyhock-2](#installation-simple) 114 | 3. [Using add-on software](doc/user/using.md) 115 | 4. [Developing applications](doc/user/developing.md) 116 | 5. [Writing programs without a PC](doc/user/exec.md) 117 | 6. [Patching your firmware (from source)](doc/user/patching.md) (complicated, you do not need this, just scroll up to section [Install](#installation-simple) 118 | 119 | -------------------------------------------------------------------------------- /launcher/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "apps.hpp" 7 | #include "bins.hpp" 8 | #include "execs.hpp" 9 | 10 | class Launcher : public GUIDialog { 11 | public: 12 | int m_selectedProg; 13 | 14 | Launcher() : GUIDialog( 15 | GUIDialog::Height95, GUIDialog::AlignTop, 16 | "Hollyhock Launcher", 17 | GUIDialog::KeyboardStateNone 18 | ), m_appNames( 19 | GetLeftX() + 10, GetTopY() + 10, GetRightX() - 10, GetBottomY() - 10, 20 | APP_NAMES_EVENT_ID 21 | ), m_progInfo( 22 | GetLeftX() + 10, GetTopY() + 90, GetRightX() - 10, GetBottomY() - 10, 23 | // Since the app info string is immediately updated based on the 24 | // selected app, if no apps are found, this is the string that stays 25 | // displayed. 26 | // Use it to communicate to the user that we couldn't find any apps. 27 | "No apps were found on your calculator.\n\nEnsure their .hhk files have been copied to the root directory of your calculator's flash." 28 | ), m_run( 29 | GetLeftX() + 10, GetTopY() + 45, GetLeftX() + 10 + 100, GetTopY() + 45 + 35, 30 | "Run", RUN_EVENT_ID 31 | ), m_close( 32 | GetRightX() - 10 - 100, GetTopY() + 45, GetRightX() - 10, GetTopY() + 45 + 35, 33 | "Close", CLOSE_EVENT_ID 34 | ) { 35 | m_selectedProg = 0; 36 | 37 | Apps::LoadAppInfo(); 38 | Bins::LoadAppInfo(); 39 | Execs::LoadExecInfo(); 40 | 41 | //Add apps to dropdown 42 | for (int i = 0; i < Apps::g_numApps; ++i) { 43 | struct Apps::AppInfo *app = &Apps::g_apps[i]; 44 | 45 | const char *name = app->path; 46 | 47 | // first char of the name will not be \0 if a name was included. 48 | if (app->name[0] != '\0') { 49 | name = app->name; 50 | } 51 | 52 | m_appNames.AddMenuItem(*( 53 | new GUIDropDownMenuItem( 54 | name, i + 1, 55 | GUIDropDownMenuItem::FlagEnabled | 56 | GUIDropDownMenuItem::FlagTextAlignLeft 57 | ) 58 | )); 59 | } 60 | 61 | //Add bins to dropdown 62 | for (int i = 0; i < Bins::g_numApps; ++i) { 63 | struct Bins::AppInfo *app = &Bins::g_apps[i]; 64 | 65 | const char *name = app->path; 66 | 67 | // first char of the name will not be \0 if a name was included. 68 | if (app->name[0] != '\0') { 69 | name = app->name; 70 | } 71 | 72 | m_appNames.AddMenuItem(*( 73 | new GUIDropDownMenuItem( 74 | name, i + 1 + Apps::g_numApps, //Add bins after apps 75 | GUIDropDownMenuItem::FlagEnabled | 76 | GUIDropDownMenuItem::FlagTextAlignLeft 77 | ) 78 | )); 79 | } 80 | 81 | //Add execs to dropdown 82 | for (int i = 0; i < Execs::g_numExecs; ++i) { 83 | struct Execs::ExecInfo *exec = &Execs::g_execs[i]; 84 | 85 | const char *name = exec->fileName; 86 | 87 | // first char of the name will not be \0 if a name was included. 88 | if (exec->name[0] != '\0') { 89 | name = exec->name; 90 | } 91 | 92 | m_appNames.AddMenuItem(*( 93 | new GUIDropDownMenuItem( 94 | name, i + 1 + Apps::g_numApps + Bins::g_numApps, //Add execs after apps and bins 95 | GUIDropDownMenuItem::FlagEnabled | 96 | GUIDropDownMenuItem::FlagTextAlignLeft 97 | ) 98 | )); 99 | } 100 | 101 | m_appNames.SetScrollBarVisibility( 102 | GUIDropDownMenu::ScrollBarVisibleWhenRequired 103 | ); 104 | AddElement(m_appNames); 105 | 106 | AddElement(m_progInfo); 107 | 108 | // Only show the Run button if there's apps or execs to display 109 | if (Apps::g_numApps+Bins::g_numApps+Execs::g_numExecs > 0) { 110 | AddElement(m_run); 111 | } 112 | 113 | AddElement(m_close); 114 | 115 | UpdateAppInfo(); 116 | } 117 | 118 | virtual int OnEvent(GUIDialog_Wrapped *dialog, GUIDialog_OnEvent_Data *event) { 119 | if (event->GetEventID() == APP_NAMES_EVENT_ID && (event->type & 0xF) == 0xD) { 120 | m_selectedProg = event->data - 1; 121 | 122 | UpdateAppInfo(); 123 | 124 | return 0; 125 | } 126 | 127 | return GUIDialog::OnEvent(dialog, event); 128 | } 129 | 130 | void UpdateAppInfo() { 131 | // If an invalid index is selected, don't do anything 132 | if (m_selectedProg >= (Apps::g_numApps + Bins::g_numApps + Execs::g_numExecs)) return; 133 | // Check if an exec is selecter or an app is selected 134 | if (m_selectedProg >= (Apps::g_numApps + Bins::g_numApps)){ 135 | 136 | //An exec is selected 137 | struct Execs::ExecInfo *exec = &Execs::g_execs[m_selectedProg-Apps::g_numApps-Bins::g_numApps]; 138 | bool hasName = exec->name[0] != '\0'; 139 | bool hasDescription = exec->description[0] != '\0'; 140 | bool hasAuthor = exec->author[0] != '\0'; 141 | bool hasVersion = exec->version[0] != '\0'; 142 | 143 | memset(m_progInfoString, 0, sizeof(m_progInfoString)); 144 | 145 | if (hasName) { 146 | strcat(m_progInfoString, exec->name); 147 | } else { 148 | strcat(m_progInfoString, "hhk/"); 149 | strcat(m_progInfoString, exec->fileName); 150 | } 151 | 152 | if (hasAuthor || hasVersion) { 153 | strcat(m_progInfoString, "\n("); 154 | 155 | if (hasVersion) { 156 | strcat(m_progInfoString, "version "); 157 | strcat(m_progInfoString, exec->version); 158 | } 159 | 160 | if (hasAuthor) { 161 | if (hasVersion) { 162 | strcat(m_progInfoString, " by "); 163 | } else { 164 | strcat(m_progInfoString, "by "); 165 | } 166 | 167 | strcat(m_progInfoString, exec->author); 168 | } 169 | 170 | strcat(m_progInfoString, ")"); 171 | } 172 | 173 | if (hasName) { 174 | strcat(m_progInfoString, "\n(from hhk/"); 175 | strcat(m_progInfoString, exec->fileName); 176 | strcat(m_progInfoString, ")"); 177 | } 178 | 179 | if (hasDescription) { 180 | strcat(m_progInfoString, "\n\n"); 181 | strcat(m_progInfoString, exec->description); 182 | } 183 | } 184 | else 185 | if (m_selectedProg >= (Apps::g_numApps)){ 186 | 187 | //A bin is selected 188 | struct Bins::AppInfo *app = &Bins::g_apps[m_selectedProg-Apps::g_numApps]; 189 | bool hasName = app->name[0] != '\0'; 190 | bool hasDescription = app->description[0] != '\0'; 191 | bool hasAuthor = app->author[0] != '\0'; 192 | bool hasVersion = app->version[0] != '\0'; 193 | 194 | memset(m_progInfoString, 0, sizeof(m_progInfoString)); 195 | 196 | if (hasName) { 197 | strcat(m_progInfoString, app->name); 198 | } else { 199 | strcat(m_progInfoString, app->path); 200 | } 201 | 202 | if (hasAuthor || hasVersion) { 203 | strcat(m_progInfoString, "\n("); 204 | 205 | if (hasVersion) { 206 | strcat(m_progInfoString, "version "); 207 | strcat(m_progInfoString, app->version); 208 | } 209 | 210 | if (hasAuthor) { 211 | if (hasVersion) { 212 | strcat(m_progInfoString, " by "); 213 | } else { 214 | strcat(m_progInfoString, "by "); 215 | } 216 | 217 | strcat(m_progInfoString, app->author); 218 | } 219 | 220 | strcat(m_progInfoString, ")"); 221 | } 222 | 223 | if (hasName) { 224 | strcat(m_progInfoString, "\n(from "); 225 | strcat(m_progInfoString, app->path); 226 | strcat(m_progInfoString, ")"); 227 | } 228 | 229 | if (hasDescription) { 230 | strcat(m_progInfoString, "\n\n"); 231 | strcat(m_progInfoString, app->description); 232 | } 233 | } 234 | else 235 | { 236 | 237 | //An app is selected 238 | struct Apps::AppInfo *app = &Apps::g_apps[m_selectedProg]; 239 | bool hasName = app->name[0] != '\0'; 240 | bool hasDescription = app->description[0] != '\0'; 241 | bool hasAuthor = app->author[0] != '\0'; 242 | bool hasVersion = app->version[0] != '\0'; 243 | 244 | memset(m_progInfoString, 0, sizeof(m_progInfoString)); 245 | 246 | if (hasName) { 247 | strcat(m_progInfoString, app->name); 248 | } else { 249 | strcat(m_progInfoString, app->path); 250 | } 251 | 252 | if (hasAuthor || hasVersion) { 253 | strcat(m_progInfoString, "\n("); 254 | 255 | if (hasVersion) { 256 | strcat(m_progInfoString, "version "); 257 | strcat(m_progInfoString, app->version); 258 | } 259 | 260 | if (hasAuthor) { 261 | if (hasVersion) { 262 | strcat(m_progInfoString, " by "); 263 | } else { 264 | strcat(m_progInfoString, "by "); 265 | } 266 | 267 | strcat(m_progInfoString, app->author); 268 | } 269 | 270 | strcat(m_progInfoString, ")"); 271 | } 272 | 273 | if (hasName) { 274 | strcat(m_progInfoString, "\n(from "); 275 | strcat(m_progInfoString, app->path); 276 | strcat(m_progInfoString, ")"); 277 | } 278 | 279 | if (hasDescription) { 280 | strcat(m_progInfoString, "\n\n"); 281 | strcat(m_progInfoString, app->description); 282 | } 283 | 284 | // App Name (version 1.0.0 by Meme King) 285 | // \fls0\meme.hhk 286 | 287 | // Meme to your heart's content. 288 | 289 | } 290 | 291 | m_progInfo.SetText(m_progInfoString); 292 | m_progInfo.Refresh(); 293 | Refresh(); 294 | } 295 | 296 | private: 297 | const uint16_t APP_NAMES_EVENT_ID = 1; 298 | GUIDropDownMenu m_appNames; 299 | 300 | GUILongLabel m_progInfo; 301 | 302 | // While GUILongLabel seems to copy the string into it's own memory when 303 | // calling SetText, when it refreshes sometimes it tries to read the old 304 | // memory. Not sure why, but that's why this is here - it'll never move 305 | // because GUIDialog has the copy/move ctor deleted. This should therefore 306 | // be a safe solution. 307 | char m_progInfoString[500]; 308 | 309 | const uint16_t RUN_EVENT_ID = GUIDialog::DialogResultOK; 310 | GUIButton m_run; 311 | 312 | const uint16_t CLOSE_EVENT_ID = GUIDialog::DialogResultCancel; 313 | GUIButton m_close; 314 | }; 315 | 316 | void main() { 317 | Launcher launcher; 318 | if (launcher.ShowDialog() == GUIDialog::DialogResultOK) { 319 | if (launcher.m_selectedProg >= Apps::g_numApps+Bins::g_numApps){ 320 | //Exec selected 321 | Execs::EntryPoint epE = Execs::RunExec(launcher.m_selectedProg-Apps::g_numApps-Bins::g_numApps); 322 | if (epE != nullptr) { 323 | epE(); 324 | } 325 | } 326 | else if (launcher.m_selectedProg >= Apps::g_numApps){ 327 | //Bin selected 328 | Bins::EntryPoint epA = Bins::RunApp(launcher.m_selectedProg-Apps::g_numApps); 329 | if (epA != nullptr) { 330 | epA(); 331 | } 332 | } 333 | 334 | else{ 335 | //App selected 336 | Apps::EntryPoint epA = Apps::RunApp(launcher.m_selectedProg); 337 | if (epA != nullptr) { 338 | epA(); 339 | } 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /demos/tetris/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | //320x520 -> 16x26 (20px x 20px) -> 12x22 7 | 8 | APP_NAME("Tetris") 9 | APP_DESCRIPTION("A simple implementation of the game Tetris.") 10 | APP_AUTHOR("De_Coder") 11 | APP_VERSION("1.0.0") 12 | 13 | void print_score(uint32_t score){ //prints to score to the top of the screen 14 | Debug_SetCursorPosition(7, 0); 15 | Debug_PrintString("Score: ", false); 16 | 17 | char *num = "0000"; 18 | num[0] = (score / 1000) % 10 + '0'; 19 | num[1] = (score / 100) % 10 + '0'; 20 | num[2] = (score / 10) % 10 + '0'; 21 | num[3] = score % 10 + '0'; 22 | Debug_SetCursorPosition(14, 0); 23 | Debug_PrintString(num, false); 24 | } 25 | 26 | uint32_t random(uint32_t rand){ //generates a pseudo random number 27 | rand ^= (rand << 13); 28 | rand ^= (rand >> 17); 29 | rand ^= (rand << 5); 30 | return rand; 31 | } 32 | 33 | void draw_square(unsigned int x, unsigned int y, uint8_t color_index){ //draws one square at x,y in the color given by color_index 34 | if(x <= 12 && y <= 22){ 35 | x = x * 20 + 41; 36 | y = y * 20 + 41; 37 | for(int i = 0; i < 18; i++){ 38 | for(int j = 0; j < 18; j++){ 39 | LCD_SetPixelFromPalette(x + i, y + j, color_index); 40 | } 41 | } 42 | } 43 | } 44 | 45 | void main() { 46 | uint32_t rand = 0; //used to save random numbers. the last number is used as the seed for the next number 47 | uint32_t score = 0; 48 | uint32_t wait_down = 0; //counter for the cycles until the block moves down one field 49 | 50 | const char blocks [7] [4] [2] = { //offsets for all 7 possible blocks 51 | { 52 | {0,0},//ox 53 | {1,0},//xx 54 | {0,1}, 55 | {1,1} 56 | }, 57 | { 58 | {-1,0},//x0xx 59 | {0,0}, 60 | {1,0}, 61 | {2,0} 62 | }, 63 | { 64 | {0,1},// x0x 65 | {-1,0},// x 66 | {0,0}, 67 | {1,0} 68 | }, 69 | { 70 | {1,1},// x0x 71 | {-1,0},// x 72 | {0,0}, 73 | {1,0} 74 | }, 75 | { 76 | {-1,1},//x0x 77 | {-1,0},//x 78 | {0,0}, 79 | {1,0} 80 | }, 81 | { 82 | {-1,0},//x0 83 | {0,0}, // xx 84 | {0,1}, 85 | {1,1} 86 | }, 87 | { 88 | {0,0},// 0x 89 | {1,0},//xx 90 | {-1,1}, 91 | {0,1} 92 | } 93 | }; 94 | 95 | struct InputEvent event; 96 | 97 | bool running = true; 98 | bool down = false; //should block go down fast? 99 | 100 | uint8_t field[12] [22]; 101 | 102 | for(int i = 0; i < 12; i++){ 103 | for(int j = 0; j < 22; j++){ 104 | field[i] [j] = 0; 105 | } 106 | } 107 | 108 | unsigned char active_block [4] [2] = {{0,0},{0,0},{0,0},{0,0}}; //positions of all fields filled by the active block 109 | unsigned char pos_x = 0; //position of the active block 110 | unsigned char pos_y = 0; //position of the active block 111 | unsigned char rot = 0; //rotation of the active block 112 | unsigned char block_type = 0; //type of the active block 113 | 114 | LCD_VRAMBackup(); 115 | 116 | LCD_ClearScreen(); 117 | LCD_Refresh(); 118 | 119 | 120 | Debug_SetCursorPosition(0, 0); 121 | Debug_PrintString("just push some buttons. :)", false); 122 | LCD_Refresh(); 123 | 124 | for(int i = 0; i < 4; i++){ //get some keycodes to generate a seed for the random() function 125 | bool r = true; 126 | while(r){ 127 | memset(&event, 0, sizeof(event)); 128 | GetInput(&event, 0xFFFFFFFF, 0x10); 129 | if(event.type == EVENT_KEY){ 130 | if(event.data.key.direction == KEY_PRESSED){ 131 | rand += event.data.key.keyCode; 132 | rand *= 100; 133 | r = false; 134 | } 135 | } 136 | } 137 | } 138 | 139 | LCD_ClearScreen(); 140 | LCD_Refresh(); 141 | 142 | for(int i = 0; i < 13; i++){ //draw vertical lines of the board 143 | for(int j = 0; j < 440; j++){ 144 | LCD_SetPixel(i * 20 + 39, j + 40, 0); 145 | LCD_SetPixel(i * 20 + 40, j + 40, 0); 146 | } 147 | } 148 | 149 | for(int i = 0; i < 23; i++){ //draw horizontal lines of the board 150 | for(int j = 0; j < 240; j++){ 151 | LCD_SetPixel(j + 40, i * 20 + 39, 0); 152 | LCD_SetPixel(j + 40, i * 20 + 40, 0); 153 | } 154 | } 155 | 156 | LCD_Refresh(); 157 | 158 | rot = 0; //get first block 159 | pos_x = 6; 160 | pos_y = 0; 161 | rand = random(rand); 162 | block_type = rand % 7; 163 | for(int i = 0; i < 4; i++){ 164 | active_block[i][0] = blocks[block_type][i][0] + 6; 165 | active_block[i][1] = blocks[block_type][i][1]; 166 | } 167 | 168 | print_score(0); 169 | 170 | while(running){ 171 | memset(&event, 0, sizeof(event)); 172 | GetInput(&event, 0x0, 0x12); 173 | 174 | if(event.type == EVENT_KEY){ 175 | if(event.data.key.direction == KEY_PRESSED){ 176 | switch(event.data.key.keyCode){ 177 | case KEYCODE_OPEN_PARENTHESIS: //turn block left 178 | { 179 | bool e = false; //error while rotating the block? 180 | rot++; 181 | rot %= 4; 182 | for(int i = 0; i < 4; i++){ //for every field of the active block 183 | active_block[i][0] = blocks[block_type][i][0];// get offset 184 | active_block[i][1] = blocks[block_type][i][1]; 185 | for(int j = 0; j < rot; j++){ // rotate offset 186 | char temp = active_block[i][0]; 187 | active_block[i][0] = -active_block[i][1]; 188 | active_block[i][1] = temp; 189 | } 190 | active_block[i][0] += pos_x; //set to position 191 | active_block[i][1] += pos_y; 192 | 193 | if(active_block[i][0] > 11 || active_block[i][1] > 21){ //check if its out of the game field 194 | e = true; 195 | break; 196 | }else if(field[active_block[i][0]][active_block[i][1]] != 0){ //check if it hits a filled field 197 | e = true; 198 | break; 199 | } 200 | } 201 | 202 | if(e){// if a error occcured rotate back and restore active_block to its state before rotation 203 | rot += 3; 204 | rot %= 4; 205 | for(int i = 0; i < 4; i++){ 206 | active_block[i][0] = blocks[block_type][i][0]; 207 | active_block[i][1] = blocks[block_type][i][1]; 208 | for(int j = 0; j < rot; j++){ 209 | char temp = active_block[i][0]; 210 | active_block[i][0] = -active_block[i][1]; 211 | active_block[i][1] = temp; 212 | } 213 | active_block[i][0] += pos_x; 214 | active_block[i][1] += pos_y; 215 | } 216 | } 217 | } 218 | break; 219 | case KEYCODE_TIMES: //turn block right 220 | { 221 | bool e = false; //for everything else look at turn left ^^ 222 | rot += 3; 223 | rot %= 4; 224 | for(int i = 0; i < 4; i++){ 225 | active_block[i][0] = blocks[block_type][i][0]; 226 | active_block[i][1] = blocks[block_type][i][1]; 227 | for(int j = 0; j < rot; j++){ 228 | char temp = active_block[i][0]; 229 | active_block[i][0] = -active_block[i][1]; 230 | active_block[i][1] = temp; 231 | } 232 | active_block[i][0] += pos_x; 233 | active_block[i][1] += pos_y; 234 | 235 | if(active_block[i][0] > 11 || active_block[i][1] > 21){ 236 | e = true; 237 | break; 238 | }else if(field[active_block[i][0]][active_block[i][1]] != 0){ 239 | e = true; 240 | break; 241 | } 242 | } 243 | 244 | if(e){ 245 | rot++; 246 | rot %= 4; 247 | for(int i = 0; i < 4; i++){ 248 | active_block[i][0] = blocks[block_type][i][0]; 249 | active_block[i][1] = blocks[block_type][i][1]; 250 | for(int j = 0; j < rot; j++){ 251 | char temp = active_block[i][0]; 252 | active_block[i][0] = -active_block[i][1]; 253 | active_block[i][1] = temp; 254 | } 255 | active_block[i][0] += pos_x; 256 | active_block[i][1] += pos_y; 257 | } 258 | } 259 | } 260 | break; 261 | case KEYCODE_4: //move block to the left 262 | { 263 | bool e = false; //error while moving the block? 264 | for(int i = 0; i < 4; i++){ //for every field of the active block 265 | if(active_block[i][0] == 0){ //block is already at the border 266 | e = true; 267 | break; 268 | }else if(field[active_block[i][0] - 1][active_block[i][1]] != 0){ //field is blocked 269 | e = true; 270 | break; 271 | } 272 | } 273 | if(!e){ //if no error occured 274 | pos_x--; //update position 275 | for(int i = 0; i < 4; i++){ //move the fields 276 | active_block[i][0]--; 277 | } 278 | } 279 | } 280 | break; 281 | case KEYCODE_6: //move block to the right 282 | { 283 | bool e = false; //for everything else look at move left 284 | for(int i = 0; i < 4; i++){ 285 | if(active_block[i][0] >= 11){ 286 | e = true; 287 | break; 288 | }else if(field[active_block[i][0] + 1][active_block[i][1]] != 0){ 289 | e = true; 290 | break; 291 | } 292 | } 293 | if(!e){ 294 | pos_x++; 295 | for(int i = 0; i < 4; i++){ 296 | active_block[i][0]++; 297 | } 298 | } 299 | } 300 | break; 301 | case KEYCODE_5: //block should go down until it hits something 302 | down = true; 303 | break; 304 | default: 305 | if (event.data.key.keyCode == KEYCODE_POWER_CLEAR){ //close the game 306 | running = false; 307 | } 308 | break; 309 | } 310 | } 311 | } 312 | 313 | for(int i = 0; i < 4; i++){ //draw fields of the active block 314 | draw_square(active_block[i][0], active_block[i][1], block_type); 315 | } 316 | LCD_Refresh(); //update screen 317 | for(int i = 0; i < 4; i++){ //draw fields of the active block in white so the block dosnt leave a colored trail 318 | draw_square(active_block[i][0], active_block[i][1], 7); 319 | } 320 | if((wait_down >= 16 - (score / 64)) || down){ //check if block should go down 321 | do{ // move down one step or if down is true move down until the block hits somthing 322 | wait_down = 0; //reset the cyclecounter 323 | bool hit = false; //did block hit something? 324 | for(int i = 0; i < 4; i++){ //for every field of the active block 325 | if(active_block[i][1] + 1 > 21){//check if it hits the ground 326 | hit = true; 327 | break; 328 | }else if(field[active_block[i][0]] [active_block[i][1] + 1]){//check if it hits a filled field 329 | hit = true; 330 | break; 331 | } 332 | } 333 | if(hit){ //if the block hit something 334 | down = false; //block hit -> dont move down anymore 335 | for(int i = 0; i < 4; i++){ //redraw all fields of the active block 336 | draw_square(active_block[i][0], active_block[i][1], block_type); 337 | } 338 | if(pos_y == 0){ //if the block hit something directly after spawning the player has lost 339 | running = false; 340 | } 341 | for(int i = 0; i < 4; i++){ //set all former fields of the active block to blocked 342 | field[active_block[i][0]][active_block[i][1]] = block_type + 1; 343 | } 344 | 345 | for(int i = 0; i < 4; i++){ //check for every field if it filled a line 346 | bool full_line = true; 347 | for(int j = 0; j < 12; j++){ //test line of the active field 348 | if(field[j][active_block[i][1]] == 0){ 349 | full_line = false; 350 | } 351 | } 352 | if(full_line){ 353 | score += 10; //increment score 354 | for(int j = i; j < 4; j++){ //move down all active fields which are above this one 355 | if(active_block[i][1] > active_block[j][1]){ 356 | active_block[j][1]++; 357 | } 358 | } 359 | for(int j = 0; j < 12; j++){ //for every column 360 | for(int k = active_block[i][1]; k > 0; k--){ //move down every field above and redraw it in the new color 361 | field[j][k] = field[j][k - 1]; 362 | draw_square(j,k,(field[j][k] != 0)?(field[j][k] - 1):(7)); 363 | } 364 | field[j][0] = 0; //make top field empty 365 | draw_square(j,0,7); //draw top field in white 366 | } 367 | } 368 | } 369 | 370 | rot = 0;// reset the blocks state and get a new one 371 | pos_x = 5; 372 | pos_y = 0; 373 | score++; 374 | rand = random(rand); 375 | block_type = rand % 7; 376 | 377 | for(int i = 0; i < 4; i++){ //get fields of the new block 378 | active_block[i][0] = blocks[block_type][i][0] + 6; 379 | active_block[i][1] = blocks[block_type][i][1]; 380 | } 381 | print_score(score); //print new score 382 | }else{ //if the didnt collide move it down one step 383 | pos_y++; 384 | for(int i = 0; i < 4; i++){ 385 | active_block[i][1]++; 386 | } 387 | } 388 | }while(down);// move down until the block hits somthing 389 | }else{ 390 | wait_down++;//increment cycle counter 391 | } 392 | } 393 | 394 | LCD_ClearScreen();// print score and wait 395 | print_score(score); 396 | LCD_Refresh(); 397 | Debug_WaitKey(); 398 | 399 | LCD_VRAMRestore(); 400 | LCD_Refresh(); 401 | } 402 | --------------------------------------------------------------------------------