core: add a hardware detection interface

This commit is contained in:
lephe 2019-07-04 12:11:43 -04:00
parent 7798f276ae
commit 3324d58afa
18 changed files with 386 additions and 201 deletions

189
include/gint/hardware.h Normal file
View file

@ -0,0 +1,189 @@
//---
// gint:hardware - Platform information and hardware detection
//
// This components centralizes detected information about the runtime
// hardware, including MPU version, peripheral modules, and how drivers
// configured them.
//
// The most common use of this header is for the isSH3() and isSH4()
// macros that let you run MPU-dependent jobs and are used like this:
// if(isSH3()) do_sh3();
// else do_sh4();
//---
#ifndef GINT_PLATFORM
#define GINT_PLATFORM
#include <gint/defs/types.h>
/* Most of the information here is going to be stored in (key, value) pairs for
predetermined keys and 32-bits values that are often integers or a set of
flags. The data will be filled by gint or its drivers. */
#define HW_KEYS 16
extern uint32_t gint[HW_KEYS];
/* MPU detection macros, with a faster version on fx-CG 50 */
#ifdef FX9860G
#define isSH3() (gint[HWMPU] & 1)
#define isSH4() (!isSH3())
#endif
#ifdef FXCG50
#define isSH3() 0
#define isSH4() 1
#endif
/* hw_detect(): Basic hardware detection
This function probes the hardware and fills in the HWMPU, HWCPUVR and
HWCPUPR fields. */
void hw_detect(void);
/* This bit should be set in all data longwords except HWMPU, HWCPUVR, HWCPUPR
and HWCALC which are guaranteed to always be loaded. If not set then the
information must be treated as invalid. */
#define HW_LOADED 0x80000000
/*
** Key list
*/
#define HWMPU 0 /* MPU type */
#define HWCPUVR 1 /* CPU Version Register */
#define HWCPUPR 2 /* CPU Product Register */
#define HWCALC 3 /* Calculator model */
#define HWRAM 4 /* Amount of RAM */
#define HWROM 5 /* Amount of ROM */
#define HWMMU 6 /* Memory Management Unit */
#define HWURAM 7 /* Userspace RAM (set iff HWMMU is loaded) */
#define HWCPG 8 /* Clock Pulse Generator */
#define HWDMA 9 /* Direct Memory Access Controller */
#define HWTMU 10 /* Timer Unit */
#define HWETMU 11 /* Extra Timer Units */
#define HWRTC 12 /* Real-Time Clock */
#define HWKBD 13 /* Keyboard */
#define HWKBDSF 14 /* Keyboard Scan Frequency (set iff HWKBD is loaded) */
#define HWDD 15 /* Display Driver */
/*
** MPU type
*/
/* Unknown MPUs are all assumed to be SH-4A-based */
#define HWMPU_UNKNOWN 0
/* Used on original fx-9860G, SH-3-based */
#define HWMPU_SH7337 1
/* Used on recent fx-9860G derivates such as the fx-9750G II, and also on the
fx-CG 10/20/50. SH-4A-based */
#define HWMPU_SH7305 2
/* Used on the fx-9860G II, SH-3-based */
#define HWMPU_SH7355 3
/* Closest documented match to the SH7305, not used in any known calculator.
Detected and included for reference only */
#define HWMPU_SH7724 4
/*
** Calculator type
*/
/* SH-3-based fx-9860G-family */
#define HWCALC_FX9860G_SH3 1
/* Other SH-4A-based fx-9860G-family */
#define HWCALC_FX9860G_SH4 2
/* Graph 35+E II, an SH-4A French extension of the fx-9860G family */
#define HWCALC_G35PE2 3
/* fx-CG 10/20, also known as the "Prizm" family */
#define HWCALC_PRIZM 4
/* fx-CG 50, a late extension to the Prizm family */
#define HWCALC_FXCG50 5
/*
** Memory Management Unit
*/
/* MMU has a unified TLB. Essentially correlated with SH4. */
#define HWMMU_UTLB 0x01
/* Add-in is fully mapped in the TLB. This means that gint needs not handle TLB
misses as exceptions. This should generally be set on fx9860g, but not on
fxcg50. */
#define HWMMU_FITTLB 0x02
/*
** Clock Pulse Generator
*/
/* Input clock frequency is known for this model and all frequencies are
computed (they used to be measured from the RTC). Should be 1 */
#define HWCPG_COMP 0x01
/* Used an extended CPG interface, correlated with SH4 */
#define HWCPG_EXT 0x02
/*
** Direct Memory Access Controller
*/
/* Nothing other than the HW_LOADED bit yet */
/*
** Timer Unit
*/
/* Nothing other than the HW_LOADED bit yet */
/*
** Extra Timer Units
*/
/* A single-timer ETMU unit was found. Correlated with SH3 */
#define HWETMU_1 0x01
/* A 6-timer ETMU unit was found. Correlated with SH4 */
#define HWETMU_6 0x02
/* Individual timer status. Not all timers might be operational after setting
up the driver due to seemingly limitless behavioral differences with the
TMU. Operational here means TCNT=TCOR=-1, interrupt disabled and cleared. */
#define HWETMU_OK0 0x04
#define HWETMU_OK1 0x08
#define HWETMU_OK2 0x10
#define HWETMU_OK3 0x20
#define HWETMU_OK4 0x40
#define HWETMU_OK5 0x80
/*
** Real-Time Clock
*/
/* The RTC timer is enabled */
#define HWRTC_TIMER 0x01
/*
** Keyboard
*/
/* The keyboard uses an I/O-port-based scan method. This is possible on both
SH3 and SH4, but gint will normally do it only on SH3. */
#define HWKBD_IO 0x01
/* When using the I/O-port scanning method on SH3, whether the watchdog is used
to delay I/O operations. */
#define HWKBD_WDD 0x02
/* The keyboard uses a KEYSC-based scan method. This is only possible on SH4 */
#define HWKBD_KSI 0x04
/*
** Display Driver
*/
/* Display driver is known. This cannot be determined on fx9860g as the Toshiba
T6K11 and its Graph 35+E II variant don't seem to have an identification
command. It is set to 0 on fx9860g and used on fxcg50. */
#define HWDD_KNOWN 0x01
/* The display driver was configured to use the full screen, instead of leaving
bands on the side. [fxcg50] */
#define HWDD_FULL 0x02
/* The contrast address for this OS version is known. [fx9860g] */
#define HWDD_CONTRAST 0x04
/* Backlight management is supported. This is used both on fx9860g models with
backlit screen (although that very fact cannot be detected) and fxcg50. */
#define HWDD_LIGHT 0x08
#endif /* GINT_PLATFORM */

View file

@ -1,87 +0,0 @@
//---
// gint:core:mpu - Runtime MPU detection
//
// 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() for MPU-dependent jobs
//
// if(isSH3()) print("SH3 code");
// else print("SH4 code");
//---
#ifndef GINT_CORE_MPU
#define GINT_CORE_MPU
#include <gint/defs/types.h>
/* mpu_t - supported MPUs */
typedef enum
{
mpu_unknown = 0,
mpu_sh7337 = 1, /* fx9860g, SH-3 based */
mpu_sh7305 = 2, /* fx9860g II, fxcg50, SH-4A based */
mpu_sh7355 = 3, /* fx9860g II, SH-3 based */
mpu_sh7724 = 4, /* For reference */
} mpu_t;
/* On fx9860g, the 256k byte RAM might be extended to 512k if the machine is
recent enough. This includes all SH4 and many SH3 fx-9750 GII; the only
model known to not have it is the old fx-9860G. */
#ifdef FX9860G
typedef struct
{
/* MPU type, one of the above values */
mpu_t mpu;
/* Extended RAM area (8804'0000:256k) is available */
int extended_ram;
} platform_t;
/* mpu_id() - get the name of the underlying MPU */
#define gint_mpu() (gint_platform.mpu)
/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */
#define isSH3() (gint_mpu() & 1)
#define isSH4() (!isSH3())
#endif /* FX9860G */
/* On fxcg50, the processor is always SH4. We can still differentiate between
modern fx-CG 50 and older fx-CG 10/20 which are called here "Prizm". (This
is done by observing the initial stack pointer.) */
#ifdef FXCG50
typedef struct
{
/* MPU type (always sh7305) */
mpu_t mpu;
/* Whether this is an fx-CG 10/20 Prizm instead of an fx-CG 50 */
int prizm;
} platform_t;
/* All fxcg50 machines have an SH7305, which makes things simpler. */
#define gint_mpu() mpu_sh7305
#define isSH3() 0
#define isSH4() 1
#endif /* FX9860G */
/* Platform details collected by mpu_init() */
extern const platform_t gint_platform;
/* mpu_init() - detect hardware information
This function must be executed before other functions of this header can be
used successfully.
@stack Starting stack address (roughly is enough) */
void mpu_init(uint32_t stack);
#endif /* GINT_CORE_MPU */

View file

@ -6,7 +6,7 @@
#define GINT_TIMER
#include <gint/defs/types.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
/* Timer identifiers

View file

@ -6,7 +6,7 @@
#include <gint/clock.h>
#include <core/std.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <gint/mpu/cpg.h>
//---
@ -65,6 +65,8 @@ static void sh7705_probe(void)
freq.Bphi_f = ckio;
freq.Iphi_f = (idiv == 3) ? ckio_3 : ckio >> idiv;
freq.Pphi_f = (pdiv == 3) ? ckio_3 : ckio >> pdiv;
gint[HWCPG] |= HWCPG_COMP;
}
#undef CPG
@ -109,6 +111,8 @@ static void sh7305_probe(void)
freq.Bphi_f = base >> (divb + 1);
freq.Iphi_f = base >> (divi + 1);
freq.Pphi_f = base >> (divp + 1);
gint[HWCPG] |= HWCPG_COMP | HWCPG_EXT;
}
#undef CPG
@ -135,11 +139,13 @@ static const char *cpg_status(void)
static void init(void)
{
/* This avoids warnings about sh7705() not being defined on fxcg50 */
gint[HWCPG] = HW_LOADED;
/* This avoids warnings about sh7705_probe() being undefined when
building for fxcg50 */
#ifdef FX9860G
isSH3() ? sh7705_probe() :
#endif
sh7305_probe();
}

View file

@ -6,7 +6,7 @@
#include <core/std.h>
#include <core/mmu.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <gint/mpu/intc.h>
#include <gint/gint.h>
@ -65,22 +65,22 @@ void bootlog_loaded(void)
uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss;
/* MPU type */
mpu_t mpu = gint_mpu();
int mpu = gint[HWMPU];
const char *names = "SH7337\0 SH7305\0 SH7355\0 SH7724";
/* TODO: Use a solid API for boot-time printing */
dclear(color_white);
print(1, 1, "gint @%7x SLmkd", GINT_VERSION);
if((uint)mpu < 4) print(1, 2, names + 8 * (mpu - 1));
if(mpu >= 1 && mpu <= 4) print(1, 2, names + 8 * (mpu - 1));
else print(1, 2, "%6d", mpu);
#ifdef FX9860G
print(8, 2, "%c?-", gint_platform.extended_ram ? 'R' : 'r');
print(8, 2, "%c?-", gint[HWRAM] >= (512 << 10) ? 'R' : 'r');
#endif
#ifdef FXCG50
print(8, 2, "--%c", gint_platform.prizm ? 'P' : 'p');
print(8, 2, "---");
#endif
char os[11];

View file

@ -4,7 +4,7 @@
#include <gint/gint.h>
#include <core/std.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <gint/mpu/intc.h>
/* Interrupt controllers */

115
src/core/hardware.c Normal file
View file

@ -0,0 +1,115 @@
//---
// gint:core:hardware - Platform information and hardware detection
//---
#include <gint/hardware.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <gint/defs/util.h>
#include <gint/mpu/pfc.h>
/* Holds information about the current platform */
GBSS uint32_t gint[HW_KEYS];
/* Processor Version Register */
#define PVR (*((volatile uint32_t *)0xff000030))
/* Product Register */
#define PRR (*((volatile uint32_t *)0xff000044))
#ifdef FX9860G
/* mpu_detect() - detect the underlying MPU
Many thanks to Simon Lothar for relevant documentation.
Processor Version Register (PVR) and Product Version Register (PRR) provide
info for SH-4-based MPUS; SH-3 based boards are detected and distinguished
by testing writable bits in the Port L Control Register (PLCR).
Returns the detected MPU type, falling back on mpu_unknown */
GSECTION(".pretext")
static int mpu_detect(void)
{
#define PLCR SH7705_PFC.PLCR
/* Detect SH-3-based MPUs by testing writable bits in PLCR */
uint16_t old = PLCR;
PLCR = 0xffff;
uint16_t tested = PLCR;
PLCR = old;
if(tested == 0x00ff) return HWMPU_SH7337;
if(tested == 0x0fff) return HWMPU_SH7355;
/* Check that we're dealing with an SH-4-based MPU */
if((PVR & 0xffffff00) != 0x10300b00) return HWMPU_UNKNOWN;
/* Tell SH-4 MPUs by testing the product version register */
uint32_t ver = PRR & 0xfffffff0;
if(ver == 0x00002c00) return HWMPU_SH7305;
if(ver == 0x00002200) return HWMPU_SH7724;
return HWMPU_UNKNOWN;
#undef PLCR
}
/* hw_detect(): Basic hardware detection */
GSECTION(".pretext")
void hw_detect(void)
{
gint[HWMPU] = mpu_detect();
if(isSH4())
{
gint[HWCPUVR] = PVR;
gint[HWCPUPR] = PRR;
}
/* Detect RAM by checking if 8804'0000 is the same as 8800'0000. */
volatile uint8_t *R4 = (void *)0x88040000;
volatile uint8_t *R0 = (void *)0x88000000;
/* Make backups */
uint8_t b0 = *R0;
uint8_t b4 = *R4;
/* Check if setting a different value in *R4 affects *R0. If not, then
we have extended RAM. */
*R4 = ~b0;
int ext = (*R0 == b0);
/* Restore backups */
*R0 = b0;
*R4 = b4;
gint[HWRAM] = ext ? (512 << 10) : (256 << 10);
/* Will be detected later on */
gint[HWURAM] = -1;
/* Traditionally 4 MiB, Graph 35+E II has 8 MiB */
gint[HWROM] = (gint[HWCALC] == HWCALC_G35PE2) ? (4 << 20) : (8 << 20);
}
#endif /* FX9860G */
#ifdef FXCG50
/* hw_detect(): Basic hardware detection */
GSECTION(".pretext")
void hw_detect(void)
{
gint[HWMPU] = HWMPU_SH7305;
gint[HWCPUVR] = PVR;
gint[HWCPUPR] = PRR;
/* Tell Prizms apart from fx-CG 50 by checking the stack address*/
uint32_t stack;
__asm__("mov r15, %0" : "=r"(stack));
gint[HWCALC] = (stack < 0x8c160000) ? HWCALC_PRIZM : HWCALC_FXCG50;
/* Basic memory information */
gint[HWRAM] = (2 << 20);
gint[HWROM] = (32 << 20);
}
#endif /* FXCG50 */

View file

@ -3,6 +3,7 @@
//---
#include <core/mmu.h>
#include <gint/hardware.h>
//---
// SH7705 TLB
@ -48,6 +49,9 @@ void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
if(p_rom) *p_rom = rom;
if(p_ram) *p_ram = ram;
gint[HWMMU] = HW_LOADED;
gint[HWURAM] = ram;
}
#endif
@ -92,4 +96,7 @@ void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
if(p_rom) *p_rom = rom;
if(p_ram) *p_ram = ram;
gint[HWMMU] = HW_LOADED | HWMMU_UTLB;
gint[HWURAM] = ram;
}

View file

@ -1,90 +0,0 @@
//---
// gint:core:mpu - Runtime MPU detection
//---
#include <gint/mpu.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <gint/defs/util.h>
/* Holds information about the current MPU */
GBSS const platform_t gint_platform;
/* This function is only used on fx9860g because all fxcg50 are SH4 */
#ifdef FX9860G
/* mpu_detect() - detect the underlying MPU
Many thanks to Simon Lothar for relevant documentation.
Processor Version Register (PVR) and Product Version Register (PRR) provide
info for SH-4-based MPUS; SH-3 based boards are detected and distinguished
by testing writable bits in the Port L Control Register (PLCR).
Returns the detected MPU type, falling back on mpu_unknown */
GSECTION(".pretext")
static mpu_t mpu_detect(void)
{
/* Processor Version Register */
volatile uint32_t *pvr = (void *)0xff000030;
/* Product Version Register */
volatile uint32_t *prr = (void *)0xff000044;
/* Port L Control Register */
volatile uint16_t *plcr = (void *)0xa4000114;
/* Detecting SH-3-based MPUs by testing writable bits in PLCR */
uint16_t old = *plcr;
*plcr = 0xffff;
uint16_t tested = *plcr;
*plcr = old;
if(tested == 0x00ff) return mpu_sh7337;
if(tested == 0x0fff) return mpu_sh7355;
/* Check that we're dealing with an SH-4-based MPU */
if((*pvr & 0xffffff00) != 0x10300b00) return mpu_unknown;
/* Tell SH-4 MPUs by testing the product version register */
uint32_t ver = *prr & 0xfffffff0;
if(ver == 0x00002c00) return mpu_sh7305;
if(ver == 0x00002200) return mpu_sh7724;
return mpu_unknown;
}
/* mpu_init() - detect and save information about the underlying MPU */
GSECTION(".pretext")
void mpu_init(GUNUSED uint32_t stack)
{
const_cast(gint_platform.mpu, mpu_t) = mpu_detect();
/* Detect additional RAM */
volatile uint8_t *after_ram = (void *)0x88040000;
volatile uint8_t *start_ram = (void *)0x88000000;
uint8_t backup = *after_ram;
*after_ram = ~backup;
int ext = (*start_ram == backup);
*after_ram = backup;
const_cint(gint_platform.extended_ram) = ext;
}
#endif /* FX9860G */
#ifdef FXCG50
/* mpu_init() - detect and save information about the underlying MPU */
GSECTION(".pretext")
void mpu_init(uint32_t stack)
{
const_cast(gint_platform.mpu, mpu_t) = mpu_sh7305;
/* Detect Prizm models */
int prizm = (stack < 0x8c160000);
const_cint(gint_platform.prizm) = prizm;
}
#endif /* FXCG50 */

View file

@ -6,7 +6,7 @@
#include <gint/drivers.h>
#include <core/std.h>
#include <core/setup.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <gint/mpu/intc.h>
/* VBR address, from the linker script */

View file

@ -1,5 +1,5 @@
//---
// gint:core:start - Kernel initialisation and C runtime
// gint:core:start - Kernel initialization and C runtime
//--
#include <gint/defs/attributes.h>
@ -8,7 +8,7 @@
#include <core/mmu.h>
#include <gint/drivers.h>
#include <gint/gint.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
/* Symbols provided by the linker script. For sections:
- l* represents the load address (source address in ROM)
@ -114,9 +114,7 @@ int start(int isappli, int optnum)
processor with some incompatible features */
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */
uint32_t stack;
__asm__("mov r15, %0" : "=r"(stack));
mpu_init(stack);
hw_detect();
/* Load data sections and wipe the bss section. This has to be done
first for static and global variables to be initialized */

View file

@ -1,4 +1,4 @@
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <gint/mpu/dma.h>
#include <gint/mpu/power.h>
#include <gint/mpu/intc.h>
@ -116,6 +116,8 @@ static void init(void)
/* Unmask the DMA0 interrupt */
INTC.MSKCLR->IMR1 = 0x01;
gint[HWDMA] = HW_LOADED;
}
//---

View file

@ -10,7 +10,7 @@
#include <gint/drivers/iokbd.h>
#include <gint/defs/attributes.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
//---
// Keyboard buffer
@ -183,6 +183,9 @@ static void init(void)
timer_setup(tid, delay, 0, callback, NULL);
timer_start(tid);
gint[HWKBD] = HW_LOADED | (isSH3() ? HWKBD_IO : HWKBD_KSI);
gint[HWKBDSF] = KEYBOARD_SCAN_FREQUENCY;
}
/* unload() - stop the support timer */

View file

@ -3,6 +3,7 @@
//---
#include <gint/defs/types.h>
#include <gint/hardware.h>
#include <gint/drivers.h>
#include <gint/dma.h>
#include <core/std.h>
@ -260,6 +261,19 @@ static void ctx_restore(void *buf)
r61524_win_set(ctx->HSA, ctx->HEA, ctx->VSA, ctx->VEA);
}
//---
// Driver initialization
//---
static void init(void)
{
select(device_code_read);
uint16_t devname = read();
gint[HWDD] = HW_LOADED | HWDD_FULL;
if(devname == 0x1524) gint[HWDD] |= HWDD_KNOWN;
}
//---
// Driver status string
//---
@ -285,7 +299,7 @@ static const char *r61524_status(void)
gint_driver_t drv_r61524 = {
.name = "R61524",
.init = NULL,
.init = init,
.status = GINT_DRIVER_STATUS(r61524_status),
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx,

View file

@ -7,7 +7,7 @@
#include <gint/gint.h>
#include <gint/defs/types.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <gint/mpu/rtc.h>
//---
@ -150,6 +150,8 @@ static void init(void)
/* Disable the periodic interrupt for now, but give it priority 5 */
RTC->RCR2.PES = RTC_NONE;
gint_intlevel(isSH3() ? 3 : 40, 5);
gint[HWRTC] = HW_LOADED | HWRTC_TIMER;
}
//---

View file

@ -1,5 +1,5 @@
#include <gint/defs/attributes.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <stddef.h>
#include <stdint.h>

View file

@ -7,7 +7,7 @@
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <gint/mpu.h>
#include <gint/hardware.h>
#include <gint/syscalls.h>
@ -222,6 +222,15 @@ static void ctx_restore(void *buf)
command(reg_counter, cnt);
}
//---
// Driver initialization
//---
static void init(void)
{
gint[HWDD] = HW_LOADED | HWDD_LIGHT;
}
//---
// Driver status string
//---
@ -243,7 +252,7 @@ static const char *t6k11_status(void)
gint_driver_t drv_t6k11 = {
.name = "T6K11",
.init = NULL,
.init = init,
.status = GINT_DRIVER_STATUS(t6k11_status),
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx,

View file

@ -155,6 +155,7 @@ uint32_t timer_delay(int tid, uint64_t delay_us)
uint64_t freq = clock->Pphi_f >> 2;
/* fxcg50: Calculated = 29491200 but it's too low */
/* TODO: Account for down spread spectrum in the CPG */
// uint64_t freq = 29020000 >> 2;
/* Extra timers all run at 32768 Hz */
@ -262,7 +263,7 @@ static void driver_sh3(void)
TSTR = (void *)0xfffffe92;
}
#endif
#endif /* FX9860G */
static void init(void)
{
@ -365,6 +366,22 @@ static void init(void)
SH7305_INTC.MSKCLR->IMR6 = 0x18;
SH7305_INTC.MSKCLR->IMR8 = 0x02;
}
/* Record details in gint's hardware information interface */
gint[HWTMU] = HW_LOADED;
gint[HWETMU] = HW_LOADED | (isSH3() ? HWETMU_1 : HWETMU_6);
for(int i = 3; i < timer_count(); i++)
{
tmu_extra_t *t = timers[i].tmu;
int v = !(t->TCOR + 1)
&& !(t->TCNT + 1)
&& !(t->TSTR)
&& !(t->TCR.UNF)
&& !(t->TCR.UNIE);
gint[HWETMU] |= v << (i - 1);
}
}
//---