#include #include #include #ifdef PISTORM #ifndef PISTORM32 #define PS_PROTOCOL_IMPL #include "pistorm/ps_protocol.h" #endif #endif extern struct List ICache[EMU68_HASHSIZE]; void M68K_LoadContext(struct M68KState *ctx); void M68K_SaveContext(struct M68KState *ctx); static inline void CallARMCode() { register void *ARM asm("x12"); asm volatile("":"=r"(ARM)); void (*ptr)() = (void*)ARM; ptr(); } static inline struct M68KTranslationUnit *FindUnit() { register uint16_t *PC asm("x18"); /* Perform search */ uint32_t hash = (uint32_t)(uintptr_t)PC; struct List *bucket = &ICache[(hash >> EMU68_HASHSHIFT) & EMU68_HASHMASK]; struct M68KTranslationUnit *node; /* Go through the list of translated units */ ForeachNode(bucket, node) { /* Force reload of PC*/ asm volatile("":"=r"(PC)); /* Check if unit is found */ if (node->mt_M68kAddress == PC) { #if 0 /* Move node to front of the list */ REMOVE(&node->mt_HashNode); ADDHEAD(bucket, &node->mt_HashNode); #elif 0 struct Node *prev = node->mt_HashNode.ln_Pred; struct Node *succ = node->mt_HashNode.ln_Succ; struct Node *prevprev = prev->ln_Pred; /* If node is not head, then move it one level up */ if (prevprev != NULL) { node->mt_HashNode.ln_Pred = prevprev; node->mt_HashNode.ln_Succ = prev; prevprev->ln_Succ = &node->mt_HashNode; prev->ln_Succ = succ; prev->ln_Pred = &node->mt_HashNode; succ->ln_Pred = prev; } #endif return node; } } return NULL; } #ifdef PISTORM #ifndef PISTORM32 extern volatile unsigned char bus_lock; static inline int GetIPLLevel() { volatile uint32_t *gpio = (void *)0xf2200000; *(gpio + 7) = LE32(REG_STATUS << PIN_A0); *(gpio + 7) = LE32(1 << PIN_RD); *(gpio + 7) = LE32(1 << PIN_RD); *(gpio + 7) = LE32(1 << PIN_RD); *(gpio + 7) = LE32(1 << PIN_RD); unsigned int value = LE32(*(gpio + 13)); *(gpio + 10) = LE32(0xffffec); return (value >> 21) & 7; } #endif #else static inline int GetIPLLevel() { return 0; } #endif static inline uint16_t *getLastPC() { uint16_t *lastPC; asm volatile("mrs %0, TPIDR_EL1":"=r"(lastPC)); return lastPC; } static inline struct M68KState *getCTX() { struct M68KState *ctx; asm volatile("mrs %0, TPIDRRO_EL0":"=r"(ctx)); return ctx; } static inline uint32_t getSR() { uint32_t sr; asm volatile("mrs %0, TPIDR_EL0":"=r"(sr)); return sr; } static inline void setLastPC(uint16_t *pc) { asm volatile("msr TPIDR_EL1, %0"::"r"(pc)); } static inline void setSR(uint32_t sr) { asm volatile("msr TPIDR_EL0, %0"::"r"(sr)); } void MainLoop() { register uint16_t *PC asm("x18"); register void *ARM asm("x12"); uint16_t *LastPC; struct M68KState *ctx = getCTX(); M68K_LoadContext(ctx); asm volatile("mov v28.d[0], xzr"); /* The JIT loop is running forever */ while(1) { /* Load m68k context and last used PC counter into temporary register */ LastPC = getLastPC(); ctx = getCTX(); /* If (unlikely) there was interrupt pending, check if it needs to be processed */ if (unlikely(ctx->INT32 != 0)) { uint32_t SR, SRcopy; int level = 0; uint32_t vector; uint32_t vbr; /* Find out requested IPL level based on ARM state and real IPL line */ if (ctx->INT.ARM_err) { level = 7; ctx->INT.ARM_err = 0; } else { if (ctx->INT.ARM) { level = 6; ctx->INT.ARM = 0; } #ifdef PISTORM32 /* On PiStorm32 IPL level is obtained by second CPU core from the GPIO directly */ if (ctx->INT.IPL > level) { level = ctx->INT.IPL; } #else /* On classic pistorm we need to obtain IPL from PiStorm status register */ if (ctx->INT.IPL) { int ipl_level; #if PISTORM_WRITE_BUFFER while(__atomic_test_and_set(&bus_lock, __ATOMIC_ACQUIRE)) { asm volatile("yield"); } #endif ipl_level = GetIPLLevel(); #if PISTORM_WRITE_BUFFER __atomic_clear(&bus_lock, __ATOMIC_RELEASE); #endif /* Obtained IPL level higher than until now detected? */ if (ipl_level > level) { level = ipl_level; } } #endif } /* Get SR and test the IPL mask value */ SR = getSR(); int IPL_mask = (SR & SR_IPL) >> SRB_IPL; /* Any unmasked interrupts? Proceess them */ if (level == 7 || level > IPL_mask) { register uint64_t sp asm("r29"); if (likely((SR & SR_S) == 0)) { /* If we are not yet in supervisor mode, the USP needs to be updated */ asm volatile("mov v31.S[1], %w0"::"r"(sp)); /* Load eiter ISP or MSP */ if (unlikely((SR & SR_M) != 0)) { asm volatile("mov %w0, v31.S[3]":"=r"(sp)); } else { asm volatile("mov %w0, v31.S[2]":"=r"(sp)); } } SRcopy = SR; /* Swap C and V flags in the copy */ if ((SRcopy & 3) != 0 && (SRcopy & 3) != 3) SRcopy ^= 3; vector = 0x60 + (level << 2); /* Set supervisor mode */ SR |= SR_S; /* Clear Trace mode */ SR &= ~(SR_T0 | SR_T1); /* Insert current level into SR */ SR &= ~SR_IPL; SR |= ((level & 7) << SRB_IPL); /* Push exception frame */ asm volatile("strh %w1, [%0, #-8]!":"=r"(sp):"r"(SRcopy),"0"(sp)); asm volatile("str %w1, [%0, #2]"::"r"(sp),"r"(PC)); asm volatile("strh %w1, [%0, #6]"::"r"(sp),"r"(vector)); /* Set SR */ setSR(SR); /* Get VBR */ vbr = ctx->VBR; /* Load PC */ asm volatile("ldr %w0, [%1, %2]":"=r"(PC):"r"(vbr),"r"(vector)); } /* All interrupts masked or new PC loaded and stack swapped, continue with code execution */ } /* Check if JIT cache is enabled */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (likely(cacr & CACR_IE)) { /* Force reload of PC*/ asm volatile("":"=r"(PC)); /* The last PC is the same as currently set PC? */ if (LastPC == PC) { asm volatile("":"=r"(ARM)); /* Jump to the code now */ CallARMCode(); continue; } else { /* Find unit in the hashtable based on the PC value */ struct M68KTranslationUnit *node = FindUnit(); /* Unit exists ? */ if (node != NULL) { /* Store m68k PC of corresponding ARM code in TPIDR_EL1 */ asm volatile("msr TPIDR_EL1, %0"::"r"(PC)); /* This is the case, load entry point into x12 */ ARM = node->mt_ARMEntryPoint; asm volatile("":"=r"(ARM):"0"(ARM)); CallARMCode(); /* Go back to beginning of the loop */ continue; } /* If we are that far there was no JIT unit found */ asm volatile("":"=r"(PC)); uint16_t *copyPC = PC; M68K_SaveContext(ctx); /* Get the code. This never fails */ node = M68K_GetTranslationUnit(copyPC); /* Load CPU context */ M68K_LoadContext(getCTX()); asm volatile("msr TPIDR_EL1, %0"::"r"(PC)); /* Prepare ARM pointer in x12 and call it */ ARM = node->mt_ARMEntryPoint; asm volatile("":"=r"(ARM):"0"(ARM)); CallARMCode(); } } else { struct M68KTranslationUnit *node = NULL; /* Uncached mode - reset LastPC */ setLastPC((void*)~(0)); /* Save context since C code will be called */ M68K_SaveContext(ctx); /* Find the unit */ node = FindUnit(); /* If node is found verify it */ if (likely(node != NULL)) { node = M68K_VerifyUnit(node); } /* If node was not found or invalidated, translate code */ if (unlikely(node == NULL)) { /* Get the code */ node = M68K_GetTranslationUnit((uint16_t *)(uintptr_t)getCTX()->PC); } M68K_LoadContext(getCTX()); ARM = node->mt_ARMEntryPoint; asm volatile("":"=r"(ARM):"0"(ARM)); CallARMCode(); } } }