Casio fx-CG series port (#324)

* Initial test - working on Linux

* Try to make it work with liba

* Stop using liba and the filesystem

* IT WORKS

* Key input, full res, fix some of the crashes

* Fix the hang when doing calculations

* Add some more key mappings

* Fix the square root issue

* Icons

* Better key mappings, brightness control, better gamma correction, more effficient framebuffer

* Cleanup stage 1

* Cleanup stage 2

* Make the build system build a g3a

* Make it not exit when you press the menu button

* Add Casio port to README

* Use omega-master instead of omega-dev

* Fix mistake with cherry-picking in the README

* Fix internal storage crash

* Fix compile error on Numworks calculators

* Upsilon branding

* Sharper icon

* Make the CI work

* Add power off and improve menu

* Map Alpha + up/down to the brightness shortcut

* Add missing file

* Fix web CI build

* Revert "Fix web CI build"

This reverts commit f19657d9fc.

* Change "prizm" to "fxcg"

* Add FASTLOAD option for Add-in Push

* Add some charatcers to the catalog on Casio and improve key mappings

* Build with -Os -flto

* Disable LTO for now as it's causing crashes

* Put back the fonts I accidently changed

I'd like to add an option for this though as I prefer the ones from Epsilon
This commit is contained in:
circuit10
2023-05-10 17:28:18 +01:00
committed by GitHub
parent aadcd37f31
commit b44a95a9b3
77 changed files with 1617 additions and 49 deletions

View File

@@ -23,8 +23,15 @@ void pullRect(KDRect r, KDColor * pixels);
bool waitForVBlank();
#ifndef _FXCG
constexpr int Width = 320;
constexpr int Height = 240;
#else
constexpr int Width = 396;
constexpr int Height = 224;
#endif
// TODO: Adjust this on the Casio calculator
constexpr int WidthInTenthOfMillimeter = 576;
constexpr int HeightInTenthOfMillimeter = 432;

View File

@@ -195,7 +195,7 @@ public:
class StorageHelper {
public:
static uint16_t unalignedShort(char * address) {
#if __EMSCRIPTEN__
#if (defined __EMSCRIPTEN__) || (defined _FXCG)
uint8_t f1 = *(address);
uint8_t f2 = *(address+1);
uint16_t f = (uint16_t)f1 + (((uint16_t)f2)<<8);
@@ -205,7 +205,7 @@ public:
#endif
}
static void writeUnalignedShort(uint16_t value, char * address) {
#if __EMSCRIPTEN__
#if (defined __EMSCRIPTEN__) || (defined _FXCG)
*((uint8_t *)address) = (uint8_t)(value & ((1 << 8) - 1));
*((uint8_t *)address+1) = (uint8_t)(value >> 8);
#else

View File

@@ -26,6 +26,12 @@ constexpr Key ValidKeys[] = {
constexpr int NumberOfKeys = 54;
constexpr int NumberOfValidKeys = 46;
enum class ModSimState : uint8_t {
None,
ForceOn,
ForceOff,
};
class State {
public:
constexpr State(uint64_t s = 0) :
@@ -50,8 +56,25 @@ public:
void clearKey(Key k) {
m_bitField &= ~((uint64_t)1 << (uint8_t)k);
}
void setSimulatedShift(ModSimState s) {
m_simulateShiftState = s;
}
ModSimState simulatedShift() const {
return m_simulateShiftState;
}
void setSimulatedAlpha(ModSimState s) {
m_simulateAlphaState = s;
}
ModSimState simulatedAlpha() const {
return m_simulateAlphaState;
}
private:
uint64_t m_bitField;
// Simulated key states
// These override the real key states and are used to map keys to keys under modifiers
ModSimState m_simulateShiftState = ModSimState::None;
ModSimState m_simulateAlphaState = ModSimState::None;
};
State scan();

View File

@@ -1,3 +1,4 @@
#include <ion/keyboard.h>
#include <ion/events.h>
#include <ion/timing.h>
#include <assert.h>
@@ -84,6 +85,16 @@ static inline Event innerGetEvent(int * timeout) {
Keyboard::Key key = (Keyboard::Key)(63-__builtin_clzll(keysSeenTransitioningFromUpToDown));
bool shift = isShiftActive() || state.keyDown(Keyboard::Key::Shift);
bool alpha = isAlphaActive() || state.keyDown(Keyboard::Key::Alpha);
// Allow the detected states to be overriden by the simulated states
// This is used for key mapping
if (state.simulatedShift() != Keyboard::ModSimState::None) {
shift = state.simulatedShift() == Keyboard::ModSimState::ForceOn;
}
if (state.simulatedAlpha() != Keyboard::ModSimState::None) {
alpha = state.simulatedAlpha() == Keyboard::ModSimState::ForceOn;
}
bool lock = isLockActive();
if ( key == Keyboard::Key::Left

View File

@@ -0,0 +1,2 @@
undefine sdl_src
undefine ion_simulator_sdl_src

View File

@@ -0,0 +1,66 @@
ion_src += $(addprefix ion/src/simulator/fxcg/, \
main.cpp \
clipboard.cpp \
display.cpp \
framebuffer.cpp \
telemetry_init.cpp \
keyboard.cpp \
events_keyboard.cpp \
events.cpp \
timing.cpp \
console.cpp \
backlight.cpp \
power.cpp \
menuHandler.cpp \
)
liba_src += $(addprefix liba/src/, \
strlcat.c \
strlcpy.c \
)
ion_src += ion/src/shared/collect_registers.cpp
sdl_simu_needs_to_be_removed += $(addprefix ion/src/simulator/shared/, \
main.cpp \
clipboard.cpp \
display.cpp \
framebuffer.cpp \
keyboard.cpp \
events_keyboard.cpp \
events_platform.cpp \
events.cpp \
layout.cpp \
actions.cpp \
window.cpp \
timing.cpp \
console.cpp \
)
sdl_simu_needs_to_be_removed += $(addprefix ion/src/shared/dummy/, \
backlight.cpp \
power.cpp \
)
#sdl_simu_needs_to_be_removed += $(addprefix ion/src/simulator/shared/dummy/, \
# display.cpp \
# led.cpp \
# usb.cpp \
# battery.cpp \
# store_script.cpp \
#)
# Remove the dummy diaplay (re-implemented) and the SDL simulator stuff.
ion_src := $(filter-out $(sdl_simu_needs_to_be_removed),$(ion_src))
SFLAGS := $(filter-out -Iion/src/simulator/external/sdl/include,$(SFLAGS))
SFLAGS += -DFXCG50 -DTARGET_FXCG50 -m4-nofpu -mb -ffreestanding -nostdlib -Wa,--dsp -fstrict-volatile-bitfields -g -Os
LDFLAGS += -nostdlib -Wl,--no-warn-rwx-segments -lgint-cg -lc -lgint-cg -lc -lgcc -lopenlibm -lstdc++ -lgcc
ifdef FASTLOAD
LDFLAGS += -T fxcg50_fastload.ld
else
LDFLAGS += -T fxcg50.ld
endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,67 @@
#include <ion/backlight.h>
#include <gint/drivers/r61524.h>
#include <gint/defs/util.h>
// From gint:
/* Interface with the controller */
static volatile uint16_t *intf = (uint16_t *)0xb4000000;
/* Bit 4 of Port R controls the RS bit of the display driver */
static volatile uint8_t *PRDR = (uint8_t *)0xa405013c;
GINLINE static void select(uint16_t reg)
{
/* Clear RS and write the register number */
*PRDR &= ~0x10;
synco();
*intf = reg;
synco();
/* Set RS back. We don't do this in read()/write() because the display
driver is optimized for consecutive GRAM access. LCD-transfers will
be faster when executing select() followed by several calls to
write(). (Although most applications should use the DMA instead.) */
*PRDR |= 0x10;
synco();
}
// From Utilities addin:
// START OF POWER MANAGEMENT CODE
#define LCDC *(unsigned int*)(0xB4000000)
int getRawBacklightSubLevel()
{
// Bdisp_DDRegisterSelect(0x5a1);
select(0x5a1);
return (LCDC & 0xFF) - 6;
}
void setRawBacklightSubLevel(int level)
{
// Bdisp_DDRegisterSelect(0x5a1);
select(0x5a1);
LCDC = (level & 0xFF) + 6;
}
// END OF POWER MANAGEMENT CODE
namespace Ion {
namespace Backlight {
uint8_t brightness() {
return getRawBacklightSubLevel();
}
void setBrightness(uint8_t b) {
setRawBacklightSubLevel(b);
}
void init() {
}
bool isInitialized() {
return true;
}
void shutdown() {
}
}
}

View File

@@ -0,0 +1,18 @@
#include <ion/clipboard.h>
#include <ion.h>
#include <string.h>
namespace Ion {
namespace Clipboard {
uint32_t localClipboardVersion;
void write(const char * text) {
}
const char * read() {
return nullptr;
}
}
}

View File

@@ -0,0 +1,24 @@
#include <ion/console.h>
#include "main.h"
#include <kandinsky/ion_context.h>
#include <stdio.h>
namespace Ion {
namespace Console {
char readChar() {
return 0;
}
void writeChar(char c) {
// fxlibc conflicts with this
#undef putchar
KDIonContext::putchar(c);
}
bool transmissionDone() {
return true;
}
}
}

View File

@@ -0,0 +1,30 @@
#include "display.h"
#include "framebuffer.h"
#include <kandinsky/color.h>
#include <stdint.h>
#include <gint/display-cg.h>
#include <ion/display.h>
#include <string.h>
#include <stdio.h>
#include <gint/display.h>
#include <gint/keyboard.h>
namespace Ion {
namespace Simulator {
namespace Display {
void init() {
}
void quit() {
}
void draw() {
dupdate();
}
}
}
}

View File

@@ -0,0 +1,19 @@
#ifndef ION_SIMULATOR_DISPLAY_H
#define ION_SIMULATOR_DISPLAY_H
#include <kandinsky.h>
namespace Ion {
namespace Simulator {
namespace Display {
void init();
void quit();
void draw();
}
}
}
#endif

View File

@@ -0,0 +1,23 @@
#include "events.h"
#include <ion/events.h>
namespace Ion {
namespace Events {
void didPressNewKey() {
}
char * sharedExternalTextBuffer() {
static char buffer[sharedExternalTextBufferSize];
return buffer;
}
const char * Event::text() const {
if (*this == ExternalText) {
return const_cast<const char *>(sharedExternalTextBuffer());
}
return defaultText();
}
}
}

View File

@@ -0,0 +1,24 @@
#ifndef ION_SIMULATOR_EVENTS_H
#define ION_SIMULATOR_EVENTS_H
#include <ion/events.h>
namespace Ion {
namespace Simulator {
namespace Events {
void dumpEventCount(int i);
void logAfter(int numberOfEvents);
}
}
namespace Events {
static constexpr int sharedExternalTextBufferSize = 2;
char * sharedExternalTextBuffer();
}
}
#endif

View File

@@ -0,0 +1,15 @@
#include <ion/events.h>
namespace Ion {
namespace Events {
Event getPlatformEvent() {
Event result = None;
return result;
}
}
}

View File

@@ -0,0 +1,56 @@
#include "framebuffer.h"
#include <gint/display-cg.h>
#include <ion/display.h>
#include "main.h"
#include <stdio.h>
#include <gint/display.h>
// static KDColor sPixels[Ion::Display::Width * Ion::Display::Height];
static_assert(sizeof(KDColor) == sizeof(uint16_t), "KDColor is not 16 bits");
static KDColor* sPixels = (KDColor*) gint_vram;
static bool sFrameBufferActive = true;
namespace Ion {
namespace Display {
static KDFrameBuffer sFrameBuffer = KDFrameBuffer(sPixels, KDSize(Ion::Display::Width, Ion::Display::Height));
void pushRect(KDRect r, const KDColor * pixels) {
if (sFrameBufferActive) {
Simulator::Main::setNeedsRefresh();
sFrameBuffer.pushRect(r, pixels);
}
}
void pushRectUniform(KDRect r, KDColor c) {
if (sFrameBufferActive) {
Simulator::Main::setNeedsRefresh();
sFrameBuffer.pushRectUniform(r, c);
}
}
void pullRect(KDRect r, KDColor * pixels) {
if (sFrameBufferActive) {
sFrameBuffer.pullRect(r, pixels);
}
}
}
}
namespace Ion {
namespace Simulator {
namespace Framebuffer {
const KDColor * address() {
return sPixels;
}
void setActive(bool enabled) {
sFrameBufferActive = enabled;
}
}
}
}

View File

@@ -0,0 +1,17 @@
#ifndef ION_SIMULATOR_FRAMEBUFFER_H
#define ION_SIMULATOR_FRAMEBUFFER_H
#include <kandinsky.h>
namespace Ion {
namespace Simulator {
namespace Framebuffer {
const KDColor * address();
void setActive(bool enabled);
}
}
}
#endif

View File

@@ -0,0 +1,262 @@
#include <stdlib.h>
#include <unistd.h>
#include <gint/display-cg.h>
#include <gint/display.h>
#include <gint/keycodes.h>
#include <gint/keyboard.h>
#include <gint/gint.h>
#include <gint/clock.h>
#include <ion/keyboard.h>
#include <ion/events.h>
#include <ion/power.h>
#include "keyboard.h"
#include "layout_keyboard.h"
#include "main.h"
#include "menuHandler.h"
using namespace Ion::Keyboard;
class KeyPair {
public:
constexpr KeyPair(Key key, bool numworksShift, bool numworksAlpha, int gintKey, bool gintShift, bool gintAlpha, bool ignoreShiftAlpha = false) :
m_key(key),
m_numworksShift(numworksShift),
m_numworksAlpha(numworksAlpha),
m_gintKey(gintKey),
m_gintShift(gintShift),
m_gintAlpha(gintAlpha),
m_ignoreShiftAlpha(ignoreShiftAlpha)
{}
Key key() const { return m_key; }
bool numworksShift() const { return m_numworksShift; }
bool numworksAlpha() const { return m_numworksAlpha; }
int gintKey() const { return m_gintKey; }
bool gintShift() const { return m_gintShift; }
bool gintAlpha() const { return m_gintAlpha; }
bool ignoreShiftAlpha() const { return m_ignoreShiftAlpha; }
private:
Key m_key;
bool m_numworksShift;
bool m_numworksAlpha;
int m_gintKey;
bool m_gintShift;
bool m_gintAlpha;
bool m_ignoreShiftAlpha;
};
constexpr static KeyPair sKeyPairs[] = {
KeyPair(Key::Down, false, false, KEY_DOWN, false, false, true),
KeyPair(Key::Left, false, false, KEY_LEFT, false, false, true),
KeyPair(Key::Right, false, false, KEY_RIGHT, false, false, true),
KeyPair(Key::Up, false, false, KEY_UP, false, false, true),
KeyPair(Key::Back, false, false, KEY_EXIT, false, false),
KeyPair(Key::Home, false, false, KEY_MENU, false, false),
KeyPair(Key::Shift, false, false, KEY_SHIFT, false, false, true),
KeyPair(Key::Alpha, false, false, KEY_ALPHA, false, false, true),
KeyPair(Key::XNT, false, false, KEY_XOT, false, false),
KeyPair(Key::Var, false, false, KEY_VARS, false, false),
KeyPair(Key::Toolbox, false, false, KEY_OPTN, false, false),
KeyPair(Key::Backspace, false, false, KEY_DEL, false, false),
KeyPair(Key::Exp, false, false, KEY_LN, true, false),
KeyPair(Key::Ln, false, false, KEY_LN, false, false),
KeyPair(Key::Log, false, false, KEY_LOG, false, false),
KeyPair(Key::Imaginary, false, false, KEY_0, true, false),
KeyPair(Key::Comma, false, false, KEY_COMMA, false, false),
KeyPair(Key::Power, false, false, KEY_POWER, false, false),
KeyPair(Key::Sine, false, false, KEY_SIN, false, false),
KeyPair(Key::Cosine, false, false, KEY_COS, false, false),
KeyPair(Key::Tangent, false, false, KEY_TAN, false, false),
KeyPair(Key::Pi, false, false, KEY_EXP, true, false),
KeyPair(Key::Sqrt, false, false, KEY_SQUARE, true, false),
KeyPair(Key::Square, false, false, KEY_SQUARE, false, false),
KeyPair(Key::Seven, false, false, KEY_7, false, false),
KeyPair(Key::Eight, false, false, KEY_8, false, false),
KeyPair(Key::Nine, false, false, KEY_9, false, false),
KeyPair(Key::LeftParenthesis, false, false, KEY_LEFTP, false, false),
KeyPair(Key::RightParenthesis, false, false, KEY_RIGHTP, false, false),
KeyPair(Key::Four, false, false, KEY_4, false, false),
KeyPair(Key::Five, false, false, KEY_5, false, false),
KeyPair(Key::Six, false, false, KEY_6, false, false),
KeyPair(Key::Multiplication, false, false, KEY_MUL, false, false),
KeyPair(Key::Division, false, false, KEY_DIV, false, false),
KeyPair(Key::Division, false, false, KEY_FRAC, false, false),
KeyPair(Key::One, false, false, KEY_1, false, false),
KeyPair(Key::Two, false, false, KEY_2, false, false),
KeyPair(Key::Three, false, false, KEY_3, false, false),
KeyPair(Key::Plus, false, false, KEY_ADD, false, false),
KeyPair(Key::Minus, false, false, KEY_SUB, false, false),
KeyPair(Key::Zero, false, false, KEY_0, false, false),
KeyPair(Key::Dot, false, false, KEY_DOT, false, false),
KeyPair(Key::EE, false, false, KEY_EXP, false, false),
KeyPair(Key::Ans, false, false, KEY_NEG, true, false),
KeyPair(Key::EXE, false, false, KEY_EXE, false, false, true),
KeyPair(Key::OnOff, false, false, KEY_ACON, true, false),
// Cut
// Not assigned
// Copy
KeyPair(Key::Var, true, false, KEY_8, true, false),
// Paste
KeyPair(Key::Toolbox, true, false, KEY_9, true, false),
// Clear
KeyPair(Key::Backspace, true, false, KEY_ACON, false, false),
// [
KeyPair(Key::Exp, true, false, KEY_ADD, true, false),
// ]
KeyPair(Key::Ln, true, false, KEY_SUB, true, false),
// {
KeyPair(Key::Log, true, false, KEY_MUL, true, false),
// }
KeyPair(Key::Imaginary, true, false, KEY_DIV, true, false),
// _
KeyPair(Key::Comma, true, false, KEY_NEG, false, false),
// ->
KeyPair(Key::Power, true, false, KEY_STORE, false, false),
// asin
KeyPair(Key::Sine, true, false, KEY_SIN, true, false),
// acos
KeyPair(Key::Cosine, true, false, KEY_COS, true, false),
// atan
KeyPair(Key::Tangent, true, false, KEY_TAN, true, false),
// =
KeyPair(Key::Pi, true, false, KEY_DOT, true, false),
// <
KeyPair(Key::Sqrt, true, false, KEY_F1, false, false),
// >
KeyPair(Key::Square, true, false, KEY_F2, false, false),
// :
KeyPair(Key::XNT, false, true, KEY_F3, false, false),
// ;
KeyPair(Key::Var, false, true, KEY_F4, false, false),
// "
KeyPair(Key::Toolbox, false, true, KEY_EXP, false, true),
// %
KeyPair(Key::Backspace, false, true, KEY_F5, false, false),
// A
KeyPair(Key::Exp, false, true, KEY_XOT, false, true),
// B
KeyPair(Key::Ln, false, true, KEY_LOG, false, true),
// C
KeyPair(Key::Log, false, true, KEY_LN, false, true),
// D
KeyPair(Key::Imaginary, false, true, KEY_SIN, false, true),
// E
KeyPair(Key::Comma, false, true, KEY_COS, false, true),
// F
KeyPair(Key::Power, false, true, KEY_TAN, false, true),
// G
KeyPair(Key::Sine, false, true, KEY_FRAC, false, true),
// H
KeyPair(Key::Cosine, false, true, KEY_FD, false, true),
// I
KeyPair(Key::Tangent, false, true, KEY_LEFTP, false, true),
// J
KeyPair(Key::Pi, false, true, KEY_RIGHTP, false, true),
// K
KeyPair(Key::Sqrt, false, true, KEY_COMMA, false, true),
// L
KeyPair(Key::Square, false, true, KEY_ARROW, false, true),
// M
KeyPair(Key::Seven, false, true, KEY_7, false, true),
// N
KeyPair(Key::Eight, false, true, KEY_8, false, true),
// O
KeyPair(Key::Nine, false, true, KEY_9, false, true),
// P
KeyPair(Key::LeftParenthesis, false, true, KEY_4, false, true),
// Q
KeyPair(Key::RightParenthesis, false, true, KEY_5, false, true),
// R
KeyPair(Key::Four, false, true, KEY_6, false, true),
// S
KeyPair(Key::Five, false, true, KEY_TIMES, false, true),
// T
KeyPair(Key::Six, false, true, KEY_DIV, false, true),
// U
KeyPair(Key::Multiplication, false, true, KEY_1, false, true),
// V
KeyPair(Key::Division, false, true, KEY_2, false, true),
// W
KeyPair(Key::One, false, true, KEY_3, false, true),
// X
KeyPair(Key::Two, false, true, KEY_PLUS, false, true),
// Y
KeyPair(Key::Three, false, true, KEY_MINUS, false, true),
// Z
KeyPair(Key::Plus, false, true, KEY_0, false, true),
// Space
KeyPair(Key::Minus, false, true, KEY_DOT, false, true),
// ?
KeyPair(Key::Zero, false, true, KEY_F6, true, false),
// !
KeyPair(Key::Dot, false, true, KEY_F6, false, false),
// Brightness control shortcut in Upsilon
KeyPair(Key::Plus, true, false, KEY_UP, false, true),
KeyPair(Key::Minus, true, false, KEY_DOWN, false, true),
};
constexpr int sNumberOfKeyPairs = sizeof(sKeyPairs)/sizeof(KeyPair);
namespace Ion {
namespace Keyboard {
int menuHeldFor = 0;
State scan() {
State state = 0;
// Grab this opportunity to refresh the display if needed
Simulator::Main::refresh();
clearevents();
if (keydown(KEY_MENU)) {
state.setKey(Key::Home);
menuHeldFor++;
if (menuHeldFor > 30) {
Simulator::FXCGMenuHandler::openMenu();
dupdate();
// Wait until EXE is released
do {
sleep_ms(10);
clearevents();
} while (keydown(KEY_EXE));
}
} else {
menuHeldFor = 0;
}
for (int i = 0; i < sNumberOfKeyPairs; i++) {
const KeyPair & keyPair = sKeyPairs[i];
if (!keyPair.ignoreShiftAlpha() &&
(keyPair.gintShift() != Events::isShiftActive() ||
keyPair.gintAlpha() != Events::isAlphaActive())) {
continue;
}
if (keydown(keyPair.gintKey())) {
if (!keyPair.ignoreShiftAlpha()) {
state.setSimulatedShift(keyPair.numworksShift() ? ModSimState::ForceOn : ModSimState::ForceOff);
state.setSimulatedAlpha(keyPair.numworksAlpha() ? ModSimState::ForceOn : ModSimState::ForceOff);
}
state.setKey(keyPair.key());
}
}
return state;
}
}
}
namespace Ion {
namespace Simulator {
namespace Keyboard {
}
}
}

View File

@@ -0,0 +1,15 @@
#ifndef ION_SIMULATOR_KEYBOARD_H
#define ION_SIMULATOR_KEYBOARD_H
#include <ion/keyboard.h>
// #include <libndls.h>
namespace Ion {
namespace Simulator {
namespace Keyboard {
}
}
}
#endif

View File

@@ -0,0 +1,114 @@
#include "main.h"
#include "display.h"
#include "platform.h"
#include <gint/display-cg.h>
#include <gint/gint.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <ion.h>
#include <ion/events.h>
extern "C" {
int main() {
Ion::Simulator::Main::init();
ion_main(0, NULL);
Ion::Simulator::Main::quit();
return 0;
}
}
namespace Ion {
namespace Simulator {
namespace Main {
static bool sNeedsRefresh = false;
void init() {
Ion::Simulator::Display::init();
setNeedsRefresh();
}
void setNeedsRefresh() {
sNeedsRefresh = true;
}
void refresh() {
if (!sNeedsRefresh) {
return;
}
Display::draw();
sNeedsRefresh = false;
}
void quit() {
Ion::Simulator::Display::quit();
}
void EnableStatusArea(int opt) {
__asm__ __volatile__ (
".align 2 \n\t"
"mov.l 2f, r2 \n\t"
"mov.l 1f, r0 \n\t"
"jmp @r2 \n\t"
"nop \n\t"
".align 2 \n\t"
"1: \n\t"
".long 0x02B7 \n\t"
".align 4 \n\t"
"2: \n\t"
".long 0x80020070 \n\t"
);
}
extern "C" void *__GetVRAMAddress(void);
uint8_t xyram_backup[16 * 1024];
uint8_t ilram_backup[4 * 1024];
void worldSwitchHandler(void (*worldSwitchFunction)(), bool prepareVRAM) {
// Back up XYRAM
uint8_t* xyram = (uint8_t*) 0xe500e000;
memcpy(xyram_backup, xyram, 16 * 1024);
// Back up ILRAM
uint8_t* ilram = (uint8_t*) 0xe5200000;
memcpy(ilram_backup, ilram, 4 * 1024);
if (prepareVRAM) {
// Copying the screen to the OS's VRAM avoids a flicker when powering on
uint16_t* dst = (uint16_t *) __GetVRAMAddress();
uint16_t* src = gint_vram + 6;
for (int y = 0; y < 216; y++, dst += 384, src += 396) {
for (int x = 0; x < 384; x++) {
dst[x] = src[x];
}
}
// Disable the status area
EnableStatusArea(3);
}
worldSwitchFunction();
// Restore XYRAM
memcpy(xyram, xyram_backup, 16 * 1024);
// Restore ILRAM
memcpy(ilram, ilram_backup, 4 * 1024);
}
void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM) {
gint_world_switch(GINT_CALL(worldSwitchHandler, powerOffSafeFunction, prepareVRAM));
}
}
}
}

View File

@@ -0,0 +1,20 @@
#ifndef ION_SIMULATOR_MAIN_H
#define ION_SIMULATOR_MAIN_H
namespace Ion {
namespace Simulator {
namespace Main {
void init();
void quit();
void setNeedsRefresh();
void refresh();
void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM);
}
}
}
#endif

View File

@@ -0,0 +1,97 @@
#include "main.h"
namespace Ion {
namespace Simulator {
namespace FXCGMenuHandler {
int saveAndOpenMainMenu(void) {
int addr;
// get the address of the syscall table in it
addr = *(unsigned int *)0x8002007C;
if (addr < (int)0x80020070)
return 1;
if (addr >= (int)0x81000000)
return 1;
// get the pointer to syscall 1E58 - SwitchToMainMenu
addr += 0x1E58 * 4;
if (addr < (int)0x80020070)
return 1;
if (addr >= (int)0x81000000)
return 1;
addr = *(unsigned int *)addr;
if (addr < (int)0x80020070)
return 1;
if (addr >= (int)0x81000000)
return 1;
// Now addr has the address of the first operation in %1e58
// Run up to 150 times (300/2). OS 3.60's is 59 instructions, so this should
// be plenty, but will let it stop if nothing is found
for (unsigned short *currentAddr = (unsigned short *)addr;
(unsigned int)currentAddr < ((unsigned int)addr + 300); currentAddr++) {
// MOV.L GetkeyToMainFunctionReturn Flag, r14
if (*(unsigned char *)currentAddr != 0xDE)
continue;
// MOV #3, 2
if (*(currentAddr + 1) != 0xE203)
continue;
// BSR <SaveAndOpenMainMenu>
if ((*(unsigned char *)(currentAddr + 2) & 0xF0) != 0xB0)
continue;
// MOV.B r2, @r14
if (*(currentAddr + 3) != 0x2E20)
continue;
// BRA <some addr>
if ((*(unsigned char *)(currentAddr + 4) & 0xF0) != 0xA0)
continue;
// NOP
if (*(currentAddr + 5) != 0x0009)
continue;
unsigned short branchInstruction = *(currentAddr + 2);
// Clear first 4 bits (BSR identifier)
branchInstruction <<= 4;
branchInstruction >>= 4;
// branchInstruction is now the displacement of BSR
// Create typedef so we can cast the pointer
typedef void (*voidFunc)(void);
// JMP to disp*2 + PC + 4
((voidFunc)((unsigned int)branchInstruction * 2 +
(unsigned int)currentAddr + 4 + 4))();
return 0;
}
return 1;
}
extern "C" void gint_osmenu_native(void);
void openMenuWrapper(void) {
if (saveAndOpenMainMenu() != 0) {
// Fallback
gint_osmenu_native();
}
}
void openMenu(void) {
Simulator::Main::runPowerOffSafe(openMenuWrapper, true);
}
}
}
}

View File

@@ -0,0 +1,14 @@
#ifndef ION_SIMULATOR_MENUHANDLER_H
#define ION_SIMULATOR_MENUHANDLER_H
namespace Ion {
namespace Simulator {
namespace FXCGMenuHandler {
void openMenu(void);
}
}
}
#endif

View File

@@ -0,0 +1,23 @@
#ifndef ION_SIMULATOR_PLATFORM_H
#define ION_SIMULATOR_PLATFORM_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Those functions should be implemented per-platform.
* They are defined as C function for easier interop. */
const char * IonSimulatorGetLanguageCode();
void IonSimulatorKeyboardKeyDown(int keyNumber);
void IonSimulatorKeyboardKeyUp(int keyNumber);
void IonSimulatorEventsPushEvent(int eventNumber);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,42 @@
#include <ion/power.h>
#include <stdint.h>
#include <string.h>
#include "main.h"
#include <gint/gint.h>
#include <gint/display.h>
void PowerOff(int displayLogo) {
__asm__ __volatile__ (
".align 2 \n\t"
"mov.l 2f, r2 \n\t"
"mov.l 1f, r0 \n\t"
"jmp @r2 \n\t"
"nop \n\t"
".align 2 \n\t"
"1: \n\t"
".long 0x1839 \n\t"
".align 4 \n\t"
"2: \n\t"
".long 0x80020070 \n\t"
);
}
void powerOff(void) {
PowerOff(1);
}
namespace Ion {
namespace Power {
void suspend(bool checkIfOnOffKeyReleased) {
Simulator::Main::runPowerOffSafe(powerOff, true);
}
void standby() {
Simulator::Main::runPowerOffSafe(powerOff, true);
}
}
}

View File

@@ -0,0 +1,15 @@
#include "platform.h"
namespace Ion {
namespace Simulator {
namespace Telemetry {
void init() {
}
void shutdown() {
}
}
}
}

View File

@@ -0,0 +1,27 @@
#include <ion/timing.h>
#include "main.h"
#include <chrono>
// #include <libndls.h>
#include <gint/clock.h>
static auto start = std::chrono::steady_clock::now();
namespace Ion {
namespace Timing {
uint64_t millis() {
auto elapsed = std::chrono::steady_clock::now() - start;
return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
}
void usleep(uint32_t us) {
sleep_us(us);
}
void msleep(uint32_t ms) {
sleep_us(ms * 1000);
}
}
}