diff --git a/CMakeLists.txt b/CMakeLists.txt index fd0176a..b72d3d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,6 +253,12 @@ set(SOURCES src/usb/write4.S # Video driver interface src/video/video.c + # Touch-screen driver + src/touch/i2c.c + src/touch/i2c_inth.c + src/touch/adconv.c + src/touch/touch.c + src/touch/driver.c ) set(ASSETS_FX src/font5x7.png src/gdb/icons-i1msb.png) diff --git a/fx9860g.ld.c b/fx9860g.ld.c index a4ac9dc..f06c72c 100644 --- a/fx9860g.ld.c +++ b/fx9860g.ld.c @@ -70,11 +70,11 @@ SECTIONS _gint_exch_start = . ; KEEP(*(.gint.exch)) - _gint_exch_size = ABSOLUTE(. - _gint_exch_start); + _gint_exch_end = . ; _gint_tlbh_start = . ; KEEP(*(.gint.tlbh)) - _gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start); + _gint_tlbh_end = . ; *(.text .text.*) *(C P) diff --git a/fxcg50.ld.c b/fxcg50.ld.c index 8ad5e3e..c4ba63b 100644 --- a/fxcg50.ld.c +++ b/fxcg50.ld.c @@ -59,11 +59,11 @@ SECTIONS _gint_exch_start = . ; KEEP(*(.gint.exch)) - _gint_exch_size = ABSOLUTE(. - _gint_exch_start); + _gint_exch_end = . ; _gint_tlbh_start = . ; KEEP(*(.gint.tlbh)) - _gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start); + _gint_tlbh_end = . ; *(.text .text.*) } > rom diff --git a/fxcp_hh2.ld.c b/fxcp_hh2.ld.c index 5036a1b..897d8da 100644 --- a/fxcp_hh2.ld.c +++ b/fxcp_hh2.ld.c @@ -68,12 +68,12 @@ SECTIONS . = ALIGN(0x10); _gint_exch_start = . ; *(.gint.exch) - _gint_exch_size = ABSOLUTE(. - _gint_exch_start); + _gint_exch_end = . ; . = ALIGN(0x10); _gint_tlbh_start = . ; *(.gint.tlbh) - _gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start); + _gint_tlbh_end = . ; *(.text .text.*) } > ram AT> bin diff --git a/include/gint/clock.h b/include/gint/clock.h index acb5479..7a8c4f5 100644 --- a/include/gint/clock.h +++ b/include/gint/clock.h @@ -35,6 +35,7 @@ typedef struct int Bphi_div; int Iphi_div; int Pphi_div; + int Sphi_div; union { int CKIO_f; @@ -44,6 +45,7 @@ typedef struct int Bphi_f; int Iphi_f; int Pphi_f; + int Sphi_f; } clock_frequency_t; @@ -155,6 +157,11 @@ void cpg_get_overclock_setting(struct cpg_overclock_setting *s); /* Applies the specified overclock setting. */ void cpg_set_overclock_setting(struct cpg_overclock_setting const *s); +/* Sets whether or node CPG parameters are retained when world switching, i.e. + mostly whether overclock setting will remain while in the OS world and when + leaving the add-in. */ +void cpg_set_overclock_permanent(bool permanent); + //--- // Sleep functions //--- diff --git a/include/gint/defs/call.h b/include/gint/defs/call.h index 5a2f2bf..1dd3c7f 100644 --- a/include/gint/defs/call.h +++ b/include/gint/defs/call.h @@ -12,6 +12,7 @@ #define GINT_DEFS_CALL #include +#include #ifdef __cplusplus extern "C" { diff --git a/include/gint/drivers/keydev.h b/include/gint/drivers/keydev.h index 4d9b956..0e15645 100644 --- a/include/gint/drivers/keydev.h +++ b/include/gint/drivers/keydev.h @@ -184,6 +184,8 @@ typedef struct { /* Candidate key for repeats (or 0 if no key is candidate yet) */ int16_t rep_key; + /* Matrix code for rep_key, if rep_key is not 0. */ + uint8_t rep_row, rep_col; /* Number of repeats already sent */ int16_t rep_count; /* Time since key was first pressed (us) */ diff --git a/include/gint/drivers/r61523.h b/include/gint/drivers/r61523.h index c4b5b3c..70d9fa1 100644 --- a/include/gint/drivers/r61523.h +++ b/include/gint/drivers/r61523.h @@ -16,6 +16,10 @@ extern "C" { /* r61523_display(): Update the entire display (320x528) */ void r61523_display(uint16_t *vram); +/* r61523_display_rect(): Update a rectangle (both bounds included). */ +void r61523_display_rect( + uint16_t *vram, int xmin, int xmax, int ymin, int ymax); + /* r61523_win_set(): Set the display window */ void r61523_win_set(int x1, int x2, int y1, int y2); diff --git a/include/gint/drivers/states.h b/include/gint/drivers/states.h index d18e36a..84ec3fd 100644 --- a/include/gint/drivers/states.h +++ b/include/gint/drivers/states.h @@ -109,6 +109,16 @@ typedef struct { } usb_state_t; +/* Touch-screen modules */ +typedef struct { + uint16_t PRCR; + uint16_t PJCR; + uint8_t ICCR; + uint8_t ICIC; + uint8_t ICCL; + uint8_t ICCH; +} touch_state_t; + #ifdef __cplusplus } #endif diff --git a/include/gint/gdb.h b/include/gint/gdb.h index 93bdab6..4f89c91 100644 --- a/include/gint/gdb.h +++ b/include/gint/gdb.h @@ -10,6 +10,7 @@ extern "C" { #endif #include +#include /* gdb_cpu_state_t: State of the CPU when breaking This struct keep the same register indices as those declared by GDB to allow diff --git a/include/gint/hardware.h b/include/gint/hardware.h index 6044ec8..6a252a2 100644 --- a/include/gint/hardware.h +++ b/include/gint/hardware.h @@ -111,6 +111,8 @@ void hw_detect(void); #define HWCALC_FX9860G_SLIM 7 /* fx-CP 400 */ #define HWCALC_FXCP400 8 +/* fx-CG 100, successor to the CG-50 in the Prizm family. Also Graph Math+ */ +#define HWCALC_FXCG100 9 /* ** Keyboard diff --git a/include/gint/intc.h b/include/gint/intc.h index 1f43ae2..e2db631 100644 --- a/include/gint/intc.h +++ b/include/gint/intc.h @@ -52,6 +52,11 @@ enum { INTC_SPU_DSP1, /* USB communication */ INTC_USB, + /* I2C */ + INTC_I2C_AL, + INTC_I2C_TACK, + INTC_I2C_WAIT, + INTC_I2C_DTE, }; //--- diff --git a/include/gint/keyboard.h b/include/gint/keyboard.h index 2777479..3734b76 100644 --- a/include/gint/keyboard.h +++ b/include/gint/keyboard.h @@ -114,27 +114,46 @@ extern "C" { * 0xffff is "just before" 0x0000, not "long after". */ typedef struct { - uint time :16; /* Time of event, unique over short periods */ + /* Time of event, unique over short periods */ + u16 time; uint :2; /* Reserved for future use */ - uint mod :1; /* Whether modifiers are used */ uint shift :1; /* If mod=1, whether SHIFT was pressed */ uint alpha :1; /* If mod=1, whether ALPHA was pressed */ - uint type :3; /* Type of key event */ - uint key :8; /* Hit key */ + + /* Key that was pressed or released. */ + u8 key; + + // The following attributes will be union'd with touch info on the CP. + + union { + struct { + /* Matrix code: physical location of the key being it. */ + u8 row; + u8 col; + }; + struct { + /* X/Y touch-screen coordinate */ + u16 x; + u16 y; + }; + }; } GPACKED(4) key_event_t; /* Keyboard event types, as in the [type] field of key_event_t */ enum { - KEYEV_NONE = 0, /* No event available (poll() only) */ - KEYEV_DOWN = 1, /* Key was pressed */ - KEYEV_UP = 2, /* Key was released */ - KEYEV_HOLD = 3, /* A key that was pressed has been held down */ - KEYEV_OSMENU = 4, /* We went to the main menu and back */ + KEYEV_NONE = 0, /* No event available (poll() only) */ + KEYEV_DOWN = 1, /* Key was pressed */ + KEYEV_UP = 2, /* Key was released */ + KEYEV_HOLD = 3, /* A key that was pressed has been held down */ + KEYEV_OSMENU = 4, /* We went to the main menu and back */ + KEYEV_TOUCH_PRESSED = 5, /* Touch was detected */ + KEYEV_TOUCH_DRAG = 6, /* A touch that was detected has been held down */ + KEYEV_TOUCH_RELEASE = 7, /* Touch was released */ }; /* Keyboard frequency analysis is a runtime setting since gint 2.4. This macro diff --git a/include/gint/keycodes.h b/include/gint/keycodes.h index 5e904fa..dc2dd3f 100644 --- a/include/gint/keycodes.h +++ b/include/gint/keycodes.h @@ -9,7 +9,6 @@ extern "C" { #endif -/* Raw matrix codes */ enum { KEY_F1 = 0x91, KEY_F2 = 0x92, @@ -87,7 +86,23 @@ enum { KEY_EQUALS = 0xa5, KEY_CLEAR = KEY_EXIT, - /* Key aliases (handle with care =D) */ + /* Key codes for the CG-100 */ + KEY_ON = 0xa6, + KEY_HOME = KEY_MENU, + KEY_PREVTAB = 0xa7, + KEY_NEXTTAB = 0xa8, + KEY_PAGEUP = 0xa9, + KEY_PAGEDOWN = 0xaa, + KEY_SETTINGS = 0xab, + KEY_BACK = KEY_EXIT, + KEY_OK = 0xac, + KEY_CATALOG = 0xad, + KEY_TOOLS = KEY_OPTN, + KEY_FORMAT = KEY_FD, + KEY_SQRT = 0xae, + KEY_EXPFUN = 0xaf, + + /* Key aliases (deprecated--no more will be added) */ KEY_X2 = KEY_SQUARE, KEY_CARET = KEY_POWER, KEY_SWITCH = KEY_FD, diff --git a/include/gint/mpu/i2c.h b/include/gint/mpu/i2c.h new file mode 100644 index 0000000..b759585 --- /dev/null +++ b/include/gint/mpu/i2c.h @@ -0,0 +1,74 @@ +#ifndef GINT_MPU_I2C_H +#define GINT_MPU_I2C_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +//--- +// SH7305 I2C Bus Interface. Refer to: +// "Renesas SH7724 User's Manual: Hardware" +// Section 32: "I2C Bus Interface (I2C)" +//--- + +/* sh7305_i2c_t - I2C peripheral definition */ +typedef struct { + // read/write register + uint8_t ICDR; + pad(3); + + // control register + byte_union(ICCR, + uint8_t ICE :1; + uint8_t RACK :1; + uint8_t :1; + uint8_t TRS :1; + uint8_t :1; + uint8_t BBSY :1; + uint8_t :1; + uint8_t SCP :1; + ); + pad(3); + + // status register + byte_union(ICSR, + uint8_t SCLM :1; + uint8_t SDAM :1; + uint8_t :1; + uint8_t BUSY :1; + uint8_t AL :1; + uint8_t TACK :1; + uint8_t WAIT :1; + uint8_t DTE :1; + ); + pad(3); + + // interrupt control register + byte_union(ICIC, + uint8_t :1; + uint8_t :1; + uint8_t :1; + uint8_t :1; + uint8_t ALE :1; + uint8_t TACKE :1; + uint8_t WAITE :1; + uint8_t DTEE :1; + ); + pad(3); + + // clock control registers + uint8_t ICCL; + pad(3); + uint8_t ICCH; +} GPACKED(1) sh7305_i2c_t; + +#define SH7305_I2C (*((volatile sh7305_i2c_t *)0xa4470000)) + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_MPU_I2C_H */ diff --git a/include/gint/mpu/intc.h b/include/gint/mpu/intc.h index 7ccb06a..be4e0ca 100644 --- a/include/gint/mpu/intc.h +++ b/include/gint/mpu/intc.h @@ -192,7 +192,7 @@ typedef volatile struct uint16_t _MSIOF0:4; /* SH7724: Sync SCIF channel 0 */ uint16_t _MSIOF1:4; /* SH7724: Sync SCIF channel 1 */ uint16_t _1 :4; /* Unknown (TODO) */ - uint16_t _2 :4; /* Unknown (TODO) */ + uint16_t I2C:4; /* I2C */ ); pad(2); diff --git a/include/gint/mpu/keysc.h b/include/gint/mpu/keysc.h new file mode 100644 index 0000000..ed6d932 --- /dev/null +++ b/include/gint/mpu/keysc.h @@ -0,0 +1,34 @@ +//--- +// gint:mpu:keysc - Key Scan Controller +//--- + +#ifndef GINT_MPU_KEYSC +#define GINT_MPU_KEYSC + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef volatile struct { + uint16_t KIUDATA[6]; + uint16_t KIUCNTREG; + uint16_t KIAUTOFIXREG; + uint16_t KIUMODEREG; + uint16_t KIUSTATEREG; + uint16_t KIUINTREG; + uint16_t KIUWSETREG; + uint16_t KIUINTERVALREG; + uint16_t KOUTPINSET; + uint16_t KINPINSET; +} GPACKED(4) sh7305_keysc_t; + +#define SH7305_KEYSC (*(sh7305_keysc_t *)0xa44b0000) + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_MPU_KEYSC */ diff --git a/include/gint/touch.h b/include/gint/touch.h new file mode 100644 index 0000000..0e65638 --- /dev/null +++ b/include/gint/touch.h @@ -0,0 +1,45 @@ +//--- +// gint:touch - touch-screen driver API +//--- +#ifndef GINT_TOUCH_H +#define GINT_TOUCH_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if GINT_HW_CP + +/* touch_calib - tounch-screen calibration information */ +typedef struct { + int x_base; + int x_div; + int y_base; + int y_div; + int dual_debounce_frame; + int dual_sensi_entry; + int dual_sensi_leave; +} touch_calibration_t; + +/* touch_calib_get() - get calibration information */ +extern int touch_calib_get(touch_calibration_t *calib); + +/* touch_calib_set() - set calibration information */ +extern int touch_calib_set(touch_calibration_t *calib); + +// low-level API + +#include + +/* touch_next_event() - get the next touchscreen event */ +extern key_event_t touch_next_event(void); + +#endif /* GINT_HW_CP */ + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_TOUCH_H */ diff --git a/src/cpg/cpg.c b/src/cpg/cpg.c index ae230b4..0d6ed07 100644 --- a/src/cpg/cpg.c +++ b/src/cpg/cpg.c @@ -84,10 +84,12 @@ static void sh7305_probe(void) int divb = CPG.FRQCR.BFC; int divi = CPG.FRQCR.IFC; int divp = CPG.FRQCR.P1FC; + int divs = CPG.FRQCR.SFC; freq.Bphi_div = 1 << (divb + 1); freq.Iphi_div = 1 << (divi + 1); freq.Pphi_div = 1 << (divp + 1); + freq.Sphi_div = 1 << (divs + 1); /* Deduce the input frequency of divider 1 */ int base = 32768; @@ -99,6 +101,7 @@ static void sh7305_probe(void) freq.Bphi_f = base >> (divb + 1); freq.Iphi_f = base >> (divi + 1); freq.Pphi_f = base >> (divp + 1); + freq.Sphi_f = base >> (divs + 1); } #undef CPG diff --git a/src/cpg/overclock.c b/src/cpg/overclock.c index 125615a..ac65ff2 100644 --- a/src/cpg/overclock.c +++ b/src/cpg/overclock.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,8 +21,8 @@ // Low-level clock speed access //--- -#define SH7305_SDMR3_CL2 ((volatile uint8_t *)0xFEC15040) -#define SH7305_SDMR3_CL3 ((volatile uint8_t *)0xFEC15060) +#define SH7305_SDMR3_CL2 ((volatile uint16_t *)0xFEC15040) +#define SH7305_SDMR3_CL3 ((volatile uint16_t *)0xFEC15060) //--- // Predefined clock speeds @@ -142,6 +143,17 @@ void cpg_set_overclock_setting(struct cpg_overclock_setting const *s) cpu_atomic_end(); } +void cpg_set_overclock_permanent(bool permanent) +{ + extern gint_driver_t drv_cpg; + int i = &drv_cpg - gint_drivers; + + if(permanent) + gint_driver_flags[i] |= GINT_DRV_SHARED; + else + gint_driver_flags[i] &= ~GINT_DRV_SHARED; +} + #if GINT_HW_FX static struct cpg_overclock_setting const settings_fx9860g_sh3[5] = { @@ -433,6 +445,66 @@ static struct cpg_overclock_setting const settings_fxcg50[5] = { .CS5aWCR = 0x000203C1 }, }; +// TODO: These structures are big and many settings overlap. Make it smaller. +// This is fxcg50[0,1,2,3,3]. +static struct cpg_overclock_setting const settings_fxcg100[5] = { + /* CLOCK_SPEED_F1 */ + { .FLLFRQ = 0x00004000 + 900, + .FRQCR = 0x0F011112, + .CS0BCR = 0x36DA0400, + .CS2BCR = 0x36DA3400, + .CS3BCR = 0x36DB4400, + .CS5aBCR = 0x17DF0400, + .CS0WCR = 0x000003C0, + .CS2WCR = 0x000003C0, + .CS3WCR = 0x000024D1, + .CS5aWCR = 0x000203C1 }, + /* CLOCK_SPEED_F2 */ + { .FLLFRQ = 0x00004000 + 900, + .FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8, + .CS0BCR = 0x24920400, + .CS2BCR = 0x24923400, + .CS3BCR = 0x24924400, + .CS5aBCR = 0x17DF0400, + .CS0WCR = 0x00000340, + .CS2WCR = 0x000003C0, + .CS3WCR = 0x000024D1, + .CS5aWCR = 0x000203C1 }, + /* CLOCK_SPEED_F3 */ + { .FLLFRQ = 0x00004000 + 900, + .FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8, + .CS0BCR = 0x24920400, + .CS2BCR = 0x24923400, + .CS3BCR = 0x24924400, + .CS5aBCR = 0x17DF0400, + .CS0WCR = 0x00000240, + .CS2WCR = 0x000003C0, + .CS3WCR = 0x000024D1, + .CS5aWCR = 0x000203C1 }, + /* CLOCK_SPEED_F4 */ + { .FLLFRQ = 0x00004000 + 900, + .FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16, + .CS0BCR = 0x24920400, + .CS2BCR = 0x24923400, + .CS3BCR = 0x24924400, + .CS5aBCR = 0x17DF0400, + .CS0WCR = 0x000002C0, + .CS2WCR = 0x000003C0, + .CS3WCR = 0x000024D1, + .CS5aWCR = 0x000203C1 }, + /* CLOCK_SPEED_F5 is made identical to CLOCK_SPEED_F4 because clearly the + Graph Math+ cannot handle the higher bus speed. */ + { .FLLFRQ = 0x00004000 + 900, + .FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16, + .CS0BCR = 0x24920400, + .CS2BCR = 0x24923400, + .CS3BCR = 0x24924400, + .CS5aBCR = 0x17DF0400, + .CS0WCR = 0x000002C0, + .CS2WCR = 0x000003C0, + .CS3WCR = 0x000024D1, + .CS5aWCR = 0x000203C1 }, +}; #endif static struct cpg_overclock_setting const *get_settings(void) @@ -451,6 +523,8 @@ static struct cpg_overclock_setting const *get_settings(void) return settings_prizm; if(gint[HWCALC] == HWCALC_FXCG50) return settings_fxcg50; + if(gint[HWCALC] == HWCALC_FXCG100) + return settings_fxcg100; #endif return NULL; diff --git a/src/intc/intc.c b/src/intc/intc.c index caba6a2..b13fccb 100644 --- a/src/intc/intc.c +++ b/src/intc/intc.c @@ -71,6 +71,11 @@ static struct info { { IPRC, 0x000f, IMR4, 0x08, _ }, /* USB */ { IPRF, 0x00f0, IMR9, 0x02, _ /* Driver not SH3-compatible yet */ }, + /* I2C */ + { IPRH, 0x000f, IMR7, 0x10, _ }, + { IPRH, 0x000f, IMR7, 0x20, _ }, + { IPRH, 0x000f, IMR7, 0x40, _ }, + { IPRH, 0x000f, IMR7, 0x80, _ }, }; /* Compact SH3 VBR-space scheme diff --git a/src/kernel/exch.c b/src/kernel/exch.c index b1c11e9..5f79567 100644 --- a/src/kernel/exch.c +++ b/src/kernel/exch.c @@ -80,6 +80,7 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code) if(code == 0x1060) name = "Memory init failed"; if(code == 0x1080) name = "Stack overflow"; if(code == 0x10a0) name = "UBC in bank 1 code"; + // if(code == 0x10c0) name = "Missing syscall"; // not on FX if(name[0]) dtext(1, 9, name); else dprint(1, 9, "%03x", code); @@ -121,6 +122,8 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code) if(code == 0x1060) name = "Memory initialization failed (heap)"; if(code == 0x1080) name = "Stack overflow during world switch"; if(code == 0x10a0) name = "UBC break in register bank 1 code"; + if(code == 0x10c0) name = "Missing syscall for this OS version"; + if(code == 0x10e0) name = "I2C (touch-screen) error"; dprint(6, 25, "%03x %s", code, name); @@ -171,6 +174,11 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code) dprint(6, 160, "Opcodes: %04x %04x [%04x] %04x", opcodes[-2], opcodes[-1], opcodes[0], opcodes[1]); } + /* I2C exception error */ + if (code == 0x10e0) + { + //todo + } #endif _WEAK_dupdate(); diff --git a/src/kernel/hardware.c b/src/kernel/hardware.c index 793c39e..3340513 100644 --- a/src/kernel/hardware.c +++ b/src/kernel/hardware.c @@ -11,6 +11,7 @@ #include #include +#include "kernel.h" /* Holds information about the current platform */ GBSS uint32_t gint[HW_KEYS]; @@ -90,10 +91,10 @@ void hw_detect(void) gint[HWFS] = HWFS_CASIOWIN; } - /* Detect RAM by checking if 8804'0000 is the same as 8800'0000. */ + /* Detect RAM by checking if a804'0000 is the same as a800'0000. */ - volatile uint8_t *R4 = (void *)0x88040000; - volatile uint8_t *R0 = (void *)0x88000000; + volatile uint8_t *R4 = (void *)0xa8040000; + volatile uint8_t *R0 = (void *)0xa8000000; /* Make backups */ uint8_t b0 = *R0; @@ -126,10 +127,28 @@ void hw_detect(void) gint[HWCPUVR] = PVR; gint[HWCPUPR] = PRR; - /* Tell Prizms apart from fx-CG 50 by checking the stack address*/ uint32_t stack; __asm__("mov r15, %0" : "=r"(stack)); - gint[HWCALC] = (stack < 0x8c000000) ? HWCALC_PRIZM : HWCALC_FXCG50; + char const *version = (void *)0x80020020; + char const *osdate = (void *)0x80b5ffe0; + + /* Tell Prizms apart from fx-CG 50 by checking the stack address*/ + if(stack < 0x8c000000) { + gint[HWCALC] = HWCALC_PRIZM; + } + /* Tell Math+/fx-CG 100 apart from CG-50 by checking OS version + date. + CG-50 OS versions use OS 3. Math+, for some reason, rewinds back to OS 1 + and got updated to OS 2 in late 2024. We decide that we are on a CG-50 OS + if the version is 3 and the date is 201x-2024. */ + else { + /* All known CG-50 versions have date at this address due to the footer + location. WARNING: not a future-proof address! */ + char d0 = osdate[0], d1 = osdate[1], d2 = osdate[2], d3 = osdate[3]; + bool cg50 = version[1] == '3' && d0 == '2' && d1 == '0' && + (d2 == '1' || (d2 == '2' && d3 <= '4')); + gint[HWCALC] = cg50 ? HWCALC_FXCG50 : HWCALC_FXCG100; + } + gint[HWFS] = HWFS_FUGUE; /* Tell the fx-CG emulator apart using the product ID */ diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 602a9db..dffd482 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -33,13 +33,29 @@ uint8_t *gint_driver_flags = NULL; /* Top of the stack */ void *gint_stack_top = NULL; +u32 *gint_load_info = NULL; + //--- // Initialization and unloading //--- /* kinit(): Install and start gint */ -void kinit(void) +void kinit(u32 *load_info) { + gint_load_info = load_info; + + /* Figure out which CASIOWIN API to use based on the OS type. */ + #if GINT_OS_CG + char *version = (void *)0x80020020; + if(!memcmp(version, "01.00", 5)) + gint_set_CASIOWIN_API(1); + else if(gint[HWCALC] == HWCALC_FXCG100 && !memcmp(version, "02.00", 5)) + gint_set_CASIOWIN_API(2); + else + gint_set_CASIOWIN_API(0); + #endif + + #if GINT_HW_FX /* On fx-9860G, VBR is loaded at the end of the user RAM. On SH4, the end of the user RAM hosts the stack, for which we leave 12 kB @@ -76,9 +92,15 @@ void kinit(void) #endif /* Event handler entry points */ + extern uint32_t gint_exch_start; + extern uint32_t gint_exch_end; + extern uint32_t gint_tlbh_start; + extern uint32_t gint_tlbh_end; void *inth_entry = isSH3() ? gint_inth_7705 : gint_inth_7305; - uint32_t exch_size = (uint32_t)&gint_exch_size; - uint32_t tlbh_size = (uint32_t)&gint_tlbh_size; + uint32_t exch_size = \ + (uint32_t)&gint_exch_end - (uint32_t)&gint_exch_start; + uint32_t tlbh_size = \ + (uint32_t)&gint_tlbh_end - (uint32_t)&gint_tlbh_start; /* Load the event handler entry points into memory */ memcpy((void *)VBR + 0x100, gint_exch, exch_size); @@ -102,16 +124,45 @@ void kinit(void) /* Create an arena in the OS stack as well, for VRAM and more data */ #if GINT_HW_CG && !defined(GINT_NO_OS_STACK) - static kmalloc_arena_t os_stack = { 0 }; - os_stack.name = "_ostk"; - os_stack.is_default = true; - if(gint[HWCALC] == HWCALC_PRIZM || gint[HWCALC] == HWCALC_FXCG_MANAGER) - os_stack.start = (void *)0x880f0000; - else - os_stack.start = (void *)0x8c0f0000; - os_stack.end = os_stack.start + (350 * 1024); - kmalloc_init_arena(&os_stack, true); - kmalloc_add_arena(&os_stack); + if(gint[HWCALC] != HWCALC_FXCG100) { + static kmalloc_arena_t os_stack = { 0 }; + os_stack.name = "_ostk"; + os_stack.is_default = true; + if(gint[HWCALC] == HWCALC_PRIZM || gint[HWCALC] == HWCALC_FXCG_MANAGER) + os_stack.start = (void *)0x880f0000; + else + os_stack.start = (void *)0x8c0f0000; + os_stack.end = os_stack.start + (350 * 1024); + kmalloc_init_arena(&os_stack, true); + kmalloc_add_arena(&os_stack); + } + #endif + + /* Create arenas in locations provided by loader */ + #if GINT_OS_CG + u32 load_arena_start = 0; + char const *names[4] = { "_ld1", "_ld2", "_ld3", "_ld4" }; + int count = 0; + for(int i = 0; load_info && load_info[i] && count < 4; i += 2) + { + if(load_info[i] == 0x00000010) + load_arena_start = load_info[i+1]; + else if(load_info[i] == 0x00000011 && load_arena_start) + { + u32 load_arena_end = load_info[i+1]; + kmalloc_arena_t *arena = kmalloc(sizeof *arena, NULL); + if(arena) + { + arena->name = names[count]; + arena->is_default = true; + arena->start = (void *)load_arena_start; + arena->end = (void *)load_arena_end; + kmalloc_init_arena(arena, true); + kmalloc_add_arena(arena); + } + load_arena_start = 0; + } + } #endif /* Allocate world buffers for the OS and for gint */ diff --git a/src/kernel/kernel.h b/src/kernel/kernel.h index 3efc8cb..717345b 100644 --- a/src/kernel/kernel.h +++ b/src/kernel/kernel.h @@ -5,6 +5,8 @@ #ifndef GINT_CORE_KERNEL #define GINT_CORE_KERNEL +#include + /* gint_load_onchip_sections(): Initialize on-chip memory sections */ void gint_load_onchip_sections(void); @@ -13,9 +15,12 @@ void gint_load_onchip_sections(void); void gint_copy_vram(void); /* kinit(): Install and start gint */ -void kinit(void); +void kinit(u32 *load_info); /* kquit(): Quit gint and give back control to the system */ void kquit(void); +/* Select the CASIOWIN API for syscalls. */ +void gint_set_CASIOWIN_API(int API); + #endif /* GINT_CORE_KERNEL */ diff --git a/src/kernel/osmenu.c b/src/kernel/osmenu.c index ea503cd..5124b67 100644 --- a/src/kernel/osmenu.c +++ b/src/kernel/osmenu.c @@ -13,7 +13,6 @@ int __Timer_Deinstall(int id); int __PutKeyCode(int row, int column, int keycode); int __GetKeyWait(int *col,int *row,int type,int time,int menu,uint16_t *key); void __ClearKeyBuffer(void); /* ? */ -void __ConfigureStatusArea(int mode); void __SetQuitHandler(void (*callback)(void)); #if !GINT_OS_CP @@ -96,15 +95,17 @@ static os_menu_function_t *find_os_menu_function(void) void gint_osmenu_native(void) { +#if GINT_OS_CG + if(gint[HWCALC] == HWCALC_FXCG100) + return; +#endif + // TODO: OS menu on fx-CP #if !GINT_OS_CP __ClearKeyBuffer(); gint_copy_vram(); #if GINT_OS_CG - /* Unfortunately ineffective (main menu probably reenables it) - __ConfigureStatusArea(3); */ - /* Try to use the internal function directly if we could figure out its address by dynamically disassembling */ os_menu_function_t *fun = find_os_menu_function(); @@ -122,11 +123,6 @@ void gint_osmenu_native(void) } #endif - /* Mysteriously crashes when coming back; might be useful another time - instead of GetKeyWait() - int C=0x04, R=0x09; - __SpecialMatrixCodeProcessing(&C, &R); */ - __osmenu_id = __Timer_Install(0, __osmenu_handler, 0 /* ms */); if(__osmenu_id <= 0) return; __Timer_Start(__osmenu_id); diff --git a/src/kernel/start.c b/src/kernel/start.c index 26e4257..bd0fc9e 100644 --- a/src/kernel/start.c +++ b/src/kernel/start.c @@ -42,8 +42,9 @@ int8_t gint_restart = 0; /* gint_setrestart(): Set whether to restart the add-in after exiting */ void gint_setrestart(int restart) { - /* There is now return-to-menu so no restart on CP */ - gint_restart = restart && !GINT_OS_CP; + /* No restart on the machines for which there is no return-to-menu, i.e. on + fx-CP and on the fx-CG 100. */ + gint_restart = restart && !GINT_OS_CP && gint[HWCALC] != HWCALC_FXCG100; } /* Return value of main() */ @@ -108,7 +109,7 @@ void gint_load_onchip_sections(void) } } -static int start2(int isappli, int optnum) +static int start2(int load_type, u32 *load_info) { /* We are currently in a dynamic userspace mapping of an add-in run from the storage memory. We are running in privileged mode with one @@ -173,8 +174,11 @@ static int start2(int isappli, int optnum) } #endif - /* Install gint, switch VBR and initialize drivers */ - kinit(); + /* Install gint, switch VBR and initialize drivers. If loading information + was provided, pass it on. */ + if(load_type == 0x4d504d30 /* 'MPM0' */ && !((u32)load_info & 3)) {} + else load_info = NULL; + kinit(load_info); /* We are now running on our own in kernel mode. Since we have taken control of interrupts, pretty much any interaction with the system @@ -189,9 +193,6 @@ static int start2(int isappli, int optnum) what it wants in exit() after main() finishes executing */ if(!setjmp(gint_exitbuf)) { callarray(&bctors, &ectors); - // TODO: record isappli and optnum in globals - (void)isappli; - (void)optnum; exit(main()); } else { @@ -210,11 +211,11 @@ static int start2(int isappli, int optnum) } GSECTION(".text.entry") -int start(int isappli, int optnum) +int start(int load_type, u32 *load_info) { int rc; while(1) { - rc = start2(isappli, optnum); + rc = start2(load_type, load_info); if(!gint_restart) break; gint_osmenu_native(); } diff --git a/src/kernel/syscalls.S b/src/kernel/syscalls.S index 387e18f..f9f0a06 100644 --- a/src/kernel/syscalls.S +++ b/src/kernel/syscalls.S @@ -144,82 +144,128 @@ syscall_table: #endif /* GINT_OS_FX */ #if GINT_OS_CG +#define CASIOWIN_API_VERSIONS 3 +.global _gint_set_CASIOWIN_API +.global _gint_get_CASIOWIN_API -/* Dynamic allocation */ +/* System for dynamically selecting between the syscall and fixed version of + each function based on the OS version. + @r0: Internal function ID (from table below) */ +_CASIOWIN_call: + mov #CASIOWIN_API_VERSIONS, r1 + mulu.w r0, r1 + mov.l 3f, r0 + mov.l @r0, r2 + sts macl, r1 + add r2, r1 + shll2 r1 -___malloc: - syscall(0x1f44) -___free: - syscall(0x1f42) -___realloc: - syscall(0x1f46) + /* API version 0 is the normal syscall table */ + tst r2, r2 + mova .CASIOWIN_TABLE, r0 + bt.s .syscall + mov.l @(r0, r1), r0 -/* BFile driver */ + /* Other API versions are the direct calls */ + tst r0, r0 + bt .missingCall -_BFile_Remove: - syscall(0x1db4) -_BFile_Rename: - syscall(0x1db3) -_BFile_Create: - syscall(0x1dae) -_BFile_Open: - mov #0, r6 - syscall(0x1da3) -_BFile_Close: - syscall(0x1da4) -_BFile_Size: - syscall(0x1da6) -_BFile_Seek: - syscall(0x1da9) -_BFile_GetPos: - syscall(0x1dab) -_BFile_Write: - syscall(0x1daf) -_BFile_Read: - syscall(0x1dac) -_BFile_FindFirst: - syscall(0x1db6) -_BFile_FindNext: - syscall(0x1db8) -_BFile_FindClose: - syscall(0x1dba) + jmp @r0 + nop -/* Return to menu */ +.missingCall: + mov.l .gint_panic, r0 + mov.w 2f, r4 + jmp @r0 + nop -___Timer_Install: - syscall(0x8d9) -___Timer_Start: - syscall(0x8db) -___Timer_Stop: - syscall(0x8dc) -___Timer_Deinstall: - syscall(0x8da) -___PutKeyCode: - syscall(0x12c6) -___GetKeyWait: - syscall(0x12bf) -___ClearKeyBuffer: - syscall(0x12c7) -___GetVRAMAddress: - syscall(0x1e6) -___ConfigureStatusArea: - syscall(0x2b7) -___SetQuitHandler: - syscall(0x1e6e) +.syscall: + mov.l 1f, r1 + jmp @r1 + nop -.global ___SpecialMatrixCodeProcessing -___SpecialMatrixCodeProcessing: - syscall(0x1e60) +2: .word 0x10c0 +.balign 4 +1: .long 0x80020070 +.gint_panic: + .long _gint_panic -/* Reset */ +_gint_set_CASIOWIN_API: + mov.l 3f, r0 + rts + mov.l r4, @r0 -___PowerOff: - syscall(0x1839) -___Reset: - syscall(0x1187) +_gint_get_CASIOWIN_API: + mov.l 3f, r0 + rts + mov.l @r0, r0 -syscall_table: - .long 0x80020070 +.balign 4 +3: .long .CASIOWIN_API + +.CASIOWIN_TABLE: + .long 0x1f44, 0x8025e0fc, 0x80366708 /* malloc */ + .long 0x1f42, 0x8025dec8, 0x803664d4 /* free */ + .long 0x1f46, 0x8025ec3c, 0x803672c8 /* realloc */ + .long 0x1db4, 0x802404d2, 0x80334212 /* BFile_Remove */ + .long 0x1db3, 0x80240482, 0x803341c2 /* BFile_Rename */ + .long 0x1dae, 0x802401b0, 0x80333ef0 /* BFile_Create */ + .long 0x1da3, 0x8023fb90, 0x803338d0 /* BFile_Open */ + .long 0x1da4, 0x8023fd0e, 0x80333a4e /* BFile_Close */ + .long 0x1da6, 0x8023fdc4, 0x80333b04 /* BFile_Size */ + .long 0x1da9, 0x8023ff2c, 0x80333c6c /* BFile_Seek */ + .long 0x1dab, 0x8024003c, 0x80333d7c /* BFile_GetPos */ + .long 0x1daf, 0x8024025e, 0x80333f9e /* BFile_Write */ + .long 0x1dac, 0x80240082, 0x80333dc2 /* BFile_Read */ + .long 0x1db6, 0x80240888, 0x803345c8 /* BFile_FindFirst */ + .long 0x1db8, 0x80240b06, 0x80334846 /* BFile_FindNext */ + .long 0x1dba, 0x80240c10, 0x80334950 /* BFile_FindClose */ + .long 0x08d9, 0x800b130c, 0x8010de28 /* Timer_Install */ + .long 0x08db, 0x800b1456, 0x8010df72 /* Timer_Start */ + .long 0x08dc, 0x800b14b2, 0x8010dfce /* Timer_Stop */ + .long 0x08da, 0x800b13d4, 0x8010def0 /* Timer_Deinstall */ + .long 0x12c6, 0, 0 /* PutKeyCode */ + .long 0x12bf, 0x8017be56, 0x802382fe /* GetKeyWait */ + .long 0x12c7, 0, 0 /* ClearKeyBuffer */ + .long 0x01e6, 0x8004579a, 0x8007569e /* GetVRAMAddress */ + .long 0x1e6e, 0, 0 /* SetQuitHandler */ + .long 0x1839, 0, 0 /* PowerOff */ + .long 0x1187, 0, 0 /* Reset */ + +#define casiowin_call(id) bra _CASIOWIN_call; mov id, r0 + +___malloc: casiowin_call(#0) +___free: casiowin_call(#1) +___realloc: casiowin_call(#2) +_BFile_Remove: casiowin_call(#3) +_BFile_Rename: casiowin_call(#4) +_BFile_Create: casiowin_call(#5) +_BFile_Open: mov #0, r6; casiowin_call(#6) +_BFile_Close: casiowin_call(#7) +_BFile_Size: casiowin_call(#8) +_BFile_Seek: casiowin_call(#9) +_BFile_GetPos: casiowin_call(#10) +_BFile_Write: casiowin_call(#11) +_BFile_Read: casiowin_call(#12) +_BFile_FindFirst: casiowin_call(#13) +_BFile_FindNext: casiowin_call(#14) +_BFile_FindClose: casiowin_call(#15) +___Timer_Install: casiowin_call(#16) +___Timer_Start: casiowin_call(#17) +___Timer_Stop: casiowin_call(#18) +___Timer_Deinstall: casiowin_call(#19) +___PutKeyCode: casiowin_call(#20) +___GetKeyWait: casiowin_call(#21) +___ClearKeyBuffer: casiowin_call(#22) +___GetVRAMAddress: casiowin_call(#23) +___ConfigureStatusArea: casiowin_call(#24) +___SetQuitHandler: casiowin_call(#25) +___PowerOff: casiowin_call(#26) +___Reset: casiowin_call(#27) + +.data +.CASIOWIN_API: + .long 0 #endif /* GINT_OS_CG */ diff --git a/src/kernel/vbr.h b/src/kernel/vbr.h index dc0c1a8..e549874 100644 --- a/src/kernel/vbr.h +++ b/src/kernel/vbr.h @@ -11,8 +11,4 @@ void gint_tlbh(void); void gint_inth_7705(void); void gint_inth_7305(void); -/* Size of exception and TLB handlers */ -extern char gint_exch_size; -extern char gint_tlbh_size; - #endif /* GINT_CORE_VBR */ diff --git a/src/keysc/keydev.c b/src/keysc/keydev.c index 4edee84..207e2a7 100644 --- a/src/keysc/keydev.c +++ b/src/keysc/keydev.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -45,14 +46,49 @@ static void keycode_to_keymatrix(int keycode, int *row, int *col) *col = 0; } #else -static GINLINE int keymatrix_to_keycode(int row, int col) +static uint8_t const CG100_keymap[] = { + KEY_ON, KEY_HOME, KEY_PREVTAB, KEY_UP, KEY_NEXTTAB, KEY_PAGEUP, + KEY_SETTINGS, KEY_BACK, KEY_LEFT, KEY_OK, KEY_RIGHT, KEY_PAGEDOWN, + KEY_SHIFT, KEY_ALPHA, KEY_VARS, KEY_DOWN, KEY_CATALOG, KEY_TOOLS, + KEY_XOT, KEY_FRAC, KEY_SQRT, KEY_POWER, KEY_SQUARE, KEY_EXPFUN, + KEY_COMMA, KEY_SIN, KEY_COS, KEY_TAN, KEY_LEFTP, KEY_RIGHTP, +}; +static int keymatrix_to_keycode(int row, int col) { + if(gint[HWCALC] == HWCALC_FXCG100) { + if(row >= 7 && row <= 9) + return CG100_keymap[6 * (9-row) + (6-col)]; + if(row == 1 && col == 3) + return KEY_FORMAT; + } return (row << 4) + (7 - col); } -static GINLINE void keycode_to_keymatrix(int keycode, int *row, int *col) +static void keycode_to_keymatrix(int keycode, int *row, int *col) { + if(gint[HWCALC] == HWCALC_FXCG100) { + if(keycode == KEY_FORMAT) { + *row = 1; + *col = 3; + return; + } + for(int i = 0; i < (int)sizeof(CG100_keymap); i++) { + if(CG100_keymap[i] == keycode) { + *row = 9 - i / 6; + *col = 6 - (i % 6); + return; + } + } + } + *row = keycode >> 4; *col = 7 - (keycode & 7); + + if(gint[HWCALC] == HWCALC_FXCG100 && + (*row > 4 || (*row == 1 && *col == 3))) { + // key that doesn't exist + *row = 0; + *col = 1; + } } #endif @@ -125,6 +161,8 @@ void keydev_process_state(keydev_t *d, uint8_t scan[12]) for(int mask = 0x80, col = 7; mask; mask >>= 1, col--) { + ev.row = row; + ev.col = col; ev.key = keymatrix_to_keycode(row, col); /* Update state only if the push succeeds */ if((diff & mask) && keydev_queue_push(d, ev)) @@ -173,6 +211,8 @@ key_event_t keydev_repeat_event(keydev_t *d) ev.type = KEYEV_HOLD; ev.key = d->rep_key; + ev.row = d->rep_row; + ev.col = d->rep_col; return ev; } @@ -188,11 +228,11 @@ void keydev_tick(keydev_t *d, uint us) /* Disable repeat if the repeating key was released */ if(d->rep_key != 0) { - int row, col; - keycode_to_keymatrix(d->rep_key, &row, &col); - if(!(d->state_now[row] & (1 << col))) + if(!(d->state_now[d->rep_row] & (1 << d->rep_col))) { d->rep_key = 0; + d->rep_row = 0; + d->rep_col = 0; d->rep_count = -1; d->rep_time = -1; d->rep_delay = -1; @@ -231,19 +271,27 @@ key_event_t keydev_unqueue_event(keydev_t *d) if(!queue_poll(d, &ev)) return ev; + /* Compatibility combinations can transform the .key attribute */ +#if GINT_HW_CG + if(gint[HWCALC] == HWCALC_FXCG100) { + if(keydev_keydown(d, KEY_CATALOG) && ev.row == 6) + ev.key = KEY_F1 + (6 - ev.col); + } +#endif + /* Update the event state accordingly */ - int row, col; - keycode_to_keymatrix(ev.key, &row, &col); - int mask = 1 << col; + int mask = 1 << ev.col; if(ev.type == KEYEV_DOWN) { - d->state_queue[row] |= mask; - d->state_flips[row] ^= mask; + d->state_queue[ev.row] |= mask; + d->state_flips[ev.row] ^= mask; /* Mark this key as the currently repeating one */ if(d->rep_key == 0 && can_repeat(d, ev.key)) { d->rep_key = ev.key; + d->rep_row = ev.row; + d->rep_col = ev.col; d->rep_count = -1; d->rep_time = 0; d->rep_delay = 0; @@ -251,8 +299,8 @@ key_event_t keydev_unqueue_event(keydev_t *d) } else if(ev.type == KEYEV_UP) { - d->state_queue[row] &= ~mask; - d->state_flips[row] ^= mask; + d->state_queue[ev.row] &= ~mask; + d->state_flips[ev.row] ^= mask; } return ev; diff --git a/src/r61523/r61523.c b/src/r61523/r61523.c index 598d236..827e63c 100644 --- a/src/r61523/r61523.c +++ b/src/r61523/r61523.c @@ -17,12 +17,18 @@ #define REG_VRANGE 0x2b #define REG_DATA 0x2c #define REG_DEVICE_CODE_READ 0xbf +#define REG_DEVICE_CODE_VARIANT 0xda /* Interface with the controller */ static volatile uint16_t *DISPLAY = (void *)0xb4000000; /* Bit 4 of Port R controls the RS bit of the display driver */ static volatile uint8_t *PRDR = (void *)0xa405013c; +/* Screen variant information + * This number is 0x00 for the old R61523 that everyone knows, and 0x16 or + * 0x52 for the newer ones found in recent FXCP400 devices. */ +static uint16_t r61523_variant = 0; + /* Select a register */ GINLINE static void select(uint16_t reg) { @@ -56,16 +62,38 @@ static void read_Nu16(uint16_t *array, int N) // Generic functions //--- -void r61523_identify(uint32_t *manufacturerCode, uint16_t *deviceCode) -{ - select(REG_DEVICE_CODE_READ); +/* r61523_identify() - identify screen hardware information + * + * notes + * Since recent versions of the FXCP400, new screens are used that break + * the current driver. These screens are particular since they have the + * same manufacturer / device code, but they use an undocumented register + * (0xda) to determine the variant. + * + * The register 0xda should be read twice. Casio ignores the first reading + * and only keep track of the second. So, let's do the same here. */ +void r61523_identify( + uint32_t *manufacturerCode, + uint16_t *deviceCode, + uint16_t *variant +) { uint16_t packets[5]; - read_Nu16(packets, 5); - if(manufacturerCode) - *manufacturerCode = (packets[1] << 16) | packets[2]; - if(deviceCode) - *deviceCode = (packets[3] << 16) | packets[4]; + if (manufacturerCode != NULL || deviceCode != NULL) + { + select(REG_DEVICE_CODE_READ); + read_Nu16(packets, 5); + if(manufacturerCode) + *manufacturerCode = (packets[1] << 16) | packets[2]; + if(deviceCode) + *deviceCode = (packets[3] << 16) | packets[4]; + } + if (variant) + { + select(REG_DEVICE_CODE_VARIANT); + *variant = read(); + *variant = read(); + } } //--- @@ -74,10 +102,13 @@ void r61523_identify(uint32_t *manufacturerCode, uint16_t *deviceCode) void r61523_win_set(int x1, int x2, int y1, int y2) { - /* R61523 has a 360x640 area; the CP-400 uses the top-right corner for its - 320x528 display, so skip over the first 40 columns */ - x1 += 40; - x2 += 40; + if (r61523_variant != 0x16 || r61523_variant != 0x52) + { + /* R61523 has a 360x640 area; the CP-400 uses the top-right corner + * for its 320x528 display, so skip over the first 40 columns */ + x1 += 40; + x2 += 40; + } uint16_t volatile *DISPLAY = (void *)0xb4000000; @@ -109,7 +140,6 @@ void r61523_win_set(int x1, int x2, int y1, int y2) void r61523_display(uint16_t *vram) { r61523_win_set(0, 319, 0, 527); - select(44); int row_offset = 0; @@ -125,6 +155,23 @@ void r61523_display(uint16_t *vram) } } +void r61523_display_rect( + uint16_t *vram, int xmin, int xmax, int ymin, int ymax) +{ + // dma_transfer_wait(0); + r61523_win_set(xmin, xmax, ymin, ymax); + select(44); + + vram += 320 * ymin + xmin; + uint16_t volatile *DISPLAY = (void *)0xb4000000; + + for(int y = 0; y < ymax - ymin + 1; y++) { + for(int x = 0; x < xmax - xmin + 1; x++) + *DISPLAY = vram[x]; + vram += 320; + } +} + static bool r61523_update(int x, int y, image_t const *fb, int flags) { if(fb->format != IMAGE_RGB565) @@ -152,10 +199,17 @@ static bool r61523_update(int x, int y, image_t const *fb, int flags) // Driver metadata //--- +/* constructor() - determine which variant of screen we have */ +static void constructor(void) +{ + r61523_identify(NULL, NULL, &r61523_variant); +} + /* As far as I can tell there's no way to read the current window from the controller so this driver is completely stateless for now. */ gint_driver_t drv_r61523 = { - .name = "R61523", + .name = "R61523", + .constructor = constructor, }; GINT_DECLARE_DRIVER(26, drv_r61523); diff --git a/src/render-cg/dvram.c b/src/render-cg/dvram.c index aac777a..7f9f287 100644 --- a/src/render-cg/dvram.c +++ b/src/render-cg/dvram.c @@ -97,16 +97,15 @@ bool dvram_init(void) { int const MARGIN = 32; + char const *arena = NULL; + if(kmalloc_get_arena("_ostk")) + arena = "_ostk"; + /* Leave MARGIN bytes on each side of the region; this enables some important optimizations in the image renderer. We also add another 32 bytes so we can manually 32-align the region */ - uint32_t region = (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32, -#if !defined(GINT_NO_OS_STACK) - "_ostk" -#else - NULL -#endif - ); + uint32_t region = + (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32, arena); if(region == 0) return false; diff --git a/src/render-cg/gint_dline.c b/src/render-cg/gint_dline.c index 18941ea..8bdafa3 100644 --- a/src/render-cg/gint_dline.c +++ b/src/render-cg/gint_dline.c @@ -4,7 +4,7 @@ #if GINT_RENDER_RGB /* gint_dhline(): Optimized horizontal line */ -void gint_dhline(int x1, int x2, int y, uint16_t color) +void gint_dhline(int x1, int x2, int y, int color) { if(y < dwindow.top || y >= dwindow.bottom) return; if(x1 > x2) swap(x1, x2); @@ -15,9 +15,19 @@ void gint_dhline(int x1, int x2, int y, uint16_t color) int offset = DWIDTH * y; /* Use longwords to do the copy, but first paint the endpoints to heed - for odd x1 and x2. Checking the parity may be a waste of time. */ - gint_vram[offset + x1] = color; - gint_vram[offset + x2] = color; + for odd x1 and x2. Checking the parity may be a waste of time for + "real" color, but must be checked when C_INVERT in involved to + avoid "cancelling" invert effect with potential overdraw of the + next operation. */ + if (color != C_INVERT) { + gint_vram[offset + x1] = color; + gint_vram[offset + x2] = color; + } else { + if (x1 & 1) + gint_vram[offset + x1] ^= 0xffff; + if (x2 & 1) + gint_vram[offset + x2] ^= 0xffff; + } /* Now round to longword boundaries and copy everything in-between with longwords */ @@ -26,13 +36,17 @@ void gint_dhline(int x1, int x2, int y, uint16_t color) uint32_t *start = (void *)(gint_vram + offset + x1); uint32_t *end = (void *)(gint_vram + offset + x2); - uint32_t op = (color << 16) | color; - while(end > start) *--end = op; + if (color != C_INVERT) { + uint32_t op = (color << 16) | color; + while(end > start) *--end = op; + } else { + while(end > start) *--end ^= 0xffffffff; + } } /* gint_dvline(): Optimized vertical line */ -void gint_dvline(int y1, int y2, int x, uint16_t color) +void gint_dvline(int y1, int y2, int x, int color) { if(x < dwindow.left || x >= dwindow.right) return; if(y1 > y2) swap(y1, y2); @@ -42,7 +56,11 @@ void gint_dvline(int y1, int y2, int x, uint16_t color) uint16_t *v = gint_vram + DWIDTH * y1 + x; int height = y2 - y1 + 1; - while(height-- > 0) *v = color, v += DWIDTH; + if (color != C_INVERT) { + while(height-- > 0) *v = color, v += DWIDTH; + } else { + while(height-- > 0) *v ^= 0xffff, v += DWIDTH; + } } #endif diff --git a/src/render/render.h b/src/render/render.h index 888e439..a292801 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -11,12 +11,12 @@ /* gint_dhline(): Optimized horizontal line @x1 @x2 @y Coordinates of endpoints of line (both included) @color Any color suitable for dline() */ -void gint_dhline(int x1, int x2, int y, color_t color); +void gint_dhline(int x1, int x2, int y, int color); /* gint_dvline(): Optimized vertical line @y1 @y2 @x Coordinates of endpoints of line (both included) @color Any color suitable for dline() */ -void gint_dvline(int y1, int y2, int x, color_t color); +void gint_dvline(int y1, int y2, int x, int color); //--- // Font rendering (topti) diff --git a/src/touch/adconv.c b/src/touch/adconv.c new file mode 100644 index 0000000..c007c02 --- /dev/null +++ b/src/touch/adconv.c @@ -0,0 +1,146 @@ +//--- +// gint:touch:adconv - 0x84 register data conversion +//--- +#include + +#include +#include +#include + +#if GINT_HW_CP + +#include "./adconv.h" +#include "./i2c.h" +#include "./driver.h" + +//--- +// Internals +//--- + +/* __touch_drv_info - global driver information */ +extern struct _touch_drv_info __touch_drv_info; + +/* _adconv_check_dual() - check if it is dual or single */ +static int _adconv_check_dual( + struct _touch_adconv *adconv, + struct _touch_adraw *adraw +) { + extern struct _touch_drv_info __touch_drv_info; + bool is_dual; + int x2; + int y2; + int z2; + int val; + + cpu_atomic_start(); + x2 = ((int)((uint)(adraw->x2) >> 6)) + ((adraw->x2 & 1) * -0x400); + y2 = ((int)((uint)(adraw->y2) >> 6)) + ((adraw->y2 & 1) * -0x400); + z2 = ((int)((uint)(adraw->z2) >> 4)) + ((adraw->z2 & 1) * -0x1000); + adconv->x2 = x2; + adconv->y2 = y2; + adconv->z2 = z2; + val = __touch_drv_info.calibration.dual_sensi_entry; + if (__touch_drv_info.adinfo.prev_is_dual) + val = __touch_drv_info.calibration.dual_sensi_leave; + is_dual = ((abs(z2) >= val) || (max(abs(x2),abs(y2)) >= val)); + __touch_drv_info.adinfo.prev_is_dual = is_dual; + cpu_atomic_end(); + return is_dual; +} + +//--- +// Public +//--- + +/* touch_adconv_get_raw() - read 0x84 register using I2C */ +int touch_adconv_get_raw(struct _touch_adraw *adraw) +{ + volatile uint8_t *IO_PRDR = (void*)0xa405013c; + + if (((*IO_PRDR) & 0x20) != 0) + { + adraw->x1 = 0; + adraw->y1 = 0; + adraw->z1 = 0; + adraw->x2 = 0; + adraw->y2 = 0; + adraw->z2 = 0; + adraw->gh = 0; + cpu_atomic_start(); + __touch_drv_info.adinfo.prev_is_dual = false; + cpu_atomic_end(); + return 0; + } + i2c_reg_read(0x84, adraw, 16); + return 1; +} + +/* touch_adconv_convert() - perform the raw conversion */ +int touch_adconv_get_conv( + struct _touch_adconv *adconv, + struct _touch_adraw *adraw, + int type +) { + if (type == 0) + return 0; + adconv->x1 = adraw->x1 >> 4; + adconv->y1 = adraw->y1 >> 4; + adconv->z1 = adraw->z1 >> 4; + adconv->gh = adraw->gh >> 4; + adconv->dm = adraw->dm >> 6; + if (_adconv_check_dual(adconv, adraw) == 0) + return 1; + return 2; +} + +/* touch_adconv_get_dots() - generate dots information */ +int touch_adconv_get_dots( + struct _touch_addots *dots, + struct _touch_adconv *adconv, + int type +) { + int x_div; + int x_base; + int y_div; + int y_base; + + cpu_atomic_start(); + x_div = __touch_drv_info.calibration.x_div; + x_base = __touch_drv_info.calibration.x_base; + y_div = __touch_drv_info.calibration.y_div; + y_base = __touch_drv_info.calibration.y_base; + switch (type) + { + case 0: + dots->type = TS_DOTS_TYPE_OFF; + dots->x1 = 0; + dots->y1 = 0; + dots->z1 = 0; + dots->x2 = 0; + dots->y2 = 0; + dots->z2 = 0; + break; + case 1: + dots->type = TS_DOTS_TYPE_SINGLE; + dots->x1 = ((adconv->x1 - x_base) * 0x100) / x_div; + dots->y1 = ((adconv->y1 - y_base) * 0x100) / y_div; + dots->z1 = adconv->z1; + dots->x2 = 0; + dots->y2 = 0; + dots->z2 = 0; + break; + case 2: + dots->type = TS_DOTS_TYPE_DUAL; + dots->x1 = ((adconv->x1 - x_base) * 0x100) / x_div; + dots->y1 = ((adconv->y1 - y_base) * 0x100) / y_div; + dots->z1 = adconv->z1; + dots->x2 = ((adconv->x2 - x_base) * 0x100) / x_div; + dots->y2 = ((adconv->y2 - y_base) * 0x100) / y_div; + dots->z2 = adconv->z2; + break; + } + cpu_atomic_end(); + return type; +} + +#endif /* GINT_HW_CP */ diff --git a/src/touch/adconv.h b/src/touch/adconv.h new file mode 100644 index 0000000..cf64b1b --- /dev/null +++ b/src/touch/adconv.h @@ -0,0 +1,90 @@ +#ifndef GINT_TOUCH_ADCONV_H +#define GINT_TOUCH_ADCONV_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#if GINT_HW_CP + +//--- +// Internals +//--- + +/* _touch_adraw - raw 0x84 register information */ +struct _touch_adraw +{ + uint16_t x1; + uint16_t y1; + uint16_t z1; + uint16_t gh; + uint16_t x2; + uint16_t y2; + uint16_t z2; + uint16_t dm; +}; + +/* _touch_adconv - post-conversion raw 0x84 register information */ +struct _touch_adconv +{ + int x1; + int y1; + int x2; + int y2; + int z1; + int z2; + uint16_t gh; + uint16_t dm; +}; + +/* _touch_addots_type - type of dots */ +enum _touch_addots_type +{ + TS_DOTS_TYPE_OFF = 0, + TS_DOTS_TYPE_SINGLE = 1, + TS_DOTS_TYPE_DUAL = 2, +}; + +/* _touch_addots - touchscreen dots information */ +struct _touch_addots +{ + enum _touch_addots_type type; + int x1; + int y1; + int z1; + int x2; + int y2; + int z2; +}; + +//--- +// Public +//--- + +/* touch_adconv_get_raw() - read 0x84 register using I2C */ +extern int touch_adconv_get_raw(struct _touch_adraw *adraw); + +/* touch_adconv_get_conv() - perform the raw conversion */ +extern int touch_adconv_get_conv( + struct _touch_adconv *adconv, + struct _touch_adraw *adraw, + int type +); + +/* touch_adconv_get_dots() - generate dots information */ +extern int touch_adconv_get_dots( + struct _touch_addots *dots, + struct _touch_adconv *adconv, + int type +); + +#endif /* GINT_HW_CP */ + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_TOUCH_ADCONV_H */ diff --git a/src/touch/driver.c b/src/touch/driver.c new file mode 100644 index 0000000..078cacd --- /dev/null +++ b/src/touch/driver.c @@ -0,0 +1,112 @@ +//--- +// gint:touch:driver - touch-screen driver declaration +//--- +#include + +#include +#include +#include + +#if GINT_HW_CP + +#include "./driver.h" +#include "./i2c.h" + +//--- +// Internals +//--- + +/* __touch_drv_info - internal driver information */ +extern struct _touch_drv_info __touch_drv_info; + +/* _touch_configure() - configure touch-screen */ +static void _touch_configure(void) +{ + volatile uint16_t *IO_PRCR = (void*)0xa405011c; + + i2c_configure(); + *(IO_PRCR) = (*(IO_PRCR) & 0xf3ff) | 0xc00; + memset(&__touch_drv_info, 0x00, sizeof(struct _touch_drv_info)); + __touch_drv_info.prev_evt.type = KEYEV_NONE; + __touch_drv_info.prev_evt.x = 0xffff; + __touch_drv_info.prev_evt.y = 0xffff; + __touch_drv_info.calibration.x_base = 0x20b; + __touch_drv_info.calibration.x_div = 0x9b6; + __touch_drv_info.calibration.y_base = 0x0f4; + __touch_drv_info.calibration.y_div = 0x66f; + __touch_drv_info.calibration.dual_debounce_frame = 0; + __touch_drv_info.calibration.dual_sensi_entry = 0x18; + __touch_drv_info.calibration.dual_sensi_leave = 0x24; +} + +/* _touch_hsave() - save hardware state */ +static void _touch_hsave(touch_state_t *state) +{ + volatile uint16_t *IO_PRCR = (void*)0xa405011c; + + i2c_hsave(state); + state->PRCR = *(IO_PRCR); +} + +/* _touch_hrestore() - restore hardware state */ +static void _touch_hrestore(touch_state_t *state) +{ + volatile uint16_t *IO_PRCR = (void*)0xa405011c; + + *(IO_PRCR) = state->PRCR; + i2c_hrestore(state); +} + +/* _touch_hpowered() - check if the module is powered */ +static bool _touch_hpowered(void) +{ + return i2c_hpowered(); +} + +/* _touch_hpoweron() - power on the module */ +static void _touch_hpoweron(void) +{ + i2c_hpoweron(); +} + +/* _touch_hpoweroff() - power off the module */ +static void _touch_hpoweroff(void) +{ + i2c_hpoweroff(); +} + +/* _touch_unbind() - unbind from gint to casio */ +static void _touch_unbind(void) +{ + i2c_unbind(); +} + +/* _touch_funbind() - funbind from casio to gint */ +static void _touch_funbind(void) +{ + i2c_funbind(); +} + +//--- +// Public +//--- + +/* __touch_drv_info - internal driver information */ +struct _touch_drv_info __touch_drv_info; + +/* drv_touch - touch-screen driver declaration */ +gint_driver_t drv_touch = { + .name = "TOUCH", + .configure = _touch_configure, + .hsave = (void *)_touch_hsave, + .hrestore = (void *)_touch_hrestore, + .hpowered = _touch_hpowered, + .hpoweron = _touch_hpoweron, + .hpoweroff = _touch_hpoweroff, + .unbind = _touch_unbind, + .funbind = _touch_funbind, + .state_size = sizeof(touch_state_t), +}; +GINT_DECLARE_DRIVER(24, drv_touch); + +#endif /* GINT_HW_CP */ diff --git a/src/touch/driver.h b/src/touch/driver.h new file mode 100644 index 0000000..f73cd1b --- /dev/null +++ b/src/touch/driver.h @@ -0,0 +1,30 @@ +#ifndef GINT_TOUCH_DRIVER_H +#define GINT_TOUCH_DRIVER_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#if GINT_HW_CP + +/* _touch_drv_info() - internal driver information */ +struct _touch_drv_info +{ + touch_calibration_t calibration; + key_event_t prev_evt; + struct { + bool prev_is_dual; + } adinfo; +}; + +#endif /* GINT_HW_CP */ + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_TOUCH_DRIVER_H */ diff --git a/src/touch/i2c.c b/src/touch/i2c.c new file mode 100644 index 0000000..517f03a --- /dev/null +++ b/src/touch/i2c.c @@ -0,0 +1,254 @@ +//--- +// gint:touch:i2c - I2C driver +//--- +#include + +#include +#include +#include +#include +#include +#include + +#if GINT_HW_CP + +#include "./i2c.h" + +//--- +// Internals +//--- + +/* __i2c_request - internal I2C request information */ +volatile struct i2c_request_info __i2c_request; + +/* i2c_hw_enable() - enable the I2C peripheral and configure the clock + * + * notes + * Clock configuration are the same used by Casio. Investigation must be + * made to ensure overclock/underclock validity */ +static void _i2c_hw_enable(void) +{ + SH7305_I2C.ICCR.ICE = 1; + while(true) { + if (SH7305_I2C.ICSR.BUSY == 0) + break; + } + SH7305_I2C.ICCL = 0x29; + SH7305_I2C.ICCH = 0x22; +} + +/* i2c_hw_start_operation() - start I2C operation + * + * notes + * Enable only data transmit and arbitration lost interrupt then perform + * a start condition (0x94) */ +static void _i2c_hw_start_operation(void) +{ + SH7305_I2C.ICIC.ALE = 1; + SH7305_I2C.ICIC.TACKE = 1; + SH7305_I2C.ICIC.WAITE = 0; + SH7305_I2C.ICIC.DTEE = 1; + SH7305_I2C.ICCR.byte = 0x94; +} + +/* i2c_request_await() - await async operation */ +static int _i2c_request_await(void) +{ + int status; + + while (true) + { + cpu_atomic_start(); + status = (__i2c_request.status == I2C_REQ_STATUS_FINISHED); + cpu_atomic_end(); + if (status) + break; + __asm__ volatile ("sleep"); + } + return 0; +} + +//--- +// Public +//--- + +/* i2c_reg_select() - select a register */ +int i2c_reg_select(int reg) +{ + cpu_atomic_start(); + _i2c_hw_enable(); + + __i2c_request.mode = I2C_REQ_MODE_REG_SELECT; + __i2c_request.target_register = reg; + __i2c_request.buffer = NULL; + __i2c_request.buffer_size = 0; + __i2c_request.buffer_size_remaning = 0; + __i2c_request.buffer_cursor = 0; + __i2c_request.status = I2C_REQ_STATUS_START; + __i2c_request.state = I2C_REQ_STATE_START; + + cpu_atomic_end(); + _i2c_hw_start_operation(); + return _i2c_request_await(); +} + +/* i2c_reg_read() - register read operation */ +int i2c_reg_read(int reg, void *buffer, size_t size) +{ + if (buffer == NULL) + return -1; + if (size == 0) + return -2; + + cpu_atomic_start(); + _i2c_hw_enable(); + + __i2c_request.mode = I2C_REQ_MODE_REG_READ; + __i2c_request.target_register = reg; + __i2c_request.buffer = buffer; + __i2c_request.buffer_size = size; + __i2c_request.buffer_size_remaning = size; + __i2c_request.buffer_cursor = 0; + __i2c_request.status = I2C_REQ_STATUS_START; + __i2c_request.state = I2C_REQ_STATE_START; + + cpu_atomic_end(); + _i2c_hw_start_operation(); + return _i2c_request_await(); +} + +/* i2c_read_stream() - "stream" read operation (skip reg selection) */ +int i2c_read_stream(void *buffer, size_t size) +{ + if (buffer == NULL) + return -1; + if (size == 0) + return -2; + + cpu_atomic_start(); + _i2c_hw_enable(); + + __i2c_request.mode = I2C_REQ_MODE_READ_STREAM; + __i2c_request.target_register = 0x00; + __i2c_request.buffer = buffer; + __i2c_request.buffer_size = size; + __i2c_request.buffer_size_remaning = size; + __i2c_request.buffer_cursor = 0; + __i2c_request.status = I2C_REQ_STATUS_START; + __i2c_request.state = I2C_REQ_STATE_START; + + cpu_atomic_end(); + _i2c_hw_start_operation(); + return _i2c_request_await(); +} + +//--- +// Driver and state management +//--- + +#include +#include +#include + + +/* i2c_hpowered() - check if the module is powered */ +bool i2c_hpowered(void) +{ + return (SH7305_POWER.MSTPCR2.I2C == 0); +} + +/* i2c_hpoweron() - power on the module */ +void i2c_hpoweron(void) +{ + SH7305_POWER.MSTPCR2.I2C = 0; + SH7305_I2C.ICCR.ICE = 0; + SH7305_I2C.ICDR = 0; + SH7305_I2C.ICCL = 0x00; + SH7305_I2C.ICCH = 0x00; +} + +/* i2c_hpoweroff() - power on the module */ +void i2c_hpoweroff(void) +{ + SH7305_POWER.MSTPCR2.I2C = 1; +} + +/* i2c_hsave() - save hardware information */ +void i2c_hsave(touch_state_t *state) +{ + state->PJCR = SH7305_PFC.PJCR.word; + state->ICCR = SH7305_I2C.ICCR.byte; + state->ICIC = SH7305_I2C.ICIC.byte; + state->ICCL = SH7305_I2C.ICCL; + state->ICCH = SH7305_I2C.ICCH; +} + +/* i2c_hrestore() - restore hardware information */ +void i2c_hrestore(touch_state_t *state) +{ + SH7305_I2C.ICCH = state->ICCH; + SH7305_I2C.ICCL = state->ICCL; + SH7305_I2C.ICIC.byte = state->ICIC; + SH7305_I2C.ICCR.byte = state->ICCR; + SH7305_PFC.PJCR.word = state->PJCR; +} + +/* i2c_configure() - configure and install interrupt handlers */ +void i2c_configure(void) +{ + extern void i2c_inth_tack(void); + extern void i2c_inth_wait(void); + extern void i2c_inth_trans(void); + extern void i2c_inth_al(void); + + // install I2C handler + intc_handler_function(0xe00, GINT_CALL(i2c_inth_al)); + intc_handler_function(0xe20, GINT_CALL(i2c_inth_tack)); + intc_handler_function(0xe40, GINT_CALL(i2c_inth_wait)); + intc_handler_function(0xe60, GINT_CALL(i2c_inth_trans)); + + // configure I2C PIN + SH7305_PFC.PJCR.P5MD = 0b00; + SH7305_PFC.PJCR.P4MD = 0b00; + + // configure I2C module + SH7305_I2C.ICCR.ICE = 0; + SH7305_I2C.ICDR = 0; + SH7305_I2C.ICCL = 0x00; + SH7305_I2C.ICCH = 0x00; + SH7305_I2C.ICSR.byte = 0x00; + SH7305_I2C.ICIC.byte = 0x00; + + // init high-level driver information + memset((void*)&__i2c_request, 0x00, sizeof(struct i2c_request_info)); + __i2c_request.status = I2C_REQ_STATUS_FINISHED; + + // Enable interrupt + intc_priority(INTC_I2C_AL, 1); + intc_priority(INTC_I2C_TACK, 1); + intc_priority(INTC_I2C_WAIT, 1); + intc_priority(INTC_I2C_DTE, 1); +} + +/* i2c_unbind() - unbind from gint to casio */ +void i2c_unbind(void) +{ + _i2c_request_await(); +} + +/* i2c_funbind() - funbind from casio to gint */ +void i2c_funbind(void) +{ + if (i2c_hpowered() == false) + return; + // fixme : avoid force terminate + // We cannot easily know the state of Casio's driver since they use + // an state machine to work, and finding the current state require a + // lot of hardcoded OS-specific offsets information. So, for now, + // force terminate the transaction and disable the module to avoid + // any error + SH7305_I2C.ICCR.ICE = 0; + SH7305_I2C.ICDR = 0; +} + +#endif /* GINT_HW_CP */ diff --git a/src/touch/i2c.h b/src/touch/i2c.h new file mode 100644 index 0000000..daf4ea5 --- /dev/null +++ b/src/touch/i2c.h @@ -0,0 +1,109 @@ +#ifndef GINT_TOUCH_I2C_H +#define GINT_TOUCH_I2C_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#if GINT_HW_CP + +//--- +// User API +//--- + +/* i2c_reg_select() - select a register */ +extern int i2c_reg_select(int reg); + +/* i2c_reg_read() - register read operation */ +extern int i2c_reg_read(int reg, void *buffer, size_t size); + +/* i2c_read_stream() - "stream" read operation (skip reg selection) */ +extern int i2c_read_stream(void *buffer, size_t size); + +//--- +// Internals +//--- + +/* i2c_request_mode - enumerate request mode */ +enum i2c_request_mode { + I2C_REQ_MODE_REG_READ = 0, + I2C_REQ_MODE_REG_WRITE = 1, + I2C_REQ_MODE_REG_SELECT = 2, + I2C_REQ_MODE_READ_STREAM = 3, +}; + +/* i2c_request_status - request status */ +enum i2c_request_status { + I2C_REQ_STATUS_START = 0, + I2C_REQ_STATUS_FINISHED = 1, +}; + +enum i2c_request_state { + I2C_REQ_STATE_START = 0, + I2C_REQ_STATE_REG_SELECT = 10, + I2C_REQ_STATE_REG_SELECT_WAIT = 11, + I2C_REQ_STATE_SWITCH_TO_RECEIVE = 20, + I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT = 21, + I2C_REQ_STATE_READ = 30, + I2C_REQ_STATE_READ_LAST = 31, + I2C_REQ_STATE_ZOMBIE = 667, + I2C_REQ_STATE_DEAD = 2617, +}; + +/* struct i2c_request_info - internal I2C request information */ +struct i2c_request_info { + enum i2c_request_mode mode; + int target_register; + uint8_t *buffer; + size_t buffer_size; + size_t buffer_size_remaning; + size_t buffer_cursor; + enum i2c_request_status status; + int state; +}; + +/* __i2c_request - internal current I2C request */ +extern volatile struct i2c_request_info __i2c_request; + +//--- +// Hardware information +//--- + +#include +#include + +/* i2c_configure() - driver/hardware configuration */ +extern void i2c_configure(void); + +/* i2c_hsave() - save hardware information */ +extern void i2c_hsave(touch_state_t *states); + +/* i2c_hrestore() - restore hardware information */ +extern void i2c_hrestore(touch_state_t *states); + +/* i2c_hpowered() - check if the module is powered */ +extern bool i2c_hpowered(void); + +/* i2c_hpoweron() - power on the module */ +extern void i2c_hpoweron(void); + +/* i2c_hpoweroff() - power off the module */ +extern void i2c_hpoweroff(void); + +/* i2c_funbind() - funbind from casio to gint */ +extern void i2c_funbind(void); + +/* i2c_unbind() - unbind from gint to casio */ +extern void i2c_unbind(void); + +#endif /* GINT_HW_CP */ + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_TOUCH_I2C_H */ diff --git a/src/touch/i2c_inth.c b/src/touch/i2c_inth.c new file mode 100644 index 0000000..233bcfd --- /dev/null +++ b/src/touch/i2c_inth.c @@ -0,0 +1,171 @@ +//--- +// gint:touch:i2c_inth - I2C interrupt handlers +//--- +#include +#include + +#if GINT_HW_CP + +#include "./i2c.h" + +//--- +// Private +//--- + +/* _i2c_inth_read() - generic read byte operation */ +static int _i2c_inth_io_operation(bool read) +{ + if (read) { + __i2c_request.buffer[ + __i2c_request.buffer_cursor + ] = SH7305_I2C.ICDR; + } else { + SH7305_I2C.ICDR = __i2c_request.buffer[ + __i2c_request.buffer_cursor + ]; + } + __i2c_request.buffer_cursor += 1; + __i2c_request.buffer_size_remaning -= 1; + return __i2c_request.buffer_size_remaning; +} + +/* _i2c_inth_stop() - stop I2C module (with force wait) */ +static void _i2c_inth_stop(void) +{ + while(true) { + if (SH7305_I2C.ICSR.BUSY == 0) + break; + SH7305_I2C.ICDR = SH7305_I2C.ICDR; + } + SH7305_I2C.ICIC.byte = 0x00; + SH7305_I2C.ICSR.byte = 0x00; + SH7305_I2C.ICCR.byte = 0x00; + __i2c_request.state = I2C_REQ_STATE_DEAD; + __i2c_request.status = I2C_REQ_STATUS_FINISHED; +} + +//--- +// Public +//--- + +/* i2c_inth_trans() - TRANS interrupt handler */ +void i2c_inth_trans(void) +{ + switch (__i2c_request.state) + { + // start byte (writing or reading) and select the proper mode + // note that the "read stream" just changes the internal state + // because the starting byte (0x81) is force-sent over there + case I2C_REQ_STATE_START: + SH7305_I2C.ICIC.DTEE = 0; + SH7305_I2C.ICIC.WAITE = 1; + if (__i2c_request.mode == I2C_REQ_MODE_READ_STREAM) { + SH7305_I2C.ICDR = 0x81; + __i2c_request.state = I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT; + } else { + SH7305_I2C.ICDR = 0x80; + __i2c_request.state = I2C_REQ_STATE_REG_SELECT; + } + break; + + // switch the I2C module to receive mode and setup the next state + // use WAIT interrupt to really switch the I2C to receive + case I2C_REQ_STATE_SWITCH_TO_RECEIVE: + SH7305_I2C.ICIC.DTEE = 0; + SH7305_I2C.ICDR = 0x81; + __i2c_request.state = I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT; + break; + + // for the read operation, the only thing to do is to send the + // ACK at the end of the operation + case I2C_REQ_STATE_READ: + SH7305_I2C.ICIC.DTEE = 0; + if (_i2c_inth_io_operation(true) == 0) + _i2c_inth_stop(); + break; + + // error, unsupported sequences display tests/debug information + default: + gint_panic(0x10e0); + } +} + +/* i2c_inth_wait() - WAIT interrupt handler */ +void i2c_inth_wait(void) +{ + switch (__i2c_request.state) + { + // indicate which register to perform the operation + // we will wait the next WAIT interrupt to ensure that the data has + // been sent + case I2C_REQ_STATE_REG_SELECT: + SH7305_I2C.ICDR = __i2c_request.target_register; + __i2c_request.state = I2C_REQ_STATE_REG_SELECT_WAIT; + SH7305_I2C.ICSR.WAIT = 0; + break; + + // the selected register is confirmed, stop or restart + // note that if the "select register" request is performed (or if + // no buffer is provided) then we send the stop condition (0x90) + // and force-stop the module. + // Otherwise, we trigger a new request (0x94) to (after + // the restart interruption) switch manually from "reading" or + // "writing" mode + case I2C_REQ_STATE_REG_SELECT_WAIT: + if ( + __i2c_request.mode == I2C_REQ_MODE_REG_SELECT + || __i2c_request.buffer_size_remaning == 0 + || __i2c_request.buffer == NULL + ) { + SH7305_I2C.ICCR.byte = 0x90; + SH7305_I2C.ICSR.WAIT = 0; + _i2c_inth_stop(); + } else { + SH7305_I2C.ICCR.byte = 0x94; + __i2c_request.state = I2C_REQ_STATE_SWITCH_TO_RECEIVE; + SH7305_I2C.ICIC.DTEE = 1; + SH7305_I2C.ICSR.WAIT = 0; + } + break; + + // switch to receive mode + // waiting the next WAIT interrupt to start reading + case I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT: + SH7305_I2C.ICCR.byte = 0x81; + __i2c_request.state = I2C_REQ_STATE_READ; + SH7305_I2C.ICSR.WAIT = 0; + break; + + // read operation + // can either be WAIT or DTE interrupt, a special case is + // performed to avoid DTE interrupt + case I2C_REQ_STATE_READ: + if (__i2c_request.buffer_size_remaning > 1) { + if (SH7305_I2C.ICSR.DTE != 0) + _i2c_inth_io_operation(true); + } + if (__i2c_request.buffer_size_remaning == 1) + SH7305_I2C.ICCR.byte = 0xc0; + SH7305_I2C.ICIC.DTEE = 1; + SH7305_I2C.ICSR.WAIT = 0; + break; + + // error cases, unable to handle the WAIT interrupt + default: + gint_panic(0x10e0); + } +} + +/* i2c_inth_tack() - TACK interrupt handler */ +void i2c_inth_tack(void) +{ + gint_panic(0x10e0); +} + +/* i2c_inth_al() - AL interrupt handler */ +void i2c_inth_al(void) +{ + gint_panic(0x10e0); +} + +#endif /* GINT_HW_CP */ diff --git a/src/touch/touch.c b/src/touch/touch.c new file mode 100644 index 0000000..f59efcb --- /dev/null +++ b/src/touch/touch.c @@ -0,0 +1,95 @@ +//--- +// gint:touch - touch driver (high-level) +//---- +#include + +#include +#include +#include + +#if GINT_HW_CP + +#include "./i2c.h" +#include "./adconv.h" +#include "./driver.h" + +//--- +// Internals +//--- + +/* __touch_drv_info - internal driver information */ +extern struct _touch_drv_info __touch_drv_info; + +//--- +// Public +//--- + +// user-API + +/* touch_calib_get() - get calibration information */ +int touch_calib_get(touch_calibration_t *calib) +{ + if (calib == NULL) + return -1; + memcpy(calib, &__touch_drv_info.calibration, sizeof(*calib)); + return 0; +} + +/* touch_calib_set() - set calibration information */ +int touch_calib_set(touch_calibration_t *calib) +{ + if (calib == NULL) + return -1; + memcpy(&__touch_drv_info.calibration, calib, sizeof(*calib)); + return 0; +} + +// low-level API + +/* touch_next_event() - get the next touch event */ +key_event_t touch_next_event(void) +{ + struct _touch_adconv adconv; + struct _touch_addots addots; + struct _touch_adraw adraw; + key_event_t evt; + int type; + + evt.type = KEYEV_TOUCH_RELEASE; + type = touch_adconv_get_raw(&adraw); + if (type != 0) + { + type = touch_adconv_get_conv(&adconv, &adraw, type); + type = touch_adconv_get_dots(&addots, &adconv, type); + if (type == 1) + { + evt.type = KEYEV_TOUCH_DRAG; + evt.x = addots.x1; + evt.y = addots.y1; + } + } + cpu_atomic_start(); + if (evt.type == KEYEV_TOUCH_DRAG) { + if ( + (__touch_drv_info.prev_evt.type == KEYEV_NONE) || + (__touch_drv_info.prev_evt.type == KEYEV_TOUCH_RELEASE) + ) { + evt.type = KEYEV_TOUCH_PRESSED; + } + } + if (evt.type == KEYEV_TOUCH_RELEASE) { + if ( + (__touch_drv_info.prev_evt.type == KEYEV_NONE) || + (__touch_drv_info.prev_evt.type == KEYEV_TOUCH_RELEASE) + ) { + evt.type = KEYEV_NONE; + } + } + __touch_drv_info.prev_evt.type = evt.type; + __touch_drv_info.prev_evt.x = evt.x; + __touch_drv_info.prev_evt.y = evt.y; + cpu_atomic_end(); + return evt; +} + +#endif /* GINT_HW_CP */ diff --git a/src/usb/setup.c b/src/usb/setup.c index 82ac80e..8b800ea 100644 --- a/src/usb/setup.c +++ b/src/usb/setup.c @@ -33,7 +33,9 @@ static usb_dc_device_t dc_device = { .bMaxPacketSize0 = 64, .idVendor = htole16(0x07cf), /* Casio Computer Co., Ltd. */ .idProduct = htole16(ID_PRODUCT), - .bcdDevice = htole16(0x0100), + /* CASIO sets bcdDevice to 0x0100. Use a different value so that Windows + won't rely on cached registry entries and check for WCID support. */ + .bcdDevice = htole16(0x0177), .iManufacturer = 0, .iProduct = 0, .iSerialNumber = 0, @@ -55,6 +57,65 @@ static usb_dc_string_t dc_string0 = { .data = { htole16(0x0409) }, /* English (US) */ }; +struct MOS1_String_0xEE_Descriptor { + usb_dc_string_t dc; + u16 signature[7]; + u8 bMS_VendorCode; + u8 bPad; +}; +static struct MOS1_String_0xEE_Descriptor dc_string_ee = { + .dc = { .bLength = 18, .bDescriptorType = USB_DC_STRING }, + .signature = { 0x4D00, 0x5300, 0x4600, 0x5400, 0x3100, 0x3000, 0x3000 }, + .bMS_VendorCode = 0x7b, /* Arbitrarily-chosen value */ + .bPad = 0, +}; + +struct MOS1_Extended_Compat_ID_Function_Section { + u8 bFirstInterfaceNumber; + u8 bReserved1; + u8 compatibleID[8]; + u8 subcompatibleID[8]; + u8 bReserved2[6]; +}; +struct MOS1_Extended_Compat_ID_Descriptor { + // 16 byte header + u32 dwLength; + u16 bcdVersion; + u16 wIndex; + u8 bCount; + u8 bReserved[7]; + + // hardcoded to size 1 for convenience here + struct MOS1_Extended_Compat_ID_Function_Section f; +}; +static struct MOS1_Extended_Compat_ID_Descriptor dc_compatid = { + .dwLength = htole32(0x10 + 24 * 1), + .bcdVersion = htole16(0x0100), + .wIndex = htole16(0x0004), + .bCount = 1, + .f = { + .bFirstInterfaceNumber = 0, + .bReserved1 = 0x01, + .compatibleID = { 0x57, 0x49, 0x4e, 0x55, 0x53, 0x42, 0x00, 0x00 }, + .subcompatibleID = { 0 }, + .bReserved2 = { 0 }, + }, +}; + +struct MOS1_Extended_Properties_Descriptor { + // 10 byte header + u32 dwLength; + u16 bcdVersion; + u16 wIndex; + u16 wCount; +}; +static struct MOS1_Extended_Properties_Descriptor dc_extprops = { + .dwLength = htole32(10), + .bcdVersion = htole16(0x0100), + .wIndex = htole16(0x0005), + .wCount = htole16(0), +}; + GCONSTRUCTOR static void set_strings(void) { char const *serial_base = @@ -139,12 +200,40 @@ static void req_get_descriptor(int wValue, int wLength) else if(type == USB_DC_STRING) { - usb_dc_string_t *dc = usb_dc_string_get(num); + usb_dc_string_t *dc; + if(num == 0xee) { + dc = &dc_string_ee.dc; + USB_LOG("Selecting MOS1 string descriptor\n"); + } + else + dc = usb_dc_string_get(num); if(dc) dcp_write(dc, dc->bLength); else USB.DCPCTR.PID = 2; } } +static void req_get_MOS_feature_descriptor(int wValue, int wIndex, int wLength) +{ + int intf = (wValue >> 8) & 0xff; + int page = (wValue & 0xff); + + USB_LOG("GET_MS_DESCRIPTOR: i%d p%d #%d len:%d\n", intf, page, wIndex, wLength); + + if(wIndex == 0x0004) { + /* Extended Compat ID descriptor */ + dcp_write(&dc_compatid, wLength); + USB_LOG("Compat ID descriptor given\n"); + } + else if(wIndex == 0x0005) { + /* Extended Properties descriptor */ + dcp_write(&dc_extprops, wLength); + USB_LOG("Extended properties descriptor given\n"); + } + else { + USB_LOG("Unknown MS descriptor!\n"); + } +} + static void req_get_configuration(void) { USB_LOG("GET_CONFIGURATION -> %d\n", 1); @@ -158,6 +247,12 @@ static void req_set_configuration(int wValue) USB.DCPCTR.PID = (wValue == 1) ? 1 : 2; } +static void req_get_device_status(void) +{ + USB_LOG("GET_STATUS device -> 0x0001\n"); + dcp_write("\x01\x00", 2); +} + void usb_req_setup(void) { /* Respond to setup requests */ @@ -181,6 +276,19 @@ void usb_req_setup(void) else if(bmRequestType == 0x00 && bRequest == SET_CONFIGURATION) req_set_configuration(wValue); + else if(bmRequestType == 0x80 && bRequest == GET_STATUS) + req_get_device_status(); + // 0x81 / GET_STATUS : get intf status + // 0x82 / GET_STATUS : get endpoint status + + // CESG502 initial 0x01 comm + // else if(bmRequestType == 0x41 && bRequest == 0x01) + // USB.DCPCTR.PID = 1; + + else if((bmRequestType == 0xc0 || bmRequestType == 0xc1) + && bRequest == 0x7b) + req_get_MOS_feature_descriptor(wValue, wIndex, wLength); + /* TODO: Other standard SETUP requests */ else USB_LOG("SETUP: bRequest=%02x bmRequestType=%02x wValue=%04x\n" " wIndex=%04x wLength=%d -> ???\n",