More interrupt system, exceptions, timers.

This commit is contained in:
lephe 2018-08-01 20:41:36 +02:00
parent 3b90b40dd7
commit 2f0e049c33
29 changed files with 1568 additions and 404 deletions

View file

@ -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
View file

@ -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"

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -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
View 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 */

View file

@ -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.

View file

@ -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 */

View file

@ -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")))

View file

@ -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
View file

@ -0,0 +1,8 @@
//---
// gint:clock - Clock signals
//---
#ifndef GINT_CLOCK
#define GINT_CLOCK
#endif /* GINT_CLOCK */

View file

@ -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 */

View file

@ -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 */

View file

@ -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
View 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
View file

View 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
View 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
View 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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 */

View file

@ -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? */

View file

@ -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,

View file

@ -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
View 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
View 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);