mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 20:43:36 +01:00
More interrupt system, exceptions, timers.
This commit is contained in:
parent
3b90b40dd7
commit
2f0e049c33
29 changed files with 1568 additions and 404 deletions
8
Makefile
8
Makefile
|
@ -16,12 +16,12 @@ cfg := config/Makefile.cfg
|
|||
cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg))
|
||||
|
||||
cf-fx := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
|
||||
-I include $(cfg_macros)
|
||||
-fstrict-volatile-bitfields -I include $(cfg_macros)
|
||||
cf-cg := -m4 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
|
||||
-I include $(cfg_macros)
|
||||
-fstrict-volatile-bitfields -I include $(cfg_macros)
|
||||
|
||||
# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-elf
|
||||
toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-elf)
|
||||
# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-nofpu-elf
|
||||
toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-nofpu-elf)
|
||||
|
||||
# Compiler flags, assembler flags, dependency generation, archiving
|
||||
cflags := $(cf-$(cfg_target))
|
||||
|
|
26
configure
vendored
26
configure
vendored
|
@ -12,6 +12,7 @@ conf_target=
|
|||
# Behavior
|
||||
conf[GINT_BOOT_LOG]=
|
||||
conf[GINT_NO_SYSCALLS]=
|
||||
cong[GINT_LAX]=
|
||||
conf[GINT_EXTENDED_LIBC]=
|
||||
conf[GINT_STATIC_GRAY]=
|
||||
|
||||
|
@ -45,28 +46,31 @@ Platform settings (specify exactly one):
|
|||
Target platform is fx-9860G II: all monochrome models that support add-ins
|
||||
or can be flashed to support them.
|
||||
${Cg}-fxcg50$C0
|
||||
Target platform is fx-CG50.
|
||||
Target platform is fx-CG 50; there is some compatibility with fx-CG 10/20.
|
||||
|
||||
Options that affect the behavior of the library:
|
||||
$Cr-boot-log $C_[default:$Cp not specified$C_]$C0
|
||||
Options that affect the behavior of the library $C_[${Cp}default$C_]$C0:
|
||||
$Cr-boot-log $C_[${Cp}disabled$C_]$C0
|
||||
Enable an on-screen log at startup if a key is kept pressed while launching
|
||||
the add-in, allowing easy debug and crash diagnoses.
|
||||
$Cr-no-syscalls $C_[default:$Cp not specified$C_]$C0
|
||||
$Cr-no-syscalls $C_[${Cp}disabled$C_]$C0
|
||||
Never use syscalls. Expect trouble with malloc() and the gray engine. Do
|
||||
not trigger this switch unless you know what you are doing.
|
||||
$Cr-extended-libc $C_[default:$Cp not specified$C_]$C0
|
||||
$Cr-lax $C_[${Cp}disabled$C_]$C0
|
||||
Make more assumptions about functions parameters. This disables coordinate
|
||||
checks in drawing functions. Be careful!
|
||||
$Cr-extended-libc $C_[${Cp}disabled$C_]$C0
|
||||
Enable specific C99 headers/features that are normally not required by
|
||||
calculator programs. This may allow porting programs from other platforms.
|
||||
$Cr-static-gray-engine $C_[default:$Cp not specified$C_]$C0
|
||||
$Cr-static-gray-engine $C_[${Cp}disabled$C_]$C0
|
||||
Place the gray engine vram in static ram instead of using the heap. Always
|
||||
use this option when using both the gray engine and -no-syscalls.
|
||||
|
||||
Options that customize size limits:
|
||||
$Cr-atexit-max$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0
|
||||
Options that customize size limits $C_[${Cp}default$C_]$C0:
|
||||
$Cr-atexit-max$C0=${Cg}integer$C_ [${Cp}16$C_]$C0
|
||||
Number of exit handlers that can be registered by atexit().
|
||||
$Cr-timer-slots$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0
|
||||
$Cr-timer-slots$C0=${Cg}integer$C_ [${Cp}16$C_]$C0
|
||||
Number of virtual timers that may be registered at the same time.
|
||||
$Cr-events-queue-size$C0=${Cg}integer$C_ [default:$Cp 64$C_]$C0
|
||||
$Cr-events-queue-size$C0=${Cg}integer$C_ [${Cp}64$C_]$C0
|
||||
Number of events simultaneously stored in the event queue.
|
||||
EOF
|
||||
|
||||
|
@ -88,6 +92,7 @@ for arg; do case "$arg" in
|
|||
|
||||
-boot-log) conf[GINT_BOOT_LOG]=true;;
|
||||
-no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
|
||||
-lax) conf[GINT_LAX]=true;;
|
||||
-extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
|
||||
-static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
|
||||
|
||||
|
@ -142,6 +147,7 @@ output_config()
|
|||
|
||||
[ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG"
|
||||
[ "${conf[GINT_NO_SYSCALLS]}" ] && echo -n " -DGINT_NO_SYSCALLS"
|
||||
[ "${conf[GINT_LAX]}" ] && echo -n " -DGINT_LAX"
|
||||
[ "${conf[GINT_EXTENDED_LIBC]}" ] && echo -n " -DGINT_EXTENDED_LIBC"
|
||||
[ "${conf[GINT_STATIC_GRAY]}" ] && echo -n " -DGINT_STATIC_GRAY"
|
||||
|
||||
|
|
|
@ -173,11 +173,14 @@ SECTIONS
|
|||
*/
|
||||
|
||||
/* Unwanted sections going to meet Dave Null:
|
||||
- Debug sections, often come out of libgcc
|
||||
- Java classes registration (why are there even here?)
|
||||
- Asynchronous unwind tables: no C++ exception handling for now ^^
|
||||
- Comments or anything the compiler might put in its assembler
|
||||
- A stack section sometimes generated for build/version.o */
|
||||
/DISCARD/ : {
|
||||
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
|
||||
.debug_ranges .debug_line .debug_str)
|
||||
*(.jcr)
|
||||
*(.eh_frame_hdr)
|
||||
*(.eh_frame)
|
||||
|
|
13
fxcg50.ld
13
fxcg50.ld
|
@ -118,15 +118,15 @@ SECTIONS
|
|||
|
||||
/*
|
||||
** gint-related sections
|
||||
** 88160000:4k VBR space
|
||||
** 88161000:4k .gint.data and .gint.bss
|
||||
** 8c160000:4k VBR space
|
||||
** 8c161000:4k .gint.data and .gint.bss
|
||||
*/
|
||||
|
||||
/* VBR address: let's just start at the beginning of the RAM area.
|
||||
There's an unused 0x100-byte gap at the start of the VBR space.
|
||||
The VBR space is already a large block (> 2 kiB), so I'm cutting off
|
||||
the gap to spare some memory */
|
||||
_gint_vbr = 0x8815ff00;
|
||||
_gint_vbr = ORIGIN(vbr) - 0x100;
|
||||
|
||||
. = ORIGIN(rram);
|
||||
|
||||
|
@ -161,10 +161,17 @@ SECTIONS
|
|||
*/
|
||||
|
||||
/* Unwanted sections going to meet Dave Null:
|
||||
- SH3-only data sections
|
||||
- Debug sections, often come out of libgcc
|
||||
- Java classes registration (why are there even here?)
|
||||
- Asynchronous unwind tables: no C++ exception handling for now ^^
|
||||
- Comments or anything the compiler might put in its assembler */
|
||||
/DISCARD/ : {
|
||||
*(.gint.bss.sh3)
|
||||
*(.gint.data.sh3)
|
||||
|
||||
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
|
||||
.debug_ranges .debug_line .debug_str)
|
||||
*(.jcr)
|
||||
*(.eh_frame_hdr)
|
||||
*(.eh_frame)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef GINT_CORE_BOOTLOG
|
||||
#define GINT_CORE_BOOTLOG
|
||||
|
||||
/* bootlog_loaded() - Section loading stage
|
||||
/* bootlog_loaded() - section loading stage
|
||||
Called when RAM sections have been wiped and copied */
|
||||
void bootlog_loaded(void);
|
||||
|
||||
|
@ -16,7 +16,7 @@ void bootlog_loaded(void);
|
|||
@ram Amount of mapped RAM, in bytes */
|
||||
void bootlog_mapped(uint32_t rom, uint32_t ram);
|
||||
|
||||
/* bootlog_kernel() - Gint loading stage
|
||||
/* bootlog_kernel() - gint loading stage
|
||||
Called when gint has been installed, the VBR switched and the interrupt
|
||||
handlers set up. */
|
||||
void bootlog_kernel(void);
|
||||
|
|
|
@ -28,75 +28,75 @@
|
|||
typedef struct
|
||||
{
|
||||
volatile word_union(*IPRA,
|
||||
uint TMU0 :4; /* Timer 0 */
|
||||
uint TMU1 :4; /* Timer 1 */
|
||||
uint TMU2 :4; /* Timer 2 */
|
||||
uint RTC :4; /* Real-Time Clock */
|
||||
uint16_t TMU0 :4; /* Timer 0 */
|
||||
uint16_t TMU1 :4; /* Timer 1 */
|
||||
uint16_t TMU2 :4; /* Timer 2 */
|
||||
uint16_t RTC :4; /* Real-Time Clock */
|
||||
);
|
||||
|
||||
volatile word_union(*IPRB,
|
||||
uint WDT :4; /* Watchdog Timer */
|
||||
uint REF :4; /* BSC Refresh Request, SDRAM (?) */
|
||||
uint :4;
|
||||
uint :4;
|
||||
uint16_t WDT :4; /* Watchdog Timer */
|
||||
uint16_t REF :4; /* BSC Refresh Request, SDRAM (?) */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
|
||||
volatile word_union(*IPRC,
|
||||
uint IRQ3 :4; /* Interrupt request 3 */
|
||||
uint IRQ2 :4; /* Interrupt request 2 */
|
||||
uint IRQ1 :4; /* Interrupt request 1 */
|
||||
uint IRQ0 :4; /* Interrupt request 0 */
|
||||
uint16_t IRQ3 :4; /* Interrupt request 3 */
|
||||
uint16_t IRQ2 :4; /* Interrupt request 2 */
|
||||
uint16_t IRQ1 :4; /* Interrupt request 1 */
|
||||
uint16_t IRQ0 :4; /* Interrupt request 0 */
|
||||
);
|
||||
|
||||
volatile word_union(*IPRD,
|
||||
uint PINT0_7 :4; /* External interrupt pins 0 to 7 */
|
||||
uint PINT8_15 :4; /* External interrupt pins 8 to 15 */
|
||||
uint IRQ5 :4; /* Interrupt request 5 */
|
||||
uint IRQ4 :4; /* Interrupt request 4 */
|
||||
uint16_t PINT0_7 :4; /* External interrupt pins 0 to 7 */
|
||||
uint16_t PINT8_15 :4; /* External interrupt pins 8 to 15 */
|
||||
uint16_t IRQ5 :4; /* Interrupt request 5 */
|
||||
uint16_t IRQ4 :4; /* Interrupt request 4 */
|
||||
);
|
||||
|
||||
volatile word_union(*IPRE,
|
||||
uint DMAC :4; /* Direct Memory Access Controller */
|
||||
uint SCIF0 :4; /* Serial Communication Interface 0 */
|
||||
uint SCIF2 :4; /* Serial Communication Interface 2 */
|
||||
uint ADC :4; /* Analog/Decimal Converter */
|
||||
uint16_t DMAC :4; /* Direct Memory Access Controller */
|
||||
uint16_t SCIF0 :4; /* Serial Communication Interface 0 */
|
||||
uint16_t SCIF2 :4; /* Serial Communication Interface 2 */
|
||||
uint16_t ADC :4; /* Analog/Decimal Converter */
|
||||
);
|
||||
|
||||
volatile word_union(*IPRF,
|
||||
uint :4;
|
||||
uint :4;
|
||||
uint USB :4; /* USB Controller */
|
||||
uint :4;
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t USB :4; /* USB Controller */
|
||||
uint16_t :4;
|
||||
);
|
||||
|
||||
volatile word_union(*IPRG,
|
||||
uint TPU0 :4; /* Timer Pulse Unit 0 */
|
||||
uint TPU1 :4; /* Timer Pulse Unit 1 */
|
||||
uint :4;
|
||||
uint :4;
|
||||
uint16_t TPU0 :4; /* Timer Pulse Unit 0 */
|
||||
uint16_t TPU1 :4; /* Timer Pulse Unit 1 */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
|
||||
volatile word_union(*IPRH,
|
||||
uint TPU2 :4; /* Timer Pulse Unit 2 */
|
||||
uint TPU3 :4; /* Timer Pulse Unit 3 */
|
||||
uint :4;
|
||||
uint :4;
|
||||
uint16_t TPU2 :4; /* Timer Pulse Unit 2 */
|
||||
uint16_t TPU3 :4; /* Timer Pulse Unit 3 */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
|
||||
} PACKED(4) sh7705_intc_ipc_t;
|
||||
|
||||
/* sh7705_intc_icr1_t - Interrupt Control Register 1 (general) */
|
||||
typedef word_union(sh7705_intc_icr1_t,
|
||||
uint MAI :1; /* Mask All Interrupts */
|
||||
uint IRQLVL :1; /* Interrupt Request Level Detect */
|
||||
uint BLMSK :1; /* Enable NMI when BL is set */
|
||||
uint :1;
|
||||
uint IRQ5E :2; /* IRQ 5 Edge Detection */
|
||||
uint IRQ4E :2; /* etc. */
|
||||
uint IRQ3E :2;
|
||||
uint IRQ2E :2;
|
||||
uint IRQ1E :2;
|
||||
uint IRQ0E :2;
|
||||
typedef volatile word_union(sh7705_intc_icr1_t,
|
||||
uint16_t MAI :1; /* Mask All Interrupts */
|
||||
uint16_t IRQLVL :1; /* Interrupt Request Level Detect */
|
||||
uint16_t BLMSK :1; /* Enable NMI when BL is set */
|
||||
uint16_t :1;
|
||||
uint16_t IRQ5E :2; /* IRQ 5 Edge Detection */
|
||||
uint16_t IRQ4E :2; /* etc. */
|
||||
uint16_t IRQ3E :2;
|
||||
uint16_t IRQ2E :2;
|
||||
uint16_t IRQ1E :2;
|
||||
uint16_t IRQ0E :2;
|
||||
);
|
||||
|
||||
/* sh7705_intc_t - the SH7705 interrupt controller */
|
||||
|
@ -109,7 +109,7 @@ typedef struct
|
|||
} PACKED(4);
|
||||
|
||||
/* Control registers */
|
||||
volatile sh7705_intc_icr1_t *ICR1;
|
||||
sh7705_intc_icr1_t *ICR1;
|
||||
|
||||
} PACKED(4) sh7705_intc_t;
|
||||
|
||||
|
@ -125,101 +125,101 @@ typedef struct
|
|||
Some of the fields have been left unnamed because they correspond to SH7724
|
||||
peripheral modules that are *very* unlikely to even exist in the SH7305, let
|
||||
alone by of any use to us */
|
||||
typedef struct
|
||||
typedef volatile struct
|
||||
{
|
||||
word_union(IPRA,
|
||||
uint TMU0_0 :4; /* TMU0 Channel 0 */
|
||||
uint TMU0_1 :4; /* TMU0 Channel 1 */
|
||||
uint TMU0_2 :4; /* TMU0 Channel 2 */
|
||||
uint IrDA :4; /* Infrared Communication */
|
||||
uint16_t TMU0_0 :4; /* TMU0 Channel 0 */
|
||||
uint16_t TMU0_1 :4; /* TMU0 Channel 1 */
|
||||
uint16_t TMU0_2 :4; /* TMU0 Channel 2 */
|
||||
uint16_t IrDA :4; /* Infrared Communication */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRB,
|
||||
uint :4; /* JPEG Processing Unit */
|
||||
uint LCDC :4; /* LCD Controller */
|
||||
uint DMAC1A :4; /* Direct Memory Access Controller 1 */
|
||||
uint :4; /* Blending Engine Unit */
|
||||
uint16_t :4;
|
||||
uint16_t LCDC :4; /* LCD Controller */
|
||||
uint16_t DMAC1A :4; /* Direct Memory Access Controller 1 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRC,
|
||||
uint TMU1_0 :4; /* TMU1 Channel 0 */
|
||||
uint TMU1_1 :4; /* TMU1 Channel 1 */
|
||||
uint TMU1_2 :4; /* TMU1 Channel 2 */
|
||||
uint :4; /* Sound Processing Unit */
|
||||
uint16_t TMU1_0 :4; /* TMU1 Channel 0 */
|
||||
uint16_t TMU1_1 :4; /* TMU1 Channel 1 */
|
||||
uint16_t TMU1_2 :4; /* TMU1 Channel 2 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRD,
|
||||
uint :4;
|
||||
uint MMCIF :4; /* MultiMedia Card Interface */
|
||||
uint :4;
|
||||
uint :4; /* ATAPI Interface */
|
||||
uint16_t :4;
|
||||
uint16_t MMCIF :4; /* MultiMedia Card Interface */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRE,
|
||||
uint DMAC0A :4; /* Direct Memory Access Controller 0 */
|
||||
uint :4; /* Various graphical engines */
|
||||
uint SCIFA3 :4; /* SCIFA channel 3 interrupt */
|
||||
uint :4; /* Video Processing Unit */
|
||||
uint16_t DMAC0A :4; /* Direct Memory Access Controller 0 */
|
||||
uint16_t :4;
|
||||
uint16_t ETMU3 :4; /* Extra TMU 3 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRF,
|
||||
uint KEYSC :4; /* Key Scan Interface */
|
||||
uint DMACOB :4; /* DMAC0 transfer/error info */
|
||||
uint USB0_1 :4; /* USB controller */
|
||||
uint CMT :4; /* Compare Match Timer */
|
||||
uint16_t KEYSC :4; /* Key Scan Interface */
|
||||
uint16_t DMACOB :4; /* DMAC0 transfer/error info */
|
||||
uint16_t USB0_1 :4; /* USB controller */
|
||||
uint16_t CMT :4; /* Compare Match Timer */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRG,
|
||||
uint SCIF0 :4; /* SCIF0 transfer/error info */
|
||||
uint SCIF1 :4; /* SCIF1 transfer/error info */
|
||||
uint SCIF2 :4; /* SCIF2 transfer/error info */
|
||||
uint :4; /* Video Engine Unit */
|
||||
uint16_t SCIF0 :4; /* SCIF0 transfer/error info */
|
||||
uint16_t ETMU1 :4; /* Extra TMU 1 */
|
||||
uint16_t ETMU2 :4; /* Extra TMU 2 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRH,
|
||||
uint MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */
|
||||
uint MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */
|
||||
uint :4; /* I2C Interface channel 0 */
|
||||
uint :4; /* I2C Interface channel 1 */
|
||||
uint16_t MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */
|
||||
uint16_t MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRI,
|
||||
uint SCIFA4 :4; /* SCIFA channel 4 interrupt */
|
||||
uint :4; /* MediaRAM InterConnected Buffers */
|
||||
uint :4; /* Transport Stream Interface */
|
||||
uint :4; /* 2D Graphics Accelerator & ICB */
|
||||
uint16_t ETMU4 :4; /* Extra TMU 4 */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRJ,
|
||||
uint :4; /* Capture Engine Unit */
|
||||
uint :4; /* Ethernet Memory Access Controller */
|
||||
uint FSI :4; /* FIFO-Buffered Serial Interface */
|
||||
uint SDHI1 :4; /* SD Card Host Interface channel 1 */
|
||||
uint16_t ETMU0 :4; /* Extra TMU 0 */
|
||||
uint16_t :4;
|
||||
uint16_t FSI :4; /* FIFO-Buffered Serial Interface */
|
||||
uint16_t SDHI1 :4; /* SD Card Host Interface channel 1 */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRK,
|
||||
uint RTC :4; /* Real-Time Clock */
|
||||
uint DMAC1B :4; /* DMAC1 transfer/error info */
|
||||
uint :4; /* MediaRAM InterConnected Buffers */
|
||||
uint SDHI0 :4; /* SD Card Host Interface channel 0 */
|
||||
uint16_t RTC :4; /* Real-Time Clock */
|
||||
uint16_t DMAC1B :4; /* DMAC1 transfer/error info */
|
||||
uint16_t :4;
|
||||
uint16_t SDHI0 :4; /* SD Card Host Interface channel 0 */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRL,
|
||||
uint SCIFA5 :4; /* SCIFA channel 5 interrupt */
|
||||
uint :4;
|
||||
uint TPU :4; /* Timer-Pulse Unit */
|
||||
uint :4; /* Image Extraction DMAC */
|
||||
uint16_t ETMU5 :4; /* Extra TMU 5 */
|
||||
uint16_t :4;
|
||||
uint16_t TPU :4; /* Timer-Pulse Unit */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
|
@ -228,124 +228,25 @@ typedef struct
|
|||
/* sh7305_intc_masks_t - Interrupt mask management
|
||||
Writing 1 to IMR masks interrupts; writing 1 to IMCRs clears the masks.
|
||||
Writing 0 is ignored; reading from IMCRs yields undefined values */
|
||||
typedef struct
|
||||
typedef volatile struct
|
||||
{
|
||||
byte_union(IMR0,
|
||||
uint :1;
|
||||
uint TUNI1_2 :1; /* TMU1 overflow interrupts */
|
||||
uint TUNI1_1 :1;
|
||||
uint TUNI1_0 :1;
|
||||
uint SDHII3 :1; /* SD Card Host 1 interrupts */
|
||||
uint SDHII2 :1;
|
||||
uint SDHII1 :1;
|
||||
uint SDHII0 :1;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR1,
|
||||
uint :4;
|
||||
uint DEI3 :1; /* DMAC0A interrupts */
|
||||
uint DEI2 :1;
|
||||
uint DEI1 :1;
|
||||
uint DEI0 :1;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR2,
|
||||
uint :7;
|
||||
uint SCIFA0 :1; /* Asynchronous Serial interrupts */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR3,
|
||||
uint DEI3 :1; /* DMAC1A interrupts */
|
||||
uint DEI2 :1;
|
||||
uint DEI1 :1;
|
||||
uint DEI0 :1;
|
||||
uint :4;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR4,
|
||||
uint :1;
|
||||
uint TUNI0_2 :1; /* TMU0 overflow interrupts */
|
||||
uint TUNI0_1 :1;
|
||||
uint TUNI0_0 :1;
|
||||
uint :3;
|
||||
uint LCDCI :1; /* LCD Controller Interrupt */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR5,
|
||||
uint KEYI :1; /* Key Interface */
|
||||
uint DADERR :1; /* DMAC0B interrupts */
|
||||
uint DEI5 :1;
|
||||
uint DEI4 :1;
|
||||
uint :1;
|
||||
uint SCIF2 :1; /* Serial Communication Interface */
|
||||
uint SCIF1 :1;
|
||||
uint SCIF0 :1;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR6,
|
||||
uint :2;
|
||||
uint :1;
|
||||
uint SCIFA4 :1; /* SCIFA4 interrupt */
|
||||
uint :1;
|
||||
uint :1;
|
||||
uint MSIOFI0 :1; /* Clock-synchronized SCIF channel 0 */
|
||||
uint MSIOFI1 :1; /* Clock-synchronized SCIF channel 1 */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
uint8_t IMR7;
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR8,
|
||||
uint SDHII3 :1; /* SD Card Host 0 interrupts */
|
||||
uint SDHII2 :1;
|
||||
uint SDHII1 :1;
|
||||
uint SDHII0 :1;
|
||||
uint :2;
|
||||
uint SCFIA5 :1; /* SCIFA5 interrupt */
|
||||
uint FSI :1; /* FIFO-Buffered Serial Interface */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR9,
|
||||
uint :3;
|
||||
uint CMTI :1; /* Compare Match Timer Interrupt */
|
||||
uint :1;
|
||||
uint USI1 :1; /* USB1 */
|
||||
uint USI0 :1; /* USB0 */
|
||||
uint :1;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR10,
|
||||
uint :1;
|
||||
uint DADERR :1; /* DMAC1B interrupts */
|
||||
uint DEI5 :1;
|
||||
uint DEI4 :1;
|
||||
uint :1;
|
||||
uint ATI :1; /* RTC Alarm interrupt */
|
||||
uint PRI :1; /* RTC Periodic interrupt */
|
||||
uint CUI :1; /* RTC Carry interrupt */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR11,
|
||||
uint :5;
|
||||
uint TPUI :1; /* Timer-Pulse Unit */
|
||||
uint :2;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
uint8_t IMR0; pad(3);
|
||||
uint8_t IMR1; pad(3);
|
||||
uint8_t IMR2; pad(3);
|
||||
uint8_t IMR3; pad(3);
|
||||
uint8_t IMR4; pad(3);
|
||||
uint8_t IMR5; pad(3);
|
||||
uint8_t IMR6; pad(3);
|
||||
uint8_t IMR7; pad(3);
|
||||
uint8_t IMR8; pad(3);
|
||||
uint8_t IMR9; pad(3);
|
||||
uint8_t IMR10; pad(3);
|
||||
uint8_t IMR11; pad(3);
|
||||
uint8_t IMR12;
|
||||
|
||||
} PACKED(4) sh7305_intc_masks_t;
|
||||
|
||||
|
||||
/* sh7305_intc_userimask_t - User Interrupt Mask
|
||||
Sets the minimum required level for interrupts to be accepted.
|
||||
|
||||
|
@ -362,11 +263,11 @@ typedef struct
|
|||
*(INTC._7305.USERIMASK) = mask;
|
||||
}
|
||||
*/
|
||||
typedef lword_union(sh7305_intc_userimask_t,
|
||||
uint _0xa5 :8; /* Always set to 0xa5 before writing */
|
||||
uint :16;
|
||||
uint UIMASK :4; /* User Interrupt Mask Level */
|
||||
uint :4;
|
||||
typedef volatile lword_union(sh7305_intc_userimask_t,
|
||||
uint32_t _0xa5 :8; /* Always set to 0xa5 before writing */
|
||||
uint32_t :16;
|
||||
uint32_t UIMASK :4; /* User Interrupt Mask Level */
|
||||
uint32_t :4;
|
||||
);
|
||||
|
||||
/* sh7305_intc_t - the SH7305 interrupt controller */
|
||||
|
@ -374,17 +275,25 @@ typedef struct
|
|||
{
|
||||
/* Interrupt priority registers */
|
||||
union {
|
||||
volatile sh7305_intc_ipc_t *_;
|
||||
volatile uint16_t *IPRS;
|
||||
sh7305_intc_ipc_t *_;
|
||||
volatile uint16_t *IPRS;
|
||||
};
|
||||
|
||||
/* Interrupt mask & mask clear registers */
|
||||
volatile sh7305_intc_masks_t *MSK;
|
||||
volatile sh7305_intc_masks_t *MSKCLR;
|
||||
sh7305_intc_masks_t *MSK;
|
||||
sh7305_intc_masks_t *MSKCLR;
|
||||
|
||||
/* Other registers */
|
||||
volatile sh7305_intc_userimask_t *USERIMASK;
|
||||
sh7305_intc_userimask_t *USERIMASK;
|
||||
|
||||
} PACKED(4) sh7305_intc_t;
|
||||
|
||||
//---
|
||||
// Forward definitions
|
||||
//---
|
||||
|
||||
/* Provided by core/gint.c */
|
||||
extern sh7705_intc_t INTC3;
|
||||
extern sh7305_intc_t INTC4;
|
||||
|
||||
#endif /* GINT_CORE_INTC */
|
||||
|
|
22
include/core/memory.h
Normal file
22
include/core/memory.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
//---
|
||||
// gint: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 */
|
|
@ -12,6 +12,68 @@
|
|||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 TLB
|
||||
//---
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
/* tlb_addr_t - address part of a TLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint VPN :22;
|
||||
uint :1;
|
||||
uint V :1;
|
||||
uint ASID :8;
|
||||
|
||||
} PACKED(4) tlb_addr_t;
|
||||
|
||||
/* tlb_data_t - data part of a TLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint :3;
|
||||
uint PPN :19;
|
||||
uint :1;
|
||||
uint V :1;
|
||||
uint :1;
|
||||
uint PR :2;
|
||||
uint SZ :1;
|
||||
uint C :1;
|
||||
uint D :1;
|
||||
uint SH :1;
|
||||
uint :1;
|
||||
|
||||
} PACKED(4) tlb_data_t;
|
||||
|
||||
/* tlb_addr() - get the P4 address of a TLB address entry
|
||||
@way TLB way (0..3)
|
||||
@E Entry number (0..31)
|
||||
Returns a pointer to the entry. */
|
||||
const tlb_addr_t *tlb_addr(uint way, uint E);
|
||||
|
||||
/* tlb_data() - get the P4 address of a TLB data entry
|
||||
@way TLB way (0..3)
|
||||
@E Entry number (0..31)
|
||||
Returns a pointer to the entry. */
|
||||
const tlb_data_t *tlb_data(uint way, uint E);
|
||||
|
||||
/* tlb_mapped_memort() - count amount of mapped memory
|
||||
This function returns the amount of mapped text and data segment memory, in
|
||||
bytes. The ranges are defined as follows:
|
||||
ROM 00300000:512k
|
||||
RAM 08100000:512k
|
||||
Other mappings are ignored. Both pointers may be NULL.
|
||||
|
||||
@rom Pointer to amount of mapped ROM
|
||||
@ram Pointer to amount of mapped RAM */
|
||||
void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram);
|
||||
|
||||
#endif
|
||||
|
||||
//---
|
||||
// SH7305 Unified TLB
|
||||
//---
|
||||
|
||||
/* utlb_addr_t - address part of a UTLB entry */
|
||||
typedef struct
|
||||
{
|
||||
|
@ -52,7 +114,7 @@ const utlb_data_t *utlb_data(uint E);
|
|||
/* utlb_mapped_memory() - count amount of mapped memory
|
||||
This function returns the amount of mapped text and data segment memory, in
|
||||
bytes. The ranges are defined as follows:
|
||||
ROM 00300000:512k
|
||||
ROM 00300000:4M
|
||||
RAM 08100000:512k
|
||||
Other mappings are ignored. Both pointers may be NULL.
|
||||
|
||||
|
|
|
@ -3,9 +3,13 @@
|
|||
//
|
||||
// This component detects the architecture and MPU type of the underlying
|
||||
// hardware by relying on version registers and/or side-information. It
|
||||
// provides macros isSH3() and isSH4() to perform MPU-dependent jobs:
|
||||
// provides macros isSH3() and isSH4(), but the best way of performing
|
||||
// MPU-dependent jobs is to use mpuSwitch():
|
||||
//
|
||||
// if(isSH3()) { ... } else { ... }
|
||||
// mpuSwitch(
|
||||
// print("SH3 code"),
|
||||
// print("SH4 code")
|
||||
// );
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_MPU
|
||||
|
@ -13,7 +17,7 @@
|
|||
|
||||
#include <defs/attributes.h>
|
||||
|
||||
/* mpu_t - Supported MPUs */
|
||||
/* mpu_t - supported MPUs */
|
||||
typedef enum
|
||||
{
|
||||
mpu_unknown = 0,
|
||||
|
@ -36,6 +40,8 @@ typedef enum
|
|||
#define isSH3() (mpu_id() & 1)
|
||||
#define isSH4() (!isSH3())
|
||||
|
||||
#define mpuSwitch(a, b) do { if(isSH3()) { a; } else { b; } } while(0)
|
||||
|
||||
/* mpu_init() - probe the MPU type
|
||||
This function must be executed before mpu_id() can be used. */
|
||||
void mpu_init(void);
|
||||
|
@ -47,6 +53,8 @@ typedef enum
|
|||
#define isSH3() 0
|
||||
#define isSH4() 1
|
||||
|
||||
#define mpuSwitch(a, b) do { b; } while(0)
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
#endif /* GINT_CORE_MPU */
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
/* Objects from the .gint.data and .gint.bss sections */
|
||||
#define GDATA __attribute__((section(".gint.data")))
|
||||
#define GBSS __attribute__((section(".gint.bss")))
|
||||
/* Additional sections that are only needed on SH3 */
|
||||
#define GDATA3 __attribute__((section(".gint.data.sh3")))
|
||||
#define GBSS3 __attribute__((section(".gint.bss.sh3")))
|
||||
/* Initialization functions */
|
||||
#define PRETEXT __attribute__((section(".pretext")))
|
||||
|
||||
|
|
|
@ -40,12 +40,13 @@ void t6k11_contrast(int contrast);
|
|||
|
||||
/* t6k11_backlight() - manage the screen backlight
|
||||
|
||||
Changes the backlight setting depending on the value of "setting":
|
||||
- If setting = 0, turns the backlight off.
|
||||
Changes the backlight status depending on the value of the argument:
|
||||
- If setting > 0, turns the backlight on.
|
||||
- If setting = 0, turns the backlight off.
|
||||
- If setting < 0, toggles backlight.
|
||||
|
||||
This function has no effect on models that do not support the backlight.
|
||||
This function has no effect on models that do not support the backlight,
|
||||
although gint does not provide any facility for detecting them.
|
||||
|
||||
@setting Requested backlight setting */
|
||||
void t6k11_backlight(int setting);
|
||||
|
|
8
include/gint/clock.h
Normal file
8
include/gint/clock.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
//---
|
||||
// gint:clock - Clock signals
|
||||
//---
|
||||
|
||||
#ifndef GINT_CLOCK
|
||||
#define GINT_CLOCK
|
||||
|
||||
#endif /* GINT_CLOCK */
|
|
@ -1,8 +1,113 @@
|
|||
//---
|
||||
// gint:display-fx - fx9860g drawing functions
|
||||
//
|
||||
// This module is in charge of all monochrome rendering. The gray engine
|
||||
// has its own functions, but often relies on this module (because the
|
||||
// gray effect is created through two monochrome buffers).
|
||||
//---
|
||||
|
||||
#ifndef GINT_DISPLAY_FX
|
||||
#define GINT_DISPLAY_FX
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
/* Screen dimensions on fx9860g */
|
||||
#define DWIDTH 128
|
||||
#define DHEIGHT 64
|
||||
|
||||
/* color_t - colors available for drawing
|
||||
The following colors are defined by the library:
|
||||
|
||||
OPAQUE COLORS (override existing pixels)
|
||||
white, black - the usual thing
|
||||
light, dark - additional colors used by the gray engine
|
||||
|
||||
OPERATORS (combine with existing pixels)
|
||||
none - leaves unchanged
|
||||
reverse - inverts white <-> black, light <-> dark
|
||||
lighten - shifts black -> dark -> light -> white -> white
|
||||
darken - shifts white -> light -> dark -> black -> black
|
||||
|
||||
Not all colors can be used with all functions. To avoid ambiguities, all
|
||||
functions explicitly indicate compatible colors. */
|
||||
typedef enum
|
||||
{
|
||||
/* Opaque colors */
|
||||
color_white = 0,
|
||||
color_light = 1,
|
||||
color_dark = 2,
|
||||
color_black = 3,
|
||||
|
||||
/* Monochrome operators */
|
||||
color_none = 4,
|
||||
color_reverse = 5,
|
||||
|
||||
/* Gray operators */
|
||||
color_lighten = 6,
|
||||
color_darken = 7,
|
||||
|
||||
} color_t;
|
||||
|
||||
//---
|
||||
// Area drawing functions
|
||||
//---
|
||||
|
||||
/* dupdate() - pushes the video RAM to the display driver
|
||||
This function makes the contents of the VRAM visible on the screen. It is
|
||||
the direct equivalent of Bdisp_PutDisp_DD() */
|
||||
void dupdate(void);
|
||||
|
||||
/* dclear() - fill the screen with a single color
|
||||
This function clears the screen by replacing all the pixels with a single
|
||||
color. This function is optimized for opaque drawing. If you wish to apply
|
||||
operators, use drect().
|
||||
|
||||
@color Allowed colors: white, black */
|
||||
void dclear(color_t color);
|
||||
|
||||
/* drect() - fill a rectangle of the screen
|
||||
This functions applies a color or an operator to a rectangle defined by two
|
||||
points (x1 y1) and (x2 y2). Both are included in the rectangle.
|
||||
|
||||
If GINT_LAX is defined, this function makes the following assumptions:
|
||||
0 <= x1 < x2 <= 127
|
||||
0 <= y1 < y2 <= 63
|
||||
|
||||
@x1 @y1 @x2 @y2 Bounding rectangle (drawn area).
|
||||
@color Allowed colors: white, black, none, reverse */
|
||||
void drect(int x1, int y1, int x2, int y2, color_t color);
|
||||
|
||||
//---
|
||||
// Point drawing functions
|
||||
//---
|
||||
|
||||
/* dpixel() - change a pixel's color
|
||||
If the requested color is an operator, the result will depend on the current
|
||||
color of the pixel.
|
||||
|
||||
If GINT_LAX is defined, this function makes the following assumptions:
|
||||
0 <= x <= 127
|
||||
0 <= y <= 63
|
||||
|
||||
@x @y Coordinates of the pixel to repaint
|
||||
@color Allowed colors: white, black, none, reverse */
|
||||
void dpixel(int x, int y, color_t color);
|
||||
|
||||
/* dline() - render a straight line
|
||||
This function draws a line using a Bresenham-style algorithm. Please note
|
||||
that the affected pixels may not be exactly the same when using dline() and
|
||||
Bdisp_DrawLineVRAM().
|
||||
|
||||
dline() has optimization facilities for horizontal and vertical lines.
|
||||
|
||||
If GINT_LAX is defined, this function makes the following assumptions:
|
||||
0 <= x1 <= x2 <= 127
|
||||
0 <= y1, y2 <= 63
|
||||
|
||||
@x1 @y1 @x2 @y2 End points of theline (both included).
|
||||
@color Allowed colors: white, black, none, reverse */
|
||||
void dline(int x1, int y1, int x2, int y2, color_t color);
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
#endif /* GINT_DISPLAY_FX */
|
||||
|
|
|
@ -8,12 +8,37 @@
|
|||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
//
|
||||
// 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()
|
||||
//
|
||||
// When the driver is unloaded, the following functions are called:
|
||||
// - ctx_restore()
|
||||
//---
|
||||
|
||||
/* gint_driver_t - driver meta-information used by gint */
|
||||
typedef struct
|
||||
{
|
||||
/* Driver name */
|
||||
const char *name;
|
||||
|
||||
/* driver_sh3() - rectify driver initialization on SH3 platforms
|
||||
This function is called during driver initialization on SH3. It may
|
||||
be NULL. */
|
||||
void (*driver_sh3)(void);
|
||||
|
||||
/* init() - initialize the driver
|
||||
This function is called after ctx_save() and needs not save the
|
||||
system setting. It is typically used to initialize registers to
|
||||
suitable values on startup. If there is no init function, this field
|
||||
may be set to NULL */
|
||||
void (*init)(void);
|
||||
|
||||
/* Size of a context object for the driver */
|
||||
uint ctx_size;
|
||||
|
||||
|
@ -44,4 +69,12 @@ typedef struct
|
|||
#define GINT_DECLARE_DRIVER(name) \
|
||||
SECTION(".gint.drivers") 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. */
|
||||
#ifdef FX9860G
|
||||
#define GINT_DRIVER_SH3(name) name
|
||||
#else
|
||||
#define GINT_DRIVER_SH3(name) NULL
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DRIVERS */
|
||||
|
|
|
@ -56,4 +56,68 @@ void gint_unload(void);
|
|||
getkey() call. */
|
||||
void gint_pause(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
|
||||
|
||||
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.
|
||||
When an interrupt request is accepted, the hardware jumps to a specific
|
||||
interrupt handler at an address that depends on the interrupt source.
|
||||
|
||||
Each interrupt handler should only refer to data within its own block
|
||||
because the relative displacement between blocks is MPU-dependent. There are
|
||||
a few exceptions to this, such as timer handlers, which are contiguous on
|
||||
all currently-used platforms. Be careful.
|
||||
|
||||
This function allows anyone to replace any interrupt handler so make sure
|
||||
you're not interfering with usual interrupt assignments.
|
||||
|
||||
The first parameter 'event_code' represents the event_code associated with
|
||||
the interrupt. These codes are normally platform-dependent, but gint always
|
||||
uses the SH7305 codes: SH3 platforms have a translation table. See the
|
||||
documentation for a list of event codes and their associated interrupts.
|
||||
|
||||
The handler function must be an interrupt handler: it should not raise
|
||||
exceptions, must end with 'rte', uses the kernel register bank... and it
|
||||
must fit within 32 bytes. If it's not written in assembler, then you're
|
||||
likely doing something wrong.
|
||||
|
||||
It is common for interrupt handlers to have a few bytes of data, such as the
|
||||
address of a callback function. gint often stores this data in the last
|
||||
bytes of the block. This function returns the VBR address of the block to
|
||||
allow the caller to edit the parameters.
|
||||
|
||||
@event_code Identifier of the interrupt block
|
||||
@handler Address of handler function
|
||||
Returns the VBR address where the handlers was installed. */
|
||||
void *gint_inthandler(int event_code, const void *handler);
|
||||
|
||||
#endif /* GINT_GINT */
|
||||
|
|
157
include/gint/timer.h
Normal file
157
include/gint/timer.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
//---
|
||||
// gint:timer - Timer operation
|
||||
//---
|
||||
|
||||
#ifndef GINT_TIMER
|
||||
#define GINT_TIMER
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <core/mpu.h>
|
||||
|
||||
/* Timer identifiers
|
||||
|
||||
Hardware timers are numbered with integers starting from 0. You can freely
|
||||
access all the available timers by using their number once you have
|
||||
configured them with timer_setup(). The number of timers depends on the MPU:
|
||||
|
||||
SH3-based: 4 timers, ids 0..3 [SH7355, SH7337]
|
||||
SH4-based: 9 timers, ids 0..8 [SH7305]
|
||||
|
||||
You should be aware that some of these timers are used by default by gint:
|
||||
- Timer 1 is used by the gray engine on fx9860g.
|
||||
- Timer 3 is used by the keyboard, unless GINT_RTC_KEYBOARD is defined. This
|
||||
macro is controlled by the -rtc-keyboard switch when gint is compiled.
|
||||
|
||||
timer_setup() will fail if you try to use a timer that's already running.
|
||||
Always check the return value of timer_setup()! Using a timer id that has
|
||||
not been validated by timer_setup() will work, but do *something else* than
|
||||
what you intended. */
|
||||
|
||||
/* timer_count() - tells how many timers are available on the platform */
|
||||
HDRFUNC int timer_count(void)
|
||||
{
|
||||
mpuSwitch(
|
||||
return 4, /* SH3-based */
|
||||
return 9 /* SH4-based */
|
||||
);
|
||||
}
|
||||
|
||||
/* Clock input
|
||||
|
||||
Timers count down when their input clock ticks, and fire when their counter
|
||||
reach 0. The choice of the input clock influences the resolution of the
|
||||
timer, but if the clock is too fast, the 32-bit counter might not be able to
|
||||
represent long delays.
|
||||
|
||||
Several input clocks are available. The peripheral clock (Po) can be divided
|
||||
by 4, 16, 64 or 256; as an alternative the external clock TCLK can be used
|
||||
for counting. I suspect TCLK runs at a fixed frequency of 32768 Hz, but this
|
||||
has yet to be verified.
|
||||
|
||||
You don't really need to choose an input clock unless you are doing
|
||||
something very specific. In most practical cases you can use timer_default
|
||||
which is 0. See the timer_delay() function for more information. */
|
||||
typedef enum
|
||||
{
|
||||
timer_Po_4 = 0,
|
||||
timer_Po_16 = 1,
|
||||
timer_Po_64 = 2,
|
||||
timer_Po_256 = 3,
|
||||
timer_TCLK = 5,
|
||||
|
||||
timer_default = timer_Po_4,
|
||||
|
||||
} timer_input_t;
|
||||
|
||||
//---
|
||||
// Timer functions
|
||||
//---
|
||||
|
||||
/* timer_setup() - set up a timer
|
||||
|
||||
This function configures the requested timer without starting it. On
|
||||
success, it returns the first argument "timer", which is used as a timer
|
||||
identifier in all other timer functions. If the requested timer is already
|
||||
in use, this function fails and returns a negative number.
|
||||
|
||||
This function sets the timer delay, the clock source, and registers a
|
||||
callback function to be called when the timer fires. An argument can be
|
||||
supplied to the callback function in the form of a pointer.
|
||||
|
||||
When the timer fires, the callback function is called with the provided
|
||||
argument pointer. The callback decides whether the timer should continue
|
||||
running (by returning 0) or stop (by returning nonzero). In the latter case,
|
||||
events accumulated while the callback was running are dropped.
|
||||
|
||||
It is sometimes difficult to choose a timer constant and a clock source
|
||||
given a wished delay in seconds, especially when overclock is used. The
|
||||
timer_delay() function is provided for this purpose.
|
||||
|
||||
@timer Requested timer id
|
||||
@delay Delay between each event (the unit depends on the clock source)
|
||||
@clock Clock source used by the timer for counting down
|
||||
@callback Callback function (called when the timer fires)
|
||||
@arg Passed as argument to the callback function */
|
||||
int timer_setup(int timer, uint32_t delay, timer_input_t clock,
|
||||
int (*callback)(void *arg), void *arg);
|
||||
|
||||
/* timer_delay() - compute a delay constant from a duration in seconds
|
||||
|
||||
This function can used as a facility to calculate the [delay] argument to
|
||||
the timer_setup() function. It takes a microsecond delay as an argument and
|
||||
returns the corresponding timer constant. A typical use to start a timer
|
||||
with a 25 ms interval would be:
|
||||
|
||||
timer_setup(0, timer_delay(0, 25 * 1000), 0, callback, arg);
|
||||
|
||||
WARNING: Only timers 0 to 2 can count microseconds! Other timers have a
|
||||
resolution of around 30 us. Counting in ms is safe for all timers, though.
|
||||
|
||||
For standard timers (0 to 2) it uses Po / 4 as clock input, which is very
|
||||
precise and can represent up to 3 minutes' time; for extra timers (3 and
|
||||
above) the clock is fixed to 32768 Hz.
|
||||
|
||||
@timer The timer you are planning to use
|
||||
@delay_us Requested delay in microseconds */
|
||||
uint32_t timer_delay(int timer, int delay_us);
|
||||
|
||||
/* timer_start() - start a configured timer
|
||||
The specified timer will start counting down and fire callbacks at regular
|
||||
intervals.
|
||||
|
||||
@timer Timer id, as returned by timer_setup() */
|
||||
void timer_start(int timer);
|
||||
|
||||
/* timer_reload() - change a timer's delay and source
|
||||
|
||||
Changes the delay and source of the given timer. This function does not
|
||||
start the timer if it stopped. The timer will start counting down from the
|
||||
new delay when it is started (or immediately if it was running), no matter
|
||||
how much time has elapsed since it last fired. Accumulated events are not
|
||||
dropped.
|
||||
|
||||
In cases where choosing a delay and input clock is difficult (such as when
|
||||
using overclocking), use the timer_delay() function.
|
||||
|
||||
@timer Timer id, as returned by timer_setup()
|
||||
@delay New delay (unit depends on the clock source)
|
||||
@clock New clock source to be used by the timer */
|
||||
void timer_reload(int timer, uint32_t delay, timer_input_t clock);
|
||||
|
||||
/* timer_pause() - stop a running timer
|
||||
The specified timer will be paused; its counter will not be reset. A stopped
|
||||
timer can be resumed anytime by calling timer_start(). If you want to also
|
||||
reset the counter, use timer_reload().
|
||||
|
||||
@timer Timer id, as returned by timer_setup() */
|
||||
void timer_pause(int timer);
|
||||
|
||||
/* timer_free() - free a timer
|
||||
Stops and destroys a timer, making its id free for re-use. The id must not
|
||||
be used anymore until it is returned by a further call to timer_setup().
|
||||
|
||||
@timer Timer id, as returned by timer_setup() */
|
||||
void timer_free(int timer);
|
||||
|
||||
#endif /* GINT_TIMER */
|
0
src/clock/freq.c
Normal file
0
src/clock/freq.c
Normal file
|
@ -14,7 +14,7 @@ extern char
|
|||
sgdata, sgbss, sdata, sbss,
|
||||
btors, mtors, etors;
|
||||
|
||||
/* bootlog_loaded() - Section loading stage */
|
||||
/* bootlog_loaded() - section loading stage */
|
||||
void bootlog_loaded(void)
|
||||
{
|
||||
/* Version string - the string constant resides in ROM */
|
||||
|
@ -61,6 +61,7 @@ void bootlog_loaded(void)
|
|||
print_dec(11, 3, gint_size, 4);
|
||||
print_dec(17, 3, &mtors - &btors, 2);
|
||||
print_dec(20, 3, &etors - &mtors, 2);
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
}
|
||||
|
||||
|
@ -74,17 +75,17 @@ void bootlog_mapped(int rom, int ram)
|
|||
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);
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
}
|
||||
|
||||
/* bootlog_kernel() - Gint loading stage */
|
||||
/* bootlog_kernel() - gint loading stage */
|
||||
void bootlog_kernel(void)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
#ifdef FX9860G
|
||||
extern sh7705_intc_t INTC3;
|
||||
print(15, 1, " Kernel");
|
||||
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
print(1, 5, "ABCD");
|
||||
print_hex( 6, 5, INTC3._.IPRA->word, 4);
|
||||
print_hex(10, 5, INTC3._.IPRB->word, 4);
|
||||
|
@ -95,12 +96,8 @@ void bootlog_kernel(void)
|
|||
print_hex(10, 6, INTC3._.IPRF->word, 4);
|
||||
print_hex(14, 6, INTC3._.IPRG->word, 4);
|
||||
print_hex(18, 6, INTC3._.IPRH->word, 4);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
extern sh7305_intc_t INTC4;
|
||||
|
||||
},{
|
||||
/* SH4-based */
|
||||
print(1, 5, "ACFG");
|
||||
print_hex( 6, 5, INTC4._->IPRA.word, 4);
|
||||
print_hex(10, 5, INTC4._->IPRC.word, 4);
|
||||
|
@ -111,7 +108,7 @@ void bootlog_kernel(void)
|
|||
print_hex(10, 6, INTC4._->IPRJ.word, 4);
|
||||
print_hex(14, 6, INTC4._->IPRK.word, 4);
|
||||
print_hex(18, 6, INTC4._->IPRL.word, 4);
|
||||
}
|
||||
});
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
}
|
||||
|
|
32
src/core/exch.S
Normal file
32
src/core/exch.S
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
** gint:core:exch - Exception handlers
|
||||
*/
|
||||
|
||||
.global _exch_entry_7305
|
||||
|
||||
#ifdef FX9860G
|
||||
.global _exch_entry_7705
|
||||
#endif
|
||||
|
||||
.section .gint.blocks, "ax"
|
||||
.align 4
|
||||
|
||||
/* SH7305-TYPE DEBUG EXCEPTION HANDLER - 26 BYTES */
|
||||
|
||||
_exch_entry_7705:
|
||||
_exch_entry_7305:
|
||||
mov.l 1f, r0
|
||||
mov.l @r0, r4
|
||||
|
||||
sts.l pr, @-r15
|
||||
mov.l 2f, r0
|
||||
jsr @r0
|
||||
nop
|
||||
lds.l @r15+, pr
|
||||
rte
|
||||
nop
|
||||
|
||||
.zero 6
|
||||
|
||||
2: .long _debug_exc
|
||||
1: .long 0xff000024
|
64
src/core/gint.c
Normal file
64
src/core/gint.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
//---
|
||||
// gint:core:gint - Library functions
|
||||
//---
|
||||
|
||||
#include <gint/gint.h>
|
||||
#include <core/mpu.h>
|
||||
#include <core/intc.h>
|
||||
#include <core/memory.h>
|
||||
|
||||
/* Interrupt controllers */
|
||||
|
||||
GDATA3 sh7705_intc_t INTC3 = {
|
||||
.IPRS = {
|
||||
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||
(void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004,
|
||||
},
|
||||
.ICR1 = (void *)0xa4000010,
|
||||
};
|
||||
|
||||
GDATA sh7305_intc_t INTC4 = {
|
||||
.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;
|
||||
|
||||
mpuSwitch(
|
||||
ipr = INTC3.IPRS[intid >> 2], /* SH3-based */
|
||||
ipr = &INTC4.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)
|
||||
{
|
||||
extern char gint_vbr;
|
||||
|
||||
/* 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 + 0x620;
|
||||
return memcpy(dest, handler, 32);
|
||||
}
|
|
@ -5,11 +5,10 @@
|
|||
** blocks depending on its configuration.
|
||||
*/
|
||||
|
||||
|
||||
.global _inth_entry_7305
|
||||
.global _inth_entry_7305
|
||||
|
||||
#ifdef FX9860G
|
||||
.global _inth_entry_7705
|
||||
.global _inth_entry_7705
|
||||
#endif
|
||||
|
||||
.section .gint.blocks, "ax"
|
||||
|
@ -34,20 +33,13 @@
|
|||
references unless their relative order is fully known. This happens with
|
||||
the timer driver, and it works swimmingly; just be careful. */
|
||||
|
||||
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */
|
||||
/* SH7305-TYPE DEBUG INTERRUPT HANDLER - 26 BYTES */
|
||||
|
||||
#if 0
|
||||
_inth_entry_7305:
|
||||
/* Get the event code from the INTEVT register */
|
||||
mov.l 1f, r0
|
||||
/* TODO: mov.l @r0, r0 */
|
||||
mov.l @r0, r4
|
||||
|
||||
/* Interrupt codes start at 0x400 */
|
||||
mov #4, r1
|
||||
shll8 r1
|
||||
sub r1, r0
|
||||
|
||||
/* Jump to a C routine (TODO: Remove this) */
|
||||
sts.l pr, @-r15
|
||||
mov.l 2f, r0
|
||||
jsr @r0
|
||||
|
@ -56,13 +48,31 @@ _inth_entry_7305:
|
|||
rte
|
||||
nop
|
||||
|
||||
/* Add the distance between nop and the first entry, and jump
|
||||
add #16, r0
|
||||
braf r0
|
||||
nop */
|
||||
.zero 6
|
||||
|
||||
2: .long _debug
|
||||
1: .long 0xff000028
|
||||
#endif
|
||||
|
||||
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */
|
||||
|
||||
_inth_entry_7305:
|
||||
/* Get the event code from the INTEVT register */
|
||||
mov.l 1f, r0
|
||||
mov.l @r0, r0
|
||||
|
||||
/* Interrupt codes start at 0x400 */
|
||||
mov #4, r1
|
||||
shll8 r1
|
||||
sub r1, r0
|
||||
|
||||
/* Add the distance to the first entry and jump */
|
||||
add #16, r0
|
||||
braf r0
|
||||
nop
|
||||
|
||||
.zero 12
|
||||
1: .long 0xff000028
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
|
@ -90,11 +100,10 @@ _inth_entry_7705:
|
|||
1: .long 0xa4000000 /* INTEVT2 register */
|
||||
2: .long _inth_remap
|
||||
|
||||
|
||||
.section .gint.data
|
||||
.align 4
|
||||
|
||||
/* EVENT CODE TRANSLATION TABLE - 72 BYTES */
|
||||
/* EVENT CODE TRANSLATION TABLE - 96 BYTES */
|
||||
|
||||
_inth_remap:
|
||||
.byte 0x00, 0x01, 0x02, 0xfe, 0x34, 0x35, 0x36, 0xff
|
||||
|
@ -106,5 +115,8 @@ _inth_remap:
|
|||
.byte 0xff, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff
|
||||
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
.byte 0x2d, 0x2d, 0xff, 0xff, 0x2d, 0x2d, 0xff, 0xff
|
||||
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
.byte 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
|
||||
#endif
|
||||
|
|
|
@ -85,23 +85,24 @@ void *memcpy(void * restrict dst, const void * restrict src, size_t n)
|
|||
return dst;
|
||||
}
|
||||
|
||||
void *_memmove(void *dst, const void *src, size_t n)
|
||||
void *_memmove(UNUSED void *dst, UNUSED const void *src, UNUSED size_t n)
|
||||
{
|
||||
// (same as memcpy, but heed for direction if areas overlap)
|
||||
|
||||
// more complicated
|
||||
// allocate a buffer aligned with destination (source would be ok too)
|
||||
// read unaligned from source to buffer
|
||||
// copy aligned from buffer to destination
|
||||
// copy by increasing addresses if dst < src
|
||||
// copy by decreasing addresses if dst > src
|
||||
return dst;
|
||||
}
|
||||
|
||||
int _memcmp(const void *s1, const void *s2, size_t n)
|
||||
int _memcmp(UNUSED const void *s1, UNUSED const void *s2, UNUSED size_t n)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *_memset(void *s, int byte, size_t n)
|
||||
void *memset(void *s, int byte, size_t n)
|
||||
{
|
||||
/* TODO: Do it efficiently */
|
||||
char *dst = s;
|
||||
while(n--) *dst++ = byte;
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,58 @@
|
|||
|
||||
#include <core/mmu.h>
|
||||
|
||||
//---
|
||||
// SH7705 TLB
|
||||
//---
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
/* tlb_addr() - get the P4 address of a TLB address entry */
|
||||
INLINE const tlb_addr_t *tlb_addr(uint way, uint E)
|
||||
{
|
||||
uint32_t addr = 0xf2000000 | (E << 12) | (way << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
/* tlb_data() - get the P4 address of a TLB data entry */
|
||||
INLINE const tlb_data_t *tlb_data(uint way, uint E)
|
||||
{
|
||||
uint32_t addr = 0xf3000000 | (E << 12) | (way << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
/* tlb_mapped_memort() - count amount of mapped memory */
|
||||
void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
|
||||
{
|
||||
uint32_t rom = 0, ram = 0;
|
||||
|
||||
for(int way = 0; way < 4; way++)
|
||||
for(int E = 0; E < 32; E++)
|
||||
{
|
||||
const tlb_addr_t *addr = tlb_addr(way, E);
|
||||
const tlb_data_t *data = tlb_data(way, E);
|
||||
if(!addr->V || !data->V) continue;
|
||||
|
||||
int size = data->SZ ? 4096 : 1024;
|
||||
uint32_t src;
|
||||
|
||||
if(data->SZ) src = (((addr->VPN >> 2) | E) << 12);
|
||||
else src = (addr->VPN | (E << 2)) << 10;
|
||||
|
||||
if(src >= 0x00300000 && src < 0x00380000) rom += size;
|
||||
if(src >= 0x08100000 && src < 0x08180000) ram += size;
|
||||
}
|
||||
|
||||
if(p_rom) *p_rom = rom;
|
||||
if(p_ram) *p_ram = ram;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//---
|
||||
// SH7305 Unified TLB
|
||||
//---
|
||||
|
||||
/* utlb_addr() - get the P4 address of a UTLB address entry */
|
||||
INLINE const utlb_addr_t *utlb_addr(uint E)
|
||||
{
|
||||
|
@ -29,11 +81,12 @@ void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
|
|||
const utlb_data_t *data = utlb_data(E);
|
||||
if(!addr->V || !data->V) continue;
|
||||
|
||||
/* Magic formula to get the size without using an array */
|
||||
int sz = ((data->SZ1 << 1) | data->SZ2) << 3;
|
||||
int size = 1 << ((0x14100c0a >> sz) & 0xff);
|
||||
|
||||
uint32_t src = addr->VPN << 10;
|
||||
if(src >= 0x00300000 && src < 0x00380000) rom += size;
|
||||
if(src >= 0x00300000 && src < 0x00700000) rom += size;
|
||||
if(src >= 0x08100000 && src < 0x08180000) ram += size;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,34 +3,18 @@
|
|||
//---
|
||||
|
||||
#include <gint/gint.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <core/memory.h>
|
||||
#include <core/setup.h>
|
||||
#include <core/intc.h>
|
||||
#include <core/mpu.h>
|
||||
|
||||
/* Interrupt controllers */
|
||||
|
||||
#ifdef FX9860G
|
||||
GDATA sh7705_intc_t INTC3 = {
|
||||
.IPRS = {
|
||||
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||
(void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004,
|
||||
},
|
||||
.ICR1 = (void *)0xa4000010,
|
||||
};
|
||||
#endif
|
||||
|
||||
GDATA sh7305_intc_t INTC4 = {
|
||||
.IPRS = (void *)0xa4080000,
|
||||
.MSK = (void *)0xa4080080,
|
||||
.MSKCLR = (void *)0xa40800c0,
|
||||
.USERIMASK = (void *)0xa4700000,
|
||||
};
|
||||
|
||||
/* VBR address, from the linker script */
|
||||
extern char gint_vbr;
|
||||
/* System's VBR address */
|
||||
GBSS static uint32_t system_vbr;
|
||||
/* Driver table */
|
||||
extern gint_driver_t bdrv, edrv;
|
||||
|
||||
//---
|
||||
// Context system for gint's core
|
||||
|
@ -46,32 +30,24 @@ typedef struct
|
|||
@arg ctx gint core context object */
|
||||
static void gint_ctx_save(gint_core_ctx *ctx)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
#ifdef FX9860G
|
||||
for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i];
|
||||
}
|
||||
mpuSwitch(
|
||||
/* SH3-based */
|
||||
for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]),
|
||||
/* SH4-based */
|
||||
for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i]
|
||||
);
|
||||
}
|
||||
|
||||
/* gint_ctx_restore() - restore interrupt controller configuration
|
||||
@arg ctx gint core context object */
|
||||
static void gint_ctx_restore(gint_core_ctx *ctx)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
#ifdef FX9860G
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i];
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i];
|
||||
}
|
||||
mpuSwitch(
|
||||
/* SH3-based */
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i],
|
||||
/* SH4-based */
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i]
|
||||
);
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -85,16 +61,12 @@ GBSS static gint_core_ctx sys_ctx;
|
|||
static void lock(void)
|
||||
{
|
||||
/* Just disable everything, drivers will enable what they support */
|
||||
if(isSH3())
|
||||
{
|
||||
#ifdef FX9860G
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000;
|
||||
}
|
||||
mpuSwitch(
|
||||
/* SH3-based */
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000,
|
||||
/* SH4-based */
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000
|
||||
);
|
||||
}
|
||||
|
||||
/* gint_install() - install and start gint */
|
||||
|
@ -104,8 +76,8 @@ void gint_install(void)
|
|||
uint32_t vbr = (uint32_t)&gint_vbr;
|
||||
|
||||
/* Event handler entry points */
|
||||
extern void inth_entry_7705(void);
|
||||
extern void inth_entry_7305(void);
|
||||
void *exch_entry;
|
||||
void *inth_entry;
|
||||
|
||||
/* First save the hardware configuration. This step is crucial because
|
||||
we don't want the system to find out about us directly manipulating
|
||||
|
@ -115,7 +87,21 @@ void gint_install(void)
|
|||
/* Load the event handler entry points into memory */
|
||||
/* TODO: Load an exception handler and a TLB miss handler */
|
||||
|
||||
void *inth_entry = isSH3() ? inth_entry_7705 : inth_entry_7305;
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
extern void exch_entry_7705(void);
|
||||
extern void inth_entry_7705(void);
|
||||
exch_entry = exch_entry_7705;
|
||||
inth_entry = inth_entry_7705;
|
||||
},{
|
||||
/* SH4-based */
|
||||
extern void exch_entry_7305(void);
|
||||
extern void inth_entry_7305(void);
|
||||
exch_entry = exch_entry_7305;
|
||||
inth_entry = inth_entry_7305;
|
||||
});
|
||||
|
||||
memcpy((void *)(vbr + 0x100), exch_entry, 32);
|
||||
memcpy((void *)(vbr + 0x600), inth_entry, 32);
|
||||
|
||||
/* Time to switch VBR and roll! */
|
||||
|
@ -126,6 +112,12 @@ void gint_install(void)
|
|||
static void unlock(void)
|
||||
{
|
||||
gint_ctx_restore(&sys_ctx);
|
||||
|
||||
/* Restore all driver settings */
|
||||
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
|
||||
{
|
||||
drv->ctx_restore(drv->sys_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* gint_unload() - unload gint and give back control to the system */
|
||||
|
|
|
@ -118,7 +118,6 @@ int start(int isappli, int optnum)
|
|||
mpu_init();
|
||||
#endif
|
||||
|
||||
|
||||
/* Load data sections and wipe the bss section. This has to be done
|
||||
first for static and global variables to be initialized */
|
||||
regcpy(lgdata, sgdata, rgdata);
|
||||
|
@ -129,23 +128,25 @@ int start(int isappli, int optnum)
|
|||
bootlog_loaded();
|
||||
#endif
|
||||
|
||||
/* TODO: Do the TLB investigation on SH3 (UTLB things are SH4 only) */
|
||||
|
||||
/* Traverse all ROM pages */
|
||||
explore(brom, srom);
|
||||
|
||||
/* Count how much memory got mapped from this process */
|
||||
uint32_t rom, ram;
|
||||
utlb_mapped_memory(&rom, &ram);
|
||||
mpuSwitch(
|
||||
tlb_mapped_memory(&rom, &ram), /* SH3-based */
|
||||
utlb_mapped_memory(&rom, &ram) /* SH4-based */
|
||||
);
|
||||
|
||||
#ifdef GINT_BOOT_LOG
|
||||
bootlog_mapped(rom, ram);
|
||||
#endif
|
||||
|
||||
//---
|
||||
|
||||
#ifdef GINT_BOOT_LOG
|
||||
isSH3() ? bootlog_mapped(-1, -1)
|
||||
: bootlog_mapped(rom, ram);
|
||||
#endif
|
||||
|
||||
/* Cancel add-in execution if not all pages are mapped
|
||||
TODO: Resort to better graphical display */
|
||||
TODO: Resort to better graphical display, but still fxlib since
|
||||
add-in is not mapped yet */
|
||||
if(rom < (uint32_t)&srom)
|
||||
{
|
||||
Bdisp_AllClr_VRAM();
|
||||
|
@ -176,7 +177,9 @@ int start(int isappli, int optnum)
|
|||
/* Initialize all drivers by saving the system settings */
|
||||
for(drv = &bdrv; drv < &edrv; drv++)
|
||||
{
|
||||
if(isSH3() && drv->driver_sh3) drv->driver_sh3();
|
||||
drv->ctx_save(drv->sys_ctx);
|
||||
if(drv->init) drv->init();
|
||||
}
|
||||
|
||||
/* With gint fully initialized, we are ready to start the hosted user
|
||||
|
@ -195,15 +198,10 @@ int start(int isappli, int optnum)
|
|||
/* Before leaving the application, we need to clean up our mess. We
|
||||
have changed many OS settings while accessing the peripheral
|
||||
modules. The OS is bound to be confused (and crash) if we don't
|
||||
resoture them. */
|
||||
restore them */
|
||||
|
||||
/* Restore all driver settings */
|
||||
for(drv = &drv; drv < &edrv; drv++)
|
||||
{
|
||||
drv->ctx_restore(drv->sys_ctx);
|
||||
}
|
||||
|
||||
/* Finally, unload gint and give back control to the system */
|
||||
/* Unload gint and give back control to the system. Driver settings
|
||||
will be restored while interrupts are disabled */
|
||||
gint_unload();
|
||||
|
||||
/* TODO: Invoke main menu instead of returning? */
|
||||
|
|
|
@ -66,8 +66,8 @@ INLINE static void select(uint16_t reg)
|
|||
|
||||
/* Set RS back. We don't do this in read()/write() because the display
|
||||
driver is optimized for consecutive GRAM access. LCD-transfers will
|
||||
be faster when executing select() followed by many write().
|
||||
(Most applications should use DMA though.) */
|
||||
be faster when executing select() followed by several calls to
|
||||
write(). (Although most applications should use the DMA instead.) */
|
||||
*PRDR |= 0x10;
|
||||
synco();
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ void r61524_test(void)
|
|||
{
|
||||
print(1, 2, "Aborting.");
|
||||
Bdisp_PutDisp_DD();
|
||||
delay(20);
|
||||
delay(40);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ void r61524_test(void)
|
|||
print_hex(12, 6, COL, 1);
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
delay(10);
|
||||
delay(50);
|
||||
|
||||
//---
|
||||
|
||||
|
@ -211,7 +211,12 @@ void r61524_test(void)
|
|||
print_hex(14, 3, VEA, 3);
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
delay(20);
|
||||
delay(50);
|
||||
|
||||
//
|
||||
|
||||
Bdisp_AllClr_VRAM();
|
||||
Bdisp_PutDisp_DD();
|
||||
|
||||
//---
|
||||
|
||||
|
@ -227,57 +232,24 @@ void r61524_test(void)
|
|||
|
||||
//---
|
||||
|
||||
// Bdisp_AllClr_VRAM();
|
||||
// Bdisp_PutDisp_DD();
|
||||
|
||||
select(ram_address_horizontal);
|
||||
write(HEA);
|
||||
select(ram_address_vertical);
|
||||
write(VSA);
|
||||
select(write_data);
|
||||
|
||||
uint16_t color = 0xf3e4;
|
||||
for(int v = 0; v < 396; v++)
|
||||
for(int h = 0; h < 224; h++)
|
||||
uint16_t color;
|
||||
for(int v = 0; v < 224; v++)
|
||||
for(int h = 0; h < 396; h++)
|
||||
{
|
||||
color = (v << 7) ^ h ^ 0x3754;
|
||||
// int offset = 396 * v + h;
|
||||
// uint8_t *src = gimp_image.pixel_data + 2 * offset;
|
||||
color = 0x3eb7; // (src[1] << 8) | src[0];
|
||||
|
||||
write(color);
|
||||
}
|
||||
|
||||
delay(60);
|
||||
|
||||
//---
|
||||
|
||||
/* Shift by 9, not 8, because of horizontal/vertical range inversion */
|
||||
select(ram_address_horizontal);
|
||||
AD = read();
|
||||
select(ram_address_vertical);
|
||||
AD |= read() << 9;
|
||||
|
||||
select(horizontal_ram_start);
|
||||
HSA = read();
|
||||
select(horizontal_ram_end);
|
||||
HEA = read();
|
||||
|
||||
select(vertical_ram_start);
|
||||
VSA = read();
|
||||
select(vertical_ram_end);
|
||||
VEA = read();
|
||||
|
||||
Bdisp_AllClr_VRAM();
|
||||
|
||||
print(1, 1, "Address=?????");
|
||||
print_hex(9, 1, AD, 5);
|
||||
|
||||
print(1, 2, "HSA=??? HEA=???");
|
||||
print_hex(5, 2, HSA, 3);
|
||||
print_hex(14, 2, HEA, 3);
|
||||
print(1, 3, "VSA=??? VEA=???");
|
||||
print_hex(5, 3, VSA, 3);
|
||||
print_hex(14, 3, VEA, 3);
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
delay(10);
|
||||
delay(200);
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -330,6 +302,7 @@ static void ctx_restore(void *buf)
|
|||
|
||||
gint_driver_t drv_r61524 = {
|
||||
.name = "Renesas R61524",
|
||||
.init = NULL,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
.sys_ctx = &sys_ctx,
|
||||
.ctx_save = ctx_save,
|
||||
|
|
|
@ -140,16 +140,15 @@ void t6k11_backlight(int setting)
|
|||
/* This setting is mapped to an I/O port:
|
||||
- On SH3, bit 7 of port G
|
||||
- On SH4, bit 4 of port N */
|
||||
if(isSH3())
|
||||
{
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
port = (void *)0xa400012c;
|
||||
mask = 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
},{
|
||||
/* SH4-based */
|
||||
port = (void *)0xa4050138;
|
||||
mask = 0x10;
|
||||
}
|
||||
});
|
||||
|
||||
if(!setting) *port &= ~mask;
|
||||
if(setting > 0) *port |= mask;
|
||||
|
@ -198,6 +197,7 @@ static void ctx_restore(void *buf)
|
|||
|
||||
gint_driver_t drv_t6k11 = {
|
||||
.name = "Toshiba T6K11",
|
||||
.init = NULL,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
.sys_ctx = &sys_ctx,
|
||||
.ctx_save = ctx_save,
|
||||
|
|
225
src/tmu/inth.s
Normal file
225
src/tmu/inth.s
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
** gint:tmu:inth - Interrupt handlers for the timer units
|
||||
** Perhaps the most technical of my interrupt handlers. They implement a
|
||||
** simple kind of interrupt handler communication by letting the control flow
|
||||
** from each interrupt handler to the next.
|
||||
*/
|
||||
|
||||
/* Gates for the standard Timer Unit (TMU) */
|
||||
.global _inth_tmu_0
|
||||
.global _inth_tmu_1
|
||||
.global _inth_tmu_2
|
||||
.global _inth_tmu_storage
|
||||
|
||||
/* Gates for the extra timers (informally called ETMU) */
|
||||
.global _inth_tmu_extra2
|
||||
.global _inth_tmu_extra_help
|
||||
.global _inth_tmu_extra_others
|
||||
|
||||
.section .gint.blocks, "ax"
|
||||
.align 4
|
||||
|
||||
/* TMU INTERRUPT HANDLERS - 128 BYTES
|
||||
Unfortunately I did not manage to write a handler that cleared the interrupt
|
||||
flag and invoked a callback in less than 34 bytes data included. So I
|
||||
decided to make several gates operate as a whole and add a bit more features
|
||||
in them. Basically, these handlers:
|
||||
- Clear the interrupt flag
|
||||
- Invoke a callback function and pass it a user-provided argument
|
||||
- Stop the timer if the callback returns non-zero
|
||||
- Host their own callback and arguments
|
||||
|
||||
It is important to notice that the code of the following gates looks like
|
||||
they are contiguous in memory. The assembler will make that assumption, and
|
||||
turn any address reference between two gates into a *relative displacement*.
|
||||
If the gates don't have the same relative location at runtime, the code will
|
||||
crash because we will have broken the references. This is why we can only do
|
||||
it with handlers that are mapped to consecutive event codes. */
|
||||
|
||||
/* FIRST GATE - TMU0 entry, clear underflow flag and call back */
|
||||
_inth_tmu_0:
|
||||
mova .storage0, r0
|
||||
mov #-2, r1
|
||||
|
||||
/*** This is the first shared section ***/
|
||||
|
||||
.clearflag:
|
||||
mov.l r1, @-r15
|
||||
|
||||
/* Load the TCR address and clear the interrupt flag */
|
||||
mov.l .mask, r3
|
||||
mov.l @(8, r0), r1
|
||||
mov.w @r1, r2
|
||||
and r3, r2
|
||||
mov.w r2, @r1
|
||||
|
||||
/* Invoke the callback function and pass the argument */
|
||||
sts.l pr, @-r15
|
||||
mov.l @r0, r1
|
||||
jsr @r1
|
||||
mov.l @(4, r0), r4
|
||||
lds.l @r15+, pr
|
||||
|
||||
/* Prepare stopping the timer */
|
||||
mov.l .tstr, r5
|
||||
bra .stoptimer
|
||||
mov.l @r15+, r1
|
||||
|
||||
/* SECOND GATE - TMU1 entry and stop timer */
|
||||
_inth_tmu_1:
|
||||
mova .storage1, r0
|
||||
bra .clearflag
|
||||
mov #-3, r1
|
||||
|
||||
/*** This is the second shared section ***/
|
||||
|
||||
.stoptimer:
|
||||
/* Stop the timer if the return value is not zero */
|
||||
tst r0, r0
|
||||
bt .end
|
||||
mov.b @r5, r2
|
||||
and r1, r2
|
||||
mov.b r2, @r5
|
||||
|
||||
.end:
|
||||
rte
|
||||
nop
|
||||
|
||||
.zero 12
|
||||
|
||||
/* THIRD GATE - TMU2 entry and storage for TMU0 */
|
||||
_inth_tmu_2:
|
||||
mova .storage2, r0
|
||||
bra .clearflag
|
||||
mov #-5, r1
|
||||
|
||||
.zero 14
|
||||
|
||||
.storage0:
|
||||
.long 0 /* Callback: Configured dynamically */
|
||||
.long 0 /* Argument: Configured dynamically */
|
||||
.long 0xa4490010 /* TCR0: Overridden at startup on SH3 */
|
||||
|
||||
/* FOURTH GATE - Storage for TMU1, TMU2 and other values */
|
||||
_inth_tmu_storage:
|
||||
|
||||
.mask: .long 0x0000feff
|
||||
.tstr: .long 0xa4490004 /* TSTR: Overridden at startup on SH3 */
|
||||
|
||||
.storage1:
|
||||
.long 0 /* Callback: Configured dynamically */
|
||||
.long 0 /* Argument: Configured dynamically */
|
||||
.long 0xa449001c /* TCR1: Overridden at startup on SH3 */
|
||||
|
||||
.storage2:
|
||||
.long 0 /* Callback: Configured dynamically */
|
||||
.long 0 /* Argument: Configured dynamically */
|
||||
.long 0xa4490028 /* TCR2: Overridden at startup on SH3 */
|
||||
|
||||
|
||||
|
||||
/* EXTRA TMU INTERRUPT HANDLERS - 96 BYTES
|
||||
To implement the same functionalities as the standard timers, several blocks
|
||||
are once again needed. But the handlers for the extra timers are not located
|
||||
in adjacent gates, except for ETMU1 and ETMU2 which have event codes 0xc20
|
||||
and 0xc40. Since handler 0xc60 is free on both SH3 and SH4, I'm using it to
|
||||
build a three-handler block and achieve the same result as above.
|
||||
|
||||
On SH4 this means that an extra gate has to be installed, but no interrupt
|
||||
pointed here. On SH3 this means that four gates are used for the only extra
|
||||
timer, but the incurred cost is minimal (96 bytes on the binary file)
|
||||
because the size of the VBR area can hardly be shrunk anyway.
|
||||
|
||||
It *is* possible to do generalized communication between interrupt handlers
|
||||
that do not reside in consecutive gates. The general way of performing a
|
||||
jump or data access between two interrupt handlers would be to store at
|
||||
runtime the address of the target resource in a reserved longword in the
|
||||
source handler. But longwords are costly in 32-byte areas. Even if the event
|
||||
codes of the interrupt handlers are known at development time, the best I
|
||||
can think of is hardcoding the relative displacements, and one would need to
|
||||
use the unnatural and unmaintainable @(disp, pc) addressing modes. */
|
||||
|
||||
/* FIRST GATE - ETMU2 entry, clear flag and prepare callback */
|
||||
_inth_tmu_extra2:
|
||||
/* Warning: the size of this section (2 bytes) is hardcoded in another
|
||||
interrupt handler, _inth_tmu_extra_others */
|
||||
mova .storage_extra_1, r0
|
||||
|
||||
.extra_callback:
|
||||
mov.l r8, @-r15
|
||||
mov r0, r8
|
||||
stc.l gbr, @-r15
|
||||
|
||||
/* Invoke the callback function */
|
||||
sts.l pr, @-r15
|
||||
mov.l @r8, r1
|
||||
jsr @r1
|
||||
mov.l @(4, r8), r4
|
||||
|
||||
bra .extra_clearflag
|
||||
lds.l @r15+, pr
|
||||
|
||||
.storage_extra_1:
|
||||
.long 0 /* Callback: Configured dynamically */
|
||||
.long 0 /* Argument: Configured dynamically */
|
||||
.long 0 /* Structure address: Edited at startup */
|
||||
|
||||
/* SECOND GATE - Helper entry, invoke callback and stop timer if requested */
|
||||
_inth_tmu_extra_help:
|
||||
|
||||
.extra_clearflag:
|
||||
mov r0, r5
|
||||
|
||||
/* Load struture address */
|
||||
mov.l @(8, r8), r0
|
||||
ldc r0, gbr
|
||||
mov #12, r0
|
||||
|
||||
.extra_loopclear:
|
||||
/* Aggressively clear the interrupt flag. The loop is required because
|
||||
clearing takes "some time". The system does it. Not doing it will
|
||||
cause the interrupt to be triggered again until the flag is cleared,
|
||||
which can be ~20 interrupts if the handler is fast! */
|
||||
and.b #0xfd, @(r0, gbr)
|
||||
tst.b #0x02, @(r0, gbr)
|
||||
bf .extra_loopclear
|
||||
|
||||
/* Check whether to stop the timer */
|
||||
tst r5, r5
|
||||
bt .extra_end
|
||||
|
||||
.extra_stoptimer:
|
||||
mov.b @(0, gbr), r0
|
||||
and #0xfe, r0
|
||||
mov.b r0, @(0, gbr)
|
||||
|
||||
.extra_end:
|
||||
ldc.l @r15+, gbr
|
||||
mov.l @r15+, r8
|
||||
rte
|
||||
nop
|
||||
|
||||
/* FOURTH GATE - All other ETMU entries, deferred to the previous ones */
|
||||
_inth_tmu_extra_others:
|
||||
/* Dynamically compute the target of the jump */
|
||||
stc vbr, r1
|
||||
mov.l 1f, r2
|
||||
add r2, r1
|
||||
|
||||
mova .storage_extra_others, r0
|
||||
jmp @r1
|
||||
nop
|
||||
|
||||
/* Offset from VBR where extra timer 2 is located:
|
||||
- 0x600 to reach the interrupt handlers
|
||||
- 0x020 to jump over the entry gate
|
||||
- 0x840 to reach the handler of extra timer 2
|
||||
- 0x002 to jump over the first part of its code */
|
||||
1: .long 0xe62
|
||||
|
||||
.zero 4
|
||||
|
||||
.storage_extra_others:
|
||||
.long 0 /* Callback: Configured dynamically */
|
||||
.long 0 /* Argument: Configured dynamically */
|
||||
.long 0 /* Structure address: Edited at startup */
|
429
src/tmu/tmu.c
Normal file
429
src/tmu/tmu.c
Normal file
|
@ -0,0 +1,429 @@
|
|||
//---
|
||||
// gint:tmu - Timer operation
|
||||
//---
|
||||
|
||||
#include <gint/timer.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
|
||||
#include <core/intc.h>
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// Timer structures
|
||||
//---
|
||||
|
||||
/* tmu_t - a single timer from a standard timer unit */
|
||||
typedef volatile struct
|
||||
{
|
||||
uint32_t TCOR; /* Constant register */
|
||||
uint32_t TCNT; /* Counter register, counts down */
|
||||
|
||||
word_union(TCR,
|
||||
uint16_t :7;
|
||||
uint16_t UNF :1; /* Underflow flag */
|
||||
uint16_t :2;
|
||||
uint16_t UNIE :1; /* Underflow interrupt enable */
|
||||
uint16_t CKEG :2; /* Input clock edge */
|
||||
uint16_t TPSC :3; /* Timer prescaler (input clock) */
|
||||
);
|
||||
|
||||
} PACKED(4) tmu_t;
|
||||
|
||||
/* tmu_extra_t - extra timers on sh7337, sh7355 and sh7305 */
|
||||
typedef volatile struct
|
||||
{
|
||||
uint8_t TSTR; /* Only bit 0 is used */
|
||||
pad(3);
|
||||
|
||||
uint32_t TCOR; /* Constant register */
|
||||
uint32_t TCNT; /* Counter register */
|
||||
|
||||
byte_union(TCR,
|
||||
uint8_t :6;
|
||||
uint8_t UNF :1; /* Underflow flag */
|
||||
uint8_t UNIE :1; /* Underflow interrupt enable */
|
||||
);
|
||||
|
||||
} PACKED(4) tmu_extra_t;
|
||||
|
||||
/* inth_data_t - data storage inside interrupt handlers */
|
||||
typedef struct
|
||||
{
|
||||
int (*callback)(void *arg); /* User-provided callback function */
|
||||
void *arg; /* Argument for [callback] */
|
||||
volatile void *structure; /* Either TCR or timer address */
|
||||
|
||||
} PACKED(4) inth_data_t;
|
||||
|
||||
/* timer_t - all data required to run a single timer */
|
||||
typedef struct
|
||||
{
|
||||
void *tmu; /* Address of timer structure */
|
||||
inth_data_t *data; /* Interrupt handler data */
|
||||
uint16_t event; /* Interrupt event code */
|
||||
} timer_t;
|
||||
|
||||
|
||||
//---
|
||||
// Driver storage
|
||||
//---
|
||||
|
||||
/* This is the description of the structure on SH4. SH3-based fx9860g models,
|
||||
which are already very rare, will adapt the values in init functions */
|
||||
GDATA static timer_t timers[9] = {
|
||||
{ .tmu = (void *)0xa4490008, .event = 0x400 },
|
||||
{ .tmu = (void *)0xa4490014, .event = 0x420 },
|
||||
{ .tmu = (void *)0xa4490020, .event = 0x440 },
|
||||
{ .tmu = (void *)0xa44d0030, .event = 0x9e0 },
|
||||
{ .tmu = (void *)0xa44d0050, .event = 0xc20 },
|
||||
{ .tmu = (void *)0xa44d0070, .event = 0xc40 },
|
||||
{ .tmu = (void *)0xa44d0090, .event = 0x900 },
|
||||
{ .tmu = (void *)0xa44d00b0, .event = 0xd00 },
|
||||
{ .tmu = (void *)0xa44d00d0, .event = 0xfa0 },
|
||||
};
|
||||
|
||||
/* TSTR register for standard timers */
|
||||
GDATA static volatile uint8_t *TSTR = (void *)0xa4490004;
|
||||
|
||||
//---
|
||||
// Timer API
|
||||
//---
|
||||
|
||||
/* timer_setup() - set up a timer */
|
||||
int timer_setup(int tid, uint32_t delay, timer_input_t clock,
|
||||
int (*callback)(void *arg), void *arg)
|
||||
{
|
||||
/* We need to distinguish normal and extra timers */
|
||||
if(tid < 3)
|
||||
{
|
||||
/* Refuse to setup timers that are already in use */
|
||||
tmu_t *t = timers[tid].tmu;
|
||||
if(t->TCR.UNIE) return -1;
|
||||
|
||||
/* Configure the registers of the target timer */
|
||||
t->TCOR = delay;
|
||||
t->TCNT = delay;
|
||||
t->TCR.TPSC = clock;
|
||||
|
||||
/* Clear the interrupt flag */
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
|
||||
t->TCR.UNIE = 1; /* Enable interrupt on underflow */
|
||||
t->TCR.CKEG = 0; /* Count on rising edge (SH7705) */
|
||||
}
|
||||
|
||||
/* Extra timers have a simpler structure */
|
||||
else
|
||||
{
|
||||
tmu_extra_t *t = timers[tid].tmu;
|
||||
if(t->TCR.UNIE) return -1;
|
||||
|
||||
/* There is no clock input and no clock edge settings */
|
||||
t->TCOR = delay;
|
||||
t->TCNT = delay;
|
||||
|
||||
/* Clear the interrupt flag */
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
|
||||
t->TCR.UNIE = 1;
|
||||
}
|
||||
|
||||
/* Register the callback and its argument */
|
||||
timers[tid].data->callback = callback;
|
||||
timers[tid].data->arg = arg;
|
||||
|
||||
/* Return the timer id, since configuration was successful */
|
||||
return tid;
|
||||
}
|
||||
|
||||
/* timer_delay() - compute a delay constant from a duration in seconds */
|
||||
uint32_t timer_delay(int tid, int delay_us)
|
||||
{
|
||||
/* TODO: Proper timer_delay() */
|
||||
#ifdef FX9860G
|
||||
uint64_t freq = 14750000 >> 2; /* 14.75 MHz / 4 */
|
||||
#else
|
||||
uint64_t freq = 29020000 >> 2; /* 29.49 MHz / 4 */
|
||||
#endif
|
||||
if(tid >= 3) freq = 32768; /* 32768 Hz */
|
||||
|
||||
uint64_t product = freq * (uint64_t)delay_us;
|
||||
return product / 1000000;
|
||||
}
|
||||
|
||||
/* timer_control() - start or stop a timer
|
||||
@timer Timer ID to configure
|
||||
@state 0 to start the timer, 1 to stop it (nothing else!) */
|
||||
static void timer_control(int tid, int state)
|
||||
{
|
||||
if(tid < 3)
|
||||
{
|
||||
/* For standard timers, use the MPU's TSTR register */
|
||||
*TSTR = (*TSTR | (1 << tid)) ^ (state << tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Extra timers all have their own TSTR register */
|
||||
tmu_extra_t *t = timers[tid].tmu;
|
||||
t->TSTR = (t->TSTR | 1) ^ state;
|
||||
}
|
||||
}
|
||||
|
||||
/* timer_start() - start a configured timer */
|
||||
void timer_start(int tid)
|
||||
{
|
||||
timer_control(tid, 0);
|
||||
}
|
||||
|
||||
/* timer_reload() - change a timer's constant register for next interrupts */
|
||||
void timer_reload(int tid, uint32_t delay, timer_input_t clock)
|
||||
{
|
||||
if(tid < 3) ((tmu_t *)timers[tid].tmu)->TCOR = delay;
|
||||
else ((tmu_extra_t *)timers[tid].tmu)->TCOR = delay;
|
||||
}
|
||||
|
||||
/* timer_pause() - stop a running timer */
|
||||
void timer_pause(int tid)
|
||||
{
|
||||
timer_control(tid, 1);
|
||||
}
|
||||
|
||||
/* timer_free() - free a timer */
|
||||
void timer_free(int tid)
|
||||
{
|
||||
/* Stop the timer and disable UNIE to indicate that it's free */
|
||||
timer_pause(tid);
|
||||
|
||||
if(tid < 3)
|
||||
{
|
||||
tmu_t *t = timers[tid].tmu;
|
||||
t->TCR.UNIE = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmu_extra_t *t = timers[tid].tmu;
|
||||
t->TCR.UNIE = 0;
|
||||
|
||||
/* Also clear TCOR and TCNT to avoid spurious interrupts */
|
||||
t->TCOR = 0xffffffff;
|
||||
t->TCNT = 0xffffffff;
|
||||
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
//---
|
||||
|
||||
/* Interrupt handlers provided by tmu/inth.s for standard timers */
|
||||
extern void inth_tmu_0(void);
|
||||
extern void inth_tmu_1(void);
|
||||
extern void inth_tmu_2(void);
|
||||
extern void inth_tmu_storage(void);
|
||||
|
||||
/* Interrupt handlers provided by tmu/inth.s for extra timers */
|
||||
extern void inth_tmu_extra1(void);
|
||||
extern void inth_tmu_extra2(void);
|
||||
extern void inth_tmu_extra_help(void);
|
||||
extern void inth_tmu_extra_others(void);
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
/* Install the standard's TMU interrupt handlers. By chance TMU gates
|
||||
use the same event codes on SH7705 and SH7305 */
|
||||
UNUSED void *h0, *h1, *h2, *hs;
|
||||
h0 = gint_inthandler(0x400, inth_tmu_0);
|
||||
h1 = gint_inthandler(0x420, inth_tmu_1);
|
||||
h2 = gint_inthandler(0x440, inth_tmu_2);
|
||||
hs = gint_inthandler(0x460, inth_tmu_storage);
|
||||
|
||||
/* User information in interrupt handlers */
|
||||
timers[0].data = h2 + 20;
|
||||
timers[1].data = hs + 8;
|
||||
timers[2].data = hs + 20;
|
||||
|
||||
/* SH3: Override the address of TSTR in the interrupt handler helper */
|
||||
if(isSH3()) *(volatile uint8_t **)(hs + 4) = TSTR;
|
||||
|
||||
/* Stop all timers */
|
||||
*TSTR = 0;
|
||||
|
||||
/* This driver uses the UNIE (UNderflow Interrupt Enable) bit of the
|
||||
TCR register to indicate which timers are being used; reset them */
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
tmu_t *t = timers[i].tmu;
|
||||
t->TCR.UNIE = 0;
|
||||
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
|
||||
/* Standard timers: TCR is provided to the interrupt handler */
|
||||
timers[i].data->structure = &t->TCR;
|
||||
}
|
||||
|
||||
/* Clear the extra timers */
|
||||
for(int i = 3; i < timer_count(); i++)
|
||||
{
|
||||
tmu_extra_t *t = timers[i].tmu;
|
||||
|
||||
/* This is *extremely important*: extra timers will generate
|
||||
interrupts as long as TCNT = 0 *regardless of TSTR*! */
|
||||
t->TCOR = 0xffffffff;
|
||||
t->TCNT = 0xffffffff;
|
||||
|
||||
t->TCR.UNIE = 0;
|
||||
t->TSTR = 0;
|
||||
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
}
|
||||
|
||||
/* Install the extra timers. We need three extra timers for the
|
||||
interrupt handlers to work, so install 3 on SH3, even if only one
|
||||
actually exists */
|
||||
int limit = isSH3() ? 6 : 9;
|
||||
|
||||
for(int i = 3; i < limit; i++)
|
||||
{
|
||||
void *handler = (i == 5)
|
||||
? inth_tmu_extra2
|
||||
: inth_tmu_extra_others;
|
||||
void *h = gint_inthandler(timers[i].event, handler);
|
||||
|
||||
timers[i].data = h + 20;
|
||||
timers[i].data->structure = timers[i].tmu;
|
||||
}
|
||||
|
||||
/* Also install the helper handler */
|
||||
gint_inthandler(0xc60, inth_tmu_extra_help);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Enable the extra TMUs at level 7 */
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
gint_intlevel(23, 7);
|
||||
},{
|
||||
/* SH4-based */
|
||||
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 interrupts */
|
||||
INTC4.MSKCLR->IMR2 = 0x01;
|
||||
INTC4.MSKCLR->IMR5 = 0x06;
|
||||
INTC4.MSKCLR->IMR6 = 0x18;
|
||||
INTC4.MSKCLR->IMR8 = 0x02;
|
||||
});
|
||||
}
|
||||
|
||||
//---
|
||||
// Context system for this driver
|
||||
//---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tmu_t std[3];
|
||||
tmu_extra_t extra[6];
|
||||
uint8_t TSTR;
|
||||
|
||||
} PACKED(4) ctx_t;
|
||||
|
||||
/* Allocate a system buffer in gint's BSS area */
|
||||
GBSS static ctx_t sys_ctx;
|
||||
|
||||
#ifdef FX9860G
|
||||
static void driver_sh3(void)
|
||||
{
|
||||
timers[0].tmu = (void *)0xfffffe94;
|
||||
timers[1].tmu = (void *)0xfffffea0;
|
||||
timers[2].tmu = (void *)0xfffffeac;
|
||||
/* We don't need to change the event code of ETMU0 since it's
|
||||
translated to the SH4 code by the interrupt handler */
|
||||
timers[3].tmu = (void *)0xa44c0030;
|
||||
|
||||
TSTR = (void *)0xfffffe92;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ctx_save(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
tmu_t *t = timers[i].tmu;
|
||||
ctx->std[i].TCOR = t->TCOR;
|
||||
ctx->std[i].TCNT = t->TCNT;
|
||||
ctx->std[i].TCR.word = t->TCR.word;
|
||||
}
|
||||
|
||||
ctx->TSTR = *TSTR;
|
||||
|
||||
for(int i = 0; i < timer_count() - 3; i++)
|
||||
{
|
||||
tmu_extra_t *t = timers[i + 3].tmu;
|
||||
ctx->extra[i].TCOR = t->TCOR;
|
||||
ctx->extra[i].TCNT = t->TCNT;
|
||||
ctx->extra[i].TCR.byte = t->TCR.byte;
|
||||
ctx->extra[i].TSTR = t->TSTR;
|
||||
}
|
||||
}
|
||||
|
||||
static void ctx_restore(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
tmu_t *t = timers[i].tmu;
|
||||
t->TCNT = ctx->std[i].TCNT;
|
||||
t->TCOR = ctx->std[i].TCOR;
|
||||
t->TCR.word = ctx->std[i].TCR.word;
|
||||
}
|
||||
|
||||
*TSTR = ctx->TSTR;
|
||||
|
||||
for(int i = 0; i < timer_count() - 3; i++)
|
||||
{
|
||||
tmu_extra_t *t = timers[i + 3].tmu;
|
||||
/* Avoid some interrupts that occur when TCNT = 0 */
|
||||
t->TSTR = 0;
|
||||
t->TCNT = 0xffffffff;
|
||||
|
||||
t->TCNT = ctx->extra[i].TCNT;
|
||||
t->TCOR = ctx->extra[i].TCOR;
|
||||
t->TCR.byte = ctx->extra[i].TCR.byte;
|
||||
t->TSTR = ctx->extra[i].TSTR;
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver structure definition
|
||||
//---
|
||||
|
||||
gint_driver_t drv_tmu = {
|
||||
.name = "Timer Unit",
|
||||
.init = init,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
.sys_ctx = &sys_ctx,
|
||||
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
|
||||
.ctx_save = ctx_save,
|
||||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(drv_tmu);
|
Loading…
Reference in a new issue