* Now uses topti instead of fxlib for text (including MMU failure) * Fit .pretext into 4k for everything before MMU succeeds * A short version of sprintf() for dynamic messages * Support a driver function, status(), to allow early driver debug * Expose more useful platform information in <gint/mpu.h> * Expose the first of a few CASIOWIN syscallspull/1/head
@@ -21,7 +21,7 @@ all-targets := $(foreach b,$(builds),all-$b) | |||
all: $(all-targets) | |||
all-build%: build% | |||
@ echo -e "$B::$W Making into $<$N" | |||
@ echo -e "\n$B::$W Making into $<$N" | |||
@ $(MAKE) --no-print-directory -C $< | |||
# | |||
@@ -33,7 +33,7 @@ install-targets := $(foreach b,$(builds),install-$b) | |||
install: $(install-targets) | |||
install-build%: build% | |||
@ echo -e "$B::$W Installing from $<$N" | |||
@ echo -e "\n$B::$W Installing from $<$N" | |||
@ $(MAKE) --no-print-directory -C $< install | |||
# | |||
@@ -45,7 +45,7 @@ uninstall-targets := $(foreach b,$(builds),uninstall-$b) | |||
uninstall: $(uninstall-targets) | |||
uninstall-build%: build% | |||
@ echo -e "$B::$W Uninstalling from $<$N" | |||
@ echo -e "\n$B::$W Uninstalling from $<$N" | |||
@ $(MAKE) --no-print-directory -C $< uninstall | |||
# |
@@ -30,7 +30,7 @@ access to the low-level MPU features, among which: | |||
The library also offers powerful higher-level features: | |||
* An enhanced versio of the system's GetKey() and GetKeyWait() | |||
* An enhanced version of the system's GetKey() and GetKeyWait() | |||
* A gray engine that works by rapidly swapping monochrome images on fx-9860G II | |||
* Blazingly fast drawing functions when working with the fxSDK (image rendering | |||
is 10 times faster than MonochromeLib) |
@@ -1,89 +1,45 @@ | |||
# Since compatibility | |||
Another, more recent list of things to do: | |||
- Complete memory functions in [core/memory.c] | |||
- Finish the boot log in <core/bootlog.h> | |||
- Finish the boot log in [core/bootlog.c] | |||
- Document the SH7305 PFC in <gint/mpu/pfc.h> | |||
- Change the description of the version number in <gint/gint.h> | |||
- Define the version number from full Git info | |||
- Do this display in <gint/display-fx.h> and <gint/display-cg.h> | |||
- Remove all mentions of GINT_LAX in the code | |||
- Write exception handlers | |||
- Do overclock in [clock/] | |||
- Test getkey() | |||
* Make keysc/keysc.c/state static and review forgotten global variables | |||
* Find out what happened to the clock frequency on fxcg50 | |||
* Implement profiling | |||
* Save and restore interrupt masks | |||
* More keyboard | |||
Strange, (irreproducible?) SysERRORs on SH3? | |||
- Happened when dealing with overclock and after a transfer | |||
- SysERROR when add-in fully returns, nothing during execution | |||
- Affects all add-ins of the calculator! | |||
* Make sure interrupt for extra timers is masked when you ctx_restore to avoid | |||
interrupts | |||
* For modules, use #define TMU ((tmu_t *)0xdeadbeef)) instead of tmu_t *TMU | |||
* Don't hardcode fx-cg50 stack address in fxcg50.ld for Prizm compatibility | |||
* Implement the gint_switch() | |||
On the bootlog. | |||
* Better review of .pretext to make sure everything fits in 4k | |||
* Move topti to .pretext to use it in the bootlog, also update() and dclear() | |||
* Use topti in the error message for the missing mappings (hurray o/) | |||
* Use a serious formatted printing function | |||
Crucial, missing things. | |||
! core: the four basic memory functions | |||
! core: build an exception handler and a TLB miss handler | |||
! core: gint_switch() (driver contexts on stack; arbitrary code?) | |||
Tests to run. | |||
* core: run the alignment/size automated tests | |||
* bopti: more sizes, gray | |||
* topti: all charsets, colors | |||
Completeness elements on existing code. | |||
* keyboard: finish the interface | |||
* clock: spread spectrum on fxcg50 | |||
* bopti: blending modes for monochrome bitmaps (use topti assembler) | |||
* display: use more of topti's assembler in drect() | |||
* core: use cmp/str for memchr() | |||
* stdio: serious formatted printing function | |||
* timer: try putting the definitions in <gint/mpu/tmu.h> | |||
Keep in mind. | |||
* keyboard: make keysc.c@state static and review globals in the project | |||
* prizm: don't hardcode stack address in fxcg50.ld | |||
* prizm: detect P1 static RAM (stack) in TLB | |||
* core: prove and use qdiv10() instead of __sdivsi3 | |||
* setjmp: more registers may need to be saved | |||
* core: free heap when a task-switch results in leaving the app | |||
* core: save and restore interrupt masks | |||
* timer: make sure ETMU interrupts are disabled in ctx_restore() | |||
* core: document the SH7305 PFC in <gint/mpu/pfc.h> | |||
Future directions. | |||
* File management | |||
* Drawing functions, bopti and tales | |||
* Gray engine | |||
* Overclock | |||
* Add a driver init function to the r61524 driver. | |||
* Finish the bootlog and use the keyboard to control it. | |||
* Save driver contexts on the stack in gint_pause(). | |||
* Write the core memory functions. | |||
- Allow arbitrary code in gint_pause() instead of just main menu? | |||
- Dynamically detect P1 static RAM in TLB for runtime Prizm compatibility. | |||
* Use a solid API for boot-time printing (or just use fxlib) | |||
- Use qdiv10() (there should be no __sdivsi3 in gint) | |||
(bootlog, timer_delay(), getkey_repeat(), int2bcd()) | |||
* Load an exception handler and a TLB miss handler. | |||
# Before compatibility | |||
Bugs to fix: | |||
- Alignment of ALL .data / .rodata files is required to ensure converted data | |||
is properly aligned -> Ok using ALIGN(4) | |||
- Ensure heap data is freed when a task-switch results in leaving the app (?) | |||
Things to do before 1.0: | |||
- bopti: Test partial transparency | |||
- perf: Try 284x124 at (-60, -28) (all disadvantages) | |||
- project: Check size of *all* library structures | |||
- time: Compute CLOCKS_PER_SEC | |||
Things to do later: | |||
- bopti: Implement blending modes for monochrome bitmaps | |||
- clock: Only measure if requires as option, otherwise trust {FTune} | |||
- clock: Handle overclock (relaunch clocks when overclocking) | |||
- clock: Split code into several files, change clock_config_t type | |||
- core: Remove redundant code linked to environment saves | |||
- display: Try to make this module lighter (lots of code in text section) | |||
- esper: Cleaner playback, synthesizing | |||
- events: Allow customization of keyboard event system (option to return | |||
| events with modifiers, etc) | |||
- events: Generate keyboard events on-the-fly by reading state arrays, | |||
| allowing both a faster interrupt and avoiding supressing other | |||
| events inside getkey() and multigetkey() | |||
- gray: Same as display, it's quite heavy | |||
- serial: Implement a driver | |||
- stdio: More serious formatted printing functions and headers | |||
- string: Use cmp/str to implement memchr() (assembler examples) | |||
- string: Do some tests for memcmp() and memcpy() | |||
- usb: Implement a driver | |||
Things to investigate: | |||
- Registers that may need to be saved within setjmp() | |||
Possibly useful modules: | |||
- DMAC | |||
- SCIF, SCIFA | |||
- TPU | |||
- USB | |||
- CMT on SH7305, WDT on SH7705 | |||
* Integrate overclock management | |||
* A library for profiling with manual calls | |||
* Audio playback using Martin Poupe's method | |||
* Serial communication [SCIF] [SCIFA] | |||
* USB communication [USB] | |||
* Driver for the watchdog timer [WDT] [SH7705] |
@@ -5,10 +5,18 @@ | |||
#ifndef GINT_CORE_BOOTLOG | |||
#define GINT_CORE_BOOTLOG | |||
#include <gint/defs/attributes.h> | |||
/* bootlog_loaded() - section loading stage | |||
Called when RAM sections have been wiped and copied */ | |||
void bootlog_loaded(void); | |||
/* bootlog_unmapped() - ROM mapping stage failed | |||
Called if not enough ROM pages have been mapped. | |||
@rom Amount of mapped ROM, in bytes | |||
@size Minimum mapping required */ | |||
void bootlog_unmapped(int rom, int size); | |||
/* bootlog_mapped() - ROM mapping stage | |||
Called after all ROM pages have been traversed. All of them may not have | |||
been mapped. | |||
@@ -21,11 +29,19 @@ void bootlog_mapped(uint32_t rom, uint32_t ram); | |||
handlers set up. */ | |||
void bootlog_kernel(void); | |||
/* bootlog_driver() - driver load | |||
Called for very loaded driver. */ | |||
void bootlog_driver(const char *driver_name, const char *status); | |||
void bootlog_driver_summary(void); | |||
/* All these functions are enabled only if GINT_BOOT_LOG is defined */ | |||
#ifndef GINT_BOOT_LOG | |||
#define bootlog_loaded(...) | |||
#define bootlog_unmapped(...) | |||
#define bootlog_mapped(...) | |||
#define bootlog_kernel(...) | |||
#define bootlog_driver(...) | |||
#define bootlog_driver_summary(...) | |||
#endif | |||
#endif /* GINT_CORE_BOOTLOG */ |
@@ -1,22 +0,0 @@ | |||
//--- | |||
// core:memory - Core memory functions | |||
// | |||
// These are the basic standard memory functions required by GCC. | |||
//--- | |||
#ifndef GINT_CORE_MEMORY | |||
#define GINT_CORE_MEMORY | |||
/* memcpy() - copy a chunk of memory to a non-overlapping destination */ | |||
void *memcpy(void * restrict dest, const void * restrict src, size_t n); | |||
/* memmove() - copy a chunk of memory to any destination */ | |||
void *memmove(void *dest, const void *src, size_t n); | |||
/* memcmp() - compare two chunks of memory of the same size */ | |||
int memcmp(const void *src1, const void *src2, size_t n); | |||
/* memset() - fill a chunk of memory with a single byte */ | |||
void *memset(void *dest, int byte, size_t n); | |||
#endif /* GINT_CORE_MEMORY */ |
@@ -0,0 +1,29 @@ | |||
//--- | |||
// gint:core:std - a few standard functions implemented in gint | |||
// | |||
// There are few enough of them that it felt unnecessary to use a full- | |||
// fledged standard library. | |||
//--- | |||
#ifndef GINT_CORE_STD | |||
#define GINT_CORE_STD | |||
#include <gint/defs/types.h> | |||
#include <stdarg.h> | |||
/* memcpy() - copy a chunk of memory to a non-overlapping destination */ | |||
void *memcpy(void * restrict dest, const void * restrict src, size_t n); | |||
/* memset() - fill a chunk of memory with a single byte */ | |||
void *memset(void *dest, int byte, size_t n); | |||
/* strlen() - length of a NUL-terminated string */ | |||
size_t strlen(const char *str); | |||
/* vsprintf() - an almost-empty subset of the real one */ | |||
void vsprintf(char *str, const char *format, va_list args); | |||
/* sprintf() - an almost-empty subset of the real one */ | |||
void sprintf(char *str, const char *format, ...); | |||
#endif /* GINT_CORE_STD */ |
@@ -0,0 +1,11 @@ | |||
//--- | |||
// gint:core:syscalls - calls to CASIOWIN | |||
//--- | |||
#ifndef GINT_CORE_SYSCALLS | |||
#define GINT_CORE_SYSCALLS | |||
/* __os_version(): Get OS version on the form MM.mm.iiii (10 bytes) */ | |||
void __os_version(char *version); | |||
#endif /* GINT_CORE_SYSCALLS */ |
@@ -27,4 +27,7 @@ | |||
access sizes silently fail - honestly you don't want this to happen */ | |||
#define GPACKED(x) __attribute__((packed, aligned(x))) | |||
/* Weak symbols */ | |||
#define GWEAK __attribute__((weak)) | |||
#endif /* GINT_DEFS_ATTRIBUTES */ |
@@ -27,7 +27,9 @@ extern uint32_t *vram; | |||
#ifdef FX9860G | |||
#include <gint/display-fx.h> | |||
#else | |||
#endif | |||
#ifdef FXCG50 | |||
#include <gint/display-cg.h> | |||
#endif | |||
@@ -9,16 +9,29 @@ | |||
#include <gint/defs/types.h> | |||
//--- | |||
// Driver initialization | |||
// Driver procedure flow | |||
// | |||
// Drivers are initialized in linking order. For each driver, the | |||
// following functions are called: | |||
// - driver_sh3() [if running on an SH3-based machine] | |||
// - ctx_save() | |||
// - init() | |||
// Drivers are initialized in priority order, and in linking order within | |||
// the same priority (which is pretty much undefined). Make sure every | |||
// driver's priority level is higher than those of its dependencies. | |||
// | |||
// When the driver is unloaded, the following functions are called: | |||
// - ctx_restore() | |||
// At initialization, the following functions are called: | |||
// 1. driver_sh3() [if not NULL, SH3 fx9860G only] | |||
// 2. ctx_save(sys_ctx) [if not NULL] | |||
// 3. init() [if not NULL] | |||
// | |||
// Then, if the on-screen boot log is enabled, the status() function is | |||
// called and the returned function is displayed (21 characters max). | |||
// 4. status() [if not NULL, if GINT_BOOT_LOG is defined] | |||
// | |||
// If the gint_switch() function is called to temporarily give back | |||
// control to the operating system, the state of each driver is saved to | |||
// the stack, then restored from there. | |||
// 5. ctx_save(stack) [if not NULL] | |||
// 6. ctx_restore(stack) [if not NULL] | |||
// | |||
// When finally the driver is unloaded, the system context is restored. | |||
// 7. ctx_restore(sys_ctx) [if not NULL] | |||
//--- | |||
/* gint_driver_t - driver meta-information used by gint */ | |||
@@ -65,6 +78,14 @@ typedef struct | |||
@ctx A context buffer filled by ctx_save() */ | |||
void (*ctx_restore)(void *ctx); | |||
/* status() - status string generation | |||
When the boot log is defined, this function is called to print | |||
information returned by the driver, for debugging purposes. This is | |||
expected to be a short (max 21 bytes) string because only a few | |||
lines are available for all drivers. | |||
Returns a pointer to a string; a static buffer is suitable. */ | |||
const char * (*status)(void); | |||
} GPACKED(4) gint_driver_t; | |||
/* GINT_DECLARE_DRIVER() - make a driver visible to gint | |||
@@ -81,11 +102,26 @@ typedef struct | |||
GSECTION(".gint.drivers." #level) extern gint_driver_t name; | |||
/* GINT_DRIVER_SH3() - declare a function for SH3-rectification | |||
This macros allows the argument function to not exist on fxcg50. */ | |||
This macro makes its argument NULL on fxcg50, this way the named function | |||
can be defined under #ifdef FX9860G while keeping the structure clean. */ | |||
#ifdef FX9860G | |||
#define GINT_DRIVER_SH3(name) name | |||
#define GINT_DRIVER_SH3(name) name | |||
#endif | |||
#ifdef FXCG50 | |||
#define GINT_DRIVER_SH3(name) NULL | |||
#endif | |||
/* GINT_DRIVER_STATUS() - declare a function for status string generation | |||
This macro makes its argument NULL when GINT_BOOT_LOG is defuned, this way | |||
the named function can be defined under #ifdef GINT_BOOT_LOG while keeping | |||
the structure clean. */ | |||
#ifdef GINT_BOOT_LOG | |||
#define GINT_DRIVER_STATUS(name) name | |||
#else | |||
#define GINT_DRIVER_SH3(name) NULL | |||
#define GINT_DRIVER_STATUS(name) NULL | |||
#endif | |||
#endif /* GINT_DRIVERS */ |
@@ -8,20 +8,11 @@ | |||
#include <gint/defs/types.h> | |||
/* GINT_VERSION - the library version number | |||
Provides gint's running version number, which is made of four fields: | |||
31 24 23 20 19 16 15 0 | |||
+---------------+---------------+---------------+---------------+ | |||
| channel | major | minor | build | | |||
+---------------+---------------+---------------+---------------+ | |||
gint is versioned from it's repository commits on the master branch. The | |||
GINT_VERSION integer contains the short commit hash. | |||
The first field is a letter indicating the type of version ('a'lpha, 'b'eta, | |||
'r'elease, 'd'ev, etc). The second and third field are the version number on | |||
the form "major.minor". The last field is the build number for this version. | |||
The build number uniquely identifies a binary version of the library. | |||
For instance, 0x72100053 translates as "release 1.0 build 83". */ | |||
/* TODO: Fix version number */ | |||
For instance, 0x03f7c0a0 means commit 3f7c0a0. */ | |||
extern char GINT_VERSION; | |||
#define GINT_VERSION ((uint32_t)&GINT_VERSION) | |||
@@ -12,6 +12,8 @@ | |||
#ifndef GINT_CORE_MPU | |||
#define GINT_CORE_MPU | |||
#include <gint/defs/types.h> | |||
/* mpu_t - supported MPUs */ | |||
typedef enum | |||
{ | |||
@@ -22,27 +24,64 @@ typedef enum | |||
mpu_sh7724 = 4, /* For reference */ | |||
} mpu_t; | |||
/* On fx9860g, the 256k byte RAM might be extended to 512k if the machine is | |||
recent enough. This includes all SH4 and many SH3 fx-9750 GII; the only | |||
model known to not have it is the old fx-9860G. */ | |||
#ifdef FX9860G | |||
/* mpu_id() - get the name of the underlying MPU */ | |||
extern const mpu_t gint_mpu_id; | |||
#define gint_mpu() (gint_mpu_id) | |||
typedef struct | |||
{ | |||
/* MPU type, one of the above values */ | |||
mpu_t mpu; | |||
/* Extended RAM area (8804'0000:256k) is available */ | |||
int extended_ram; | |||
/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */ | |||
#define isSH3() (gint_mpu() & 1) | |||
#define isSH4() (!isSH3()) | |||
} platform_t; | |||
/* mpu_init() - probe the MPU type | |||
This function must be executed before mpu_id() can be used. */ | |||
void mpu_init(void); | |||
/* mpu_id() - get the name of the underlying MPU */ | |||
#define gint_mpu() (gint_platform.mpu) | |||
#else /* FXCG50 */ | |||
/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */ | |||
#define isSH3() (gint_mpu() & 1) | |||
#define isSH4() (!isSH3()) | |||
/* All fxcg50 machines have an SH7305, which makes things simpler. */ | |||
#define gint_mpu() mpu_sh7305 | |||
#define isSH3() 0 | |||
#define isSH4() 1 | |||
#endif /* FX9860G */ | |||
/* On fxcg50, the processor is always SH4. We can still differentiate between | |||
modern fx-CG 50 and older fx-CG 10/20 which are called here "Prizm". (This | |||
is done by observing the initial stack pointer.) */ | |||
#ifdef FXCG50 | |||
typedef struct | |||
{ | |||
/* MPU type (always sh7305) */ | |||
mpu_t mpu; | |||
/* Whether this is an fx-CG 10/20 Prizm instead of an fx-CG 50 */ | |||
int prizm; | |||
} platform_t; | |||
/* All fxcg50 machines have an SH7305, which makes things simpler. */ | |||
#define gint_mpu() mpu_sh7305 | |||
#define isSH3() 0 | |||
#define isSH4() 1 | |||
#endif /* FX9860G */ | |||
/* Platform details collected by mpu_init() */ | |||
extern const platform_t gint_platform; | |||
/* mpu_init() - detect hardware information | |||
This function must be executed before other functions of this header can be | |||
used successfully. | |||
@stack Starting stack address (roughly is enough) */ | |||
void mpu_init(uint32_t stack); | |||
#endif /* GINT_CORE_MPU */ |
@@ -65,6 +65,7 @@ typedef volatile struct | |||
uint32_t CKOFF :1; /* CKO Output Stop */ | |||
uint32_t :1; | |||
); | |||
/* TODO: CPG: Add the SSCGCR register */ | |||
pad(0x28); | |||
lword_union(FLLFRQ, |
@@ -4,6 +4,7 @@ | |||
#include <gint/drivers.h> | |||
#include <gint/clock.h> | |||
#include <core/std.h> | |||
#include <gint/mpu.h> | |||
#include <gint/mpu/cpg.h> | |||
@@ -53,8 +54,7 @@ static void sh7705_probe(void) | |||
freq.Iphi_div = idiv + 1; | |||
freq.Pphi_div = pdiv + 1; | |||
/* Deduce the frequency of the main clocks. The following piece of code | |||
hardcodes ckio / 3 and avoids using the division operator */ | |||
/* Deduce the frequency of the main clocks. This value is ckio/3 */ | |||
int ckio_3 = 9830400; | |||
/* Exchange the setting values 2 and 3 */ | |||
@@ -68,11 +68,6 @@ static void sh7705_probe(void) | |||
} | |||
#undef CPG | |||
#else | |||
/* This prototype will silence warnings on fxcg50 */ | |||
void sh7705_probe(void); | |||
#endif /* FX9860G */ | |||
//--- | |||
@@ -122,15 +117,36 @@ static void sh7305_probe(void) | |||
// Other driver stuff | |||
//--- | |||
#ifdef GINT_BOOT_LOG | |||
static const char *cpg_status(void) | |||
{ | |||
static char status[18]; | |||
sprintf(status, "I%3d B%3d P%3d C%c", | |||
freq.Iphi_f / 1000000, | |||
freq.Bphi_f / 1000000, | |||
freq.Pphi_f / 1000000, | |||
isSH3() ? 'e' : 'E' | |||
); | |||
return status; | |||
} | |||
#endif /* GINT_BOOT_LOG */ | |||
static void init(void) | |||
{ | |||
isSH3() ? sh7705_probe() | |||
: sh7305_probe(); | |||
/* This avoids warnings about sh7705() not being defined on fxcg50 */ | |||
#ifdef FX9860G | |||
isSH3() ? sh7705_probe() : | |||
#endif | |||
sh7305_probe(); | |||
} | |||
gint_driver_t drv_cpg = { | |||
.name = "CPG", | |||
.init = init, | |||
.status = GINT_DRIVER_STATUS(cpg_status), | |||
.ctx_size = 0, | |||
.sys_ctx = NULL, | |||
.ctx_save = NULL, |
@@ -2,16 +2,22 @@ | |||
// gint:core:bootlog - Boot-time on-screen log for extreme debugging | |||
//--- | |||
/* TODO: Review, enhance and fix bootlog */ | |||
#include <gint/defs/types.h> | |||
#include <core/std.h> | |||
#include <core/mmu.h> | |||
#include <gint/mpu.h> | |||
#include <gint/mpu/intc.h> | |||
#include <core/mmu.h> | |||
#include <gint/gint.h> | |||
#include <gint/display.h> | |||
#include <core/syscalls.h> | |||
#include <gint/clock.h> | |||
#ifdef FXCG50 | |||
void Bdisp_AllClr_VRAM(void); | |||
void Bdisp_PutDisp_DD(void); | |||
#define dclear(c) Bdisp_AllClr_VRAM() | |||
#define dupdate() Bdisp_PutDisp_DD() | |||
#endif | |||
@@ -22,28 +28,34 @@ extern char | |||
sgdata, sgbss, sdata, sbss, | |||
btors, mtors, etors; | |||
/* bootlog_loaded() - section loading stage */ | |||
void bootlog_loaded(void) | |||
/* print() - formatted printing shorthand */ | |||
static void print(int x, int y, const char *format, ...) | |||
{ | |||
/* Version string - the string constant resides in ROM */ | |||
const char *base = "gint @"; | |||
const char *hexa = "0123456789abcdef"; | |||
char str[14]; | |||
char str[45]; | |||
va_list args; | |||
va_start(args, format); | |||
for(int i = 0; i < 6; i++) str[i] = base[i]; | |||
str[13] = 0; | |||
vsprintf(str + 2, format, args); | |||
/* Retrieve the commit number */ | |||
for(int i = 0; i < 7; i++) | |||
{ | |||
int shift = 24 - (i << 2); | |||
str[i + 6] = hexa[(GINT_VERSION >> shift) & 0xf]; | |||
} | |||
#ifdef FX9860G | |||
dtext(6 * (x - 1) + 1, 7 * (y - 1), str + 2, color_black, color_white); | |||
#endif | |||
#ifdef FXCG50 | |||
PrintXY(x, y, str, 0, 0); | |||
#endif | |||
va_end(args); | |||
} | |||
/* bootlog_loaded() - section loading stage */ | |||
GSECTION(".pretext") | |||
void bootlog_loaded(void) | |||
{ | |||
/* Size of memory sections */ | |||
uint32_t rom_size = (uint32_t)&srom; | |||
uint32_t ram_size = (uint32_t)&sdata + (uint32_t)&sbss; | |||
uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss; | |||
uint32_t rom_size = (uint32_t)&srom; | |||
uint32_t ram_size = (uint32_t)&sdata + (uint32_t)&sbss; | |||
uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss; | |||
/* MPU type */ | |||
mpu_t mpu = gint_mpu(); | |||
@@ -51,68 +63,121 @@ void bootlog_loaded(void) | |||
/* TODO: Use a solid API for boot-time printing */ | |||
dclear(color_white); | |||
print(1, 1, str); | |||
print(15, 1, " Loaded"); | |||
print(1, 1, "gint @%7x SLmkd", GINT_VERSION); | |||
if((uint)mpu < 4) print(16, 2, names + 8 * (mpu - 1)); | |||
else print_dec(16, 2, mpu, 6); | |||
if((uint)mpu < 4) print(1, 2, names + 8 * (mpu - 1)); | |||
else print(1, 2, "%6d", mpu); | |||
print(1, 2, "ROM RAM GINT"); | |||
print(4, 3, "k c d"); | |||
print_dec(1, 3, (rom_size + 0x3ff) >> 10, 3); | |||
print_dec(6, 3, ram_size, 4); | |||
print_dec(11, 3, gint_size, 4); | |||
print_dec(17, 3, &mtors - &btors, 2); | |||
print_dec(20, 3, &etors - &mtors, 2); | |||
#ifdef FX9860G | |||
print(8, 2, "%c?-", gint_platform.extended_ram ? 'R' : 'r'); | |||
#endif | |||
#ifdef FXCG50 | |||
print(8, 2, "--%c", gint_platform.prizm ? 'P' : 'p'); | |||
#endif | |||
char os[11]; | |||
__os_version(os); | |||
print(12, 2, "OS%2s%2s%4s", os, os+3, os+6); | |||
print(1, 3, "ROM%4dk RAM%3d+%1dk ??", | |||
(rom_size + 0x3ff) >> 10, | |||
(ram_size + 0x3ff) >> 10, | |||
(gint_size + 0x3ff) >> 10); | |||
dupdate(); | |||
} | |||
/* bootlog_mapped() - ROM mapping stage */ | |||
GSECTION(".pretext") | |||
void bootlog_mapped(int rom, int ram) | |||
{ | |||
rom = (rom + 0x3ff) >> 10; | |||
ram = (ram + 0x3ff) >> 10; | |||
/* Check whether all ROM is mapped */ | |||
uint32_t rom_size = (uint32_t)&srom; | |||
print(15, 1, " Mapped"); | |||
print(1, 4, "MMU ROM: k RAM: k"); | |||
(rom < 0) ? print(9, 4, "???") : print_dec(9, 4, rom, 3); | |||
(ram < 0) ? print(18, 4, "???") : print_dec(18, 4, ram, 3); | |||
print(20, 3, "%c%c", (rom >= (int)rom_size) ? 'F' : 'f', | |||
isSH3() ? 'u' : 'U'); | |||
print(9, 2, (ram > 8192) ? "E" : "e"); | |||
print(19, 1, "M"); | |||
dupdate(); | |||
} | |||
/* bootlog_unmapped() - ROM mapping stage failure */ | |||
void bootlog_unmapped(int rom, int size) | |||
{ | |||
print(1, 6, "MMU: add-in too large"); | |||
print(1, 7, "%8x < %8x", rom, size); | |||
dupdate(); | |||
} | |||
/* bootlog_kernel() - gint loading stage */ | |||
void bootlog_kernel(void) | |||
{ | |||
print(15, 1, " Kernel"); | |||
print(20, 1, "K"); | |||
} | |||
/* bootlog_driver() - driver load | |||
Called for very loaded driver. */ | |||
void bootlog_driver(const char *drv, const char *status) | |||
{ | |||
/* Positioning for the driver name */ | |||
static int x = 1, y = 4; | |||
if(y > 5) | |||
{ | |||
print(21, 4, "+"); | |||
return; | |||
} | |||
print(x, y, "%3s", drv); | |||
x += 4; | |||
if(x + y >= 22) y++; | |||
/* Positioning for the driver message */ | |||
if(!status) return; | |||
int len = strlen(status); | |||
static int mx = 1, my = 6; | |||
if(mx + len > 22) mx = 1, my++; | |||
if(my > 9) return; | |||
print(mx, my, "%s", status); | |||
mx += len + 1; | |||
dupdate(); | |||
} | |||
/* bootlog_driver_summary() - all drivers loaded */ | |||
void bootlog_driver_summary(void) | |||
{ | |||
int interrupts = 0; | |||
uint16_t ipr; | |||
print(21, 1, "D"); | |||
if(isSH3()) | |||
/* Count number of enabled interrupts */ | |||
if(isSH3()) for(int i = 0; i < 8; i++) | |||
{ | |||
print(1, 5, "ABCD"); | |||
print_hex( 6, 5, SH7705_INTC._.IPRA->word, 4); | |||
print_hex(10, 5, SH7705_INTC._.IPRB->word, 4); | |||
print_hex(14, 5, SH7705_INTC._.IPRC->word, 4); | |||
print_hex(18, 5, SH7705_INTC._.IPRD->word, 4); | |||
print(1, 6, "EFGH"); | |||
print_hex( 6, 6, SH7705_INTC._.IPRE->word, 4); | |||
print_hex(10, 6, SH7705_INTC._.IPRF->word, 4); | |||
print_hex(14, 6, SH7705_INTC._.IPRG->word, 4); | |||
print_hex(18, 6, SH7705_INTC._.IPRH->word, 4); | |||
ipr = *SH7705_INTC.IPRS[i]; | |||
while(ipr > 0) | |||
{ | |||
interrupts += (ipr & 0xf) != 0; | |||
ipr >>= 4; | |||
} | |||
} | |||
else | |||
else for(int i = 0; i < 12; i++) | |||
{ | |||
print(1, 5, "ACFG"); | |||
print_hex( 6, 5, SH7305_INTC._->IPRA.word, 4); | |||
print_hex(10, 5, SH7305_INTC._->IPRC.word, 4); | |||
print_hex(14, 5, SH7305_INTC._->IPRF.word, 4); | |||
print_hex(18, 5, SH7305_INTC._->IPRG.word, 4); | |||
print(1, 6, "HJKL"); | |||
print_hex( 6, 6, SH7305_INTC._->IPRH.word, 4); | |||
print_hex(10, 6, SH7305_INTC._->IPRJ.word, 4); | |||
print_hex(14, 6, SH7305_INTC._->IPRK.word, 4); | |||
print_hex(18, 6, SH7305_INTC._->IPRL.word, 4); | |||
ipr = SH7305_INTC.IPRS[2 * i]; | |||
while(ipr > 0) | |||
{ | |||
interrupts += (ipr & 0xf) != 0; | |||
ipr >>= 4; | |||
} | |||
} | |||
print(19, 5, "#%2d", interrupts); | |||
dupdate(); | |||
} |
@@ -3,7 +3,7 @@ | |||
//--- | |||
#include <gint/gint.h> | |||
#include <core/memory.h> | |||
#include <core/std.h> | |||
#include <gint/mpu.h> | |||
#include <gint/mpu/intc.h> | |||
@@ -7,11 +7,11 @@ | |||
#include <gint/defs/types.h> | |||
#include <gint/defs/util.h> | |||
/* This file is only useful on fx9860g machines because all fxcg50 are SH4 */ | |||
#ifdef FX9860G | |||
/* Holds information about the current MPU */ | |||
GBSS const platform_t gint_platform; | |||
/* Holds the name of the current MPU; initialized at startup by mpu_init() */ | |||
GBSS const mpu_t gint_mpu_id; | |||
/* This function is only used on fx9860g because all fxcg50 are SH4 */ | |||
#ifdef FX9860G | |||
/* mpu_detect() - detect the underlying MPU | |||
Many thanks to Simon Lothar for relevant documentation. | |||
@@ -21,6 +21,7 @@ GBSS const mpu_t gint_mpu_id; | |||
by testing writable bits in the Port L Control Register (PLCR). | |||
Returns the detected MPU type, falling back on mpu_unknown */ | |||
GSECTION(".pretext") | |||
static mpu_t mpu_detect(void) | |||
{ | |||
/* Processor Version Register */ | |||
@@ -53,9 +54,37 @@ static mpu_t mpu_detect(void) | |||
} | |||
/* mpu_init() - detect and save information about the underlying MPU */ | |||
void mpu_init(void) | |||
GSECTION(".pretext") | |||
void mpu_init(GUNUSED uint32_t stack) | |||
{ | |||
const_cast(gint_mpu_id, mpu_t) = mpu_detect(); | |||
const_cast(gint_platform.mpu, mpu_t) = mpu_detect(); | |||
/* Detect additional RAM */ | |||
volatile uint8_t *after_ram = (void *)0x88040000; | |||
volatile uint8_t *start_ram = (void *)0x88000000; | |||
uint8_t backup = *after_ram; | |||
*after_ram = ~backup; | |||
int ext = (*start_ram == backup); | |||
*after_ram = backup; | |||
const_cint(gint_platform.extended_ram) = ext; | |||
} | |||
#endif /* FX9860G */ | |||
#ifdef FXCG50 | |||
/* mpu_init() - detect and save information about the underlying MPU */ | |||
GSECTION(".pretext") | |||
void mpu_init(uint32_t stack) | |||
{ | |||
const_cast(gint_platform.mpu, mpu_t) = mpu_sh7305; | |||
/* Detect Prizm models */ | |||
int prizm = (stack < 0x8c160000); | |||
const_cint(gint_platform.prizm) = prizm; | |||
} | |||
#endif | |||
#endif /* FXCG50 */ |
@@ -4,7 +4,7 @@ | |||
#include <gint/gint.h> | |||
#include <gint/drivers.h> | |||
#include <core/memory.h> | |||
#include <core/std.h> | |||
#include <core/setup.h> | |||
#include <gint/mpu.h> | |||
#include <gint/mpu/intc.h> |
@@ -1,5 +1,5 @@ | |||
//--- | |||
// gint:core:start - Kernel initialization and C runtime | |||
// gint:core:start - Kernel initialisation and C runtime | |||
//-- | |||
#include <gint/defs/attributes.h> | |||
@@ -108,15 +108,15 @@ int start(int isappli, int optnum) | |||
redirecting interrupts and reimplementing drivers, so we can't rely | |||
too much on the system. Ladies and gentlemen, let's have fun! ;D */ | |||
/* For now, we rely on the system to map ROM pages. RAM is always | |||
/* For now, we use the system's memory mapper for ROM. RAM is always | |||
fully mapped, but we need to initialize it. We also need to do some | |||
hardware detection because old fx9860g models have an different | |||
hardware detection because old fx9860g models have a different | |||
processor with some incompatible features */ | |||
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */ | |||
#ifdef FX9860G | |||
mpu_init(); | |||
#endif | |||
uint32_t stack; | |||
__asm__("mov r15, %0" : "=r"(stack)); | |||
mpu_init(stack); | |||
/* Load data sections and wipe the bss section. This has to be done | |||
first for static and global variables to be initialized */ | |||
@@ -134,32 +134,20 @@ int start(int isappli, int optnum) | |||
: utlb_mapped_memory(&rom, &ram); | |||
bootlog_mapped(rom, ram); | |||
//--- | |||
/* Cancel add-in execution if not all pages are mapped | |||
TODO: Resort to better graphical display, although still fxlib since | |||
add-in is not mapped yet */ | |||
/* Cancel add-in execution if not all pages are mapped */ | |||
if(rom < (uint32_t)&srom) | |||
{ | |||
Bdisp_AllClr_VRAM(); | |||
PrintXY(0, 0, "Missing memory!", 0); | |||
print_hex(1, 2, rom, 8); | |||
PrintXY(0, 16, "<", 0); | |||
print_hex(1, 4, (uint32_t)&srom, 8); | |||
Bdisp_PutDisp_DD(); | |||
bootlog_unmapped(rom, (uint32_t)&srom); | |||
while(1); | |||
// delay(20); | |||
return 1; | |||
} | |||
//--- | |||
/* Install gint and switch VBR */ | |||
gint_install(); | |||
bootlog_kernel(); | |||
/* We are now running on our own in kernel mode. Since we have taken | |||
control of interrupts, pretty much any interaction with the sytem | |||
control of interrupts, pretty much any interaction with the system | |||
will break it. We'll limit our use of syscalls and do device driving | |||
ourselves. (Hopefully we can add cool features in the process.) */ | |||
@@ -173,7 +161,13 @@ int start(int isappli, int optnum) | |||
if(drv->ctx_save) drv->ctx_save(drv->sys_ctx); | |||
if(drv->init) drv->init(); | |||
#ifdef GINT_BOOT_LOG | |||
const char *status = drv->status ? drv->status() : NULL; | |||
bootlog_driver(drv->name, status); | |||
#endif | |||
} | |||
bootlog_driver_summary(); | |||
/* With gint fully initialized, we are ready to start the hosted user | |||
application. We have already loaded the RAM sections earlier; all |
@@ -0,0 +1,50 @@ | |||
/* | |||
** gint:core:syscalls - calls to CASIOWIN | |||
** | |||
** This files can be seen as a list of everywhere gint relies on the | |||
** underlying OS. Although I wish to make gint free-standing, there are | |||
** still a few hard dependencies, namely: | |||
** * MMU management, because doing it wrong might break the calculator. | |||
** * Dynamic allocation, because we can't trash the system heap. | |||
** * File system, because it's a mess and we might ruin the ROM. | |||
*/ | |||
/* Dynamic allocation */ | |||
.global ___malloc | |||
.global ___calloc | |||
.global ___free | |||
.global ___realloc | |||
/* OS version, for debugging purposes */ | |||
.global ___os_version | |||
.section ".pretext" | |||
#ifdef FX9860G | |||
/* OS version */ | |||
___os_version: | |||
mov.l syscall_table, r2 | |||
mov.l 1f, r0 | |||
jmp @r2 | |||
nop | |||
1: .long 0x02ee | |||
syscall_table: | |||
.long 0x80010070 | |||
#endif /* FX9860G */ | |||
#ifdef FXCG50 | |||
___os_version: | |||
mov.l syscall_table, r2 | |||
mov.l 1f, r0 | |||
jmp @r2 | |||
nop | |||
1: .long 0x1406 | |||
syscall_table: | |||
.long 0x80020070 | |||
#endif /* FXCG50 */ |
@@ -159,7 +159,7 @@ key_event_t waitevent(volatile int *timeout) | |||
} | |||
//--- | |||
// Driver initialization | |||
// Driver initialization and status | |||
//--- | |||
static int callback(GUNUSED volatile void *arg) | |||
@@ -192,6 +192,18 @@ static void unload(void) | |||
timer_stop(tid); | |||
} | |||
#ifdef GINT_BOOT_LOG | |||
/* keysc_status() - status string of the driver */ | |||
static const char *keysc_status(void) | |||
{ | |||
static char str[3] = "Sw"; | |||
if(isSH3()) str[0] = 's'; | |||
return str; | |||
} | |||
#endif /* GINT_BOOT_LOG */ | |||
//--- | |||
// Driver structure definition | |||
//--- | |||
@@ -199,6 +211,7 @@ static void unload(void) | |||
gint_driver_t drv_keysc = { | |||
.name = "KEYSC", | |||
.init = init, | |||
.status = GINT_DRIVER_STATUS(keysc_status), | |||
.unload = unload, | |||
.ctx_size = 0, | |||
.sys_ctx = NULL, |
@@ -6,7 +6,7 @@ | |||
void dpixel(int x, int y, color_t color) | |||
{ | |||
/* Sanity checks */ | |||
if((uint)x >= 64 || (uint)y >= 128) return; | |||
if((uint)x >= 128 || (uint)y >= 64) return; | |||
uint32_t *lword = vram + (y << 2) + (x >> 5); | |||
uint32_t mask = 1 << (~x & 31); |
@@ -1,5 +1,6 @@ | |||
#define GINT_NEED_VRAM | |||
#include <gint/defs/types.h> | |||
#include <gint/defs/attributes.h> | |||
#include <gint/display.h> | |||
#include "topti-asm.h" | |||
@@ -8,6 +9,7 @@ extern font_t gint_font5x6; | |||
font_t const * topti_font = &gint_font5x6; | |||
/* dfont() - set the default font for text rendering */ | |||
GSECTION(".pretext") | |||
void dfont(font_t const * font) | |||
{ | |||
topti_font = font ? font : &gint_font5x6; | |||
@@ -28,6 +30,7 @@ enum charset | |||
/* charset_size(): Number of elements in each character set | |||
@set Character set ID | |||
Returns the expected number of glyphs, -1 if charset ID is invalid. */ | |||
GSECTION(".pretext") | |||
int charset_size(enum charset set) | |||
{ | |||
int size[] = { 10, 26, 52, 62, 95, 128 }; | |||
@@ -39,6 +42,7 @@ int charset_size(enum charset set) | |||
-1 if [c] is not part of that set. | |||
@set Any character set | |||
@c Character to decode */ | |||
GSECTION(".pretext") | |||
int charset_decode(enum charset set, uint c) | |||
{ | |||
int x, y; | |||
@@ -76,6 +80,7 @@ int charset_decode(enum charset set, uint c) | |||
@glyph Glyph number obtained by charset_decode(), must be nonnegative. | |||
Returns the offset the this glyph's data in the font's data array. When | |||
using a proportional font, the size array is not heeded for. */ | |||
GSECTION(".pretext") | |||
int topti_offset(font_t const *f, uint glyph) | |||
{ | |||
/* Non-proportional fonts don't need an index */ | |||
@@ -110,6 +115,7 @@ int topti_offset(font_t const *f, uint glyph) | |||
0, call topti_draw() and reset the operators. If it's neative, call | |||
topti_draw() then do another pass of topti_split() to recover the missing | |||
information. */ | |||
GSECTION(".pretext") | |||
int topti_split(uint32_t const * glyph, int width, int height, int free, | |||
uint32_t *operators) | |||
{ | |||
@@ -174,6 +180,7 @@ int topti_split(uint32_t const * glyph, int width, int height, int free, | |||
@f Font | |||
@asm_fg Assembler function for text rendering | |||
@asm_bg Assembler function for background rendering */ | |||
GSECTION(".pretext") | |||
void topti_render(int x, int y, const char *str, font_t const *f, | |||
asm_text_t *asm_fg, asm_text_t *asm_bg) | |||
{ | |||
@@ -291,6 +298,7 @@ void dsize(const char *str, font_t const * f, int *w, int *h) | |||
} | |||
/* dtext() - display a string of text */ | |||
GSECTION(".pretext") | |||
void dtext(int x, int y, const char *str, color_t fg, color_t bg) | |||
{ | |||
if((uint)fg >= 8 || (uint)bg >= 8) return; |
@@ -0,0 +1,96 @@ | |||
//--- | |||
// gint:core:string - replicas of a few string functions | |||
//--- | |||
#include <gint/defs/types.h> | |||
#include <gint/defs/attributes.h> | |||
#include <stdarg.h> | |||
GWEAK size_t strlen(const char *str) | |||
{ | |||
int len = 0; | |||
while(str[len]) len++; | |||
return len; | |||
} | |||
/* vsprintf() - a trimmed-down version of the function | |||
This function supports formats '%%', '%nd', '%nx' and '%s' where 'n' is a | |||
1-digit size, and is mandatory. For '%d' and '%x', '0' is always set. | |||
Always outputs exactly the requested number of characters, even if it's not | |||
enough to completely print the value. | |||
Does whatever it wants if the format is invalid. This is really a basic | |||
function to format output without needing 18 kB of code. */ | |||
GWEAK void vsprintf(char *str, const char *format, va_list args) | |||
{ | |||
#define in() (c = *format++) | |||
const char *digits = "0123456789abcdef"; | |||
int c, len; | |||
while(in()) | |||
{ | |||
if(c != '%') | |||
{ | |||
*str++ = c; | |||
continue; | |||
} | |||
in(); | |||
/* Length indications (only one character, not '%12d') */ | |||
if(c >= '0' && c <= '9') len = c - '0', in(); | |||
else len = -1; | |||
if(c == '%') | |||
{ | |||
*str++ = '%'; | |||
} | |||
else if(c == 'd') | |||
{ | |||
int n = va_arg(args, int); | |||
if(n < 0) *str++ = '-', n = -n, len--; | |||
for(int i = len - 1; i >= 0; i--) | |||
{ | |||
int m = n / 10; | |||
str[i] = digits[n - 10 * m]; | |||
n = m; | |||
} | |||
str += len; | |||
} | |||
else if(c == 'x') | |||
{ | |||
uint32_t n = va_arg(args, uint32_t); | |||
for(int i = len - 1; i >= 0; i--) | |||
{ | |||
str[i] = digits[n & 0xf]; | |||
n >>= 4; | |||
} | |||
str += len; | |||
} | |||
else if(c == 'c') | |||
{ | |||
int c = va_arg(args, int); | |||
*str++ = c; | |||
} | |||
else if(c == 's') | |||
{ | |||
const char *s = va_arg(args, const char *); | |||
while(*s && len) *str++ = *s++, len--; | |||
} | |||
} | |||
*str = 0; | |||
#undef in | |||
#undef out | |||
} | |||
/* sprintf() */ | |||
GWEAK void sprintf(char *str, const char *format, ...) | |||
{ | |||
va_list args; | |||
va_start(args, format); | |||
vsprintf(str, format, args); | |||
va_end(args); | |||
} |
@@ -198,6 +198,21 @@ static void ctx_restore(void *buf) | |||
command(reg_counter, cnt); | |||
} | |||
//--- | |||
// Driver status string | |||
//--- | |||
#ifdef GINT_BOOT_LOG | |||
/* t6k11_status() - status string of the driver */ | |||
static const char *t6k11_status(void) | |||
{ | |||
/* TODO: t6k11: Detect backlight existence */ | |||
return "6K11-cB"; | |||
} | |||
#endif /* GINT_BOOT_LOG */ | |||
//--- | |||
// Driver structure definition | |||
//--- | |||
@@ -205,6 +220,7 @@ static void ctx_restore(void *buf) | |||
gint_driver_t drv_t6k11 = { | |||
.name = "T6K11", | |||
.init = NULL, | |||
.status = GINT_DRIVER_STATUS(t6k11_status), | |||
.ctx_size = sizeof(ctx_t), | |||
.sys_ctx = &sys_ctx, | |||
.ctx_save = ctx_save, |
@@ -367,6 +367,50 @@ static void init(void) | |||
} | |||
} | |||
//--- | |||
// Status function | |||
//--- | |||
#ifdef GINT_BOOT_LOG | |||
/* tmu_status() - status string of extra TMUs for the boot log | |||
The status string has a two-character code for each of the extra timers. | |||
The first character is a digit character describing a value between 0 and 7. | |||
* Bit 0 is set if TCOR=0xffffffff | |||
* Bit 1 is set if TCNT=0xffffffff | |||
* Bit 2 is set if TSTR=0 | |||
The second character indicates the status of interrupts. | |||
* "D" (Disabled) if UNIE=0, UNF=0 | |||
* "L" (Low) if UNIE=1, UNF=0 | |||
* "H" (High) if UNIE=1, UNF=1 | |||
* "!" (Error) if UNIE=0, UNF=1 | |||
So the normal status string would be made of "7D"'s. */ | |||
static const char *tmu_status(void) | |||
{ | |||
static char status[18] = "ETMU "; | |||
int j = 5; | |||
for(int i = 3; i < timer_count(); i++) | |||
{ | |||
tmu_extra_t *t = timers[i].tmu; | |||
int v1 = (!(t->TCOR + 1)) | |||
| (!(t->TCNT + 1) << 1) | |||
| (!(t->TSTR) << 2); | |||
int v2 = (t->TCR.UNF << 1) | (t->TCR.UNIE); | |||
status[j++] = '0' + v1; | |||
status[j++] = "DLH!"[v2]; | |||
} | |||
status[j] = 0; | |||
return status; | |||
} | |||
#endif /* GINT_BOOT_LOG */ | |||
//--- | |||
// Context system for this driver | |||
//--- | |||
@@ -444,6 +488,7 @@ gint_driver_t drv_tmu = { | |||
.name = "TMU", | |||
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3), | |||
.init = init, | |||
.status = GINT_DRIVER_STATUS(tmu_status), | |||
.ctx_size = sizeof(ctx_t), | |||
.sys_ctx = &sys_ctx, | |||
.ctx_save = ctx_save, |