mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 04:23:36 +01:00
general cleanup of the kernel
* Removed .pretext sections since the TLB is now entirely dynamic; left only .text.entry for the start symbol. * Reworked the main files of src/core to move the INTC to its own driver and let the kernel handle only VBR space and CPU (now: VBR & CPUOPM). * Moved src/core/gint.c to src/core/kernel.c and centralized all driver loops that save or restore context for more robustness. This leaves the callbacks of cpu_setVBR() (formerly gint_setvbr()) pretty short. * Coalesced gint_switch_out() and gint_switch_in() into a single function callback for cpu_setVBR(). * Added an abstraction of interrupt signals as an enumerated value so that drivers no longer hardcode the IPR and IMR numbers and bits, sometimes even with isSH3() due to differences in the SH7705. * Changed the interrupt blocking method in cpu_setVBR() from SR.BL=1 to SR.IMASK=15 so that TLB misses are still handled. This removes the need for callback functions to be GMAPPED. * Moved gint_osmenu() and its utilities to a new file src/core/osmenu.c.
This commit is contained in:
parent
b7de559b78
commit
1c7b1350b4
22 changed files with 652 additions and 551 deletions
23
fx9860g.ld
23
fx9860g.ld
|
@ -42,23 +42,22 @@ SECTIONS
|
|||
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
|
||||
|
||||
/* Machine code going to ROM:
|
||||
- Initialization sections (.pretext.entry and .pretext)
|
||||
- Entry function (.text.entry)
|
||||
- Compiler-provided constructors (.ctors) and destructors (.dtors)
|
||||
- All text from .text and .text.* (including user code)
|
||||
- Code sections from fxlib, named "C" and "P" */
|
||||
|
||||
.pretext : {
|
||||
*(.pretext.entry)
|
||||
*(.pretext)
|
||||
|
||||
_btors = . ;
|
||||
*(.ctors .ctors.*)
|
||||
_mtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
_etors = . ;
|
||||
} > rom
|
||||
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
|
||||
_bctors = . ;
|
||||
*(.ctors .ctors.*)
|
||||
_ectors = . ;
|
||||
|
||||
_bdtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
_edtors = . ;
|
||||
|
||||
_gint_exch_start = . ;
|
||||
*(.gint.exch)
|
||||
_gint_exch_size = ABSOLUTE(. - _gint_exch_start);
|
||||
|
|
13
fxcg50.ld
13
fxcg50.ld
|
@ -43,18 +43,19 @@ SECTIONS
|
|||
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
|
||||
|
||||
/* Machine code going to ROM:
|
||||
- Initialization sections (.pretext.entry and .pretext)
|
||||
- Entry function (.text.entry)
|
||||
- Compiler-provided constructors (.ctors) and destructors (.dtors)
|
||||
- All text from .text and .text.* (including user code) */
|
||||
.text : {
|
||||
*(.pretext.entry)
|
||||
*(.pretext)
|
||||
*(.text.entry)
|
||||
|
||||
_btors = . ;
|
||||
_bctors = . ;
|
||||
*(.ctors .ctors.*)
|
||||
_mtors = . ;
|
||||
_ectors = . ;
|
||||
|
||||
_bdtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
_etors = . ;
|
||||
_edtors = . ;
|
||||
|
||||
_gint_exch_start = . ;
|
||||
*(.gint.exch)
|
||||
|
|
|
@ -10,85 +10,40 @@
|
|||
/* GINT_VERSION - the library version number
|
||||
|
||||
gint is versioned from its repository commits on the master branch. The
|
||||
GINT_VERSION integer contains the short commit hash.
|
||||
GINT_VERSION integer contains the short commit hash with 7 digits.
|
||||
|
||||
For instance, 0x03f7c0a0 means commit 3f7c0a0. */
|
||||
extern char GINT_VERSION;
|
||||
#define GINT_VERSION ((uint32_t)&GINT_VERSION)
|
||||
|
||||
//---
|
||||
// Library management
|
||||
//---
|
||||
|
||||
/* gint_install() - install and start gint
|
||||
This function installs event handlers, masks interrupts and switches VBR.
|
||||
Unless you are doing experimental runtime switching and you know how this
|
||||
function is implemented, you should not call it. */
|
||||
void gint_install(void);
|
||||
|
||||
/* gint_unload() - unload gint and give back control to the system
|
||||
This function restores the runtime environment saved by gint_install(). It
|
||||
is only called when the add-in terminates. To temporarily leave gint during
|
||||
execution, use gint_switch(). When possible, use syscalls without leaving
|
||||
gint for better performance. */
|
||||
void gint_unload(void);
|
||||
|
||||
/* gint_switch() - temporarily switch out of gint
|
||||
/* gint_switch(): Switch out of gint to execute a function
|
||||
|
||||
This function can be used to leave gint, restore the system's driver
|
||||
context, and execute code there before returning to gint. By doing this one
|
||||
can effectively interleave gint with the standard OS execution. The
|
||||
limitations are quite extreme though, so unless you know precisely why
|
||||
you're calling this function, you're likely doing it wrong.
|
||||
limitations are quite extreme though, so unless you know precisely what
|
||||
you're doing that requires getting out of gint (eg. BFile), be careful.
|
||||
|
||||
This switch is used to get back to the main menu and to answer TLB misses.
|
||||
To go back to the menu, use getkey(), or getkey_opt() with the GETKEY_MENU
|
||||
flag set, or call gint_osmenu() for an immediate return.
|
||||
This main uses for this switch are going back to the main menu and using
|
||||
BFile function. You can go back to the main menu easily by calling getkey()
|
||||
(or getkey_opt() with the GETKEY_MENU flag set) and pressing the MENU key,
|
||||
or by calling gint_osmenu() below which uses this switch.
|
||||
|
||||
@function Function to call in OS mode */
|
||||
void gint_switch(void (*function)(void));
|
||||
|
||||
/* gint_osmenu() - switch out of gint and call the calculator's main menu
|
||||
/* gint_osmenu(): Call the calculator's main menu
|
||||
|
||||
This function safely invokes the calculator's main menu by unloading gint.
|
||||
This function safely invokes the calculator's main menu with gint_switch().
|
||||
If the user selects the gint application again in the menu, this function
|
||||
reloads gint and returns. Otherwise, the add-in is fully unloaded by the
|
||||
system and the application terminates.
|
||||
|
||||
This function is typically called when the [MENU] key is pressed during a
|
||||
getkey() call. */
|
||||
call to getkey(), but can also be called manually. */
|
||||
void gint_osmenu(void);
|
||||
|
||||
//---
|
||||
// Public functions
|
||||
//---
|
||||
|
||||
/* gint_intlevel() - configure the level of interrupts
|
||||
|
||||
This function changes the interrupt level of the requested interrupt. Make
|
||||
sure you are aware of interrupt assignments to avoid breaking other code.
|
||||
This function is mainly used by drivers to enable the interrupts that they
|
||||
support.
|
||||
|
||||
The first parameter 'intid' identifies an interrupt by its position in the
|
||||
sequence:
|
||||
|
||||
IPRA & 0xf000 ; IPRA & 0x0f00 .. IPRA & 0x000f ; IPRB & 0xf000 ..
|
||||
|
||||
For instance ID 7 refers to the low nibble of IPRB. These IDs and the range
|
||||
for which there are valid is heavily platform-dependent and any call to this
|
||||
function should be wrapped inside an MPU type check. This function will
|
||||
crash if the provided interrupt ID is invalid.
|
||||
|
||||
The interrupt level should be in the range 0 (disabled) .. 15 (highest
|
||||
priority).
|
||||
|
||||
@intid Interrupt ID of the targeted interrupt
|
||||
@level Requested interrupt level
|
||||
Returns the interrupt level that was assigned before the call. */
|
||||
int gint_intlevel(int intid, int level);
|
||||
|
||||
/* gint_inthandler() - configure interrupt handlers
|
||||
/* gint_inthandler(): Install interrupt handlers
|
||||
|
||||
This function installs (copies) interrupt handlers in the VBR space of the
|
||||
application. Each handler is a 32-byte block aligned on a 32-byte boundary.
|
||||
|
@ -100,24 +55,27 @@ int gint_intlevel(int intid, int level);
|
|||
The assembler program will assume that consecutive blocks in the source code
|
||||
will be consecutive in memory, which is not always true. Avoiding cross-
|
||||
references is a practical rule to avoid problems. (gint breaks this rule
|
||||
very often but does it carefully... I guess?)
|
||||
quite often but does it safely.)
|
||||
|
||||
This function allows anyone to replace any interrupt handler so make sure
|
||||
you're not interfering with usual interrupt assignments.
|
||||
you're not interfering with interrupt assignments from gint or a library.
|
||||
|
||||
The first parameter event_code represents the event code associated with the
|
||||
interrupt. If it's not a multiple of 0x20 then you're doing something wrong.
|
||||
The codes are normally platform-dependent, but gint always uses SH7305
|
||||
codes: SH3 platforms have a translation table. See the documentation for a
|
||||
list of event codes and their associated interrupts.
|
||||
list of event codes and their associated interrupts. Also check the
|
||||
translation table in gint's source in src/core/inth.S for SH3 support.
|
||||
|
||||
The handler function must be an interrupt handler: it must not raise
|
||||
exceptions, must end with 'rts', and it will use the kernel register bank.
|
||||
You read that right, it must end with rts because gint's main handler saves
|
||||
registers besides the automated r0..r7. Do *not* use 'rte' in the handler.
|
||||
For convenience I allow any block size to be loaded as an interrupt handler,
|
||||
but it should really be a multiple of 0x20 bytes and not override other
|
||||
handlers. If it's not written in assembler, then you're likely doing
|
||||
The handler function is run in the kernel register bank with interrupts
|
||||
disabled and must end with 'rts' (not 'rte') as the main interrupt handler
|
||||
saves some registers for you. By default, user bank registers are not saved
|
||||
except for gbr/mach/macl; if you want to call back to arbitrary code safely,
|
||||
use gint_inth_callback() in your handler.
|
||||
|
||||
For convenience gint allows any block size to be loaded as an interrupt
|
||||
handler, but it should really be a multiple of 0x20 bytes and not override
|
||||
other handlers. If it's not written in assembler, then you're likely doing
|
||||
something wrong, especially with __attribute__((interrupt_handler)) which
|
||||
uses rte.
|
||||
|
||||
|
@ -129,7 +87,23 @@ int gint_intlevel(int intid, int level);
|
|||
@event_code Identifier of the interrupt block
|
||||
@handler Address of handler function
|
||||
@size How many bytes to copy
|
||||
Returns the VBR address where the handlers was installed. */
|
||||
Returns the VBR address where the handler was installed. */
|
||||
void *gint_inthandler(int event_code, void const *handler, size_t size);
|
||||
|
||||
/* gint_inth_callback(): Call back arbitrary code from an interrupt handler
|
||||
|
||||
Calls the specified function with the given argument after saving the user
|
||||
context, enabling interrupts and going to user bank. This function is used
|
||||
to call user code from interrupt handlers, typically from timer or RTC
|
||||
callbacks.
|
||||
|
||||
It is not safe to call from C code in any capacity and is mentioned here
|
||||
only for documentation purposes; you should really only call this from
|
||||
an interrupt handler's assembler code.
|
||||
|
||||
@callback Callback function, may take no argument in practice
|
||||
@arg Argument
|
||||
Returns the return value of the callback. */
|
||||
int gint_inth_callback(int (*function)(void *arg), void *arg);
|
||||
|
||||
#endif /* GINT_GINT */
|
||||
|
|
63
include/gint/intc.h
Normal file
63
include/gint/intc.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
//---
|
||||
// gint:intc - Interrupt Controller
|
||||
//---
|
||||
|
||||
#ifndef GINT_INTC
|
||||
#define GINT_INTC
|
||||
|
||||
//---
|
||||
// Interrupt names
|
||||
//---
|
||||
|
||||
/* Because of SH3/SH4 differences and the different ways interrupts are mapped
|
||||
to bits of IPR and IMR registers, gint defines the following names for
|
||||
interrupt signals, and their corresponding IPR and IMR bits are defined in
|
||||
the INTC driver. */
|
||||
enum {
|
||||
/* Timer Unit [TMU] */
|
||||
INTC_TMU_TUNI0,
|
||||
INTC_TMU_TUNI1,
|
||||
INTC_TMU_TUNI2,
|
||||
/* Extra Timer Unit [TMU]; SH7305-only except for ETMU_TUNI0 */
|
||||
INTC_ETMU_TUNI0,
|
||||
INTC_ETMU_TUNI1,
|
||||
INTC_ETMU_TUNI2,
|
||||
INTC_ETMU_TUNI3,
|
||||
INTC_ETMU_TUNI4,
|
||||
INTC_ETMU_TUNI5,
|
||||
/* DMA Controller [DMA0]; a single IPR is used for DEI0..3, and another
|
||||
IPR is used for DEI4..5 and DADERR together */
|
||||
INTC_DMA_DEI0,
|
||||
INTC_DMA_DEI1,
|
||||
INTC_DMA_DEI2,
|
||||
INTC_DMA_DEI3,
|
||||
INTC_DMA_DEI4,
|
||||
INTC_DMA_DEI5,
|
||||
INTC_DMA_DADERR,
|
||||
/* Real-Time Clock [RTC]; a single IPR covers all 3 interrupts */
|
||||
INTC_RTC_ATI,
|
||||
INTC_RTC_PRI,
|
||||
INTC_RTC_CUI,
|
||||
};
|
||||
|
||||
//---
|
||||
// Interrupt control functions
|
||||
//---
|
||||
|
||||
/* intc_priority(): Configure the level of interrupts
|
||||
|
||||
This function changes the interrupt level of the requested interrupt. Make
|
||||
sure you are aware of interrupt assignments to avoid breaking other code.
|
||||
This function is mainly used by drivers to enable the interrupts that they
|
||||
support.
|
||||
|
||||
The interrupt level should be in the range 0 (disabled) .. 15 (highest
|
||||
priority). On SH7305, if the level is not 0, this function also clears the
|
||||
associated interrupt mask.
|
||||
|
||||
@intname Name of the targeted interrupt, from the enumeration above
|
||||
@level Requested interrupt level
|
||||
Returns the interrupt level that was assigned before the call. */
|
||||
int intc_priority(int intname, int level);
|
||||
|
||||
#endif /* GINT_INTC */
|
|
@ -105,7 +105,7 @@ typedef struct
|
|||
/* All interrupt priority registers */
|
||||
union {
|
||||
sh7705_intc_ipc_t _;
|
||||
volatile uint16_t *IPRS[8];
|
||||
volatile uint16_t *IPR[8];
|
||||
} GPACKED(4);
|
||||
|
||||
/* Control registers */
|
||||
|
@ -276,7 +276,7 @@ typedef struct
|
|||
/* Interrupt priority registers */
|
||||
union {
|
||||
sh7305_intc_ipc_t *_;
|
||||
volatile uint16_t *IPRS;
|
||||
volatile uint16_t *IPR;
|
||||
};
|
||||
|
||||
/* Interrupt mask & mask clear registers */
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
@vbr New VBR address
|
||||
@conf_intc Configuration function
|
||||
@arg Additional argument for conf_intc
|
||||
Returns the previous VBR address. */
|
||||
uint32_t cpu_setVBR(uint32_t vbr, void (*conf_intc)(void));
|
||||
uint32_t cpu_setVBR(uint32_t vbr, void (*conf_intc)(int arg), int arg);
|
||||
|
||||
/* cpu_setCPUOPM(): Change the CPU Operation Mode register
|
||||
|
||||
|
|
|
@ -10,38 +10,32 @@
|
|||
|
||||
/* cpu_setVBR(): Change VBR address */
|
||||
_cpu_setVBR:
|
||||
mov.l r9, @-r15
|
||||
mov.l r8, @-r15
|
||||
sts.l pr, @-r15
|
||||
stc.l sr, @-r15
|
||||
|
||||
mov #1, r8
|
||||
mov #28, r0
|
||||
shld r0, r8
|
||||
|
||||
/* Block all interrupts */
|
||||
/* Block all interrupts by setting IMASK=15 */
|
||||
mov #0xf, r1
|
||||
shll2 r1
|
||||
shll2 r1
|
||||
stc sr, r0
|
||||
or r8, r0
|
||||
or r1, r0
|
||||
ldc r0, sr
|
||||
|
||||
/* Set the new VBR address */
|
||||
stc vbr, r9
|
||||
stc vbr, r8
|
||||
ldc r4, vbr
|
||||
|
||||
/* Call the configuration function */
|
||||
jsr @r5
|
||||
nop
|
||||
|
||||
/* Enable interrupts again */
|
||||
stc sr, r0
|
||||
not r8, r8
|
||||
and r8, r0
|
||||
ldc r0, sr
|
||||
mov r6, r4
|
||||
|
||||
/* Enable interrupts again by restoring the status register */
|
||||
ldc.l @r15+, sr
|
||||
lds.l @r15+, pr
|
||||
mov.l @r15+, r8
|
||||
mov r9, r0
|
||||
mov r8, r0
|
||||
rts
|
||||
mov.l @r15+, r9
|
||||
mov.l @r15+, r8
|
||||
|
||||
.text
|
||||
|
||||
|
|
23
src/core/drivers.h
Normal file
23
src/core/drivers.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
//---
|
||||
// core:drivers - Driver utilities for the kernel
|
||||
//
|
||||
// These are internal definitions; for general driver definitions, see
|
||||
// <gint/drivers.h> instead.
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_DRIVERS
|
||||
#define GINT_CORE_DRIVERS
|
||||
|
||||
#include <gint/drivers.h>
|
||||
|
||||
/* Linker script symbols for drivers by increasing levels of priority */
|
||||
extern gint_driver_t bdrv, edrv;
|
||||
|
||||
/* Iterate on drivers in increasing level of priority */
|
||||
#define driver_asc(var) \
|
||||
(gint_driver_t *var = &bdrv; var < &edrv; var++)
|
||||
/* Iterate on drivers in decreasing level of priority */
|
||||
#define driver_dsc(var) \
|
||||
(gint_driver_t *var = &edrv; (--var) >= &bdrv;)
|
||||
|
||||
#endif /* GINT_CORE_DRIVERS */
|
|
@ -1,62 +0,0 @@
|
|||
//---
|
||||
// gint:core:gint - Library functions
|
||||
//---
|
||||
|
||||
#include <gint/gint.h>
|
||||
#include <gint/std/string.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
#include "vbr.h"
|
||||
|
||||
/* Interrupt controllers */
|
||||
|
||||
GDATA3 sh7705_intc_t SH7705_INTC = {
|
||||
.IPRS = {
|
||||
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||
(void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004,
|
||||
},
|
||||
.ICR1 = (void *)0xa4000010,
|
||||
};
|
||||
|
||||
GDATA sh7305_intc_t SH7305_INTC = {
|
||||
.IPRS = (void *)0xa4080000,
|
||||
.MSK = (void *)0xa4080080,
|
||||
.MSKCLR = (void *)0xa40800c0,
|
||||
.USERIMASK = (void *)0xa4700000,
|
||||
};
|
||||
|
||||
//---
|
||||
// Library functions
|
||||
//---
|
||||
|
||||
/* gint_intlevel() - configure the level of interrupts */
|
||||
int gint_intlevel(int intid, int level)
|
||||
{
|
||||
int shift = (~intid & 0x3) << 2;
|
||||
volatile uint16_t *ipr;
|
||||
level &= 0xf;
|
||||
|
||||
ipr = isSH3()
|
||||
? SH7705_INTC.IPRS[intid >> 2] /* SH3-based */
|
||||
: &SH7305_INTC.IPRS[2 * (intid >> 2)]; /* SH4-based */
|
||||
|
||||
int oldlevel = (*ipr >> shift) & 0xf;
|
||||
*ipr = (*ipr & ~(0xf << shift)) | (level << shift);
|
||||
|
||||
return oldlevel;
|
||||
}
|
||||
|
||||
/* gint_inthandler() - configure interrupt handlers */
|
||||
void *gint_inthandler(int event_code, const void *handler, size_t size)
|
||||
{
|
||||
/* Normalize the event code */
|
||||
event_code -= 0x400;
|
||||
event_code &= ~0x1f;
|
||||
|
||||
/* Prevent overriding the entry gate */
|
||||
if(event_code < 0) return NULL;
|
||||
|
||||
void *dest = (void *)gint_vbr + event_code + 0x640;
|
||||
return memcpy(dest, handler, size);
|
||||
}
|
|
@ -26,7 +26,6 @@ GBSS uint32_t gint[HW_KEYS];
|
|||
by testing writable bits in the Port L Control Register (PLCR).
|
||||
|
||||
Returns the detected MPU type, falling back on mpu_unknown */
|
||||
GSECTION(".pretext")
|
||||
static int mpu_detect(void)
|
||||
{
|
||||
#define PLCR SH7705_PFC.PLCR
|
||||
|
@ -54,7 +53,6 @@ static int mpu_detect(void)
|
|||
}
|
||||
|
||||
/* hw_detect(): Basic hardware detection */
|
||||
GSECTION(".pretext")
|
||||
void hw_detect(void)
|
||||
{
|
||||
gint[HWMPU] = mpu_detect();
|
||||
|
@ -109,7 +107,6 @@ void hw_detect(void)
|
|||
#ifdef FXCG50
|
||||
|
||||
/* hw_detect(): Basic hardware detection */
|
||||
GSECTION(".pretext")
|
||||
void hw_detect(void)
|
||||
{
|
||||
gint[HWMPU] = HWMPU_SH7305;
|
||||
|
|
167
src/core/kernel.c
Normal file
167
src/core/kernel.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
//---
|
||||
// gint:core:kernel - Installing and unloading the library
|
||||
//---
|
||||
|
||||
#include <gint/gint.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/std/string.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "vbr.h"
|
||||
#include "drivers.h"
|
||||
#include "kernel.h"
|
||||
|
||||
static void kinit_cpu(void);
|
||||
|
||||
//---
|
||||
// Context for the CPU and registers not directly managed by a driver
|
||||
//---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t VBR;
|
||||
uint32_t CPUOPM;
|
||||
} ctx_t;
|
||||
|
||||
/* System context and gint context for the CPU and VBR */
|
||||
GBSS static ctx_t sys_ctx, gint_ctx;
|
||||
|
||||
static void ctx_save(ctx_t *ctx)
|
||||
{
|
||||
if(isSH4()) ctx->CPUOPM = cpu_getCPUOPM();
|
||||
}
|
||||
static void ctx_restore(ctx_t *ctx)
|
||||
{
|
||||
if(isSH4()) cpu_setCPUOPM(ctx->CPUOPM);
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver control
|
||||
//---
|
||||
|
||||
static void drivers_wait(void)
|
||||
{
|
||||
for driver_asc(d)
|
||||
{
|
||||
if(d->wait) d->wait();
|
||||
}
|
||||
}
|
||||
|
||||
static void drivers_save_and_init(GUNUSED int zero)
|
||||
{
|
||||
/* Initialize the CPU, which is done here instead of in a driver */
|
||||
ctx_save(&sys_ctx);
|
||||
kinit_cpu();
|
||||
|
||||
for driver_asc(d)
|
||||
{
|
||||
if(d->ctx_save) d->ctx_save(d->sys_ctx);
|
||||
|
||||
if(isSH3() && d->driver_sh3) d->driver_sh3();
|
||||
if(d->init) d->init();
|
||||
}
|
||||
}
|
||||
|
||||
static void drivers_restore(int who)
|
||||
{
|
||||
for driver_dsc(d)
|
||||
{
|
||||
if(d->ctx_restore) d->ctx_restore(who?d->gint_ctx:d->sys_ctx);
|
||||
}
|
||||
|
||||
ctx_restore(who ? &gint_ctx : &sys_ctx);
|
||||
}
|
||||
|
||||
static void drivers_switch(int who)
|
||||
{
|
||||
/* Save all drivers in reverse order */
|
||||
for driver_dsc(d)
|
||||
{
|
||||
if(!d->ctx_save || !d->ctx_restore) continue;
|
||||
d->ctx_save(who ? d->gint_ctx : d->sys_ctx);
|
||||
}
|
||||
ctx_save(who ? &gint_ctx : &sys_ctx);
|
||||
|
||||
/* Restore the other context */
|
||||
ctx_restore(who ? &sys_ctx : &gint_ctx);
|
||||
for driver_asc(d)
|
||||
{
|
||||
if(!d->ctx_save || !d->ctx_restore) continue;
|
||||
d->ctx_restore(who ? d->sys_ctx : d->gint_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Initialization and unloading
|
||||
//---
|
||||
|
||||
static void kinit_cpu(void)
|
||||
{
|
||||
cpu_setCPUOPM(cpu_getCPUOPM() | 0x00000008);
|
||||
}
|
||||
|
||||
/* kinit(): Install and start gint */
|
||||
void kinit(void)
|
||||
{
|
||||
/* VBR address, provided by the linker script */
|
||||
#ifdef FX9860G
|
||||
gint_ctx.VBR = (uint32_t)&gint_vbr_fx9860g;
|
||||
#endif
|
||||
#ifdef FXCG50
|
||||
gint_ctx.VBR = (gint[HWCALC] == HWCALC_FXCG50)
|
||||
? (uint32_t)&gint_vbr_fxcg50
|
||||
: (uint32_t)&gint_vbr_fxcg20;
|
||||
#endif
|
||||
|
||||
/* Event handler entry points */
|
||||
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;
|
||||
|
||||
/* Load the event handler entry points into memory */
|
||||
void *vbr = (void *)gint_ctx.VBR;
|
||||
memcpy(vbr + 0x100, gint_exch, exch_size);
|
||||
memcpy(vbr + 0x400, gint_tlbh, tlbh_size);
|
||||
memcpy(vbr + 0x600, inth_entry, 64);
|
||||
|
||||
/* Take control of the VBR and roll! */
|
||||
drivers_wait();
|
||||
sys_ctx.VBR = cpu_setVBR(gint_ctx.VBR, drivers_save_and_init, 0);
|
||||
}
|
||||
|
||||
/* gint_inthandler(): Install interrupt handlers */
|
||||
void *gint_inthandler(int event_code, const void *handler, size_t size)
|
||||
{
|
||||
/* Normalize the event code */
|
||||
if(event_code < 0x400) return NULL;
|
||||
event_code -= 0x400;
|
||||
event_code &= ~0x1f;
|
||||
|
||||
void *dest = (void *)gint_ctx.VBR + event_code + 0x640;
|
||||
return memcpy(dest, handler, size);
|
||||
}
|
||||
|
||||
/* gint_switch(): Temporarily switch out of gint */
|
||||
void gint_switch(void (*function)(void))
|
||||
{
|
||||
/* Switch from gint to the OS after a short wait */
|
||||
drivers_wait();
|
||||
cpu_setVBR(sys_ctx.VBR, drivers_switch, 1);
|
||||
|
||||
if(function) function();
|
||||
|
||||
/* Then switch back to gint once the OS finishes working */
|
||||
drivers_wait();
|
||||
cpu_setVBR(gint_ctx.VBR, drivers_switch, 0);
|
||||
}
|
||||
|
||||
/* kquit(): Quit gint and give back control to the system */
|
||||
void kquit(void)
|
||||
{
|
||||
/* Wait for hardware tasks then restore all of the drivers' state and
|
||||
return the VBR space to the OS */
|
||||
drivers_wait();
|
||||
cpu_setVBR(sys_ctx.VBR, drivers_restore, 0);
|
||||
}
|
14
src/core/kernel.h
Normal file
14
src/core/kernel.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
//---
|
||||
// core:kernel - Kernel functions
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_KERNEL
|
||||
#define GINT_CORE_KERNEL
|
||||
|
||||
/* kinit(): Install and start gint */
|
||||
void kinit(void);
|
||||
|
||||
/* kquit(): Quit gint and give back control to the system */
|
||||
void kquit(void);
|
||||
|
||||
#endif /* GINT_CORE_KERNEL */
|
73
src/core/osmenu.c
Normal file
73
src/core/osmenu.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include <gint/gint.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/std/string.h>
|
||||
|
||||
int __Timer_Install(int id, void (*handler)(void), int delay);
|
||||
int __Timer_Start(int id);
|
||||
int __Timer_Stop(int id);
|
||||
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 *__GetVRAMAddress(void);
|
||||
void __ConfigureStatusArea(int mode);
|
||||
|
||||
static int __osmenu_id;
|
||||
|
||||
static void __osmenu_handler(void)
|
||||
{
|
||||
__PutKeyCode(0x04, 0x09, 0);
|
||||
|
||||
__Timer_Stop(__osmenu_id);
|
||||
__Timer_Deinstall(__osmenu_id);
|
||||
}
|
||||
|
||||
static void __osmenu(void)
|
||||
{
|
||||
__ClearKeyBuffer();
|
||||
|
||||
#ifdef FX9860G
|
||||
memcpy(__GetVRAMAddress(), gint_vram, 1024);
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
/* Unfortunately ineffective (main menu probably reenables it)
|
||||
__ConfigureStatusArea(3); */
|
||||
|
||||
/* TODO: Improve copied VRAM behavior in gint_osmenu() on fxcg50 */
|
||||
uint16_t *vram1, *vram2;
|
||||
dgetvram(&vram1, &vram2);
|
||||
|
||||
uint16_t *dst = __GetVRAMAddress();
|
||||
uint16_t *src = (gint_vram == vram1) ? vram2 + 6 : vram1 + 6;
|
||||
|
||||
for(int y = 0; y < 216; y++, dst+=384, src+=396)
|
||||
for(int x = 0; x < 384; x++)
|
||||
{
|
||||
dst[x] = src[x];
|
||||
}
|
||||
#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);
|
||||
|
||||
int column, row;
|
||||
unsigned short keycode;
|
||||
__GetKeyWait(&column, &row,
|
||||
0 /* KEYWAIT_HALTON_TIMEROFF */,
|
||||
1 /* Delay in seconds */,
|
||||
0 /* Enable return to main menu */,
|
||||
&keycode);
|
||||
}
|
||||
|
||||
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
|
||||
void gint_osmenu(void)
|
||||
{
|
||||
gint_switch(__osmenu);
|
||||
}
|
294
src/core/setup.c
294
src/core/setup.c
|
@ -1,294 +0,0 @@
|
|||
//---
|
||||
// gint:core:setup - Installing and unloading the library
|
||||
//---
|
||||
|
||||
#include <gint/gint.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/std/string.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/display.h>
|
||||
#include "cpu.h"
|
||||
#include "vbr.h"
|
||||
|
||||
/* VBR addresses for gint and the system */
|
||||
GBSS uint32_t gint_vbr;
|
||||
GBSS static uint32_t system_vbr;
|
||||
/* Driver table */
|
||||
extern gint_driver_t bdrv, edrv;
|
||||
|
||||
//---
|
||||
// Context system for gint's core
|
||||
//---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t iprs[12];
|
||||
uint8_t masks[13];
|
||||
uint32_t CPUOPM;
|
||||
|
||||
} GPACKED(2) gint_core_ctx;
|
||||
|
||||
/* gint_ctx_save() - save interrupt controller configuration
|
||||
@arg ctx gint core context object */
|
||||
GMAPPED static void gint_ctx_save(gint_core_ctx *ctx)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
ctx->iprs[i] = *(SH7705_INTC.IPRS[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++)
|
||||
ctx->iprs[i] = SH7305_INTC.IPRS[2 * i];
|
||||
|
||||
uint8_t *IMR = (void *)SH7305_INTC.MSK;
|
||||
for(int i = 0; i < 13; i++, IMR += 4)
|
||||
ctx->masks[i] = *IMR;
|
||||
|
||||
ctx->CPUOPM = cpu_getCPUOPM();
|
||||
}
|
||||
}
|
||||
|
||||
/* gint_ctx_restore() - restore interrupt controller configuration
|
||||
@arg ctx gint core context object */
|
||||
GMAPPED static void gint_ctx_restore(gint_core_ctx *ctx)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
*(SH7705_INTC.IPRS[i]) = ctx->iprs[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++)
|
||||
SH7305_INTC.IPRS[2 * i] = ctx->iprs[i];
|
||||
|
||||
/* Setting masks it a bit more involved than reading them */
|
||||
uint8_t *IMCR = (void *)SH7305_INTC.MSKCLR;
|
||||
uint8_t *IMR = (void *)SH7305_INTC.MSK;
|
||||
for(int i = 0; i < 13; i++, IMR += 4, IMCR += 4)
|
||||
{
|
||||
*IMCR = 0xff;
|
||||
*IMR = ctx->masks[i];
|
||||
}
|
||||
|
||||
cpu_setCPUOPM(ctx->CPUOPM);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Initialization and unloading
|
||||
//---
|
||||
|
||||
/* System context */
|
||||
GBSS static gint_core_ctx sys_ctx;
|
||||
|
||||
/* lock() - lock interrupts to avoid receiving unsupported signals */
|
||||
GMAPPED static void lock(void)
|
||||
{
|
||||
/* Just disable everything, drivers will enable what they support */
|
||||
if(isSH3()) for(int i = 0; i < 8; i++)
|
||||
*(SH7705_INTC.IPRS[i]) = 0x0000;
|
||||
else for(int i = 0; i < 12; i++)
|
||||
SH7305_INTC.IPRS[2 * i] = 0x0000;
|
||||
|
||||
cpu_setCPUOPM(cpu_getCPUOPM() | 0x00000008);
|
||||
}
|
||||
|
||||
/* gint_install() - install and start gint */
|
||||
void gint_install(void)
|
||||
{
|
||||
/* VBR address, provided by the linker script */
|
||||
#ifdef FX9860G
|
||||
gint_vbr = (uint32_t)&gint_vbr_fx9860g;
|
||||
#endif
|
||||
#ifdef FXCG50
|
||||
gint_vbr = (gint[HWCALC] == HWCALC_FXCG50)
|
||||
? (uint32_t)&gint_vbr_fxcg50
|
||||
: (uint32_t)&gint_vbr_fxcg20;
|
||||
#endif
|
||||
|
||||
/* Event handler entry points */
|
||||
void *inth_entry = isSH3() ? gint_inth_7705 : gint_inth_7305;
|
||||
|
||||
/* Size of the exception and TLB handlers */
|
||||
uint32_t exch_size = (uint32_t)&gint_exch_size;
|
||||
uint32_t tlbh_size = (uint32_t)&gint_tlbh_size;
|
||||
|
||||
/* First save the hardware configuration. This step is crucial because
|
||||
we don't want the system to find out about us directly manipulating
|
||||
the peripheral modules */
|
||||
gint_ctx_save(&sys_ctx);
|
||||
|
||||
/* Load the event handler entry points into memory */
|
||||
void *vbr = (void *)gint_vbr;
|
||||
memcpy(vbr + 0x100, gint_exch, exch_size);
|
||||
memcpy(vbr + 0x400, gint_tlbh, tlbh_size);
|
||||
memcpy(vbr + 0x600, inth_entry, 64);
|
||||
|
||||
/* Time to switch VBR and roll! */
|
||||
system_vbr = cpu_setVBR(gint_vbr, lock);
|
||||
}
|
||||
|
||||
/* unlock() - unlock interrupts, restoring system settings */
|
||||
GMAPPED static void unlock(void)
|
||||
{
|
||||
/* Restore all driver settings, but do it in reverse order of loading
|
||||
to honor the dependency system */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
|
||||
{
|
||||
if(drv->ctx_restore) drv->ctx_restore(drv->sys_ctx);
|
||||
}
|
||||
|
||||
gint_ctx_restore(&sys_ctx);
|
||||
}
|
||||
|
||||
/* gint_unload() - unload gint and give back control to the system */
|
||||
void gint_unload(void)
|
||||
{
|
||||
/* First wait for all the drivers to finish their current jobs */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
|
||||
{
|
||||
if(drv->wait) drv->wait();
|
||||
}
|
||||
|
||||
cpu_setVBR(system_vbr, unlock);
|
||||
}
|
||||
|
||||
//---
|
||||
// Hot switching to operating system
|
||||
//---
|
||||
|
||||
static gint_core_ctx gint_ctx;
|
||||
extern gint_driver_t bdrv, edrv;
|
||||
|
||||
GMAPPED static void gint_switch_out(void)
|
||||
{
|
||||
/* Save all drivers in reverse order */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
|
||||
{
|
||||
if(!drv->ctx_save || !drv->ctx_restore) continue;
|
||||
drv->ctx_save(drv->gint_ctx);
|
||||
}
|
||||
gint_ctx_save(&gint_ctx);
|
||||
|
||||
/* Restore the system context */
|
||||
gint_ctx_restore(&sys_ctx);
|
||||
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
|
||||
{
|
||||
if(!drv->ctx_save || !drv->ctx_restore) continue;
|
||||
drv->ctx_restore(drv->sys_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
GMAPPED static void gint_switch_in(void)
|
||||
{
|
||||
/* Save system state again */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
|
||||
{
|
||||
if(!drv->ctx_save || !drv->ctx_restore) continue;
|
||||
drv->ctx_save(drv->sys_ctx);
|
||||
}
|
||||
gint_ctx_save(&sys_ctx);
|
||||
|
||||
/* Restore all drivers to their gint state */
|
||||
gint_ctx_restore(&gint_ctx);
|
||||
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
|
||||
{
|
||||
if(!drv->ctx_save || !drv->ctx_restore) continue;
|
||||
drv->ctx_restore(drv->gint_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* gint_switch() - temporarily switch out of gint */
|
||||
void gint_switch(void (*function)(void))
|
||||
{
|
||||
/* Wait for all the drivers to finish their current jobs */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
|
||||
{
|
||||
if(drv->wait) drv->wait();
|
||||
}
|
||||
|
||||
cpu_setVBR(system_vbr, gint_switch_out);
|
||||
if(function) function();
|
||||
|
||||
/* Wait for the OS to finish working */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
|
||||
{
|
||||
if(drv->wait) drv->wait();
|
||||
}
|
||||
cpu_setVBR(gint_vbr, gint_switch_in);
|
||||
}
|
||||
|
||||
int __Timer_Install(int id, void (*handler)(void), int delay);
|
||||
int __Timer_Start(int id);
|
||||
int __Timer_Stop(int id);
|
||||
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 *__GetVRAMAddress(void);
|
||||
void __ConfigureStatusArea(int mode);
|
||||
|
||||
static int __osmenu_id;
|
||||
|
||||
static void __osmenu_handler(void)
|
||||
{
|
||||
__PutKeyCode(0x04, 0x09, 0);
|
||||
|
||||
__Timer_Stop(__osmenu_id);
|
||||
__Timer_Deinstall(__osmenu_id);
|
||||
}
|
||||
|
||||
static void __osmenu(void)
|
||||
{
|
||||
__ClearKeyBuffer();
|
||||
|
||||
#ifdef FX9860G
|
||||
memcpy(__GetVRAMAddress(), gint_vram, 1024);
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
/* Unfortunately ineffective (main menu probably reenables it)
|
||||
__ConfigureStatusArea(3); */
|
||||
|
||||
/* TODO: Improve copied VRAM behavior in gint_osmenu() on fxcg50 */
|
||||
uint16_t *vram1, *vram2;
|
||||
dgetvram(&vram1, &vram2);
|
||||
|
||||
uint16_t *dst = __GetVRAMAddress();
|
||||
uint16_t *src = (gint_vram == vram1) ? vram2 + 6 : vram1 + 6;
|
||||
|
||||
for(int y = 0; y < 216; y++, dst+=384, src+=396)
|
||||
for(int x = 0; x < 384; x++)
|
||||
{
|
||||
dst[x] = src[x];
|
||||
}
|
||||
#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);
|
||||
|
||||
int column, row;
|
||||
unsigned short keycode;
|
||||
__GetKeyWait(&column, &row,
|
||||
0 /* KEYWAIT_HALTON_TIMEROFF */,
|
||||
1 /* Delay in seconds */,
|
||||
0 /* Enable return to main menu */,
|
||||
&keycode);
|
||||
}
|
||||
|
||||
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
|
||||
void gint_osmenu(void)
|
||||
{
|
||||
gint_switch(__osmenu);
|
||||
}
|
|
@ -10,6 +10,8 @@
|
|||
#include <gint/hardware.h>
|
||||
#include <gint/exc.h>
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Symbols provided by the linker script. For sections:
|
||||
- l* represents the load address (source address in ROM)
|
||||
- s* represents the size of the section
|
||||
|
@ -22,19 +24,19 @@ extern uint32_t
|
|||
lilram, silram, rilram, /* IL memory section */
|
||||
lxram, sxram, rxram, /* X memory section */
|
||||
lyram, syram, ryram, /* Y memory section */
|
||||
sbss, rbss, /* User's BSS section */
|
||||
btors, mtors, etors; /* Constructor/destructor arrays */
|
||||
extern gint_driver_t
|
||||
bdrv, edrv; /* Driver table */
|
||||
sbss, rbss; /* User's BSS section */
|
||||
|
||||
/* Constructor and destructor arrays */
|
||||
extern void (*bctors)(void), (*ectors)(void);
|
||||
extern void (*bdtors)(void), (*edtors)(void);
|
||||
|
||||
/* User-provided main() function */
|
||||
int main(int isappli, int optnum);
|
||||
|
||||
/* regcpy() - copy a memory region using symbol information
|
||||
/* regcpy(): Copy a memory region using symbol information
|
||||
@l Source pointer (load address)
|
||||
@s Size of area (should be a multiple of 16)
|
||||
@r Destination pointer (relocation address) */
|
||||
GSECTION(".pretext")
|
||||
static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
|
||||
{
|
||||
while(s > 0)
|
||||
|
@ -48,10 +50,9 @@ static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
|
|||
}
|
||||
#define regcpy(l, s, r) regcpy(&l, (int32_t)&s, &r)
|
||||
|
||||
/* regclr() - clear a memory region using symbol information
|
||||
/* regclr(): Clear a memory region using symbol information
|
||||
@r Source pointer (base address)
|
||||
@s Size of area (should be a multiple of 16) */
|
||||
GSECTION(".pretext")
|
||||
static void regclr(uint32_t *r, int32_t s)
|
||||
{
|
||||
while(s > 0)
|
||||
|
@ -65,20 +66,17 @@ static void regclr(uint32_t *r, int32_t s)
|
|||
}
|
||||
#define regclr(r, s) regclr(&r, (int32_t)&s)
|
||||
|
||||
/* acall() - call an array of functions (constructors or destructors)
|
||||
/* callarray(): Call an array of functions (constructors or destructors)
|
||||
@f First element of array
|
||||
@l First element outside of the array */
|
||||
GSECTION(".pretext")
|
||||
static void acall(void (**f)(void), void (**l)(void))
|
||||
static void callarray(void (**f)(void), void (**l)(void))
|
||||
{
|
||||
while(f < l) (*(*f++))();
|
||||
}
|
||||
#define acall(f, l) acall((void (**)(void))&f, (void (**)(void))&l)
|
||||
|
||||
|
||||
/* start() - this is where it all starts
|
||||
Returns a status code. Invoking main menu is better than returning! */
|
||||
GSECTION(".pretext.entry")
|
||||
GSECTION(".text.entry")
|
||||
int start(int isappli, int optnum)
|
||||
{
|
||||
/* We are currently in a dynamic userspace mapping of an add-in run
|
||||
|
@ -88,17 +86,17 @@ int start(int isappli, int optnum)
|
|||
Do not disturb the operating system.
|
||||
|
||||
gint will silently run in an isolated part of the memory (fx9860g)
|
||||
or at the start of the RAM (fxcg50). It acts as a kernel,
|
||||
redirecting interrupts and reimplementing drivers, so we can't rely
|
||||
too much on the system. Ladies and gentlemen, let's have fun! ;D */
|
||||
or at the start of the RAM (fxcg50). The kernel will redirect
|
||||
interrupts and load its own drivers, so we can't rely too much on
|
||||
the system. Ladies and gentlemen, let's have fun! ;D */
|
||||
|
||||
/* For now, we use the system's memory mapper for ROM. We'll still do
|
||||
it from our TLB miss handler once we're installed. RAM is always
|
||||
it later in our TLB miss handler once we're installed. RAM is always
|
||||
fully mapped, but we need to initialize it. We also need to do some
|
||||
hardware detection because old fx9860g models have a different
|
||||
processor with some incompatible features. */
|
||||
|
||||
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */
|
||||
/* Detect architecture; this will tell SH3 from SH4 on fx9860g */
|
||||
hw_detect();
|
||||
|
||||
/* Load data sections and wipe the bss section. This has to be done
|
||||
|
@ -110,46 +108,31 @@ int start(int isappli, int optnum)
|
|||
regcpy(lyram, syram, ryram);
|
||||
regclr(rbss, sbss);
|
||||
|
||||
/* Install gint and switch VBR */
|
||||
gint_install();
|
||||
/* Install gint, switch VBR and initialize drivers */
|
||||
kinit();
|
||||
|
||||
/* We are now running on our own in kernel mode. Since we have taken
|
||||
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!) */
|
||||
|
||||
gint_driver_t *drv;
|
||||
/* Now that we have initialized the kernel, we are ready to start the
|
||||
hosted user application, which has its own constructors and
|
||||
destructors to work with. */
|
||||
|
||||
/* Initialize all drivers by saving the system settings */
|
||||
for(drv = &bdrv; drv < &edrv; drv++)
|
||||
{
|
||||
/* Wait for hardware availability */
|
||||
if(drv->wait) drv->wait();
|
||||
|
||||
/* Hook for old SH3 fx9860g machines */
|
||||
if(isSH3() && drv->driver_sh3) drv->driver_sh3();
|
||||
|
||||
if(drv->ctx_save) drv->ctx_save(drv->sys_ctx);
|
||||
if(drv->init) drv->init();
|
||||
}
|
||||
|
||||
/* With gint fully initialized, we are ready to start the hosted user
|
||||
application. We have already loaded the RAM sections earlier; all
|
||||
that's left is calling the constructors and rolling main(). */
|
||||
|
||||
acall(btors, mtors);
|
||||
callarray(&bctors, &ectors);
|
||||
int ret = main(isappli, optnum);
|
||||
acall(mtors, etors);
|
||||
callarray(&bdtors, &edtors);
|
||||
|
||||
/* Before leaving the application, we need to clean up our mess. We
|
||||
have changed many hardware settings while accessing the peripheral
|
||||
modules. The OS is bound to be confused (and hang, or crash, or any
|
||||
other kind of giving up) if we don't restore them. */
|
||||
/* Before leaving the application, we need to clean everything we
|
||||
changed to hardware settings and peripheral modules. The OS is bound
|
||||
to be confused (and hang, or crash, or any other kind of giving up)
|
||||
if we don't restore them. */
|
||||
|
||||
/* Unload gint and give back control to the system. Driver settings
|
||||
will be restored while interrupts are disabled */
|
||||
gint_unload();
|
||||
kquit();
|
||||
|
||||
/* TODO: Invoke main menu instead of returning? */
|
||||
/* TODO: Invoke main menu so that add-in can restart? */
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
** * File system, because it's a mess and we might ruin the ROM.
|
||||
*/
|
||||
|
||||
.section .pretext
|
||||
.text
|
||||
|
||||
/* Dynamic allocation */
|
||||
.global _malloc
|
||||
|
@ -114,8 +114,6 @@ syscall_table:
|
|||
|
||||
/* Dynamic allocation */
|
||||
|
||||
.section .pretext
|
||||
|
||||
_malloc:
|
||||
syscall(0x1f44)
|
||||
_free:
|
||||
|
|
|
@ -16,9 +16,6 @@ extern char gint_vbr_fxcg50[];
|
|||
extern char gint_vbr_fxcg20[];
|
||||
#endif
|
||||
|
||||
/* gint's VBR address as determined at runtime; not valid before kinit() */
|
||||
extern uint32_t gint_vbr;
|
||||
|
||||
/* The kernel's interrupt and exception handlers' entry points */
|
||||
void gint_exch(void);
|
||||
void gint_tlbh(void);
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
#include <gint/mpu/power.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/intc.h>
|
||||
#include <gint/dma.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/exc.h>
|
||||
|
||||
#define DMA SH7305_DMA
|
||||
|
@ -201,12 +201,13 @@ static void init(void)
|
|||
|
||||
/* Set interrupt priority to 3 (IPRE[15..12] for first three channels,
|
||||
IPRF[11..8] for last two and error gate */
|
||||
gint_intlevel(16, 3);
|
||||
gint_intlevel(21, 3);
|
||||
|
||||
/* Unmask the channel interrupts and the address error */
|
||||
INTC.MSKCLR->IMR1 = 0x0f;
|
||||
INTC.MSKCLR->IMR5 = 0x70;
|
||||
intc_priority(INTC_DMA_DEI0, 3);
|
||||
intc_priority(INTC_DMA_DEI1, 3);
|
||||
intc_priority(INTC_DMA_DEI2, 3);
|
||||
intc_priority(INTC_DMA_DEI3, 3);
|
||||
intc_priority(INTC_DMA_DEI4, 3);
|
||||
intc_priority(INTC_DMA_DEI5, 3);
|
||||
intc_priority(INTC_DMA_DADERR, 3);
|
||||
|
||||
/* Clear blocking flags and enable the master switch */
|
||||
DMA.OR.AE = 0;
|
||||
|
|
180
src/intc/intc.c
Normal file
180
src/intc/intc.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
#include <gint/drivers.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
|
||||
/* Interrupt controllers */
|
||||
|
||||
GDATA3 sh7705_intc_t SH7705_INTC = {
|
||||
.IPR = {
|
||||
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||
(void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004,
|
||||
},
|
||||
.ICR1 = (void *)0xa4000010,
|
||||
};
|
||||
|
||||
GDATA sh7305_intc_t SH7305_INTC = {
|
||||
.IPR = (void *)0xa4080000,
|
||||
.MSK = (void *)0xa4080080,
|
||||
.MSKCLR = (void *)0xa40800c0,
|
||||
.USERIMASK = (void *)0xa4700000,
|
||||
};
|
||||
|
||||
/* Interrupt IPR and IMR positions. The order of entries is as in the named
|
||||
list of interrupt signals in <gint/intc.h>. */
|
||||
|
||||
/* Friendly names for IPR and IMR register numbers */
|
||||
enum{ IPRA, IPRB, IPRC, IPRD, IPRE, IPRF, IPRG, IPRH, IPRI, IPRJ, IPRK, IPRL };
|
||||
enum{ IMR0, IMR1, IMR2, IMR3, IMR4, IMR5, IMR6, IMR7, IMR8, IMR9, IMR10 };
|
||||
#define _ 0,0
|
||||
|
||||
struct info {
|
||||
uint16_t IPR4, IPR4bits, IMR, IMRbits;
|
||||
/* Only set if different than IPR4 with IPR4bits */
|
||||
uint16_t IPR3, IPR3bits;
|
||||
} info[] = {
|
||||
/* Standard TMU */
|
||||
{ IPRA, 0xf000, IMR4, 0x10, _ },
|
||||
{ IPRA, 0x0f00, IMR4, 0x20, _ },
|
||||
{ IPRA, 0x00f0, IMR4, 0x40, _ },
|
||||
/* ETMU */
|
||||
{ IPRJ, 0xf000, IMR6, 0x08, IPRF, 0x000f },
|
||||
{ IPRG, 0x0f00, IMR5, 0x02, _ },
|
||||
{ IPRG, 0x00f0, IMR5, 0x04, _ },
|
||||
{ IPRE, 0x00f0, IMR2, 0x01, _ },
|
||||
{ IPRI, 0xf000, IMR6, 0x10, _ },
|
||||
{ IPRL, 0xf000, IMR8, 0x02, _ },
|
||||
/* DMA */
|
||||
{ IPRE, 0xf000, IMR1, 0x01, _ /* Not supported on SH3! */ },
|
||||
{ IPRE, 0xf000, IMR1, 0x02, _ },
|
||||
{ IPRE, 0xf000, IMR1, 0x04, _ },
|
||||
{ IPRE, 0xf000, IMR1, 0x08, _ },
|
||||
{ IPRF, 0x0f00, IMR5, 0x10, _ },
|
||||
{ IPRF, 0x0f00, IMR5, 0x20, _ },
|
||||
{ IPRF, 0x0f00, IMR5, 0x40, _ },
|
||||
/* RTC */
|
||||
{ IPRK, 0xf000, IMR10, 0x04, IPRA, 0x000f },
|
||||
{ IPRK, 0xf000, IMR10, 0x02, IPRA, 0x000f },
|
||||
{ IPRK, 0xf000, IMR10, 0x01, IPRA, 0x000f },
|
||||
};
|
||||
|
||||
|
||||
//---
|
||||
// Interrupt controller functions
|
||||
//---
|
||||
|
||||
/* intc_priority(): Configure the level of interrupts */
|
||||
int intc_priority(int intname, int level)
|
||||
{
|
||||
struct info *i = &info[intname];
|
||||
int IPRn = i->IPR4, IPRbits = i->IPR4bits;
|
||||
|
||||
if(isSH3() && i->IPR3bits != 0)
|
||||
{
|
||||
IPRn = i->IPR3;
|
||||
IPRbits = i->IPR3bits;
|
||||
}
|
||||
|
||||
/* Bit-shift for the mask */
|
||||
int shift = 0;
|
||||
while(IPRbits >>= 4) shift += 4;
|
||||
|
||||
uint16_t volatile *IPR;
|
||||
IPR = isSH3() ? SH7705_INTC.IPR[IPRn] : &SH7305_INTC.IPR[2*IPRn];
|
||||
|
||||
int oldlevel = (*IPR >> shift) & 0xf;
|
||||
*IPR = (*IPR & ~(0xf << shift)) | (level << shift);
|
||||
|
||||
if(isSH4() && level > 0 && i->IMRbits)
|
||||
{
|
||||
uint8_t volatile *MSKCLR = &SH7305_INTC.MSKCLR->IMR0;
|
||||
MSKCLR[4*i->IMR] = i->IMRbits;
|
||||
}
|
||||
|
||||
return oldlevel;
|
||||
}
|
||||
|
||||
//---
|
||||
// Initialization
|
||||
//---
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
/* Just disable everything, drivers will enable what they support */
|
||||
if(isSH3()) for(int i = 0; i < 8; i++)
|
||||
*(SH7705_INTC.IPR[i]) = 0x0000;
|
||||
else for(int i = 0; i < 12; i++)
|
||||
SH7305_INTC.IPR[2 * i] = 0x0000;
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver context
|
||||
//---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t IPR[12];
|
||||
uint8_t MSK[13];
|
||||
} ctx_t;
|
||||
|
||||
GBSS static ctx_t sys_ctx, gint_ctx;
|
||||
|
||||
static void ctx_save(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
ctx->IPR[i] = *(SH7705_INTC.IPR[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++)
|
||||
ctx->IPR[i] = SH7305_INTC.IPR[2 * i];
|
||||
|
||||
uint8_t *IMR = (void *)SH7305_INTC.MSK;
|
||||
for(int i = 0; i < 13; i++, IMR += 4)
|
||||
ctx->MSK[i] = *IMR;
|
||||
}
|
||||
}
|
||||
|
||||
static void ctx_restore(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
*(SH7705_INTC.IPR[i]) = ctx->IPR[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++)
|
||||
SH7305_INTC.IPR[2 * i] = ctx->IPR[i];
|
||||
|
||||
/* Setting masks it a bit more involved than reading them */
|
||||
uint8_t *IMCR = (void *)SH7305_INTC.MSKCLR;
|
||||
uint8_t *IMR = (void *)SH7305_INTC.MSK;
|
||||
for(int i = 0; i < 13; i++, IMR += 4, IMCR += 4)
|
||||
{
|
||||
*IMCR = 0xff;
|
||||
*IMR = ctx->MSK[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver structure definition
|
||||
//---
|
||||
|
||||
gint_driver_t drv_intc = {
|
||||
.name = "INTC",
|
||||
.init = init,
|
||||
.sys_ctx = &sys_ctx,
|
||||
.gint_ctx = &gint_ctx,
|
||||
.ctx_save = ctx_save,
|
||||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(0, drv_intc);
|
|
@ -57,7 +57,7 @@ GDATA static uint time = 0;
|
|||
|
||||
/* buffer_push(): Add an event in the keyboard buffer
|
||||
Returns non-zero if the event cannot be pushed. */
|
||||
GMAPPED static int buffer_push(driver_event_t ev)
|
||||
static int buffer_push(driver_event_t ev)
|
||||
{
|
||||
int next = (buffer_end + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
if(next == buffer_start) return 1;
|
||||
|
@ -79,7 +79,7 @@ static int buffer_poll(driver_event_t *ev)
|
|||
}
|
||||
|
||||
/* keysc_frame(): Generate driver events from KEYSC state */
|
||||
GMAPPED static void keysc_frame(void)
|
||||
static void keysc_frame(void)
|
||||
{
|
||||
GALIGNED(2) uint8_t scan[12] = { 0 };
|
||||
|
||||
|
@ -248,7 +248,7 @@ int keydown_any(int key, ...)
|
|||
// Driver initialization
|
||||
//---
|
||||
|
||||
GMAPPED static int callback(GUNUSED volatile void *arg)
|
||||
static int callback(GUNUSED volatile void *arg)
|
||||
{
|
||||
keysc_frame();
|
||||
time++;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <gint/rtc.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/intc.h>
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/hardware.h>
|
||||
|
@ -147,7 +148,7 @@ static void init(void)
|
|||
|
||||
/* Disable the periodic interrupt for now, but give it priority 5 */
|
||||
RTC->RCR2.PES = RTC_NONE;
|
||||
gint_intlevel(isSH3() ? 3 : 40, 5);
|
||||
intc_priority(INTC_RTC_PRI, 5);
|
||||
|
||||
gint[HWRTC] = HW_LOADED | HWRTC_TIMER;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/intc.h>
|
||||
|
||||
#include <gint/mpu/intc.h>
|
||||
#include <gint/mpu/tmu.h>
|
||||
|
@ -183,7 +184,7 @@ void timer_wait(int id)
|
|||
//---
|
||||
|
||||
/* timer_timeout() - callback that sets a flag and halts the timer */
|
||||
GMAPPED int timer_timeout(volatile void *arg)
|
||||
int timer_timeout(volatile void *arg)
|
||||
{
|
||||
volatile int *x = arg;
|
||||
(*x)++;
|
||||
|
@ -283,29 +284,19 @@ static void init(void)
|
|||
gint_inthandler(0xc60, inth_etmu_help, 32);
|
||||
|
||||
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
|
||||
gint_intlevel(0, 13);
|
||||
gint_intlevel(1, 11);
|
||||
gint_intlevel(2, 9);
|
||||
intc_priority(INTC_TMU_TUNI0, 13);
|
||||
intc_priority(INTC_TMU_TUNI1, 11);
|
||||
intc_priority(INTC_TMU_TUNI2, 9);
|
||||
|
||||
/* Enable the extra TMUs at level 7 */
|
||||
if(isSH3()) gint_intlevel(23, 7);
|
||||
else
|
||||
intc_priority(INTC_ETMU_TUNI0, 7);
|
||||
if(isSH4())
|
||||
{
|
||||
/* Unmask the standard timers' interrupts */
|
||||
SH7305_INTC.MSKCLR->IMR4 = 0x70;
|
||||
|
||||
gint_intlevel(36, 7);
|
||||
gint_intlevel(25, 7);
|
||||
gint_intlevel(26, 7);
|
||||
gint_intlevel(18, 7);
|
||||
gint_intlevel(32, 7);
|
||||
gint_intlevel(44, 7);
|
||||
|
||||
/* Unmask the extra timers' interrupts */
|
||||
SH7305_INTC.MSKCLR->IMR2 = 0x01;
|
||||
SH7305_INTC.MSKCLR->IMR5 = 0x06;
|
||||
SH7305_INTC.MSKCLR->IMR6 = 0x18;
|
||||
SH7305_INTC.MSKCLR->IMR8 = 0x02;
|
||||
intc_priority(INTC_ETMU_TUNI1, 7);
|
||||
intc_priority(INTC_ETMU_TUNI2, 7);
|
||||
intc_priority(INTC_ETMU_TUNI3, 7);
|
||||
intc_priority(INTC_ETMU_TUNI4, 7);
|
||||
intc_priority(INTC_ETMU_TUNI5, 7);
|
||||
}
|
||||
|
||||
/* Record details in gint's hardware information interface */
|
||||
|
|
Loading…
Reference in a new issue