Index: trunk/i386/libsaio/cpu.c =================================================================== --- trunk/i386/libsaio/cpu.c (revision 2823) +++ trunk/i386/libsaio/cpu.c (revision 2824) @@ -251,7 +251,7 @@ } // Bronya C1E fix -void post_startup_cpu_fixups(void) +static void post_startup_cpu_fixups(void) { /* * Some AMD processors support C1E state. Entering this state will @@ -272,6 +272,90 @@ } /* + * Large memcpy() into MMIO space can take longer than 1 clock tick (55ms). + * The timer interrupt must remain responsive when updating VRAM so + * as not to miss timer interrupts during countdown(). + * + * If interrupts are enabled, use normal memcpy. + * + * If interrupts are disabled, breaks memcpy down + * into 128K chunks, times itself and makes a bios + * real-mode call every 25 msec in order to service + * pending interrupts. + * + * -- zenith432, May 22nd, 2016 + */ +void* memcpy_interruptible(void* dst, const void* src, size_t len) +{ + uint64_t tscFreq, lastTsc; + uint32_t eflags, threshold; + ptrdiff_t offset; + const size_t chunk = 131072U; // 128K + + if (len <= chunk) + { + /* + * Short memcpy - use normal. + */ + return memcpy(dst, src, len); + } + + __asm__ volatile("pushfl; popl %0" : "=r"(eflags)); + if (eflags & 0x200U) + { + /* + * Interrupts are enabled - use normal memcpy. + */ + return memcpy(dst, src, len); + } + + tscFreq = Platform.CPU.TSCFrequency; + if ((uint32_t) (tscFreq >> 32)) + { + /* + * If TSC Frequency >= 2 ** 32, use a default time threshold. + */ + threshold = (~0U) / 40U; + } + else if (!(uint32_t) tscFreq) + { + /* + * If early on and TSC Frequency hasn't been estimated yet, + * use normal memcpy. + */ + return memcpy(dst, src, len); + } + else + { + threshold = ((uint32_t) tscFreq) / 40U; + } + + /* + * Do the work + */ + offset = 0; + lastTsc = rdtsc64(); + do + { + (void) memcpy((char*) dst + offset, (const char*) src + offset, chunk); + offset += (ptrdiff_t) chunk; + len -= chunk; + if ((rdtsc64() - lastTsc) < threshold) + { + continue; + } + (void) readKeyboardStatus(); // visit real-mode + lastTsc = rdtsc64(); + } + while (len > chunk); + if (len) + { + (void) memcpy((char*) dst + offset, (const char*) src + offset, len); + } + return dst; +} + +/* * Calculates the FSB and CPU frequencies using specific MSRs for each CPU * - multi. is read from a specific MSR. In the case of Intel, there is: * a max multi. (used to calculate the FSB freq.), Index: trunk/i386/libsaio/biosfn.c =================================================================== --- trunk/i386/libsaio/biosfn.c (revision 2823) +++ trunk/i386/libsaio/biosfn.c (revision 2824) @@ -1078,5 +1078,6 @@ bb.intno = 0x15; bb.eax.rr = 0x2401; bios(&bb); +// return !bb.flags.cf; } */ Index: trunk/i386/libsaio/fake_efi.c =================================================================== --- trunk/i386/libsaio/fake_efi.c (revision 2823) +++ trunk/i386/libsaio/fake_efi.c (revision 2824) @@ -759,12 +759,14 @@ */ if (PMRepeatCount) { - --PMRepeatCount; - continue; // jb 0x17e55 (retry) + --PMRepeatCount; + continue; // jb 0x17e55 (retry) } } else + { PMRepeatCount = 0xffff; + } cpuTick = (EFI_UINT32) getCPUTick(); // callq 0x121a7 // printf("value: 0x%x\n", getCPUTick()); Index: trunk/i386/boot2/gui.c =================================================================== --- trunk/i386/boot2/gui.c (revision 2823) +++ trunk/i386/boot2/gui.c (revision 2824) @@ -1308,9 +1308,10 @@ static inline void vramwrite (void *data, int width, int height) { + extern void* memcpy_interruptible(void*, const void*, size_t); if (VIDEO (depth) == 32 && VIDEO (rowBytes) == gui.backbuffer->width * 4) { - memcpy((uint8_t *)vram, gui.backbuffer->pixels, VIDEO (rowBytes)*VIDEO (height)); + memcpy_interruptible((uint8_t *)vram, gui.backbuffer->pixels, VIDEO (rowBytes)*VIDEO (height)); } else { Index: branches/zenith432/i386/libsaio/cpu.c =================================================================== --- branches/zenith432/i386/libsaio/cpu.c (revision 2823) +++ branches/zenith432/i386/libsaio/cpu.c (revision 2824) @@ -251,7 +251,7 @@ } // Bronya C1E fix -void post_startup_cpu_fixups(void) +static void post_startup_cpu_fixups(void) { /* * Some AMD processors support C1E state. Entering this state will @@ -272,6 +272,90 @@ } /* + * Large memcpy() into MMIO space can take longer than 1 clock tick (55ms). + * The timer interrupt must remain responsive when updating VRAM so + * as not to miss timer interrupts during countdown(). + * + * If interrupts are enabled, use normal memcpy. + * + * If interrupts are disabled, breaks memcpy down + * into 128K chunks, times itself and makes a bios + * real-mode call every 25 msec in order to service + * pending interrupts. + * + * -- zenith432, May 22nd, 2016 + */ +void* memcpy_interruptible(void* dst, const void* src, size_t len) +{ + uint64_t tscFreq, lastTsc; + uint32_t eflags, threshold; + ptrdiff_t offset; + const size_t chunk = 131072U; // 128K + + if (len <= chunk) + { + /* + * Short memcpy - use normal. + */ + return memcpy(dst, src, len); + } + + __asm__ volatile("pushfl; popl %0" : "=r"(eflags)); + if (eflags & 0x200U) + { + /* + * Interrupts are enabled - use normal memcpy. + */ + return memcpy(dst, src, len); + } + + tscFreq = Platform.CPU.TSCFrequency; + if ((uint32_t) (tscFreq >> 32)) + { + /* + * If TSC Frequency >= 2 ** 32, use a default time threshold. + */ + threshold = (~0U) / 40U; + } + else if (!(uint32_t) tscFreq) + { + /* + * If early on and TSC Frequency hasn't been estimated yet, + * use normal memcpy. + */ + return memcpy(dst, src, len); + } + else + { + threshold = ((uint32_t) tscFreq) / 40U; + } + + /* + * Do the work + */ + offset = 0; + lastTsc = rdtsc64(); + do + { + (void) memcpy((char*) dst + offset, (const char*) src + offset, chunk); + offset += (ptrdiff_t) chunk; + len -= chunk; + if ((rdtsc64() - lastTsc) < threshold) + { + continue; + } + (void) readKeyboardStatus(); // visit real-mode + lastTsc = rdtsc64(); + } + while (len > chunk); + if (len) + { + (void) memcpy((char*) dst + offset, (const char*) src + offset, len); + } + return dst; +} + +/* * Calculates the FSB and CPU frequencies using specific MSRs for each CPU * - multi. is read from a specific MSR. In the case of Intel, there is: * a max multi. (used to calculate the FSB freq.), Index: branches/zenith432/i386/libsaio/biosfn.c =================================================================== --- branches/zenith432/i386/libsaio/biosfn.c (revision 2823) +++ branches/zenith432/i386/libsaio/biosfn.c (revision 2824) @@ -1078,5 +1078,6 @@ bb.intno = 0x15; bb.eax.rr = 0x2401; bios(&bb); +// return !bb.flags.cf; } */ Index: branches/zenith432/i386/libsaio/fake_efi.c =================================================================== --- branches/zenith432/i386/libsaio/fake_efi.c (revision 2823) +++ branches/zenith432/i386/libsaio/fake_efi.c (revision 2824) @@ -759,12 +759,14 @@ */ if (PMRepeatCount) { - --PMRepeatCount; - continue; // jb 0x17e55 (retry) + --PMRepeatCount; + continue; // jb 0x17e55 (retry) } } else + { PMRepeatCount = 0xffff; + } cpuTick = (EFI_UINT32) getCPUTick(); // callq 0x121a7 // printf("value: 0x%x\n", getCPUTick()); Index: branches/zenith432/i386/boot2/gui.c =================================================================== --- branches/zenith432/i386/boot2/gui.c (revision 2823) +++ branches/zenith432/i386/boot2/gui.c (revision 2824) @@ -1308,9 +1308,10 @@ static inline void vramwrite (void *data, int width, int height) { + extern void* memcpy_interruptible(void*, const void*, size_t); if (VIDEO (depth) == 32 && VIDEO (rowBytes) == gui.backbuffer->width * 4) { - memcpy((uint8_t *)vram, gui.backbuffer->pixels, VIDEO (rowBytes)*VIDEO (height)); + memcpy_interruptible((uint8_t *)vram, gui.backbuffer->pixels, VIDEO (rowBytes)*VIDEO (height)); } else {